Rizin
unix-like reverse engineering framework and cli tools
agraph.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2014-2020 pancake
2 // SPDX-FileCopyrightText: 2014-2020 ret2libc
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 #include <rz_core.h>
6 #include <rz_cons.h>
8 #include <ht_pu.h>
9 #include <ctype.h>
10 #include <limits.h>
11 #include "core_private.h"
12 
13 static int mousemode = 0;
14 static int disMode = 0;
15 static int discroll = 0;
16 static bool graphCursor = false;
17 static const char *mousemodes[] = {
18  "canvas-y",
19  "canvas-x",
20  "node-y",
21  "node-x",
22  NULL
23 };
24 
25 #define GRAPH_MERGE_FEATURE 0
26 
27 #define BORDER 3
28 #define BORDER_WIDTH 4
29 #define BORDER_HEIGHT 3
30 #define MARGIN_TEXT_X 2
31 #define MARGIN_TEXT_Y 2
32 #define HORIZONTAL_NODE_SPACING 4
33 #define VERTICAL_NODE_SPACING 2
34 #define MIN_NODE_WIDTH 22
35 #define MIN_NODE_HEIGHT BORDER_HEIGHT
36 #define TITLE_LEN 128
37 #define DEFAULT_SPEED 1
38 #define PAGEKEY_SPEED (h / 2)
39 /* 15 */
40 #define MINIGRAPH_NODE_TEXT_CUR "<@@@@@@>"
41 #define MINIGRAPH_NODE_MIN_WIDTH 12
42 #define MINIGRAPH_NODE_TITLE_LEN 4
43 #define MINIGRAPH_NODE_CENTER_X 3
44 #define MININODE_MIN_WIDTH 16
45 
46 #define ZOOM_STEP 10
47 #define ZOOM_DEFAULT 100
48 
49 #define BODY_OFFSETS 0x1
50 #define BODY_SUMMARY 0x2
51 #define BODY_COMMENTS 0x4
52 
53 #define NORMALIZE_MOV(x) ((x) < 0 ? -1 : ((x) > 0 ? 1 : 0))
54 
55 /* don't use macros for this */
56 #define get_anode(gn) ((gn) ? (RzANode *)(gn)->data : NULL)
57 
58 #define graph_foreach_anode(list, it, pos, anode) \
59  if (list) \
60  for ((it) = (list)->head; (it) && ((pos) = (it)->data) && (pos) && ((anode) = (RzANode *)(pos)->data); (it) = (it)->n)
61 
62 struct len_pos_t {
63  int len;
64  int pos;
65 };
66 
67 struct dist_t {
68  const RzGraphNode *from;
69  const RzGraphNode *to;
70  int dist;
71 };
72 
73 struct g_cb {
77  void *data;
78 };
79 
80 typedef struct ascii_edge_t {
83  RzList *x, *y;
86 
87 struct layer_t {
88  int n_nodes;
90  int position;
91  int height;
92  int width;
93  int gap;
94 };
95 
101  int fs;
102 };
103 
105  int x;
106  int y;
107 };
108 
109 #define G(x, y) rz_cons_canvas_gotoxy(g->can, x, y)
110 #define W(x) rz_cons_canvas_write(g->can, x)
111 #define F(x, y, x2, y2, c) rz_cons_canvas_fill(g->can, x, y, x2, y2, c)
112 
113 static bool is_offset(const RzAGraph *g) {
114  return g->mode == RZ_AGRAPH_MODE_OFFSET;
115 }
116 
117 static bool is_mini(const RzAGraph *g) {
118  return g->mode == RZ_AGRAPH_MODE_MINI;
119 }
120 
121 static bool is_tiny(const RzAGraph *g) {
122  return g->is_tiny || g->mode == RZ_AGRAPH_MODE_TINY;
123 }
124 
125 static bool is_summary(const RzAGraph *g) {
126  return g->mode == RZ_AGRAPH_MODE_SUMMARY;
127 }
128 
129 static bool is_comments(const RzAGraph *g) {
130  return g->mode == RZ_AGRAPH_MODE_COMMENTS;
131 }
132 
133 static int next_mode(int mode) {
134  return (mode + 1) % RZ_AGRAPH_MODE_MAX;
135 }
136 
137 static int prev_mode(int mode) {
138  return (mode + RZ_AGRAPH_MODE_MAX - 1) % RZ_AGRAPH_MODE_MAX;
139 }
140 
141 static RzGraphNode *agraph_get_title(const RzAGraph *g, RzANode *n, bool in) {
142  if (!n) {
143  return NULL;
144  }
145  if (n->title && *n->title) {
146  return n->gnode;
147  }
148  const RzList *outnodes = in ? n->gnode->in_nodes : n->gnode->out_nodes;
149  RzGraphNode *gn;
150  RzListIter *iter;
151 
152  rz_list_foreach (outnodes, iter, gn) {
153  RzANode *an = gn->data;
154  return agraph_get_title(g, an, in);
155  }
156  return NULL;
157 }
158 
159 static int mode2opts(const RzAGraph *g) {
160  int opts = 0;
161  if (is_offset(g)) {
162  opts |= BODY_OFFSETS;
163  }
164  if (is_comments(g)) {
165  opts |= BODY_COMMENTS;
166  }
167  if (is_summary(g)) {
168  opts |= BODY_SUMMARY;
169  }
170  return opts;
171 }
172 
173 // duplicated from visual.c
174 static void rotateAsmemu(RzCore *core) {
175  const bool isEmuStr = rz_config_get_i(core->config, "emu.str");
176  const bool isEmu = rz_config_get_i(core->config, "asm.emu");
177  if (isEmu) {
178  if (isEmuStr) {
179  rz_config_set(core->config, "emu.str", "false");
180  } else {
181  rz_config_set(core->config, "asm.emu", "false");
182  }
183  } else {
184  rz_config_set(core->config, "emu.str", "true");
185  }
186 }
187 
188 static void showcursor(RzCore *core, int x) {
189  if (!x) {
190  int wheel = rz_config_get_i(core->config, "scr.wheel");
191  if (wheel) {
192  rz_cons_enable_mouse(true);
193  }
194  } else {
195  rz_cons_enable_mouse(false);
196  }
198 }
199 
200 static char *get_title(ut64 addr) {
201  return rz_str_newf("0x%" PFMT64x, addr);
202 }
203 
204 static void agraph_node_free(RzANode *n) {
205  free(n->title);
206  free(n->body);
207  free(n);
208 }
209 
210 static int agraph_refresh(struct agraph_refresh_data *grd);
211 
212 static void update_node_dimension(const RzGraph *g, int is_mini, int zoom, int edgemode, bool callgraph, int layout) {
213  const RzList *nodes = rz_graph_get_nodes(g);
214  RzGraphNode *gn;
215  RzListIter *it;
216  RzANode *n;
217  graph_foreach_anode (nodes, it, gn, n) {
218  if (is_mini) {
219  n->h = 1;
221  } else if (n->is_mini) {
222  n->h = 1;
223  n->w = MININODE_MIN_WIDTH;
224  } else {
225  n->w = rz_str_bounds(n->body, (int *)&n->h);
226  ut32 len = strlen(n->title) + MARGIN_TEXT_X;
227  if (len > INT_MAX) {
228  len = INT_MAX;
229  }
230  if (len > n->w) {
231  n->w = len;
232  }
233  // n->w = n->w; //RZ_MIN (n->w, (int)len);
234  n->w += BORDER_WIDTH;
235  n->h += BORDER_HEIGHT;
236  /* scale node by zoom */
237  n->w = RZ_MAX(MIN_NODE_WIDTH, (n->w * zoom) / 100);
238  n->h = RZ_MAX(MIN_NODE_HEIGHT, (n->h * zoom) / 100);
239 
240  if (edgemode == 2 && !callgraph) {
241  if (!layout) {
242  n->w = RZ_MAX(n->w, (rz_list_length(n->gnode->out_nodes) * 2 + 1) + RZ_EDGES_X_INC * 2);
243  n->w = RZ_MAX(n->w, (rz_list_length(n->gnode->in_nodes) * 2 + 1) + RZ_EDGES_X_INC * 2);
244  } else {
245  n->h = RZ_MAX(n->h, (rz_list_length(n->gnode->out_nodes) + 1) + RZ_EDGES_X_INC);
246  n->h = RZ_MAX(n->h, (rz_list_length(n->gnode->in_nodes) + 1) + RZ_EDGES_X_INC);
247  }
248  }
249  }
250  }
251 }
252 
253 static void append_shortcut(const RzAGraph *g, char *title, char *nodetitle, int left) {
254  const char *shortcut = sdb_const_get(g->db, sdb_fmt("agraph.nodes.%s.shortcut", nodetitle), 0);
255  if (shortcut) {
256  if (g->can->color) {
257  // XXX: do not hardcode color here
258  strncat(title, sdb_fmt(Color_YELLOW "[o%s]" Color_RESET, shortcut), left);
259  } else {
260  strncat(title, sdb_fmt("[o%s]", shortcut), left);
261  }
262  }
263 }
264 
265 static void mini_RzANode_print(const RzAGraph *g, const RzANode *n, int cur, bool details) {
266  char title[TITLE_LEN];
267  int x, delta_x = 0;
268 
269  if (!G(n->x + MINIGRAPH_NODE_CENTER_X, n->y) &&
270  !G(n->x + MINIGRAPH_NODE_CENTER_X + n->w, n->y)) {
271  return;
272  }
273 
274  x = n->x + MINIGRAPH_NODE_CENTER_X + g->can->sx;
275  if (x < 0) {
276  delta_x = -x;
277  }
278  if (!G(n->x + MINIGRAPH_NODE_CENTER_X + delta_x, n->y)) {
279  return;
280  }
281 
282  if (details) {
283  if (cur) {
284  W(&MINIGRAPH_NODE_TEXT_CUR[delta_x]);
285  (void)G(-g->can->sx, -g->can->sy + 2);
286  snprintf(title, sizeof(title) - 1,
287  "[ %s ]", n->title);
288  W(title);
289  if (discroll > 0) {
290  char *body = rz_str_ansi_crop(n->body, 0, discroll, -1, -1);
291  (void)G(-g->can->sx, -g->can->sy + 3);
292  W(body);
293  free(body);
294  } else {
295  (void)G(-g->can->sx, -g->can->sy + 3);
296  W(n->body);
297  }
298  } else {
299  char *str = "____";
300  if (n->title) {
301  int l = strlen(n->title);
302  str = n->title;
303  if (l > MINIGRAPH_NODE_TITLE_LEN) {
305  }
306  }
307  if (g->can->color) {
308  snprintf(title, sizeof(title) - 1, "%s__%s__", Color_RESET, str);
309  } else {
310  snprintf(title, sizeof(title) - 1, "__%s__", str);
311  }
312  append_shortcut(g, title, n->title, sizeof(title) - strlen(title) - 1);
313  W(rz_str_ansi_crop(title, delta_x, 0, 20, 1));
314  }
315  } else {
316  snprintf(title, sizeof(title) - 1,
317  cur ? "[ %s ]" : " %s ", n->title);
318  W(title);
319  }
320  return;
321 }
322 
323 static void tiny_RzANode_print(const RzAGraph *g, const RzANode *n, int cur) {
324  G(n->x, n->y);
325  RzCons *cons = rz_cons_singleton();
326  char *circle = cons->use_utf8 ? UTF_CIRCLE : "()";
327  if (cur) {
328  W("##");
329  } else {
330  W(circle);
331  }
332 }
333 
334 static char *get_node_color(int color, int cur) {
335  RzCons *cons = rz_cons_singleton();
336  if (color == -1) {
337  return cur ? cons->context->pal.graph_box2 : cons->context->pal.graph_box;
338  }
339  return color ? (
341  : cons->context->pal.diff_new)
342  : cons->context->pal.diff_unknown;
343 }
344 
345 static void normal_RzANode_print(const RzAGraph *g, const RzANode *n, int cur) {
346  ut32 center_x = 0, center_y = 0;
347  ut32 delta_x = 0, delta_txt_x = 0;
348  ut32 delta_y = 0, delta_txt_y = 0;
349  char title[TITLE_LEN];
350  char *body;
351  int x, y;
352  int color = n->difftype;
353  const bool showTitle = g->show_node_titles;
354  const bool showBody = g->show_node_body;
355 
356  x = n->x + g->can->sx;
357  y = n->y + g->can->sy;
358  if (x + MARGIN_TEXT_X < 0) {
359  delta_x = -(x + MARGIN_TEXT_X);
360  }
361  if (x + n->w < -MARGIN_TEXT_X) {
362  return;
363  }
364  if (y < -1) {
365  delta_y = RZ_MIN(n->h - BORDER_HEIGHT - 1, -y - MARGIN_TEXT_Y);
366  }
367  /* print the title */
368  if (showTitle) {
369  if (cur) {
370  snprintf(title, sizeof(title) - 1, "[%s]", n->title);
371  } else {
372  char *color = g->can->color ? Color_RESET : "";
373  snprintf(title, sizeof(title) - 1, " %s%s ", color, n->title);
374  append_shortcut(g, title, n->title, sizeof(title) - strlen(title) - 1);
375  }
376  if ((delta_x < strlen(title)) && G(n->x + MARGIN_TEXT_X + delta_x, n->y + 1)) {
377  char *res = rz_str_ansi_crop(title, delta_x, 0, n->w - BORDER_WIDTH, 1);
378  W(res);
379  free(res);
380  }
381  }
382 
383  /* print the body */
384  if (g->zoom > ZOOM_DEFAULT) {
385  center_x = (g->zoom - ZOOM_DEFAULT) / 10;
386  center_y = (g->zoom - ZOOM_DEFAULT) / 30;
387  delta_txt_x = RZ_MIN(delta_x, center_x);
388  delta_txt_y = RZ_MIN(delta_y, center_y);
389  }
390  if (showBody) {
391  if (G(n->x + MARGIN_TEXT_X + delta_x + center_x - delta_txt_x,
392  n->y + MARGIN_TEXT_Y + delta_y + center_y - delta_txt_y)) {
393  ut32 body_x = center_x >= delta_x ? 0 : delta_x - center_x;
394  ut32 body_y = center_y >= delta_y ? 0 : delta_y - center_y;
395  ut32 body_h = BORDER_HEIGHT >= n->h ? 1 : n->h - BORDER_HEIGHT;
396 
397  if (g->zoom < ZOOM_DEFAULT) {
398  body_h--;
399  }
400  if (body_y + 1 <= body_h) {
401  body = rz_str_ansi_crop(n->body,
402  body_x, body_y,
403  n->w - BORDER_WIDTH,
404  body_h);
405  if (body) {
406  W(body);
407  if (g->zoom < ZOOM_DEFAULT) {
408  W("\n");
409  }
410  free(body);
411  } else {
412  W(n->body);
413  }
414  }
415  /* print some dots when the body is cropped because of zoom */
416  if (n->body && *n->body) {
417  if (body_y <= body_h && g->zoom < ZOOM_DEFAULT) {
418  char *dots = "...";
419  if (delta_x < strlen(dots)) {
420  dots += delta_x;
421  W(dots);
422  }
423  }
424  }
425  }
426  }
427 
428  // TODO: check if node is traced or not and show proper color
429  // This info must be stored inside RzANode* from RzCore*
430  rz_cons_canvas_box(g->can, n->x, n->y, n->w, n->h, get_node_color(color, cur));
431 }
432 
433 static int **get_crossing_matrix(const RzGraph *g,
434  const struct layer_t layers[],
435  int maxlayer, int i, int from_up,
436  int *n_rows) {
437  int j, len = layers[i].n_nodes;
438 
439  int **m = RZ_NEWS0(int *, len);
440  if (!m) {
441  return NULL;
442  }
443  for (j = 0; j < len; j++) {
444  m[j] = RZ_NEWS0(int, len);
445  if (!m[j]) {
446  goto err_row;
447  }
448  }
449  /* calculate crossings between layer i and layer i-1 */
450  /* consider the crossings generated by each pair of edges */
451  if (i > 0 && from_up) {
452  if (rz_cons_is_breaked()) {
453  goto err_row;
454  }
455  for (j = 0; j < layers[i - 1].n_nodes; j++) {
456  const RzGraphNode *gj = layers[i - 1].nodes[j];
457  const RzList *neigh = rz_graph_get_neighbours(g, gj);
458  RzGraphNode *gk;
459  RzListIter *itk;
460 
461  rz_list_foreach (neigh, itk, gk) {
462  int s;
463  // skip self-loop
464  if (gj == gk) {
465  continue;
466  }
467  for (s = 0; s < j; s++) {
468  const RzGraphNode *gs = layers[i - 1].nodes[s];
469  const RzList *neigh_s = rz_graph_get_neighbours(g, gs);
470  RzGraphNode *gt;
471  RzListIter *itt;
472 
473  rz_list_foreach (neigh_s, itt, gt) {
474  const RzANode *ak, *at; /* k and t should be "indexes" on layer i */
475  if (gt == gk || gt == gs) {
476  continue;
477  }
478  ak = get_anode(gk);
479  at = get_anode(gt);
480  if (ak->layer != i || at->layer != i) {
481  // this should never happen
482  // but it happens if we do graph.dummy = false, so better hide it for now
483 #if 0
484  eprintf ("(WARNING) \"%s\" (%d) or \"%s\" (%d) are not on the right layer (%d)\n",
485  ak->title, ak->layer,
486  at->title, at->layer,
487  i);
488 #endif
489  continue;
490  }
491  m[ak->pos_in_layer][at->pos_in_layer]++;
492  }
493  }
494  }
495  }
496  }
497 
498  /* calculate crossings between layer i and layer i+1 */
499  if (i < maxlayer - 1 && !from_up) {
500  if (rz_cons_is_breaked()) {
501  goto err_row;
502  }
503  for (j = 0; j < layers[i].n_nodes; j++) {
504  const RzGraphNode *gj = layers[i].nodes[j];
505  const RzList *neigh = rz_graph_get_neighbours(g, gj);
506  const RzANode *ak, *aj = get_anode(gj);
507  RzGraphNode *gk;
508  RzListIter *itk;
509 
510  if (rz_cons_is_breaked()) {
511  goto err_row;
512  }
513  graph_foreach_anode (neigh, itk, gk, ak) {
514  int s;
515  for (s = 0; s < layers[i].n_nodes; s++) {
516  const RzGraphNode *gs = layers[i].nodes[s];
517  const RzList *neigh_s;
518  RzGraphNode *gt;
519  RzListIter *itt;
520  const RzANode *at, *as = get_anode(gs);
521 
522  if (gs == gj) {
523  continue;
524  }
525  neigh_s = rz_graph_get_neighbours(g, gs);
526  graph_foreach_anode (neigh_s, itt, gt, at) {
527  if (at->pos_in_layer < ak->pos_in_layer) {
528  m[aj->pos_in_layer][as->pos_in_layer]++;
529  }
530  }
531  }
532  }
533  }
534  }
535 
536  if (n_rows) {
537  *n_rows = len;
538  }
539  return m;
540 
541 err_row:
542  for (i = 0; i < len; i++) {
543  free(m[i]);
544  }
545  free(m);
546  return NULL;
547 }
548 
549 static int layer_sweep(const RzGraph *g, const struct layer_t layers[],
550  int maxlayer, int i, int from_up) {
551  RzGraphNode *u, *v;
552  const RzANode *au, *av;
553  int n_rows, j, changed = false;
554  int len = layers[i].n_nodes;
555 
556  int **cross_matrix = get_crossing_matrix(g, layers, maxlayer, i, from_up, &n_rows);
557  if (!cross_matrix) {
558  return -1; // ERROR HAPPENS
559  }
560 
561  for (j = 0; j < len - 1; j++) {
562  int auidx, avidx;
563 
564  u = layers[i].nodes[j];
565  v = layers[i].nodes[j + 1];
566  au = get_anode(u);
567  av = get_anode(v);
568  auidx = au->pos_in_layer;
569  avidx = av->pos_in_layer;
570 
571  if (cross_matrix[auidx][avidx] > cross_matrix[avidx][auidx]) {
572  /* swap elements */
573  layers[i].nodes[j] = v;
574  layers[i].nodes[j + 1] = u;
575  changed = true;
576  }
577  }
578 
579  /* update position in the layer of each node. During the swap of some
580  * elements we didn't swap also the pos_in_layer because the cross_matrix
581  * is indexed by it, so do it now! */
582  for (j = 0; j < layers[i].n_nodes; j++) {
583  RzANode *n = get_anode(layers[i].nodes[j]);
584  n->pos_in_layer = j;
585  }
586 
587  for (j = 0; j < n_rows; j++) {
588  free(cross_matrix[j]);
589  }
590  free(cross_matrix);
591  return changed;
592 }
593 
594 static void view_cyclic_edge(const RzGraphEdge *e, const RzGraphVisitor *vis) {
595  const RzAGraph *g = (RzAGraph *)vis->data;
596  RzGraphEdge *new_e = RZ_NEW0(RzGraphEdge);
597  if (!new_e) {
598  return;
599  }
600  new_e->from = e->from;
601  new_e->to = e->to;
602  new_e->nth = e->nth;
603  rz_list_append(g->back_edges, new_e);
604 }
605 
606 static void view_dummy(const RzGraphEdge *e, const RzGraphVisitor *vis) {
607  const RzANode *a = get_anode(e->from);
608  const RzANode *b = get_anode(e->to);
609  RzList *long_edges = (RzList *)vis->data;
610  if (!a || !b) {
611  return;
612  }
613  if (RZ_ABS(a->layer - b->layer) > 1) {
614  RzGraphEdge *new_e = RZ_NEW0(RzGraphEdge);
615  if (!new_e) {
616  return;
617  }
618  new_e->from = e->from;
619  new_e->to = e->to;
620  new_e->nth = e->nth;
621  rz_list_append(long_edges, new_e);
622  }
623 }
624 
625 /* find a set of edges that, removed, makes the graph acyclic */
626 /* invert the edges identified in the previous step */
627 static void remove_cycles(RzAGraph *g) {
628  RzGraphVisitor cyclic_vis = {
629  NULL, NULL, NULL, NULL, NULL, NULL
630  };
631  const RzGraphEdge *e;
632  const RzListIter *it;
633 
634  g->back_edges = rz_list_newf(free);
636  cyclic_vis.data = g;
637  rz_graph_dfs(g->graph, &cyclic_vis);
638 
639  rz_list_foreach (g->back_edges, it, e) {
640  RzANode *from = e->from ? get_anode(e->from) : NULL;
641  RzANode *to = e->to ? get_anode(e->to) : NULL;
642  if (from && to) {
644  rz_agraph_add_edge_at(g, to, from, e->nth);
645  }
646  }
647 }
648 
649 static void add_sorted(RzGraphNode *n, RzGraphVisitor *vis) {
650  RzList *l = (RzList *)vis->data;
651  rz_list_prepend(l, n);
652 }
653 
654 /* assign a layer to each node of the graph.
655  *
656  * It visits the nodes of the graph in the topological sort, so that every time
657  * you visit a node, you can be sure that you have already visited all nodes
658  * that can lead to that node and thus you can easily compute the layer based
659  * on the layer of these "parent" nodes. */
660 static void assign_layers(const RzAGraph *g) {
661  RzGraphVisitor layer_vis = {
662  NULL, NULL, NULL, NULL, NULL, NULL
663  };
664  const RzGraphNode *gn;
665  const RzListIter *it;
666  RzANode *n;
667  RzList *topological_sort = rz_list_new();
668 
669  layer_vis.data = topological_sort;
671  rz_graph_dfs(g->graph, &layer_vis);
672 
673  graph_foreach_anode (topological_sort, it, gn, n) {
674  const RzList *innodes = rz_graph_innodes(g->graph, gn);
675  RzListIter *it;
676  RzGraphNode *prev;
677  RzANode *preva;
678 
679  n->layer = 0;
680  graph_foreach_anode (innodes, it, prev, preva) {
681  if (preva->layer + 1 > n->layer) {
682  n->layer = preva->layer + 1;
683  }
684  }
685  }
686 
687  rz_list_free(topological_sort);
688 }
689 
690 static int find_edge(const RzGraphEdge *a, const RzGraphEdge *b) {
691  return a->from == b->to && a->to == b->from ? 0 : 1;
692 }
693 
694 static bool is_reversed(const RzAGraph *g, const RzGraphEdge *e) {
695  return (bool)rz_list_find(g->back_edges, e, (RzListComparator)find_edge);
696 }
697 
698 /* add dummy nodes when there are edges that span multiple layers */
700  if (!g->dummy) {
701  return;
702  }
703  RzGraphVisitor dummy_vis = {
704  NULL, NULL, NULL, NULL, NULL, NULL
705  };
706  const RzListIter *it;
707  const RzGraphEdge *e;
708 
709  g->long_edges = rz_list_newf((RzListFree)free);
710  dummy_vis.data = g->long_edges;
713  rz_graph_dfs(g->graph, &dummy_vis);
714 
715  rz_list_foreach (g->long_edges, it, e) {
716  RzANode *from = get_anode(e->from);
717  RzANode *to = get_anode(e->to);
718  int diff_layer = RZ_ABS(from->layer - to->layer);
719  RzANode *prev = get_anode(e->from);
720  int i, nth = e->nth;
721 
723  for (i = 1; i < diff_layer; i++) {
724  RzANode *dummy = rz_agraph_add_node(g, NULL, NULL);
725  if (!dummy) {
726  return;
727  }
728  dummy->is_dummy = true;
729  dummy->layer = from->layer + i;
730  dummy->is_reversed = is_reversed(g, e);
731  dummy->w = 1;
732  rz_agraph_add_edge_at(g, prev, dummy, nth);
733  rz_list_append(g->dummy_nodes, dummy);
734 
735  prev = dummy;
736  nth = -1;
737  }
738  rz_graph_add_edge(g->graph, prev->gnode, e->to);
739  }
740 }
741 
742 /* create layers and assign an initial ordering of the nodes into them */
743 static void create_layers(RzAGraph *g) {
744  const RzList *nodes = rz_graph_get_nodes(g->graph);
745  RzGraphNode *gn;
746  const RzListIter *it;
747  RzANode *n;
748  int i;
749 
750  /* identify max layer */
751  g->n_layers = 0;
752  graph_foreach_anode (nodes, it, gn, n) {
753  if (n->layer > g->n_layers) {
754  g->n_layers = n->layer;
755  }
756  }
757 
758  /* create a starting ordering of nodes for each layer */
759  g->n_layers++;
760  if (sizeof(struct layer_t) * g->n_layers < g->n_layers) {
761  return;
762  }
763  g->layers = RZ_NEWS0(struct layer_t, g->n_layers);
764 
765  graph_foreach_anode (nodes, it, gn, n) {
766  g->layers[n->layer].n_nodes++;
767  }
768 
769  for (i = 0; i < g->n_layers; i++) {
770  if (sizeof(RzGraphNode *) * g->layers[i].n_nodes < g->layers[i].n_nodes) {
771  continue;
772  }
773  g->layers[i].nodes = RZ_NEWS0(RzGraphNode *,
774  1 + g->layers[i].n_nodes);
775  g->layers[i].position = 0;
776  }
777  graph_foreach_anode (nodes, it, gn, n) {
778  n->pos_in_layer = g->layers[n->layer].position;
779  g->layers[n->layer].nodes[g->layers[n->layer].position++] = gn;
780  }
781 }
782 
783 /* layer-by-layer sweep */
784 /* it permutes each layer, trying to find the best ordering for each layer
785  * to minimize the number of crossing edges */
786 static void minimize_crossings(const RzAGraph *g) {
787  int i, cross_changed, max_changes = 4096;
788 
789  do {
790  cross_changed = false;
791  max_changes--;
792 
793  for (i = 0; i < g->n_layers; i++) {
794  int rc = layer_sweep(g->graph, g->layers, g->n_layers, i, true);
795  if (rc == -1) {
796  return;
797  }
798  cross_changed |= !!rc;
799  }
800  } while (cross_changed && max_changes);
801 
802  max_changes = 4096;
803 
804  do {
805  cross_changed = false;
806  max_changes--;
807 
808  for (i = g->n_layers - 1; i >= 0; i--) {
809  int rc = layer_sweep(g->graph, g->layers, g->n_layers, i, false);
810  if (rc == -1) {
811  return;
812  }
813  cross_changed |= !!rc;
814  }
815  } while (cross_changed && max_changes);
816 }
817 
818 static int find_dist(const struct dist_t *a, const struct dist_t *b) {
819  return a->from == b->from && a->to == b->to ? 0 : 1;
820 }
821 
822 /* returns the distance between two nodes */
823 /* if the distance between two nodes were explicitly set, returns that;
824  * otherwise calculate the distance of two nodes on the same layer */
825 static int dist_nodes(const RzAGraph *g, const RzGraphNode *a, const RzGraphNode *b) {
826  struct dist_t d;
827  const RzANode *aa, *ab;
828  RzListIter *it;
829  int res = 0;
830 
831  if (g->dists) {
832  d.from = a;
833  d.to = b;
834  it = rz_list_find(g->dists, &d, (RzListComparator)find_dist);
835  if (it) {
836  struct dist_t *old = (struct dist_t *)rz_list_iter_get_data(it);
837  return old->dist;
838  }
839  }
840 
841  aa = get_anode(a);
842  ab = get_anode(b);
843  if (aa && ab && aa->layer == ab->layer) {
844  int i;
845 
846  res = aa == ab && !aa->is_reversed ? HORIZONTAL_NODE_SPACING : 0;
847  for (i = aa->pos_in_layer; i < ab->pos_in_layer; i++) {
848  const RzGraphNode *cur = g->layers[aa->layer].nodes[i];
849  const RzGraphNode *next = g->layers[aa->layer].nodes[i + 1];
850  const RzANode *anext = get_anode(next);
851  const RzANode *acur = get_anode(cur);
852  int found = false;
853 
854  if (g->dists) {
855  d.from = cur;
856  d.to = next;
857  it = rz_list_find(g->dists, &d, (RzListComparator)find_dist);
858  if (it) {
859  struct dist_t *old = (struct dist_t *)rz_list_iter_get_data(it);
860  res += old->dist;
861  found = true;
862  }
863  }
864 
865  if (acur && anext && !found) {
866  int space = HORIZONTAL_NODE_SPACING;
867  if (acur->is_reversed && anext->is_reversed) {
868  if (!acur->is_reversed) {
869  res += acur->w / 2;
870  } else if (!anext->is_reversed) {
871  res += anext->w / 2;
872  }
873  res += 1;
874  } else {
875  res += acur->w / 2 + anext->w / 2 + space;
876  }
877  }
878  }
879  }
880 
881  return res;
882 }
883 
884 /* explicitly set the distance between two nodes on the same layer */
885 static void set_dist_nodes(const RzAGraph *g, int l, int cur, int next) {
886  struct dist_t *d, find_el;
887  const RzGraphNode *vi, *vip;
888  const RzANode *avi, *avip;
889  RzListIter *it;
890 
891  if (!g->dists) {
892  return;
893  }
894  vi = g->layers[l].nodes[cur];
895  vip = g->layers[l].nodes[next];
896  avi = get_anode(vi);
897  avip = get_anode(vip);
898 
899  find_el.from = vi;
900  find_el.to = vip;
901  it = rz_list_find(g->dists, &find_el, (RzListComparator)find_dist);
902  d = it ? (struct dist_t *)rz_list_iter_get_data(it) : RZ_NEW0(struct dist_t);
903 
904  d->from = vi;
905  d->to = vip;
906  d->dist = (avip && avi) ? avip->x - avi->x : 0;
907  if (!it) {
908  rz_list_push(g->dists, d);
909  }
910 }
911 
912 static int is_valid_pos(const RzAGraph *g, int l, int pos) {
913  return pos >= 0 && pos < g->layers[l].n_nodes;
914 }
915 
916 static void free_vertical_nodes_kv(HtPPKv *kv) {
917  rz_list_free(kv->value);
918 }
919 
920 /* computes the set of vertical classes in the graph */
921 /* if v is an original node, L(v) = { v }
922  * if v is a dummy node, L(v) is the set of all the dummies node that belongs
923  * to the same long edge */
924 static HtPP *compute_vertical_nodes(const RzAGraph *g) {
925  HtPPOptions ht_opt = { 0 };
926  ht_opt.freefn = free_vertical_nodes_kv;
927  HtPP *res = ht_pp_new_opt(&ht_opt);
928  if (!res) {
929  return NULL;
930  }
931  for (int i = 0; i < g->n_layers; i++) {
932  for (int j = 0; j < g->layers[i].n_nodes; j++) {
933  RzGraphNode *gn = g->layers[i].nodes[j];
934  const RzList *Ln = ht_pp_find(res, gn, NULL);
935  const RzANode *an = get_anode(gn);
936 
937  if (!Ln) {
938  RzList *vert = rz_list_new();
939  ht_pp_insert(res, gn, vert);
940  if (an->is_dummy) {
941  RzGraphNode *next = gn;
942  const RzANode *anext = get_anode(next);
943 
944  while (anext->is_dummy) {
945  rz_list_append(vert, next);
946  next = rz_graph_nth_neighbour(g->graph, next, 0);
947  if (!next) {
948  break;
949  }
950  anext = get_anode(next);
951  }
952  } else {
953  rz_list_append(vert, gn);
954  }
955  }
956  }
957  }
958 
959  return res;
960 }
961 
962 /* computes left or right classes, used to place dummies node */
963 /* classes respect three properties:
964  * - v E C
965  * - w E C => L(v) is a subset of C
966  * - w E C, the s+(w) exists and is not in any class yet => s+(w) E C */
967 static RzList **compute_classes(const RzAGraph *g, HtPP *v_nodes, int is_left, int *n_classes) {
968  int i, j, c;
969  RzList **res = RZ_NEWS0(RzList *, g->n_layers);
970  RzGraphNode *gn;
971  const RzListIter *it;
972  RzANode *n;
973 
974  graph_foreach_anode (rz_graph_get_nodes(g->graph), it, gn, n) {
975  n->klass = -1;
976  }
977 
978  for (i = 0; i < g->n_layers; i++) {
979  c = i;
980 
981  for (j = is_left ? 0 : g->layers[i].n_nodes - 1;
982  (is_left && j < g->layers[i].n_nodes) || (!is_left && j >= 0);
983  j = is_left ? j + 1 : j - 1) {
984  const RzGraphNode *gj = g->layers[i].nodes[j];
985  const RzANode *aj = get_anode(gj);
986 
987  if (aj->klass == -1) {
988  const RzList *laj = ht_pp_find(v_nodes, gj, NULL);
989 
990  if (!res[c]) {
991  res[c] = rz_list_new();
992  }
993  graph_foreach_anode (laj, it, gn, n) {
994  rz_list_append(res[c], gn);
995  n->klass = c;
996  }
997  } else {
998  c = aj->klass;
999  }
1000  }
1001  }
1002 
1003  if (n_classes) {
1004  *n_classes = g->n_layers;
1005  }
1006  return res;
1007 }
1008 
1009 static int cmp_dist(const size_t a, const size_t b) {
1010  return (a < b) - (a > b);
1011 }
1012 
1013 static RzGraphNode *get_sibling(const RzAGraph *g, const RzANode *n, int is_left, int is_adjust_class) {
1014  RzGraphNode *res = NULL;
1015  int pos = n->pos_in_layer;
1016 
1017  if ((is_left && is_adjust_class) || (!is_left && !is_adjust_class)) {
1018  pos++;
1019  } else {
1020  pos--;
1021  }
1022 
1023  if (is_valid_pos(g, n->layer, pos)) {
1024  res = g->layers[n->layer].nodes[pos];
1025  }
1026  return res;
1027 }
1028 
1029 static int hash_get_int(HtPU *ht, const void *key) {
1030  bool found;
1031  int val = (int)(size_t)ht_pu_find(ht, key, &found);
1032  if (!found) {
1033  val = 0;
1034  }
1035  return val;
1036 }
1037 
1038 static int adjust_class_val(const RzAGraph *g, const RzGraphNode *gn, const RzGraphNode *sibl, HtPU *res, int is_left) {
1039  if (is_left) {
1040  return hash_get_int(res, sibl) - hash_get_int(res, gn) - dist_nodes(g, gn, sibl);
1041  }
1042  return hash_get_int(res, gn) - hash_get_int(res, sibl) - dist_nodes(g, sibl, gn);
1043 }
1044 
1045 /* adjusts the position of previously placed left/right classes */
1046 /* tries to place classes as close as possible */
1047 static void adjust_class(const RzAGraph *g, int is_left, RzList **classes, HtPU *res, int c) {
1048  const RzGraphNode *gn;
1049  const RzListIter *it;
1050  const RzANode *an;
1051  int dist = INT_MAX, v, is_first = true;
1052 
1053  graph_foreach_anode (classes[c], it, gn, an) {
1054  const RzGraphNode *sibling;
1055  const RzANode *sibl_anode;
1056 
1057  sibling = get_sibling(g, an, is_left, true);
1058  if (!sibling) {
1059  continue;
1060  }
1061  sibl_anode = get_anode(sibling);
1062  if (sibl_anode->klass == c) {
1063  continue;
1064  }
1065  v = adjust_class_val(g, gn, sibling, res, is_left);
1066  dist = is_first ? v : RZ_MIN(dist, v);
1067  is_first = false;
1068  }
1069 
1070  if (is_first) {
1071  RzList *heap = rz_list_new();
1072  int len;
1073 
1074  graph_foreach_anode (classes[c], it, gn, an) {
1075  const RzList *neigh = rz_graph_all_neighbours(g->graph, gn);
1076  const RzGraphNode *gk;
1077  const RzListIter *itk;
1078  const RzANode *ak;
1079 
1080  graph_foreach_anode (neigh, itk, gk, ak) {
1081  if (ak->klass < c) {
1082  size_t d = (ak->x - an->x);
1083  if (d > 0) {
1084  rz_list_append(heap, (void *)d);
1085  }
1086  }
1087  }
1088  }
1089 
1090  len = rz_list_length(heap);
1091  if (len == 0) {
1092  dist = 0;
1093  } else {
1095  dist = (int)(size_t)rz_list_get_n(heap, len / 2);
1096  }
1097 
1098  rz_list_free(heap);
1099  }
1100 
1101  graph_foreach_anode (classes[c], it, gn, an) {
1102  const int old_val = hash_get_int(res, gn);
1103  const int new_val = is_left ? old_val + dist : old_val - dist;
1104  ht_pu_update(res, gn, (ut64)(size_t)new_val);
1105  }
1106 }
1107 
1108 static int place_nodes_val(const RzAGraph *g, const RzGraphNode *gn, const RzGraphNode *sibl, HtPU *res, int is_left) {
1109  if (is_left) {
1110  return hash_get_int(res, sibl) + dist_nodes(g, sibl, gn);
1111  }
1112  return hash_get_int(res, sibl) - dist_nodes(g, gn, sibl);
1113 }
1114 
1115 static int place_nodes_sel_p(int newval, int oldval, int is_first, int is_left) {
1116  if (is_first) {
1117  return newval;
1118  }
1119  if (is_left) {
1120  return RZ_MAX(oldval, newval);
1121  }
1122  return RZ_MIN(oldval, newval);
1123 }
1124 
1125 /* places left/right the nodes of a class */
1126 static void place_nodes(const RzAGraph *g, const RzGraphNode *gn, int is_left, HtPP *v_nodes, RzList **classes, HtPU *res, SetP *placed) {
1127  const RzList *lv = ht_pp_find(v_nodes, gn, NULL);
1128  int p = 0, v, is_first = true;
1129  const RzGraphNode *gk;
1130  const RzListIter *itk;
1131  const RzANode *ak;
1132 
1133  graph_foreach_anode (lv, itk, gk, ak) {
1134  const RzGraphNode *sibling;
1135  const RzANode *sibl_anode;
1136 
1137  sibling = get_sibling(g, ak, is_left, false);
1138  if (!sibling) {
1139  continue;
1140  }
1141  sibl_anode = get_anode(sibling);
1142  if (ak->klass == sibl_anode->klass) {
1143  if (!set_p_contains(placed, sibling)) {
1144  place_nodes(g, sibling, is_left, v_nodes, classes, res, placed);
1145  }
1146 
1147  v = place_nodes_val(g, gk, sibling, res, is_left);
1148  p = place_nodes_sel_p(v, p, is_first, is_left);
1149  is_first = false;
1150  }
1151  }
1152 
1153  if (is_first) {
1154  p = is_left ? 0 : 50;
1155  }
1156 
1157  graph_foreach_anode (lv, itk, gk, ak) {
1158  ht_pu_update(res, gk, (ut64)(size_t)p);
1159  set_p_add(placed, gk);
1160  }
1161 }
1162 
1163 /* computes the position to the left/right of all the nodes */
1164 static HtPU *compute_pos(const RzAGraph *g, int is_left, HtPP *v_nodes) {
1165  int n_classes, i;
1166 
1167  RzList **classes = compute_classes(g, v_nodes, is_left, &n_classes);
1168  if (!classes) {
1169  return NULL;
1170  }
1171 
1172  HtPUOptions pu_opt = { 0 };
1173  HtPPOptions pp_opt = { 0 };
1174  HtPU *res = ht_pu_new_opt(&pu_opt);
1175  SetP *placed = (SetP *)ht_pp_new_opt(&pp_opt);
1176  if (!res || !placed) {
1177  ht_pu_free(res);
1178  set_p_free(placed);
1179  return NULL;
1180  }
1181  for (i = 0; i < n_classes; i++) {
1182  const RzGraphNode *gn;
1183  const RzListIter *it;
1184 
1185  rz_list_foreach (classes[i], it, gn) {
1186  if (!set_p_contains(placed, gn)) {
1187  place_nodes(g, gn, is_left, v_nodes, classes, res, placed);
1188  }
1189  }
1190 
1191  adjust_class(g, is_left, classes, res, i);
1192  }
1193 
1194  set_p_free(placed);
1195  for (i = 0; i < n_classes; i++) {
1196  if (classes[i]) {
1198  }
1199  }
1200  free(classes);
1201  return res;
1202 }
1203 
1204 /* calculates position of all nodes, but in particular dummies nodes */
1205 /* computes two different placements (called "left"/"right") and set the final
1206  * position of each node to the average of the values in the two placements */
1207 static void place_dummies(const RzAGraph *g) {
1208  const RzList *nodes;
1209  const RzGraphNode *gn;
1210  const RzListIter *it;
1211  RzANode *n;
1212 
1213  HtPP *vertical_nodes = compute_vertical_nodes(g);
1214  if (!vertical_nodes) {
1215  return;
1216  }
1217  HtPU *xminus = compute_pos(g, true, vertical_nodes);
1218  if (!xminus) {
1219  goto xminus_err;
1220  }
1221  HtPU *xplus = compute_pos(g, false, vertical_nodes);
1222  if (!xplus) {
1223  goto xplus_err;
1224  }
1225 
1226  nodes = rz_graph_get_nodes(g->graph);
1227  graph_foreach_anode (nodes, it, gn, n) {
1228  n->x = (hash_get_int(xminus, gn) + hash_get_int(xplus, gn)) / 2;
1229  }
1230 
1231  ht_pu_free(xplus);
1232 xplus_err:
1233  ht_pu_free(xminus);
1234 xminus_err:
1235  ht_pp_free(vertical_nodes);
1236 }
1237 
1239  const RzANode *an = get_anode(n);
1240  if (!an) {
1241  return NULL;
1242  }
1243  int k, layer = an->layer;
1244 
1245  for (k = an->pos_in_layer + 1; k < g->layers[layer].n_nodes; k++) {
1246  RzGraphNode *gk = g->layers[layer].nodes[k];
1247  const RzANode *ak = get_anode(gk);
1248  if (!ak) {
1249  break;
1250  }
1251 
1252  if (ak->is_dummy) {
1253  return gk;
1254  }
1255  }
1256  return NULL;
1257 }
1258 
1259 static void adjust_directions(const RzAGraph *g, int i, int from_up, HtPU *D, HtPU *P) {
1260  const RzGraphNode *vm = NULL, *wm = NULL;
1261  const RzANode *vma = NULL, *wma = NULL;
1262  int j, d = from_up ? 1 : -1;
1263 
1264  if (i + d < 0 || i + d >= g->n_layers) {
1265  return;
1266  }
1267  for (j = 0; j < g->layers[i + d].n_nodes; j++) {
1268  const RzGraphNode *wp, *vp = g->layers[i + d].nodes[j];
1269  const RzANode *wpa, *vpa = get_anode(vp);
1270 
1271  if (!vpa || !vpa->is_dummy) {
1272  continue;
1273  }
1274  if (from_up) {
1275  wp = rz_list_get_n(rz_graph_innodes(g->graph, vp), 0);
1276  } else {
1277  wp = rz_graph_nth_neighbour(g->graph, vp, 0);
1278  }
1279  wpa = get_anode(wp);
1280  if (!wpa || !wpa->is_dummy) {
1281  continue;
1282  }
1283  if (vm) {
1284  int p = hash_get_int(P, wm);
1285  int k;
1286 
1287  for (k = wma->pos_in_layer + 1; k < wpa->pos_in_layer; k++) {
1288  const RzGraphNode *w = g->layers[wma->layer].nodes[k];
1289  const RzANode *aw = get_anode(w);
1290  if (aw && aw->is_dummy) {
1291  p &= hash_get_int(P, w);
1292  }
1293  }
1294  if (p) {
1295  ht_pu_update(D, vm, (ut64)(size_t)from_up);
1296  for (k = vma->pos_in_layer + 1; k < vpa->pos_in_layer; k++) {
1297  const RzGraphNode *v = g->layers[vma->layer].nodes[k];
1298  const RzANode *av = get_anode(v);
1299  if (av && av->is_dummy) {
1300  ht_pu_update(D, v, (ut64)(size_t)from_up);
1301  }
1302  }
1303  }
1304  }
1305  vm = vp;
1306  wm = wp;
1307  vma = get_anode(vm);
1308  wma = get_anode(wm);
1309  }
1310 }
1311 
1312 /* find a placement for a single node */
1313 static void place_single(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int va) {
1314  const RzGraphNode *gk, *v = g->layers[l].nodes[va];
1315  const RzANode *ak;
1316  RzANode *av = get_anode(v);
1317  if (!av) {
1318  return;
1319  }
1320  const RzListIter *itk;
1321 
1322  const RzList *neigh = from_up
1323  ? rz_graph_innodes(g->graph, v)
1324  : rz_graph_get_neighbours(g->graph, v);
1325 
1326  int len = rz_list_length(neigh);
1327  if (len == 0) {
1328  return;
1329  }
1330 
1331  int sum_x = 0;
1332  graph_foreach_anode (neigh, itk, gk, ak) {
1333  if (ak->is_reversed) {
1334  len--;
1335  continue;
1336  }
1337  sum_x += ak->x;
1338  }
1339 
1340  if (len == 0) {
1341  return;
1342  }
1343  if (av) {
1344  av->x = sum_x / len;
1345  }
1346  if (bm) {
1347  const RzANode *bma = get_anode(bm);
1348  av->x = RZ_MAX(av->x, bma->x + dist_nodes(g, bm, v));
1349  }
1350  if (bp) {
1351  const RzANode *bpa = get_anode(bp);
1352  av->x = RZ_MIN(av->x, bpa->x - dist_nodes(g, v, bp));
1353  }
1354 }
1355 
1356 static int RM_listcmp(const struct len_pos_t *a, const struct len_pos_t *b) {
1357  return (a->pos < b->pos) - (a->pos > b->pos);
1358 }
1359 
1360 static int RP_listcmp(const struct len_pos_t *a, const struct len_pos_t *b) {
1361  return (a->pos > b->pos) - (a->pos < b->pos);
1362 }
1363 
1364 static void collect_changes(const RzAGraph *g, int l, const RzGraphNode *b, int from_up, int s, int e, RzList *list, int is_left) {
1365  const RzGraphNode *vt = g->layers[l].nodes[e - 1];
1366  const RzGraphNode *vtp = g->layers[l].nodes[s];
1367  struct len_pos_t *cx;
1368  int i;
1369 
1371 
1372  for (i = is_left ? s : e - 1; (is_left && i < e) || (!is_left && i >= s); i = is_left ? i + 1 : i - 1) {
1373  const RzGraphNode *v, *vi = g->layers[l].nodes[i];
1374  const RzANode *av, *avi = get_anode(vi);
1375  const RzList *neigh;
1376  const RzListIter *it;
1377  int c = 0;
1378 
1379  if (!avi) {
1380  continue;
1381  }
1382  neigh = from_up
1383  ? rz_graph_innodes(g->graph, vi)
1384  : rz_graph_get_neighbours(g->graph, vi);
1385 
1386  graph_foreach_anode (neigh, it, v, av) {
1387  if ((is_left && av->x >= avi->x) || (!is_left && av->x <= avi->x)) {
1388  c++;
1389  } else {
1390  cx = RZ_NEW(struct len_pos_t);
1391  c--;
1392  cx->len = 2;
1393  cx->pos = av->x;
1394  if (is_left) {
1395  cx->pos += dist_nodes(g, vi, vt);
1396  } else {
1397  cx->pos -= dist_nodes(g, vtp, vi);
1398  }
1399  rz_list_add_sorted(list, cx, lcmp);
1400  }
1401  }
1402 
1403  cx = RZ_NEW0(struct len_pos_t);
1404  cx->len = c;
1405  cx->pos = avi->x;
1406  if (is_left) {
1407  cx->pos += dist_nodes(g, vi, vt);
1408  } else {
1409  cx->pos -= dist_nodes(g, vtp, vi);
1410  }
1411  rz_list_add_sorted(list, cx, lcmp);
1412  }
1413 
1414  if (b) {
1415  const RzANode *ab = get_anode(b);
1416  cx = RZ_NEW(struct len_pos_t);
1417  if (cx) {
1418  cx->len = is_left ? INT_MAX : INT_MIN;
1419  cx->pos = ab->x;
1420  if (is_left) {
1421  cx->pos += dist_nodes(g, b, vt);
1422  } else {
1423  cx->pos -= dist_nodes(g, vtp, b);
1424  }
1425  rz_list_add_sorted(list, cx, lcmp);
1426  }
1427  }
1428 }
1429 
1430 static void combine_sequences(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int a, int r) {
1431  RzList *Rm = rz_list_new(), *Rp = rz_list_new();
1432  const RzGraphNode *vt, *vtp;
1433  RzANode *at, *atp;
1434  int rm, rp, t, m, i;
1435  Rm->free = (RzListFree)free;
1436  Rp->free = (RzListFree)free;
1437 
1438  t = (a + r) / 2;
1439  vt = g->layers[l].nodes[t - 1];
1440  vtp = g->layers[l].nodes[t];
1441  at = get_anode(vt);
1442  atp = get_anode(vtp);
1443 
1444  collect_changes(g, l, bm, from_up, a, t, Rm, true);
1445  collect_changes(g, l, bp, from_up, t, r, Rp, false);
1446  rm = rp = 0;
1447 
1448  m = dist_nodes(g, vt, vtp);
1449  if (at && atp) {
1450  while (atp->x - at->x < m) {
1451  if (atp->x == at->x) {
1452  int step = m / 2;
1453  at->x -= step;
1454  atp->x += m - step;
1455  } else {
1456  if (rm < rp) {
1457  if (rz_list_empty(Rm)) {
1458  at->x = atp->x - m;
1459  } else {
1460  struct len_pos_t *cx = (struct len_pos_t *)rz_list_pop(Rm);
1461  rm = rm + cx->len;
1462  at->x = RZ_MAX(cx->pos, atp->x - m);
1463  free(cx);
1464  }
1465  } else {
1466  if (rz_list_empty(Rp)) {
1467  atp->x = at->x + m;
1468  } else {
1469  struct len_pos_t *cx = (struct len_pos_t *)rz_list_pop(Rp);
1470  rp = rp + cx->len;
1471  atp->x = RZ_MIN(cx->pos, at->x + m);
1472  free(cx);
1473  }
1474  }
1475  }
1476  }
1477  }
1478 
1479  rz_list_free(Rm);
1480  rz_list_free(Rp);
1481 
1482  for (i = t - 2; i >= a; i--) {
1483  const RzGraphNode *gv = g->layers[l].nodes[i];
1484  RzANode *av = get_anode(gv);
1485  if (av && at) {
1486  av->x = RZ_MIN(av->x, at->x - dist_nodes(g, gv, vt));
1487  }
1488  }
1489 
1490  for (i = t + 1; i < r; i++) {
1491  const RzGraphNode *gv = g->layers[l].nodes[i];
1492  RzANode *av = get_anode(gv);
1493  if (av && atp) {
1494  av->x = RZ_MAX(av->x, atp->x + dist_nodes(g, vtp, gv));
1495  }
1496  }
1497 }
1498 
1499 /* places a sequence of consecutive original nodes */
1500 /* it tries to minimize the distance between each node in the sequence and its
1501  * neighbours in the "previous" layer. Those neighbours are considered as
1502  * "fixed". The previous layer depends on the direction used during the layers
1503  * traversal */
1504 static void place_sequence(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int va, int vr) {
1505  if (vr == va + 1) {
1506  place_single(g, l, bm, bp, from_up, va);
1507  } else if (vr > va + 1) {
1508  int vt = (vr + va) / 2;
1509  place_sequence(g, l, bm, bp, from_up, va, vt);
1510  place_sequence(g, l, bm, bp, from_up, vt, vr);
1511  combine_sequences(g, l, bm, bp, from_up, va, vr);
1512  }
1513 }
1514 
1515 /* finds the placements of nodes while traversing the graph in the given
1516  * direction */
1517 /* places all the sequences of consecutive original nodes in each layer. */
1518 static void original_traverse_l(const RzAGraph *g, HtPU *D, HtPU *P, int from_up) {
1519  int i, k, va, vr;
1520 
1521  for (i = from_up ? 0 : g->n_layers - 1;
1522  (from_up && i < g->n_layers) || (!from_up && i >= 0);
1523  i = from_up ? i + 1 : i - 1) {
1524  int j;
1525  const RzGraphNode *bm = NULL;
1526  const RzANode *bma = NULL;
1527 
1528  j = 0;
1529  while (j < g->layers[i].n_nodes && !bm) {
1530  const RzGraphNode *gn = g->layers[i].nodes[j];
1531  const RzANode *an = get_anode(gn);
1532  if (an && an->is_dummy) {
1533  va = 0;
1534  vr = j;
1535  bm = gn;
1536  bma = an;
1537  }
1538  j++;
1539  }
1540  if (!bm) {
1541  va = 0;
1542  vr = g->layers[i].n_nodes;
1543  }
1544  place_sequence(g, i, NULL, bm, from_up, va, vr);
1545  for (k = va; k < vr - 1; k++) {
1546  set_dist_nodes(g, i, k, k + 1);
1547  }
1548  if (is_valid_pos(g, i, vr - 1) && bm) {
1549  set_dist_nodes(g, i, vr - 1, bma->pos_in_layer);
1550  }
1551  while (bm) {
1552  const RzGraphNode *bp = get_right_dummy(g, bm);
1553  const RzANode *bpa = NULL;
1554  bma = get_anode(bm);
1555 
1556  if (!bp) {
1557  va = bma->pos_in_layer + 1;
1558  vr = g->layers[bma->layer].n_nodes;
1559  place_sequence(g, i, bm, NULL, from_up, va, vr);
1560  for (k = va; k < vr - 1; k++) {
1561  set_dist_nodes(g, i, k, k + 1);
1562  }
1563 
1564  if (is_valid_pos(g, i, va)) {
1565  set_dist_nodes(g, i, bma->pos_in_layer, va);
1566  }
1567  } else if (hash_get_int(D, bm) == from_up) {
1568  bpa = get_anode(bp);
1569  va = bma->pos_in_layer + 1;
1570  vr = bpa->pos_in_layer;
1571  place_sequence(g, i, bm, bp, from_up, va, vr);
1572  ht_pu_update(P, bm, 1);
1573  }
1574  bm = bp;
1575  }
1576  adjust_directions(g, i, from_up, D, P);
1577  }
1578 }
1579 
1580 /* computes a final position of original nodes, considering dummies nodes as
1581  * fixed */
1582 /* set the node placements traversing the graph downward and then upward */
1583 static void place_original(RzAGraph *g) {
1584  const RzList *nodes = rz_graph_get_nodes(g->graph);
1585  const RzGraphNode *gn;
1586  const RzListIter *itn;
1587  const RzANode *an;
1588  HtPUOptions opt = { 0 };
1589 
1590  HtPU *D = ht_pu_new_opt(&opt);
1591  if (!D) {
1592  return;
1593  }
1594  HtPU *P = ht_pu_new_opt(&opt);
1595  if (!P) {
1596  ht_pu_free(D);
1597  return;
1598  }
1599  g->dists = rz_list_newf((RzListFree)free);
1600  if (!g->dists) {
1601  ht_pu_free(D);
1602  ht_pu_free(P);
1603  return;
1604  }
1605 
1606  graph_foreach_anode (nodes, itn, gn, an) {
1607  if (!an->is_dummy) {
1608  continue;
1609  }
1610  const RzGraphNode *right_v = get_right_dummy(g, gn);
1611  const RzANode *right = get_anode(right_v);
1612  if (right_v && right) {
1613  ht_pu_update(D, gn, 0);
1614  int dt_eq = right->x - an->x == dist_nodes(g, gn, right_v);
1615  ht_pu_update(P, gn, (ut64)(size_t)dt_eq);
1616  }
1617  }
1618 
1619  original_traverse_l(g, D, P, true);
1620  original_traverse_l(g, D, P, false);
1621 
1622  rz_list_free(g->dists);
1623  g->dists = NULL;
1624  ht_pu_free(P);
1625  ht_pu_free(D);
1626 }
1627 
1628 #if 0
1629 static void remove_dummy_nodes(const RzAGraph *g) {
1630  const RzList *nodes = rz_graph_get_nodes (g->graph);
1631  RzGraphNode *gn;
1632  RzListIter *it;
1633  RzANode *n;
1634 
1635  graph_foreach_anode (nodes, it, gn, n) {
1636  if (n->is_dummy) {
1637  rz_graph_del_node (g->graph, gn);
1638  n->gnode = NULL;
1639  free_anode (n);
1640  }
1641  }
1642 }
1643 #endif
1644 
1645 static void set_layer_gap(RzAGraph *g) {
1646  int gap = 0;
1647  int i = 0, j = 0;
1648  RzListIter *itn;
1649  RzGraphNode *ga, *gb;
1650  RzANode *a, *b;
1651  const RzList *outnodes;
1652 
1653  g->layers[0].gap = 0;
1654  for (i = 0; i < g->n_layers; i++) {
1655  gap = 0;
1656  if (i + 1 < g->n_layers) {
1657  g->layers[i + 1].gap = gap;
1658  }
1659  for (j = 0; j < g->layers[i].n_nodes; j++) {
1660  ga = g->layers[i].nodes[j];
1661  if (!ga) {
1662  continue;
1663  }
1664  a = (RzANode *)ga->data;
1665  outnodes = ga->out_nodes;
1666 
1667  if (!outnodes || !a) {
1668  continue;
1669  }
1670  graph_foreach_anode (outnodes, itn, gb, b) {
1671  if (g->layout == 0) { // vertical layout
1672  if ((b->x != a->x) || b->layer <= a->layer) {
1673  gap += 1;
1674  if (b->layer <= a->layer) {
1675  g->layers[b->layer].gap += 1;
1676  }
1677  } else if ((!a->is_dummy && b->is_dummy) || (a->is_dummy && !b->is_dummy)) {
1678  gap += 1;
1679  }
1680  } else {
1681  if ((b->y == a->y && b->h != a->h) || b->y != a->y || b->layer <= a->layer) {
1682  gap += 1;
1683  if (b->layer <= a->layer) {
1684  g->layers[b->layer].gap += 1;
1685  }
1686  } else if ((!a->is_dummy && b->is_dummy) || (a->is_dummy && !b->is_dummy)) {
1687  gap += 1;
1688  }
1689  }
1690  }
1691  }
1692  if (i + 1 < g->n_layers) {
1693  g->layers[i + 1].gap += gap;
1694  }
1695  }
1696 }
1697 
1699  RzANode *v, *tmp = NULL;
1700  RzGraphNode *gv = NULL;
1701  RzListIter *it;
1702  int i;
1703  rz_return_if_fail(g && from && to);
1704  const RzList *neighbours = rz_graph_get_neighbours(g->graph, to->gnode);
1705  graph_foreach_anode (neighbours, it, gv, v) {
1706  tmp = v;
1707  while (tmp->is_dummy) {
1708  tmp = (RzANode *)(((RzGraphNode *)rz_list_first(tmp->gnode->out_nodes))->data);
1709  }
1710  if (tmp->gnode->idx == from->gnode->idx) {
1711  break;
1712  }
1713  tmp = NULL;
1714  }
1715  if (tmp) {
1716  tmp = v;
1717  while (tmp->gnode->idx != from->gnode->idx) {
1718  v = tmp;
1719  tmp = (RzANode *)(((RzGraphNode *)rz_list_first(v->gnode->out_nodes))->data);
1720 
1721  i = 0;
1722  while (v->gnode->idx != g->layers[v->layer].nodes[i]->idx) {
1723  i += 1;
1724  }
1725 
1726  while (i + 1 < g->layers[v->layer].n_nodes) {
1727  g->layers[v->layer].nodes[i] = g->layers[v->layer].nodes[i + 1];
1728  i++;
1729  }
1730  g->layers[v->layer].nodes[g->layers[v->layer].n_nodes - 1] = 0;
1731  g->layers[v->layer].n_nodes -= 1;
1732 
1733  rz_graph_del_node(g->graph, v->gnode);
1734  }
1735  }
1736 }
1737 
1738 static int get_edge_number(const RzAGraph *g, RzANode *src, RzANode *dst, bool outgoing) {
1739  RzListIter *itn;
1740  RzGraphNode *gv;
1741  int cur_nth = 0;
1742  int nth = 0;
1743  RzANode *v;
1744 
1745  if (outgoing && src->is_dummy) {
1746  RzANode *in = (RzANode *)(((RzGraphNode *)rz_list_first((src->gnode)->in_nodes))->data);
1747  cur_nth = get_edge_number(g, in, src, outgoing);
1748  } else {
1749  const RzList *neighbours = outgoing
1750  ? rz_graph_get_neighbours(g->graph, src->gnode)
1751  : rz_graph_innodes(g->graph, dst->gnode);
1752  const int exit_edges = rz_list_length(neighbours);
1753  graph_foreach_anode (neighbours, itn, gv, v) {
1754  cur_nth = nth;
1755  if (g->is_callgraph) {
1756  cur_nth = 0;
1757  } else if (exit_edges == 1) {
1758  cur_nth = -1;
1759  }
1760  if (outgoing && gv->idx == (dst->gnode)->idx) {
1761  break;
1762  }
1763  if (!outgoing && gv->idx == (src->gnode)->idx) {
1764  break;
1765  }
1766  nth++;
1767  }
1768  }
1769  return cur_nth;
1770 }
1771 
1772 static int count_edges(const RzAGraph *g, RzANode *src, RzANode *dst) {
1773  return get_edge_number(g, src, dst, true);
1774 }
1775 
1776 static void backedge_info(RzAGraph *g) {
1777  int i, j, k;
1778  int min, max;
1779  int inedge = 0;
1780  int outedge = 0;
1781 
1782  int **arr = RZ_NEWS0(int *, g->n_layers);
1783  if (!arr) {
1784  return;
1785  }
1786  for (i = 0; i < g->n_layers; i++) {
1787  arr[i] = RZ_NEWS0(int, 2);
1788  if (!arr[i]) {
1789  goto err;
1790  }
1791  }
1792 
1793  for (i = 0; i < g->n_layers; i++) {
1794  for (j = 0; j < g->layers[i].n_nodes; j++) {
1795  RzGraphNode *gt = g->layers[i].nodes[j];
1796  if (!gt) {
1797  continue;
1798  }
1799  RzANode *t = (RzANode *)gt->data;
1800  if (!t) {
1801  continue;
1802  }
1803  int tc = g->layout == 0 ? t->x : t->y;
1804  int tl = g->layout == 0 ? t->w : t->h;
1805  if (!j) {
1806  arr[i][0] = tc;
1807  arr[i][1] = tc + tl;
1808  }
1809 
1810  if (arr[i][0] > tc) {
1811  arr[i][0] = tc;
1812  }
1813 
1814  if (arr[i][1] < tc + tl) {
1815  arr[i][1] = tc + tl;
1816  }
1817  }
1818 
1819  for (j = 0; j < g->layers[i].n_nodes; j++) {
1820  RzANode *a = get_anode(g->layers[i].nodes[j]);
1821  if (!a || a->is_dummy) {
1822  continue;
1823  }
1824 
1825  const RzList *neighbours = rz_graph_get_neighbours(g->graph, a->gnode);
1826  RzGraphNode *gb;
1827  RzANode *b;
1828  RzListIter *itm;
1829 
1830  if (i == 0) {
1831  inedge += rz_list_length(rz_graph_innodes(g->graph, a->gnode));
1832  } else if (i == g->n_layers - 1) {
1833  outedge += rz_list_length(neighbours);
1834  }
1835 
1836  graph_foreach_anode (neighbours, itm, gb, b) {
1837  if (b->layer > a->layer) {
1838  continue;
1839  }
1840 
1841  int nth = count_edges(g, a, b);
1842  int xinc = RZ_EDGES_X_INC + 2 * (nth + 1);
1843 
1844  int ax = g->layout == 0 ? a->x + xinc : a->y + (a->h / 2) + nth;
1845  int bx = g->layout == 0 ? b->x + xinc : b->y + (b->h / 2) + nth;
1846 
1847  if (g->layout == 0 && nth == 0 && bx > ax) {
1848  ax += 4;
1849  }
1850 
1851  min = arr[b->layer][0];
1852  max = arr[b->layer][1];
1853  for (k = b->layer; k <= a->layer; k++) {
1854  if (min > arr[k][0]) {
1855  min = arr[k][0];
1856  }
1857 
1858  if (max < arr[k][1]) {
1859  max = arr[k][1];
1860  }
1861  }
1862 
1863  int l = (ax - min) + (bx - min);
1864  int r = (max - ax) + (max - bx);
1865 
1866  for (k = b->layer; k <= a->layer; k++) {
1867  if (r < l) {
1868  arr[k][1] = max + 1;
1869  } else {
1870  arr[k][0] = min - 1;
1871  }
1872  }
1873 
1874  AEdge *e = RZ_NEW0(AEdge);
1875  if (!e) {
1876  free(arr);
1877  return;
1878  }
1879 
1880  e->is_reversed = true;
1881  e->from = a;
1882  e->to = b;
1883  e->x = rz_list_new();
1884  e->y = rz_list_new();
1885 
1886  if (r < l) {
1887  rz_list_append((g->layout == 0 ? e->x : e->y), (void *)(size_t)(max + 1));
1888  } else {
1889  rz_list_append((g->layout == 0 ? e->x : e->y), (void *)(size_t)(min - 1));
1890  }
1891 
1892  rz_list_append(g->edges, e);
1893  }
1894  }
1895  }
1896 
1897  // Assumption: layer layout is not changed w.r.t x-coordinate/y-coordinate for horizontal/vertical layout respectively.
1898  if (inedge) {
1899  RzANode *n = (RzANode *)g->layers[0].nodes[0]->data;
1900  AEdge *e = RZ_NEW0(AEdge);
1901  if (!e) {
1902  free(arr);
1903  return;
1904  }
1905  e->is_reversed = true;
1906  e->from = NULL;
1907  e->to = NULL;
1908  e->x = rz_list_new();
1909  e->y = rz_list_new();
1910  if (g->layout == 0) {
1911  rz_list_append(e->y, (void *)(size_t)(n->y - 1 - inedge));
1912  } else {
1913  rz_list_append(e->x, (void *)(size_t)(n->x - 1 - inedge));
1914  }
1915  rz_list_append(g->edges, e);
1916  }
1917 
1918  if (outedge) {
1919  RzANode *n = (RzANode *)g->layers[g->n_layers - 1].nodes[0]->data;
1920  AEdge *e = RZ_NEW0(AEdge);
1921  if (!e) {
1922  free(arr);
1923  return;
1924  }
1925 
1926  e->is_reversed = true;
1927  e->from = NULL;
1928  e->to = NULL;
1929  e->x = rz_list_new();
1930  e->y = rz_list_new();
1931  if (g->layout == 0) {
1932  rz_list_append(e->y, (void *)(size_t)(n->y + g->layers[g->n_layers - 1].height + 2 + outedge));
1933  } else {
1934  rz_list_append(e->x, (void *)(size_t)(n->x + g->layers[g->n_layers - 1].width + 2 + outedge));
1935  }
1936  rz_list_append(g->edges, e);
1937  }
1938 err:
1939  for (i = i - 1; i >= 0; i--) {
1940  free(arr[i]);
1941  }
1942  free(arr);
1943  return;
1944 }
1945 
1946 static void agraph_edge_free(AEdge *e) {
1947  rz_list_free(e->x);
1948  rz_list_free(e->y);
1949  free(e);
1950 }
1951 
1952 /* 1) trasform the graph into a DAG
1953  * 2) partition the nodes in layers
1954  * 3) split long edges that traverse multiple layers
1955  * 4) reorder nodes in each layer to reduce the number of edge crossing
1956  * 5) assign x and y coordinates to each node
1957  * 6) restore the original graph, with long edges and cycles */
1958 static void set_layout(RzAGraph *g) {
1959  int i, j, k;
1960 
1961  rz_list_free(g->edges);
1963 
1964  remove_cycles(g);
1965  assign_layers(g);
1967  create_layers(g);
1969 
1970  if (rz_cons_is_breaked()) {
1972  return;
1973  }
1974  /* identify row height */
1975  for (i = 0; i < g->n_layers; i++) {
1976  int rh = 0;
1977  int rw = 0;
1978  for (j = 0; j < g->layers[i].n_nodes; j++) {
1979  const RzANode *n = get_anode(g->layers[i].nodes[j]);
1980  if (n->h > rh) {
1981  rh = n->h;
1982  }
1983  if (n->w > rw) {
1984  rw = n->w;
1985  }
1986  }
1987  g->layers[i].height = rh;
1988  g->layers[i].width = rw;
1989  }
1990 
1991  for (i = 0; i < g->n_layers; i++) {
1992  for (j = 0; j < g->layers[i].n_nodes; j++) {
1993  RzANode *a = (RzANode *)g->layers[i].nodes[j]->data;
1994  if (a->is_dummy) {
1995  if (g->layout == 0) {
1996  a->h = g->layers[i].height;
1997  } else {
1998  a->w = g->layers[i].width;
1999  }
2000  }
2001  a->layer_height = g->layers[i].height;
2002  a->layer_width = g->layers[i].width;
2003  }
2004  }
2005 
2006  /* x-coordinate assignment: algorithm based on:
2007  * A Fast Layout Algorithm for k-Level Graphs
2008  * by C. Buchheim, M. Junger, S. Leipert */
2009  place_dummies(g);
2010  place_original(g);
2011 
2012  /* IDEA: need to put this hack because of the way algorithm is implemented.
2013  * I think backedges should be restored to their original state instead of
2014  * converting them to longedges and adding dummy nodes. */
2015  const RzListIter *it;
2016  const RzGraphEdge *e;
2017  rz_list_foreach (g->back_edges, it, e) {
2018  RzANode *from = e->from ? get_anode(e->from) : NULL;
2019  RzANode *to = e->to ? get_anode(e->to) : NULL;
2022  rz_agraph_add_edge_at(g, from, to, e->nth);
2023  }
2024 
2025  switch (g->layout) {
2026  default:
2027  case 0: // vertical layout
2028  /* horizontal finalize x coordinate */
2029  for (i = 0; i < g->n_layers; i++) {
2030  for (j = 0; j < g->layers[i].n_nodes; j++) {
2031  RzANode *n = get_anode(g->layers[i].nodes[j]);
2032  if (n) {
2033  n->x -= n->w / 2;
2034  if (g->is_tiny) {
2035  n->x /= 8;
2036  }
2037  }
2038  }
2039  }
2040 
2041  set_layer_gap(g);
2042 
2043  /* vertical align */
2044  for (i = 0; i < g->n_layers; i++) {
2045  int tmp_y = 0;
2046  tmp_y = g->layers[0].gap; // TODO: XXX: set properly
2047  for (k = 1; k <= i; k++) {
2048  tmp_y += g->layers[k - 1].height + g->layers[k].gap + 3; // XXX: should be 4?
2049  }
2050  if (g->is_tiny) {
2051  tmp_y = i;
2052  }
2053  for (j = 0; j < g->layers[i].n_nodes; j++) {
2054  RzANode *n = get_anode(g->layers[i].nodes[j]);
2055  if (n) {
2056  n->y = tmp_y;
2057  }
2058  }
2059  }
2060  break;
2061  /* experimental */
2062  case 1: // horizontal layout
2063  /* vertical y coordinate */
2064  for (i = 0; i < g->n_layers; i++) {
2065  for (j = 0; j < g->layers[i].n_nodes; j++) {
2066  RzANode *n = get_anode(g->layers[i].nodes[j]);
2067  n->y = 1;
2068  for (k = 0; k < j; k++) {
2069  RzANode *m = get_anode(g->layers[i].nodes[k]);
2070  n->y -= (m->h + VERTICAL_NODE_SPACING);
2071  }
2072  }
2073  }
2074 
2075  set_layer_gap(g);
2076 
2077  /* horizontal align */
2078  for (i = 0; i < g->n_layers; i++) {
2079  int xval = 1 + g->layers[0].gap + 1;
2080  for (k = 1; k <= i; k++) {
2081  xval += g->layers[k - 1].width + g->layers[k].gap + 3;
2082  }
2083  for (j = 0; j < g->layers[i].n_nodes; j++) {
2084  RzANode *n = get_anode(g->layers[i].nodes[j]);
2085  n->x = xval;
2086  }
2087  }
2088  break;
2089  }
2090 
2091  backedge_info(g);
2092 
2093  /* free all temporary structures used during layout */
2094  for (i = 0; i < g->n_layers; i++) {
2095  free(g->layers[i].nodes);
2096  }
2097 
2098  free(g->layers);
2099  rz_list_free(g->long_edges);
2100  rz_list_free(g->back_edges);
2102 }
2103 
2104 static char *get_body(RzCore *core, ut64 addr, int size, int opts) {
2105  char *body;
2106  RzConfigHold *hc = rz_config_hold_new(core->config);
2107  if (!hc) {
2108  return NULL;
2109  }
2110  rz_config_hold_i(hc, "asm.lines", "asm.bytes",
2111  "asm.cmt.col", "asm.marks", "asm.offset",
2112  "asm.comments", "asm.cmt.right", "asm.bb.line", NULL);
2113  const bool o_comments = rz_config_get_i(core->config, "graph.comments");
2114  const bool o_cmtright = rz_config_get_i(core->config, "graph.cmtright");
2115  const bool o_bytes = rz_config_get_i(core->config, "graph.bytes");
2116  const bool o_flags_in_bytes = rz_config_get_i(core->config, "asm.flags.inbytes");
2117  const bool o_graph_offset = rz_config_get_i(core->config, "graph.offset");
2118  int o_cursor = core->print->cur_enabled;
2119  if (opts & BODY_COMMENTS) {
2120  rz_core_visual_toggle_decompiler_disasm(core, true, false);
2121  char *res = rz_core_cmd_strf(core, "pD %d @ 0x%08" PFMT64x, size, addr);
2122  res = rz_str_replace(res, "; ", "", true);
2123  // res = rz_str_replace (res, "\n", "(\n)", true);
2124  rz_str_trim(res);
2125  res = rz_str_trim_lines(res);
2126  rz_core_visual_toggle_decompiler_disasm(core, true, false);
2128  rz_config_hold_free(hc);
2129  return res;
2130  }
2131  const char *cmd = (opts & BODY_SUMMARY) ? "pds" : "pD";
2132 
2133  // configure options
2134  rz_config_set_i(core->config, "asm.bb.line", false);
2135  rz_config_set_i(core->config, "asm.lines", false);
2136  rz_config_set_i(core->config, "asm.cmt.col", 0);
2137  rz_config_set_i(core->config, "asm.marks", false);
2138  rz_config_set_i(core->config, "asm.cmt.right", (opts & BODY_SUMMARY) || o_cmtright);
2139  rz_config_set_i(core->config, "asm.comments", (opts & BODY_SUMMARY) || o_comments);
2140  rz_config_set_i(core->config, "asm.bytes",
2141  (opts & (BODY_SUMMARY | BODY_OFFSETS)) || o_bytes || o_flags_in_bytes);
2142  rz_config_set_i(core->config, "asm.bb.middle", false);
2143  core->print->cur_enabled = false;
2144 
2145  if (opts & BODY_OFFSETS || opts & BODY_SUMMARY || o_graph_offset) {
2146  rz_config_set_i(core->config, "asm.offset", true);
2147  } else {
2148  rz_config_set_i(core->config, "asm.offset", false);
2149  }
2150 
2151  bool html = rz_config_get_i(core->config, "scr.html");
2152  rz_config_set_i(core->config, "scr.html", 0);
2153  if (rz_config_get_i(core->config, "graph.aeab")) {
2154  body = rz_core_cmd_strf(core, "%s 0x%08" PFMT64x, "aeab", addr);
2155  } else {
2156  body = rz_core_cmd_strf(core, "%s %d @ 0x%08" PFMT64x, cmd, size, addr);
2157  }
2158  rz_config_set_i(core->config, "scr.html", html);
2159 
2160  // restore original options
2161  core->print->cur_enabled = o_cursor;
2163  rz_config_hold_free(hc);
2164  return body;
2165 }
2166 
2167 static char *get_bb_body(RzCore *core, RzAnalysisBlock *b, int opts, RzAnalysisFunction *fcn, bool emu, ut64 saved_gp, ut8 *saved_arena) {
2168  if (emu) {
2169  core->analysis->gp = saved_gp;
2170  if (b->parent_reg_arena) {
2171  rz_reg_arena_poke(core->analysis->reg, b->parent_reg_arena);
2172  RZ_FREE(b->parent_reg_arena);
2173  ut64 gp = rz_reg_getv(core->analysis->reg, "gp");
2174  if (gp) {
2175  core->analysis->gp = gp;
2176  }
2177  } else {
2178  rz_reg_arena_poke(core->analysis->reg, saved_arena);
2179  }
2180  }
2181  if (b->parent_stackptr != INT_MAX) {
2182  core->analysis->stackptr = b->parent_stackptr;
2183  }
2184  char *body = get_body(core, b->addr, b->size, opts);
2185  if (b->jump != UT64_MAX) {
2186  if (b->jump > b->addr) {
2187  RzAnalysisBlock *jumpbb = rz_analysis_get_block_at(b->analysis, b->jump);
2188  if (jumpbb && rz_list_contains(jumpbb->fcns, fcn)) {
2189  if (emu && core->analysis->last_disasm_reg != NULL && !jumpbb->parent_reg_arena) {
2191  }
2192  if (jumpbb->parent_stackptr == INT_MAX) {
2193  jumpbb->parent_stackptr = core->analysis->stackptr + b->stackptr;
2194  }
2195  }
2196  }
2197  }
2198  if (b->fail != UT64_MAX) {
2199  if (b->fail > b->addr) {
2200  RzAnalysisBlock *failbb = rz_analysis_get_block_at(b->analysis, b->fail);
2201  if (failbb && rz_list_contains(failbb->fcns, fcn)) {
2202  if (emu && core->analysis->last_disasm_reg != NULL && !failbb->parent_reg_arena) {
2204  }
2205  if (failbb->parent_stackptr == INT_MAX) {
2206  failbb->parent_stackptr = core->analysis->stackptr + b->stackptr;
2207  }
2208  }
2209  }
2210  }
2211  return body;
2212 }
2213 
2215  return a->addr - b->addr;
2216 }
2217 
2218 static void get_bbupdate(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
2219  RzAnalysisBlock *bb;
2220  RzListIter *iter;
2221  bool emu = rz_config_get_i(core->config, "asm.emu");
2222  ut64 saved_gp = core->analysis->gp;
2223  ut8 *saved_arena = NULL;
2224  int saved_stackptr = core->analysis->stackptr;
2225  char *shortcut = 0;
2226  int shortcuts = 0;
2227  core->keep_asmqjmps = false;
2228 
2229  if (emu) {
2230  saved_arena = rz_reg_arena_peek(core->analysis->reg);
2231  }
2232  if (!fcn) {
2233  RZ_FREE(saved_arena);
2234  return;
2235  }
2237 
2238  shortcuts = rz_config_get_i(core->config, "graph.nodejmps");
2239  rz_list_foreach (fcn->bbs, iter, bb) {
2240  if (bb->addr == UT64_MAX) {
2241  continue;
2242  }
2243  char *body = get_bb_body(core, bb, mode2opts(g), fcn, emu, saved_gp, saved_arena);
2244  char *title = get_title(bb->addr);
2245 
2246  if (shortcuts) {
2247  shortcut = rz_core_add_asmqjmp(core, bb->addr);
2248  if (shortcut) {
2249  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.shortcut", title), shortcut, 0);
2250  free(shortcut);
2251  }
2252  }
2253  RzANode *node = rz_agraph_get_node(g, title);
2254  if (node) {
2255  free(node->body);
2256  node->body = body;
2257  } else {
2258  free(body);
2259  }
2260  free(title);
2261  core->keep_asmqjmps = true;
2262  }
2263 
2264  if (emu) {
2265  core->analysis->gp = saved_gp;
2266  if (saved_arena) {
2267  rz_reg_arena_poke(core->analysis->reg, saved_arena);
2268  RZ_FREE(saved_arena);
2269  }
2270  }
2271  core->analysis->stackptr = saved_stackptr;
2272 }
2273 
2274 static void fold_asm_trace(RzCore *core, RzAGraph *g) {
2275  const RzList *nodes = rz_graph_get_nodes(g->graph);
2276  RzGraphNode *gn;
2277  RzListIter *it;
2278  RzANode *n;
2279 
2280  RzANode *curnode = get_anode(g->curnode);
2281  graph_foreach_anode (nodes, it, gn, n) {
2282  if (curnode == n) {
2283  n->is_mini = false;
2284  g->need_reload_nodes = true;
2285  continue;
2286  }
2287  ut64 addr = rz_num_get(NULL, n->title);
2289  n->is_mini = (tp == NULL);
2290  }
2291  g->need_update_dim = 1;
2292  // agraph_refresh (rz_cons_singleton ()->event_data);
2293 }
2294 
2295 static void delete_dup_edges(RzAGraph *g) {
2296  RzListIter *it, *in_it, *in_it2, *in_it2_tmp;
2297  RzGraphNode *n, *a, *b;
2298  rz_list_foreach (g->graph->nodes, it, n) {
2299  rz_list_foreach (n->out_nodes, in_it, a) {
2300  for (in_it2 = in_it->n; in_it2 && (b = in_it2->data, in_it2_tmp = in_it2->n, 1); in_it2 = in_it2_tmp) {
2301  if (a->idx == b->idx) {
2302  rz_list_delete(n->out_nodes, in_it2);
2303  rz_list_delete_data(n->all_neighbours, b);
2304  rz_list_delete_data(b->in_nodes, n);
2305  rz_list_delete_data(b->all_neighbours, n);
2306  g->graph->n_edges--;
2307  }
2308  }
2309  }
2310  }
2311 }
2312 
2313 static bool isbbfew(RzAnalysisBlock *curbb, RzAnalysisBlock *bb) {
2314  if (bb->addr == curbb->addr || bb->addr == curbb->jump || bb->addr == curbb->fail) {
2315  // do nothing
2316  return true;
2317  }
2318  if (curbb->switch_op) {
2319  RzListIter *it;
2320  RzAnalysisCaseOp *cop;
2321  rz_list_foreach (curbb->switch_op->cases, it, cop) {
2322  if (cop->addr == bb->addr) {
2323  return true;
2324  }
2325  }
2326  }
2327  return false;
2328 }
2329 
2330 /* build the RzGraph inside the RzAGraph g, starting from the Basic Blocks */
2331 static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
2332  RzAnalysisBlock *bb;
2333  RzListIter *iter;
2334  char *shortcut = NULL;
2335  int shortcuts = 0;
2336  bool emu = rz_config_get_i(core->config, "asm.emu");
2337  bool few = rz_config_get_i(core->config, "graph.few");
2338  int ret = false;
2339  ut64 saved_gp = core->analysis->gp;
2340  ut8 *saved_arena = NULL;
2341  int saved_stackptr = core->analysis->stackptr;
2342  core->keep_asmqjmps = false;
2343 
2344  if (!fcn) {
2345  return false;
2346  }
2347  if (emu) {
2348  saved_arena = rz_reg_arena_peek(core->analysis->reg);
2349  }
2351  RzAnalysisBlock *curbb = NULL;
2352  if (few) {
2353  rz_list_foreach (fcn->bbs, iter, bb) {
2354  if (!curbb) {
2355  curbb = bb;
2356  }
2357  if (rz_analysis_block_contains(bb, core->offset)) {
2358  curbb = bb;
2359  break;
2360  }
2361  }
2362  }
2363 
2364  core->keep_asmqjmps = false;
2365  rz_list_foreach (fcn->bbs, iter, bb) {
2366  if (bb->addr == UT64_MAX) {
2367  continue;
2368  }
2369  if (few && !isbbfew(curbb, bb)) {
2370  continue;
2371  }
2372  char *body = get_bb_body(core, bb, mode2opts(g), fcn, emu, saved_gp, saved_arena);
2373  char *title = get_title(bb->addr);
2374 
2375  RzANode *node = rz_agraph_add_node(g, title, body);
2376  shortcuts = g->is_interactive ? rz_config_get_i(core->config, "graph.nodejmps") : false;
2377 
2378  if (shortcuts) {
2379  shortcut = rz_core_add_asmqjmp(core, bb->addr);
2380  if (shortcut) {
2381  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.shortcut", title), shortcut, 0);
2382  free(shortcut);
2383  }
2384  }
2385  free(body);
2386  free(title);
2387  if (!node) {
2388  goto cleanup;
2389  }
2390  core->keep_asmqjmps = true;
2391  }
2392 
2393  rz_list_foreach (fcn->bbs, iter, bb) {
2394  if (bb->addr == UT64_MAX) {
2395  continue;
2396  }
2397  if (few && !isbbfew(curbb, bb)) {
2398  continue;
2399  }
2400 
2401  char *title = get_title(bb->addr);
2402  RzANode *u = rz_agraph_get_node(g, title);
2403  RzANode *v;
2404  free(title);
2405  if (bb->jump != UT64_MAX) {
2406  title = get_title(bb->jump);
2407  v = rz_agraph_get_node(g, title);
2408  free(title);
2409  rz_agraph_add_edge(g, u, v);
2410  }
2411  if (bb->fail != UT64_MAX) {
2412  title = get_title(bb->fail);
2413  v = rz_agraph_get_node(g, title);
2414  free(title);
2415  rz_agraph_add_edge(g, u, v);
2416  }
2417  if (bb->switch_op) {
2418  RzListIter *it;
2419  RzAnalysisCaseOp *cop;
2420  rz_list_foreach (bb->switch_op->cases, it, cop) {
2421  title = get_title(cop->addr);
2422  v = rz_agraph_get_node(g, title);
2423  free(title);
2424  rz_agraph_add_edge(g, u, v);
2425  }
2426  }
2427  }
2428 
2430  ret = true;
2431 
2432 cleanup:
2433  if (emu) {
2434  core->analysis->gp = saved_gp;
2435  if (saved_arena) {
2436  rz_reg_arena_poke(core->analysis->reg, saved_arena);
2437  RZ_FREE(saved_arena);
2438  }
2439  }
2440  core->analysis->stackptr = saved_stackptr;
2441  return ret;
2442 }
2443 
2444 /* build the RzGraph inside the RzAGraph g, starting from the Call Graph
2445  * information */
2446 static bool get_cgnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
2448  RzANode *node, *fcn_anode;
2449  RzListIter *iter;
2450  RzAnalysisXRef *xref;
2451  if (!f) {
2452  return false;
2453  }
2454  if (!fcn) {
2455  fcn = f;
2456  }
2457 
2458  rz_core_seek(core, f->addr, true);
2459 
2460  char *title = get_title(fcn->addr);
2461  fcn_anode = rz_agraph_add_node(g, title, "");
2462 
2463  free(title);
2464  if (!fcn_anode) {
2465  return false;
2466  }
2467 
2468  fcn_anode->x = 10;
2469  fcn_anode->y = 3;
2470 
2472  rz_list_foreach (xrefs, iter, xref) {
2473  title = get_title(xref->to);
2474  if (rz_agraph_get_node(g, title) != NULL) {
2475  continue;
2476  }
2477  free(title);
2478 
2479  int size = 0;
2481  if (bb) {
2482  size = bb->size;
2483  }
2484 
2485  char *body = get_body(core, xref->to, size, mode2opts(g));
2486  title = get_title(xref->to);
2487 
2488  node = rz_agraph_add_node(g, title, body);
2489  if (!node) {
2490  return false;
2491  }
2492 
2493  free(title);
2494  free(body);
2495 
2496  node->x = 10;
2497  node->y = 10;
2498 
2499  rz_agraph_add_edge(g, fcn_anode, node);
2500  }
2501  rz_list_free(xrefs);
2502 
2503  return true;
2504 }
2505 
2506 static bool reload_nodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn) {
2507  const bool is_c = g->is_callgraph;
2508  return is_c ? get_cgnodes(g, core, fcn) : get_bbnodes(g, core, fcn);
2509 }
2510 
2511 static void update_seek(RzConsCanvas *can, RzANode *n, int force) {
2512  if (!n) {
2513  return;
2514  }
2515  int x = n->x + can->sx;
2516  int y = n->y + can->sy;
2517  int w = can->w;
2518  int h = can->h;
2519 
2520  const bool doscroll = force || y < 0 || y + 5 > h || x + 5 > w || x + n->w + 5 < 0;
2521  if (doscroll) {
2522  if (n->w > w) { // too big for centering
2523  can->sx = -n->x;
2524  } else {
2525  can->sx = -n->x - n->w / 2 + w / 2;
2526  }
2527  if (n->h > h) { // too big for centering
2528  can->sy = -n->y;
2529  } else {
2530  can->sy = -n->y - n->h / 8 + h / 4;
2531  }
2532  }
2533 }
2534 
2535 static int is_near(const RzANode *n, int x, int y, int is_next) {
2536  if (is_next) {
2537  return (n->y == y && n->x > x) || n->y > y;
2538  }
2539  return (n->y == y && n->x < x) || n->y < y;
2540 }
2541 
2543 static int is_near_h(const RzANode *n, int x, int y, int is_next) {
2544  if (is_next) {
2545  return (n->x == x && n->y > y) || n->x > x;
2546  }
2547  return (n->x == x && n->y < y) || n->x < x;
2548 }
2549 
2550 static const RzGraphNode *find_near_of(const RzAGraph *g, const RzGraphNode *cur, int is_next) {
2551  /* XXX: it's slow */
2552  const RzList *nodes = rz_graph_get_nodes(g->graph);
2553  const RzListIter *it;
2554  const RzGraphNode *gn, *resgn = NULL;
2555  const RzANode *n, *acur = cur ? get_anode(cur) : NULL;
2556  const int default_v = is_next ? INT_MIN : INT_MAX;
2557  const int start_x = acur ? acur->x : default_v;
2558  const int start_y = acur ? acur->y : default_v;
2559 
2560  graph_foreach_anode (nodes, it, gn, n) {
2561  // tab in horizontal layout is not correct, lets force vertical nextnode for now (g->layout == 0)
2562  bool isNear = true
2563  ? is_near(n, start_x, start_y, is_next)
2564  : is_near_h(n, start_x, start_y, is_next);
2565  if (isNear) {
2566  const RzANode *resn;
2567 
2568  if (!resgn) {
2569  resgn = gn;
2570  continue;
2571  }
2572 
2573  resn = get_anode(resgn);
2574  if ((is_next && resn->y > n->y) || (!is_next && resn->y < n->y)) {
2575  resgn = gn;
2576  } else if ((is_next && resn->y == n->y && resn->x > n->x) ||
2577  (!is_next && resn->y == n->y && resn->x < n->x)) {
2578  resgn = gn;
2579  }
2580  }
2581  }
2582  if (!resgn && cur) {
2583  resgn = find_near_of(g, NULL, is_next);
2584  }
2585  return resgn;
2586 }
2587 
2589  RzListIter *it;
2590  RzGraphNode *gk;
2591  RzANode *ak, *min_gn, *max_gn;
2592  int max_x, max_y;
2593  int delta_x, delta_y;
2594  AEdge *e;
2595 
2596  g->x = g->y = INT_MAX;
2597  max_x = max_y = INT_MIN;
2598  min_gn = max_gn = NULL;
2599 
2600  graph_foreach_anode (rz_graph_get_nodes(g->graph), it, gk, ak) {
2601  const RzList *nd = NULL;
2602  int len;
2603  if (ak->x < g->x) {
2604  g->x = ak->x;
2605  }
2606 
2607  nd = rz_graph_innodes(g->graph, gk);
2608  len = nd ? rz_list_length(nd) + 1 : 0;
2609  if (ak->y - len < g->y) {
2610  g->y = ak->y - len;
2611  min_gn = ak;
2612  }
2613 
2614  if (ak->x + ak->w > max_x) {
2615  max_x = ak->x + ak->w;
2616  }
2617 
2618  nd = NULL;
2619  nd = rz_graph_get_neighbours(g->graph, gk);
2620  len = nd ? rz_list_length(nd) + 2 : 0;
2621  if (ak->y + ak->h + len > max_y) {
2622  max_y = ak->y + ak->h + len;
2623  max_gn = ak;
2624  }
2625  }
2626  /* while calculating the graph size, take into account long edges */
2627  rz_list_foreach (g->edges, it, e) {
2628  RzListIter *kt;
2629  void *vv;
2630  int v;
2631  if (rz_cons_is_breaked()) {
2632  break;
2633  }
2634  rz_list_foreach (e->x, kt, vv) {
2635  v = (int)(size_t)vv;
2636  if (v < g->x) {
2637  g->x = v;
2638  }
2639  if (v + 1 > max_x) {
2640  max_x = v + 1;
2641  }
2642  }
2643  rz_list_foreach (e->y, kt, vv) {
2644  v = (int)(size_t)vv;
2645  if (v < g->y) {
2646  g->y = v;
2647  }
2648  if (v + 1 > max_y) {
2649  max_y = v + 1;
2650  }
2651  }
2652  }
2654 
2655  if (min_gn) {
2656  const RzList *neigh = rz_graph_innodes(g->graph, min_gn->gnode);
2657  if (rz_list_length(neigh) > 0) {
2658  g->y--;
2659  max_y++;
2660  }
2661  if (max_gn) {
2662  const RzList *neigh = rz_graph_get_neighbours(g->graph, min_gn->gnode);
2663  if (rz_list_length(neigh) > 0) {
2664  max_y++;
2665  }
2666  }
2667  }
2668 
2669  if (g->x != INT_MAX && g->y != INT_MAX) {
2670  g->w = max_x - g->x;
2671  if (g->title) {
2672  size_t len = strlen(g->title);
2673  if (len > INT_MAX) {
2674  g->w = INT_MAX;
2675  }
2676  if ((int)len > g->w) {
2677  g->w = len;
2678  }
2679  }
2680  g->h = max_y - g->y;
2681  } else {
2682  g->x = g->y = 0;
2683  g->w = g->h = 0;
2684  }
2685 
2686  sdb_num_set(g->db, "agraph.w", g->w, 0);
2687  sdb_num_set(g->db, "agraph.h", g->h, 0);
2688  /* delta_x, delta_y are needed to make every other x,y coordinates
2689  * unsigned, so that we can use sdb_num_ API */
2690  delta_x = g->x < 0 ? -g->x : 0;
2691  delta_y = g->y < 0 ? -g->y : 0;
2692  sdb_num_set(g->db, "agraph.delta_x", delta_x, 0);
2693  sdb_num_set(g->db, "agraph.delta_y", delta_y, 0);
2694 }
2695 
2697  if (!a) {
2698  return;
2699  }
2700  g->curnode = a->gnode;
2701  if (a->title) {
2702  sdb_set(g->db, "agraph.curnode", a->title, 0);
2703  if (g->on_curnode_change) {
2704  g->on_curnode_change(a, g->on_curnode_change_data);
2705  }
2706  }
2707 }
2708 
2709 static ut64 rebase(RzAGraph *g, int v) {
2710  return g->x < 0 ? -g->x + v : v;
2711 }
2712 
2714  RzListIter *it;
2715  RzGraphNode *n;
2716  RzANode *a;
2717 
2718  set_layout(g);
2719 
2721  graph_foreach_anode (rz_graph_get_nodes(g->graph), it, n, a) {
2722  if (a->is_dummy) {
2723  continue;
2724  }
2725  const char *k;
2726  k = sdb_fmt("agraph.nodes.%s.x", a->title);
2727  sdb_num_set(g->db, k, rebase(g, a->x), 0);
2728  k = sdb_fmt("agraph.nodes.%s.y", a->title);
2729  sdb_num_set(g->db, k, rebase(g, a->y), 0);
2730  k = sdb_fmt("agraph.nodes.%s.w", a->title);
2731  sdb_num_set(g->db, k, a->w, 0);
2732  k = sdb_fmt("agraph.nodes.%s.h", a->title);
2733  sdb_num_set(g->db, k, a->h, 0);
2734  }
2735 }
2736 
2737 /* set the willing to center the screen on a particular node */
2738 static void agraph_update_seek(RzAGraph *g, RzANode *n, int force) {
2739  g->update_seek_on = n;
2740  g->force_update_seek = force;
2741 }
2742 
2743 static void agraph_print_node(const RzAGraph *g, RzANode *n) {
2744  if (n->is_dummy) {
2745  return;
2746  }
2747  const int cur = g->curnode && get_anode(g->curnode) == n;
2748  const bool isMini = is_mini(g);
2749  if (g->is_tiny) {
2750  tiny_RzANode_print(g, n, cur);
2751  } else if (isMini || n->is_mini) {
2752  mini_RzANode_print(g, n, cur, isMini);
2753  } else {
2754  normal_RzANode_print(g, n, cur);
2755  }
2756 }
2757 
2758 static void agraph_print_nodes(const RzAGraph *g) {
2759  const RzList *nodes = rz_graph_get_nodes(g->graph);
2760  RzGraphNode *gn;
2761  RzListIter *it;
2762  RzANode *n;
2763 
2764  graph_foreach_anode (nodes, it, gn, n) {
2765  if (gn != g->curnode) {
2766  agraph_print_node(g, n);
2767  }
2768  }
2769 
2770  /* draw current node now to make it appear on top */
2771  if (g->curnode) {
2772  agraph_print_node(g, get_anode(g->curnode));
2773  }
2774 }
2775 
2776 struct tmplayer {
2777  int layer;
2778  int edgectr;
2780  int minx;
2781  int maxx;
2782 };
2784  int ax;
2785  int ay;
2786  int bx;
2787  int by;
2788  int edgectr;
2790  int tolayer;
2792 };
2793 
2794 int tmplayercmp(const void *a, const void *b) {
2795  return ((struct tmplayer *)a)->layer > ((struct tmplayer *)b)->layer;
2796 }
2797 
2799  RzCanvasLineStyle style = { 0 };
2800  RzANode *n, *n2;
2801  RzGraphNode *gn, *gn2;
2802  RzListIter *iter, *iter2;
2803  const RzList *nodes = rz_graph_get_nodes(g->graph);
2804  graph_foreach_anode (nodes, iter, gn, n) {
2805  const RzList *outnodes = n->gnode->out_nodes;
2806  graph_foreach_anode (outnodes, iter2, gn2, n2) {
2807  int sx = n->w / 2;
2808  int sy = n->h;
2809  int sx2 = n2->w / 2;
2810  if (g->is_tiny) {
2811  sx = 0;
2812  sy = 0;
2813  sx2 = 0;
2814  }
2815  // TODO: better alignments here
2816  rz_cons_canvas_line(g->can,
2817  n->x + sx, n->y + sy,
2818  n2->x + sx2, n2->y, &style);
2819 
2820  if (n2->is_dummy) {
2821  rz_cons_canvas_line(g->can,
2822  n2->x + sx2, n2->y - 1,
2823  n2->x + sx2, n2->y + n2->h, &style);
2824  }
2825  }
2826  }
2827 }
2828 
2829 static int first_x_cmp(const void *_a, const void *_b) {
2830  RzGraphNode *ga = (RzGraphNode *)_a;
2831  RzGraphNode *gb = (RzGraphNode *)_b;
2832  RzANode *a = (RzANode *)ga->data;
2833  RzANode *b = (RzANode *)gb->data;
2834  if (b->y < a->y) {
2835  return -1;
2836  }
2837  if (b->y > a->y) {
2838  return 1;
2839  }
2840  if (a->x < b->x) {
2841  return 1;
2842  }
2843  if (a->x > b->x) {
2844  return -1;
2845  }
2846  return 0;
2847 }
2848 
2850  if (!g->edgemode) {
2851  return;
2852  }
2853  if (g->edgemode == 1) {
2855  return;
2856  }
2857  int out_nth, in_nth, bendpoint;
2858  RzListIter *itn, *itm, *ito;
2859  RzCanvasLineStyle style = { 0 };
2860  const RzList *nodes = rz_graph_get_nodes(g->graph);
2861  RzGraphNode *ga;
2862  RzANode *a;
2863 
2864  RzList *lyr = rz_list_new();
2865  RzList *bckedges = rz_list_new();
2866  struct tmplayer *tl, *tm;
2867 
2868  graph_foreach_anode (nodes, itm, ga, a) {
2869  const RzGraphNode *gb;
2870  RzANode *b;
2871  RzList *neighbours = (RzList *)rz_graph_get_neighbours(g->graph, ga);
2872  int ax, ay, bx, by, a_x_inc, b_x_inc;
2873  tl = tm = NULL;
2874  if (rz_cons_is_breaked()) {
2875  break;
2876  }
2877 
2878  rz_list_foreach (lyr, ito, tl) {
2879  if (tl->layer == a->layer) {
2880  tm = tl;
2881  if (g->layout == 0) { // vertical layout
2882  if (tm->minx > a->x) {
2883  tm->minx = a->x;
2884  }
2885  if (tm->maxx < a->x + a->w) {
2886  tm->maxx = a->x + a->w;
2887  }
2888  } else {
2889  if (tm->minx > a->y) {
2890  tm->minx = a->y;
2891  }
2892  if (tm->maxx < a->y + a->h) {
2893  tm->maxx = a->y + a->h;
2894  }
2895  }
2896  break;
2897  }
2898  }
2899 
2900  if (!tm) {
2901  tm = RZ_NEW0(struct tmplayer);
2902  if (tm) {
2903  tm->layer = a->layer;
2904  tm->edgectr = 0;
2905  tm->revedgectr = 0;
2906  if (g->layout == 0) { // vertical layout
2907  tm->minx = a->x;
2908  tm->maxx = a->x + a->w;
2909  } else {
2910  tm->minx = a->y;
2911  tm->maxx = a->y + a->h;
2912  }
2913  rz_list_add_sorted(lyr, tm, tmplayercmp);
2914  }
2915  }
2916 
2917  bool many = rz_list_length(neighbours) > 2;
2918 
2919  if (many && !g->is_callgraph) {
2920  ga->out_nodes->sorted = false;
2921  rz_list_sort(neighbours, first_x_cmp);
2922  }
2923 
2924  graph_foreach_anode (neighbours, itn, gb, b) {
2925  out_nth = get_edge_number(g, a, b, true);
2926  in_nth = get_edge_number(g, a, b, false);
2927 
2928  bool parent_many = false;
2929  if (a->is_dummy) {
2930  RzANode *in = (RzANode *)(((RzGraphNode *)rz_list_first(ga->in_nodes))->data);
2931  while (in && in->is_dummy) {
2932  in = (RzANode *)(((RzGraphNode *)rz_list_first((in->gnode)->in_nodes))->data);
2933  }
2934  if (in && in->gnode) {
2935  parent_many = rz_list_length(in->gnode->out_nodes) > 2;
2936  } else {
2937  parent_many = false;
2938  }
2939  }
2940 
2941  style.dot_style = DOT_STYLE_NORMAL;
2942  if (many || parent_many) {
2943  style.color = LINE_UNCJMP;
2944  } else {
2945  switch (out_nth) {
2946  case 0:
2947  style.color = LINE_TRUE;
2949  break;
2950  case 1:
2951  style.color = LINE_FALSE;
2953  break;
2954  case -1:
2955  style.color = LINE_UNCJMP;
2956  break;
2957  default:
2958  style.color = LINE_NONE;
2959  break;
2960  }
2961  }
2962 
2963  switch (g->layout) {
2964  case 0:
2965  default:
2966  style.symbol = (!g->hints || a->is_dummy) ? LINE_NOSYM_VERT : style.color;
2967  if (a->y + a->h > b->y) {
2968  style.dot_style = DOT_STYLE_BACKEDGE;
2969  }
2970 
2971  a_x_inc = RZ_EDGES_X_INC + 2 * (out_nth + 1);
2972  b_x_inc = RZ_EDGES_X_INC + 2 * (in_nth + 1);
2973 
2974  bx = b->is_dummy ? b->x : (b->x + b_x_inc);
2975  ay = a->y + a->h;
2976  by = b->y - 1;
2977 
2978  if (many && !g->is_callgraph) {
2979  int t = RZ_EDGES_X_INC + 2 * (neighbours->length + 1);
2980  ax = a->is_dummy ? a->x : (a->x + a->w / 2 + (t / 2 - a_x_inc));
2981  bendpoint = bx < ax ? neighbours->length - out_nth : out_nth;
2982  } else {
2983  ax = a->is_dummy ? a->x : (a->x + a_x_inc);
2984  bendpoint = tm->edgectr;
2985  }
2986 
2987  if (!a->is_dummy && itn == neighbours->head && out_nth == 0 && bx > ax) {
2988  ax += (many && !g->is_callgraph) ? 0 : 4;
2989  }
2990  if (a->h < a->layer_height) {
2991  rz_cons_canvas_line(g->can, ax, ay, ax, ay + a->layer_height - a->h, &style);
2992  ay = a->y + a->layer_height;
2993  style.symbol = LINE_NOSYM_VERT;
2994  }
2995  if (by >= ay) {
2996  rz_cons_canvas_line_square_defined(g->can, ax, ay, bx, by, &style, bendpoint, true);
2997  } else {
2998  struct tmpbackedgeinfo *tmp = calloc(1, sizeof(struct tmpbackedgeinfo));
2999  tmp->ax = ax;
3000  tmp->bx = bx;
3001  tmp->ay = ay;
3002  tmp->by = by;
3003  tmp->edgectr = bendpoint;
3004  tmp->fromlayer = a->layer;
3005  tmp->tolayer = b->layer;
3006  tmp->style = style;
3007  rz_list_append(bckedges, tmp);
3008  }
3009  if (b->is_dummy) {
3011  rz_cons_canvas_line(g->can, bx, by, bx, b->y + b->h, &style);
3012  }
3013  if (b->x != a->x || b->layer <= a->layer || (!a->is_dummy && b->is_dummy) || (a->is_dummy && !b->is_dummy)) {
3014  if (tm) {
3015  tm->edgectr++;
3016  }
3017  }
3018  break;
3019  case 1:
3020  style.symbol = (!g->hints || a->is_dummy) ? LINE_NOSYM_HORIZ : style.color;
3021  if (a->x + a->w > b->x) {
3023  }
3024 
3025  ax = a->x;
3026  if (g->zoom > 0) {
3027  ax += a->w;
3028  } else {
3029  ax++;
3030  }
3031  ay = a->y;
3032  if (!a->is_dummy && g->zoom > 0) {
3033  ay += RZ_EDGES_X_INC + out_nth;
3034  }
3035  bx = b->x - 1;
3036  by = b->y;
3037  if (!b->is_dummy && g->zoom > 0) {
3038  by += RZ_EDGES_X_INC + out_nth;
3039  }
3040 
3041  if (a->w < a->layer_width) {
3042  rz_cons_canvas_line_square_defined(g->can, ax, ay, a->x + a->layer_width, ay, &style, 0, false);
3043  ax = a->x;
3044  if (g->zoom > 1) {
3045  ax += a->layer_width;
3046  } else {
3047  ax += 1;
3048  }
3050  }
3051  if (bx >= ax) {
3052  rz_cons_canvas_line_square_defined(g->can, ax, ay, bx, by, &style, tm->edgectr, false);
3053  } else {
3054  struct tmpbackedgeinfo *tmp = calloc(1, sizeof(struct tmpbackedgeinfo));
3055  if (tmp) {
3056  tmp->ax = ax;
3057  tmp->bx = bx;
3058  tmp->ay = ay;
3059  tmp->by = by;
3060  tmp->edgectr = tm->edgectr;
3061  tmp->fromlayer = a->layer;
3062  tmp->tolayer = b->layer;
3063  tmp->style = style;
3064  rz_list_append(bckedges, tmp);
3065  }
3066  }
3067  if (b->is_dummy) {
3069  rz_cons_canvas_line_square_defined(g->can, bx, by, bx + b->layer_width, by, &style, 0, false);
3070  }
3071  if ((b->y == a->y && b->h != a->h) || b->y != a->y || b->layer <= a->layer || (!a->is_dummy && b->is_dummy) || (a->is_dummy && !b->is_dummy)) {
3072  tm->edgectr += 1;
3073  }
3074  break;
3075  }
3076  }
3077  }
3078 
3079  struct tmpbackedgeinfo *temp;
3080  rz_list_foreach (bckedges, itm, temp) {
3081  int leftlen, rightlen;
3082  int minx = 0, maxx = 0;
3083  struct tmplayer *tt = NULL;
3084  if (rz_cons_is_breaked()) {
3085  break;
3086  }
3087 
3088  rz_list_foreach (lyr, ito, tl) {
3089  if (tl->layer <= temp->tolayer) {
3090  tt = tl;
3091  minx = tl->minx;
3092  maxx = tl->maxx;
3093  continue;
3094  }
3095  minx = minx < tl->minx ? minx : tl->minx;
3096  maxx = maxx > tl->maxx ? maxx : tl->maxx;
3097  if (tl->layer >= temp->fromlayer) {
3098  break;
3099  }
3100  }
3101 
3102  if (tt) {
3103  tt->revedgectr += 1;
3104  }
3105  if (g->layout == 0) {
3106  leftlen = (temp->ax - minx) + (temp->bx - minx);
3107  rightlen = (maxx - temp->ax) + (maxx - temp->bx);
3108  } else {
3109  leftlen = (temp->ay - minx) + (temp->by - minx);
3110  rightlen = (maxx - temp->ay) + (maxx - temp->by);
3111  }
3112 
3113  if (tt) {
3114  int arg = (rightlen < leftlen) ? maxx + 1 : minx - 1;
3115  rz_cons_canvas_line_back_edge(g->can, temp->ax, temp->ay, temp->bx, temp->by, &(temp->style), temp->edgectr, arg, tt->revedgectr, !g->layout);
3116  }
3117 
3118  rz_list_foreach (lyr, ito, tl) {
3119  if (tl->layer < temp->tolayer) {
3120  continue;
3121  }
3122  if (rightlen < leftlen) {
3123  tl->maxx = maxx + 1;
3124  } else {
3125  tl->minx = minx - 1;
3126  }
3127  if (tl->layer >= temp->fromlayer) {
3128  break;
3129  }
3130  }
3131  }
3132 
3133  rz_list_foreach (lyr, ito, tl) {
3134  free(tl);
3135  }
3136 
3137  rz_list_foreach (bckedges, ito, tl) {
3138  free(tl);
3139  }
3140 
3141  rz_list_free(lyr);
3142  rz_list_free(bckedges);
3144 }
3145 
3147  g->is_callgraph = !g->is_callgraph;
3148  g->need_reload_nodes = true;
3149  g->force_update_seek = true;
3150 }
3151 
3152 static void agraph_set_zoom(RzAGraph *g, int v) {
3153  if (v >= -10) {
3154  g->is_tiny = false;
3155  if (v == 0) {
3156  g->mode = RZ_AGRAPH_MODE_MINI;
3157  } else if (v < 0) {
3158  g->mode = RZ_AGRAPH_MODE_TINY;
3159  g->is_tiny = true;
3160  } else {
3161  g->mode = RZ_AGRAPH_MODE_NORMAL;
3162  }
3163  const int K = 920;
3164  if (g->zoom < v) {
3165  g->can->sy = (g->can->sy * K) / 1000;
3166  } else {
3167  g->can->sy = (g->can->sy * 1000) / K;
3168  }
3169  g->zoom = v;
3170  g->need_update_dim = true;
3171  g->need_set_layout = true;
3172  }
3173 }
3174 
3175 /* reload all the info in the nodes, depending on the type of the graph
3176  * (callgraph, CFG, etc.), set the default layout for these nodes and center
3177  * the screen on the selected one */
3179  rz_agraph_reset(g);
3180  return reload_nodes(g, core, fcn);
3181 }
3182 
3183 static void follow_nth(RzAGraph *g, int nth) {
3184  const RzGraphNode *cn = rz_graph_nth_neighbour(g->graph, g->curnode, nth);
3185  RzANode *a = get_anode(cn);
3186 
3187  while (a && a->is_dummy) {
3188  cn = rz_graph_nth_neighbour(g->graph, a->gnode, 0);
3189  a = get_anode(cn);
3190  }
3191  if (a) {
3193  }
3194 }
3195 
3196 static void move_current_node(RzAGraph *g, int xdiff, int ydiff) {
3197  RzANode *n = get_anode(g->curnode);
3198  if (n) {
3199  if (is_tiny(g)) {
3200  xdiff = NORMALIZE_MOV(xdiff);
3201  ydiff = NORMALIZE_MOV(ydiff);
3202  }
3203  n->x += xdiff;
3204  n->y += ydiff;
3205  }
3206 }
3207 
3208 #if GRAPH_MERGE_FEATURE
3209 #define K_NEIGHBOURS(x) (sdb_fmt("agraph.nodes.%s.neighbours", x->title))
3210 static void agraph_merge_child(RzAGraph *g, int idx) {
3211  const RzGraphNode *nn = rz_graph_nth_neighbour(g->graph, g->curnode, idx);
3212  const RzGraphNode *cn = g->curnode;
3213  if (cn && nn) {
3214  RzANode *ann = get_anode(nn);
3215  RzANode *acn = get_anode(cn);
3216  acn->body = rz_str_append(acn->body, ann->title);
3217  acn->body = rz_str_append(acn->body, "\n");
3218  acn->body = rz_str_append(acn->body, ann->body);
3219  /* remove node from the graph */
3220  acn->h += ann->h - 3;
3221  free(ann->body);
3222  // TODO: do not merge nodes if those have edges targeting them
3223  // TODO: Add children neighbours to current one
3224  // nn->body
3225  // rz_agraph_set_curnode (g, get_anode (cn));
3226  // agraph_refresh (grd);
3227  // rz_agraph_add_edge (g, from, to);
3228  char *neis = sdb_get(g->db, K_NEIGHBOURS(ann), 0);
3229  if (neis) {
3230  sdb_set_owned(g->db, K_NEIGHBOURS(ann), neis, 0);
3231  rz_agraph_del_node(g, ann->title);
3234  }
3235  }
3236  // agraph_update_seek (g, get_anode (g->curnode), false);
3237 }
3238 #endif
3239 
3241  g->is_tiny = !g->is_tiny;
3242  g->need_update_dim = 1;
3243  agraph_refresh(rz_cons_singleton()->event_data);
3245  // remove_dummy_nodes (g);
3246 }
3247 
3249  RzANode *n = get_anode(g->curnode);
3250  if (n) {
3251  n->is_mini = !n->is_mini;
3252  }
3253  g->need_update_dim = 1;
3254  agraph_refresh(rz_cons_singleton()->event_data);
3256 }
3257 
3258 static void agraph_follow_innodes(RzAGraph *g, bool in) {
3259  int count = 0;
3260  RzListIter *iter;
3261  RzANode *an = get_anode(g->curnode);
3262  if (!an) {
3263  return;
3264  }
3265  const RzList *list = in ? an->gnode->in_nodes : an->gnode->out_nodes;
3266  int nth = -1;
3267  if (rz_list_length(list) == 0) {
3268  return;
3269  }
3270  rz_cons_gotoxy(0, 2);
3271  rz_cons_printf(in ? "Input nodes:\n" : "Output nodes:\n");
3273  RzList *gnodes = in ? an->gnode->in_nodes : an->gnode->out_nodes;
3274  RzGraphNode *gn;
3275  rz_list_foreach (gnodes, iter, gn) {
3276  RzANode *an = get_anode(gn);
3277  RzGraphNode *gnn = agraph_get_title(g, an, in);
3278  if (gnn) {
3279  RzANode *nnn = gnn->data;
3280  RzANode *o;
3281  RzListIter *iter2;
3282  // avoid dupes
3283  rz_list_foreach (options, iter2, o) {
3284  if (!strcmp(o->title, nnn->title)) {
3285  continue;
3286  }
3287  }
3288  rz_cons_printf("%d %s\n", count, nnn->title);
3289  rz_list_append(options, nnn);
3290  count++;
3291  }
3292  }
3293  rz_cons_flush();
3294  if (rz_list_length(list) == 1) {
3295  nth = 0;
3296  } else if (rz_list_length(list) < 10) {
3297  // just 1 key
3298  char ch = rz_cons_readchar();
3299  if (ch >= '0' && ch <= '9') {
3300  nth = ch - '0';
3301  }
3302  } else {
3303  rz_cons_show_cursor(true);
3304  rz_cons_enable_mouse(false);
3305  char *nth_string = rz_cons_input("index> ");
3306  nth = atoi(nth_string);
3307  if (nth == 0 && *nth_string != '0') {
3308  nth = -1;
3309  }
3310  free(nth_string);
3311  }
3312  if (nth != -1) {
3313  RzANode *selected_node = rz_list_get_n(options, nth);
3314  rz_agraph_set_curnode(g, selected_node);
3315  }
3317  agraph_update_seek(g, get_anode(g->curnode), false);
3318 }
3319 
3321  follow_nth(g, 0);
3322  agraph_update_seek(g, get_anode(g->curnode), false);
3323 }
3324 
3326  follow_nth(g, 1);
3327  agraph_update_seek(g, get_anode(g->curnode), false);
3328 }
3329 
3330 /* seek the next node in visual order */
3331 static void agraph_next_node(RzAGraph *g) {
3332  RzANode *a = get_anode(find_near_of(g, g->curnode, true));
3333  while (a && a->is_dummy) {
3334  a = get_anode(find_near_of(g, a->gnode, true));
3335  }
3337  agraph_update_seek(g, get_anode(g->curnode), false);
3338 }
3339 
3340 /* seek the previous node in visual order */
3341 static void agraph_prev_node(RzAGraph *g) {
3342  RzANode *a = get_anode(find_near_of(g, g->curnode, false));
3343  while (a && a->is_dummy) {
3344  a = get_anode(find_near_of(g, a->gnode, false));
3345  }
3347  agraph_update_seek(g, get_anode(g->curnode), false);
3348 }
3349 
3351  RzANode *a = get_anode(g->curnode);
3353  char *new_title = rz_str_newf(
3354  "%s[0x%08" PFMT64x "]> %s # %s ",
3355  graphCursor ? "(cursor)" : "",
3356  fcn->addr, a ? a->title : "", sig);
3357  rz_agraph_set_title(g, new_title);
3358  free(new_title);
3359  free(sig);
3360 }
3361 
3362 /* look for any change in the state of the graph
3363  * and update what's necessary */
3364 static bool check_changes(RzAGraph *g, int is_interactive, RzCore *core, RzAnalysisFunction *fcn) {
3365  int oldpos[2] = {
3366  0, 0
3367  };
3368  if (g->need_reload_nodes && core) {
3369  if (!g->update_seek_on && !g->force_update_seek) {
3370  // save scroll here
3371  oldpos[0] = g->can->sx;
3372  oldpos[1] = g->can->sy;
3373  }
3374  if (!agraph_reload_nodes(g, core, fcn)) {
3375  return false;
3376  }
3377  }
3378  if (fcn) {
3379  agraph_update_title(core, g, fcn);
3380  }
3381  if (core && core->config) {
3382  if (rz_config_get_i(core->config, "graph.trace")) {
3383  // fold all bbs not traced
3384  fold_asm_trace(core, g);
3385  }
3386  }
3387  if (g->need_update_dim || g->need_reload_nodes || !is_interactive) {
3388  update_node_dimension(g->graph, is_mini(g), g->zoom, g->edgemode, g->is_callgraph, g->layout);
3389  }
3390  if (g->need_set_layout || g->need_reload_nodes || !is_interactive) {
3392  }
3393  if (core) {
3395  if (block) {
3396  char *title = get_title(block->addr);
3397  RzANode *cur_anode = get_anode(g->curnode);
3398  if (fcn && ((is_interactive && !cur_anode) || (cur_anode && strcmp(cur_anode->title, title)))) {
3399  g->update_seek_on = rz_agraph_get_node(g, title);
3400  if (g->update_seek_on) {
3401  rz_agraph_set_curnode(g, g->update_seek_on);
3402  g->force_update_seek = true;
3403  }
3404  }
3405  free(title);
3406  }
3407  g->can->color = rz_config_get_i(core->config, "scr.color");
3408  g->hints = rz_config_get_i(core->config, "graph.hints");
3409  }
3410  if (g->update_seek_on || g->force_update_seek) {
3411  RzANode *n = g->update_seek_on;
3412  if (!n && g->curnode) {
3413  n = get_anode(g->curnode);
3414  }
3415  if (n) {
3416  update_seek(g->can, n, g->force_update_seek);
3417  }
3418  }
3419  if (oldpos[0] || oldpos[1]) {
3420  g->can->sx = oldpos[0];
3421  g->can->sy = oldpos[1];
3422  }
3423  g->need_reload_nodes = false;
3424  g->need_update_dim = false;
3425  g->need_set_layout = false;
3426  g->update_seek_on = NULL;
3427  g->force_update_seek = false;
3428  return true;
3429 }
3430 
3431 static int agraph_print(RzAGraph *g, int is_interactive, RzCore *core, RzAnalysisFunction *fcn) {
3432  int h, w = rz_cons_get_size(&h);
3433  bool ret = check_changes(g, is_interactive, core, fcn);
3434  if (!ret) {
3435  return false;
3436  }
3437 
3438  if (is_interactive) {
3439  rz_cons_clear00();
3440  } else {
3441  /* TODO: limit to screen size when the output is not redirected to file */
3443  }
3444 
3445  h = is_interactive ? h : g->h + 1;
3446  w = is_interactive ? w : g->w + 2;
3447  if (!rz_cons_canvas_resize(g->can, w, h)) {
3448  return false;
3449  }
3450  // rz_cons_canvas_clear (g->can);
3451  if (!is_interactive) {
3452  g->can->sx = -g->x;
3453  g->can->sy = -g->y - 1;
3454  }
3455  if (g->is_dis) {
3456  (void)G(-g->can->sx + 1, -g->can->sy + 2);
3457  int scr_utf8 = rz_config_get_i(core->config, "scr.utf8");
3458  int asm_bytes = rz_config_get_i(core->config, "asm.bytes");
3459  int asm_cmt_right = rz_config_get_i(core->config, "asm.cmt.right");
3460  rz_config_set_i(core->config, "scr.utf8", 0);
3461  rz_config_set_i(core->config, "asm.bytes", 0);
3462  rz_config_set_i(core->config, "asm.cmt.right", 0);
3463  char *str = rz_core_cmd_str(core, "pd $r");
3464  if (str) {
3465  W(str);
3466  free(str);
3467  }
3468  rz_config_set_i(core->config, "scr.utf8", scr_utf8);
3469  rz_config_set_i(core->config, "asm.bytes", asm_bytes);
3470  rz_config_set_i(core->config, "asm.cmt.right", asm_cmt_right);
3471  }
3472  if (g->title && *g->title) {
3473  g->can->sy++;
3474  }
3477  if (g->title && *g->title) {
3478  g->can->sy--;
3479  }
3480  /* print the graph title */
3481  (void)G(-g->can->sx, -g->can->sy);
3482  if (!g->is_tiny) {
3483  W(g->title);
3484  }
3485  if (is_interactive && g->title) {
3486  int title_len = strlen(g->title);
3487  rz_cons_canvas_fill(g->can, -g->can->sx + title_len, -g->can->sy,
3488  w - title_len, 1, ' ');
3489  }
3490 
3492 
3493  if (is_interactive) {
3494  rz_cons_newline();
3495  const char *cmdv = rz_config_get(core->config, "cmd.gprompt");
3496  bool mustFlush = false;
3498  if (cmdv && *cmdv) {
3499  rz_cons_gotoxy(0, 2);
3501  rz_core_cmd0(core, cmdv);
3502  mustFlush = true;
3503  }
3504  if (core && core->scr_gadgets) {
3505  rz_core_gadget_print(core);
3506  }
3507  if (mustFlush) {
3508  rz_cons_flush();
3509  }
3510  }
3511  return true;
3512 }
3513 
3516  if (rz_config_get_i(core->config, "analysis.detectwrites") || rz_cons_yesno('y', "Function was modified. Reanalyze? (Y/n)")) {
3518  }
3519  }
3520 }
3521 
3522 static int agraph_refresh(struct agraph_refresh_data *grd) {
3523  if (!grd) {
3524  return 0;
3525  }
3526  rz_cons_singleton()->event_data = grd;
3527  RzCore *core = grd->core;
3528  RzAGraph *g = grd->g;
3530  RzAnalysisFunction **fcn = grd->fcn;
3531 
3532  if (!fcn) {
3533  return agraph_print(g, grd->fs, core, NULL);
3534  }
3535 
3536  // allow to change the current function during debugging
3537  if (g->is_instep && core->bin->is_debugger) {
3538  // seek only when the graph node changes
3539  const char *pc = rz_reg_get_name(core->dbg->reg, RZ_REG_NAME_PC);
3540  RzRegItem *r = rz_reg_get(core->dbg->reg, pc, -1);
3541  ut64 addr = rz_reg_get_value(core->dbg->reg, r);
3542  RzANode *acur = get_anode(g->curnode);
3543 
3545  char *title = get_title(block ? block->addr : addr);
3546  if (!acur || strcmp(acur->title, title)) {
3547  rz_core_seek_to_register(core, "PC", false);
3548  }
3549  free(title);
3550  g->is_instep = false;
3551  }
3552 
3553  if (grd->follow_offset) {
3554  if (rz_io_is_valid_offset(core->io, core->offset, 0)) {
3555  f = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
3556  if (!f) {
3557  if (!g->is_dis) {
3558  if (!rz_cons_yesno('y', "\rNo function at 0x%08" PFMT64x ". Define it here (Y/n)? ", core->offset)) {
3559  return 0;
3560  }
3561  rz_core_analysis_function_add(core, NULL, core->offset, false);
3562  }
3563  f = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
3564  g->need_reload_nodes = true;
3565  }
3566  if (f && fcn && f != *fcn) {
3567  *fcn = f;
3568  check_function_modified(core, *fcn);
3569  g->need_reload_nodes = true;
3570  g->force_update_seek = true;
3571  }
3572  } else {
3573  // TODO: maybe go back to avoid seeking from graph view to an scary place?
3574  rz_cons_message("This is not a valid offset\n");
3575  rz_cons_flush();
3576  }
3577  }
3578 
3579  int res = agraph_print(g, grd->fs, core, *fcn);
3580 
3581  if (rz_config_get_i(core->config, "scr.scrollbar")) {
3583  }
3584 
3585  return res;
3586 }
3587 
3589  rz_core_task_enqueue_oneshot(&grd->core->tasks, (RzCoreTaskOneShot)agraph_refresh, grd);
3590 }
3591 
3593  grd->g->need_reload_nodes = true;
3594 }
3595 
3596 static void agraph_toggle_speed(RzAGraph *g, RzCore *core) {
3597  const int alt = rz_config_get_i(core->config, "graph.scroll");
3598  g->movspeed = g->movspeed == DEFAULT_SPEED ? alt : DEFAULT_SPEED;
3599 }
3600 
3601 static void free_nodes_kv(HtPPKv *kv) {
3602  RzANode *n = (RzANode *)kv->value;
3603  if (!n->is_dummy) {
3605  }
3606 }
3607 
3608 static HtPPOptions nodes_opt = {
3609  .cmp = (HtPPListComparator)strcmp,
3610  .hashfn = (HtPPHashFunction)sdb_hash,
3611  .dupkey = NULL,
3612  .dupvalue = NULL,
3613  .calcsizeK = (HtPPCalcSizeK)strlen,
3614  .calcsizeV = NULL,
3615  .freefn = free_nodes_kv,
3616  .elem_size = sizeof(HtPPKv),
3617 };
3618 
3619 static void agraph_init(RzAGraph *g) {
3620  g->is_callgraph = false;
3621  g->is_instep = false;
3622  g->need_reload_nodes = true;
3623  g->show_node_titles = true;
3624  g->show_node_body = true;
3625  g->force_update_seek = true;
3626  g->graph = rz_graph_new();
3627  g->nodes = ht_pp_new_opt(&nodes_opt);
3628  g->dummy_nodes = rz_list_newf((RzListFree)agraph_node_free);
3629  g->edgemode = 2;
3630  g->zoom = ZOOM_DEFAULT;
3631  g->hints = 1;
3632  g->movspeed = DEFAULT_SPEED;
3633  g->db = sdb_new0();
3634  rz_vector_init(&g->ghits.word_list, sizeof(struct rz_agraph_location), NULL, NULL);
3635 }
3636 
3637 static void graphNodeMove(RzAGraph *g, int dir, int speed) {
3638  int delta = (dir == 'k') ? -1 : 1;
3639  if (dir == 'H') {
3640  return;
3641  }
3642  if (dir == 'h' || dir == 'l') {
3643  // horizontal scroll
3644  if (is_mini(g)) {
3645  discroll = 0;
3646  } else {
3647  int delta = (dir == 'l') ? 1 : -1;
3648  move_current_node(g, speed * delta, 0);
3649  }
3650  return;
3651  }
3652  RzCore *core = NULL;
3653  // vertical scroll
3654  if (is_mini(g)) {
3655  discroll += (delta * speed);
3656  } else if (g->is_dis) {
3657  rz_core_seek_opcode(core, (delta * 4) * speed, false);
3658  } else {
3659  move_current_node(g, 0, delta * speed);
3660  }
3661 }
3662 
3663 static void sdb_set_enc(Sdb *db, const char *key, const char *v, ut32 cas) {
3664  char *estr = sdb_encode((const void *)v, -1);
3665  sdb_set(db, key, estr, cas);
3666  free(estr);
3667 }
3668 
3669 static void agraph_sdb_init(const RzAGraph *g) {
3670  sdb_bool_set(g->db, "agraph.is_callgraph", g->is_callgraph, 0);
3671  RzCons *cons = rz_cons_singleton();
3672  sdb_set_enc(g->db, "agraph.color_box", cons->context->pal.graph_box, 0);
3673  sdb_set_enc(g->db, "agraph.color_box2", cons->context->pal.graph_box2, 0);
3674  sdb_set_enc(g->db, "agraph.color_box3", cons->context->pal.graph_box3, 0);
3675  sdb_set_enc(g->db, "agraph.color_true", cons->context->pal.graph_true, 0);
3676  sdb_set_enc(g->db, "agraph.color_false", cons->context->pal.graph_false, 0);
3677 }
3678 
3680  g->need_update_dim = true;
3681  g->need_set_layout = true;
3682  (void)check_changes(g, false, NULL, NULL);
3683  // remove_dummy_nodes (g);
3684  return g->db;
3685 }
3686 
3688  agraph_print(g, false, NULL, NULL);
3689  if (g->graph->n_nodes > 0) {
3690  rz_cons_newline();
3691  }
3692 }
3693 
3695  RzList *nodes = g->graph->nodes, *neighbours = NULL;
3696  RzListIter *it, *itt;
3697  RzGraphNode *node = NULL, *neighbour = NULL;
3698  if (!pj) {
3699  return;
3700  }
3701  rz_list_foreach (nodes, it, node) {
3702  RzANode *anode = (RzANode *)node->data;
3703  char *label = strdup(anode->body);
3704  pj_o(pj);
3705  pj_ki(pj, "id", anode->gnode->idx);
3706  pj_ks(pj, "title", anode->title);
3707  pj_ks(pj, "body", label);
3708  pj_k(pj, "out_nodes");
3709  pj_a(pj);
3710  neighbours = anode->gnode->out_nodes;
3711  rz_list_foreach (neighbours, itt, neighbour) {
3712  pj_i(pj, neighbour->idx);
3713  }
3714  pj_end(pj);
3715  pj_end(pj);
3716  free(label);
3717  }
3718 }
3719 
3720 RZ_API void rz_agraph_set_title(RzAGraph *g, const char *title) {
3721  free(g->title);
3722  g->title = title ? strdup(title) : NULL;
3723  sdb_set(g->db, "agraph.title", g->title, 0);
3724 }
3725 
3726 RZ_API RzANode *rz_agraph_add_node_with_color(const RzAGraph *g, const char *title, const char *body, int color) {
3727  RzANode *res = rz_agraph_get_node(g, title);
3728  if (res) {
3729  return res;
3730  }
3731  res = RZ_NEW0(RzANode);
3732  if (!res) {
3733  return NULL;
3734  }
3735 
3736  res->title = title ? rz_str_trunc_ellipsis(title, 255) : strdup("");
3737  res->body = body ? strdup(body) : strdup("");
3738  res->layer = -1;
3739  res->pos_in_layer = -1;
3740  res->is_dummy = false;
3741  res->is_reversed = false;
3742  res->klass = -1;
3743  res->difftype = color;
3744  res->gnode = rz_graph_add_node(g->graph, res);
3745  if (RZ_STR_ISNOTEMPTY(res->title)) {
3746  ht_pp_update(g->nodes, res->title, res);
3747  char *s, *estr, *b;
3748  size_t len;
3749  sdb_array_add(g->db, "agraph.nodes", res->title, 0);
3750  b = strdup(res->body);
3751  len = strlen(b);
3752  if (len > 0 && b[len - 1] == '\n') {
3753  b[len - 1] = '\0';
3754  }
3755  estr = sdb_encode((const void *)b, -1);
3756  // s = sdb_fmt ("base64:%s", estr);
3757  s = rz_str_newf("base64:%s", estr);
3758  free(estr);
3759  free(b);
3760  sdb_set_owned(g->db, sdb_fmt("agraph.nodes.%s.body", res->title), s, 0);
3761  }
3762  return res;
3763 }
3764 
3765 RZ_API RzANode *rz_agraph_add_node(const RzAGraph *g, const char *title, const char *body) {
3766  return rz_agraph_add_node_with_color(g, title, body, -1);
3767 }
3768 
3769 RZ_API bool rz_agraph_del_node(const RzAGraph *g, const char *title) {
3770  char *title_trunc = rz_str_trunc_ellipsis(title, 255);
3771  RzANode *an, *res = rz_agraph_get_node(g, title_trunc);
3772  free(title_trunc);
3773  RzGraphNode *gn;
3774  RzListIter *it;
3775 
3776  if (!res) {
3777  return false;
3778  }
3779  sdb_array_remove(g->db, "agraph.nodes", res->title, 0);
3780  sdb_set(g->db, sdb_fmt("agraph.nodes.%s", res->title), NULL, 0);
3781  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.body", res->title), 0, 0);
3782  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.x", res->title), NULL, 0);
3783  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.y", res->title), NULL, 0);
3784  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.w", res->title), NULL, 0);
3785  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.h", res->title), NULL, 0);
3786  sdb_set(g->db, sdb_fmt("agraph.nodes.%s.neighbours", res->title), NULL, 0);
3787 
3788  const RzList *innodes = rz_graph_innodes(g->graph, res->gnode);
3789  graph_foreach_anode (innodes, it, gn, an) {
3790  const char *key = sdb_fmt("agraph.nodes.%s.neighbours", an->title);
3791  sdb_array_remove(g->db, key, res->title, 0);
3792  }
3793 
3794  rz_graph_del_node(g->graph, res->gnode);
3795  res->gnode = NULL;
3796 
3797  ht_pp_delete(g->nodes, res->title);
3798  return true;
3799 }
3800 
3801 static bool user_node_cb(struct g_cb *user, RZ_UNUSED const void *k, const void *v) {
3802  RzANodeCallback cb = user->node_cb;
3803  void *user_data = user->data;
3804  RzANode *n = (RzANode *)v;
3805  if (n) {
3806  cb(n, user_data);
3807  }
3808  return true;
3809 }
3810 
3811 static bool user_edge_cb(struct g_cb *user, RZ_UNUSED const void *k, const void *v) {
3812  RAEdgeCallback cb = user->edge_cb;
3813  RzAGraph *g = user->graph;
3814  void *user_data = user->data;
3815  RzANode *an, *n = (RzANode *)v;
3816  if (!n) {
3817  return false;
3818  }
3819  const RzList *neigh = rz_graph_get_neighbours(g->graph, n->gnode);
3820  RzListIter *it;
3821  RzGraphNode *gn;
3822 
3823  graph_foreach_anode (neigh, it, gn, an) {
3824  cb(n, an, user_data);
3825  }
3826  return true;
3827 }
3828 
3830  struct g_cb u = {
3831  .node_cb = cb,
3832  .data = user
3833  };
3834  ht_pp_foreach(g->nodes, (HtPPForeachCallback)user_node_cb, &u);
3835 }
3836 
3838  struct g_cb u = {
3839  .graph = g,
3840  .edge_cb = cb,
3841  .data = user
3842  };
3843  ht_pp_foreach(g->nodes, (HtPPForeachCallback)user_edge_cb, &u);
3844 }
3845 
3847  const RzList *l = rz_graph_get_nodes(g->graph);
3848  RzGraphNode *rgn = rz_list_first(l);
3849  return get_anode(rgn);
3850 }
3851 
3852 RZ_API RzANode *rz_agraph_get_node(const RzAGraph *g, const char *title) {
3853  char *title_trunc = title ? rz_str_trunc_ellipsis(title, 255) : NULL;
3854  if (!title_trunc) {
3855  return NULL;
3856  }
3857  RzANode *node = ht_pp_find(g->nodes, title_trunc, NULL);
3858  free(title_trunc);
3859  return node;
3860 }
3861 
3863  rz_return_if_fail(g && a && b);
3864  rz_graph_add_edge(g->graph, a->gnode, b->gnode);
3865  if (a->title && b->title) {
3866  char *k = sdb_fmt("agraph.nodes.%s.neighbours", a->title);
3867  sdb_array_add(g->db, k, b->title, 0);
3868  }
3869 }
3870 
3872  rz_return_if_fail(g && a && b);
3873  if (a->title && b->title) {
3874  char *k = sdb_fmt("agraph.nodes.%s.neighbours", a->title);
3875  sdb_array_insert(g->db, k, nth, b->title, 0);
3876  }
3877  rz_graph_add_edge_at(g->graph, a->gnode, b->gnode, nth);
3878 }
3879 
3881  rz_return_if_fail(g && a && b);
3882  if (a->title && b->title) {
3883  const char *k = sdb_fmt("agraph.nodes.%s.neighbours", a->title);
3884  sdb_array_remove(g->db, k, b->title, 0);
3885  }
3886  rz_graph_del_edge(g->graph, a->gnode, b->gnode);
3887 }
3888 
3890  ht_pp_free(g->nodes);
3891  rz_list_free(g->dummy_nodes);
3892  rz_graph_reset(g->graph);
3894  sdb_reset(g->db);
3895  if (g->edges) {
3896  rz_list_purge(g->edges);
3897  }
3898  g->nodes = ht_pp_new_opt(&nodes_opt);
3899  g->dummy_nodes = rz_list_newf((RzListFree)agraph_node_free);
3900  g->update_seek_on = NULL;
3901  g->need_reload_nodes = false;
3902  g->need_set_layout = true;
3903  g->need_update_dim = true;
3904  g->x = g->y = g->w = g->h = 0;
3905  agraph_sdb_init(g);
3906  g->curnode = NULL;
3907 }
3908 
3910  if (g) {
3911  ht_pp_free(g->nodes);
3912  rz_list_free(g->dummy_nodes);
3913  rz_graph_free(g->graph);
3914  rz_list_free(g->edges);
3916  sdb_free(g->db);
3917  rz_cons_canvas_free(g->can);
3918  free(g);
3919  }
3920 }
3921 
3923  RzAGraph *g = RZ_NEW0(RzAGraph);
3924  if (!g) {
3925  return NULL;
3926  }
3927  g->can = can;
3928  g->dummy = true;
3929  agraph_init(g);
3930  agraph_sdb_init(g);
3931  return g;
3932 }
3933 
3934 static void visual_offset(RzAGraph *g, RzCore *core) {
3935  char buf[256];
3936  int rows;
3937  rz_cons_get_size(&rows);
3938  rz_cons_gotoxy(0, rows);
3939  rz_cons_flush();
3942  rz_line_set_prompt("[offset]> ");
3943  strcpy(buf, "s ");
3944  if (rz_cons_fgets(buf + 2, sizeof(buf) - 2, 0, NULL) > 0) {
3945  if (buf[2] == '.') {
3946  buf[1] = '.';
3947  }
3948  rz_core_cmd0(core, buf);
3950  }
3952 }
3953 
3954 static void goto_asmqjmps(RzAGraph *g, RzCore *core) {
3955  const char *h = "[Fast goto call/jmp]> ";
3957  int rows, i = 0;
3958  bool cont;
3959 
3960  rz_cons_get_size(&rows);
3961  rz_cons_gotoxy(0, rows);
3962  rz_cons_clear_line(0);
3963  rz_cons_print(Color_RESET);
3964  rz_cons_print(h);
3965  rz_cons_flush();
3966 
3967  do {
3968  char ch = rz_cons_readchar();
3969  obuf[i++] = ch;
3970  rz_cons_printf("%c", ch);
3971  cont = isalpha((ut8)ch) && !islower((ut8)ch);
3972  } while (i < RZ_CORE_ASMQJMPS_LEN_LETTERS && cont);
3973  rz_cons_flush();
3974 
3975  obuf[i] = '\0';
3977  if (addr != UT64_MAX) {
3978  char *title = get_title(addr);
3979  RzANode *addr_node = rz_agraph_get_node(g, title);
3980  if (addr_node) {
3981  rz_agraph_set_curnode(g, addr_node);
3982  rz_core_seek(core, addr, false);
3983  agraph_update_seek(g, addr_node, true);
3984  } else {
3985  rz_core_seek_and_save(core, addr, false);
3986  }
3987  free(title);
3988  }
3989 }
3990 
3991 static void seek_to_node(RzANode *n, RzCore *core) {
3993  char *title = get_title(block ? block->addr : core->offset);
3994 
3995  if (title && strcmp(title, n->title)) {
3996  char *cmd = rz_str_newf("s %s", n->title);
3997  if (cmd) {
3998  if (*cmd) {
3999  rz_core_cmd0(core, cmd);
4000  }
4001  free(cmd);
4002  }
4003  }
4004  free(title);
4005 }
4006 
4007 static void graph_single_step_in(RzCore *core, RzAGraph *g) {
4009  g->is_instep = true;
4010  g->need_reload_nodes = true;
4011 }
4012 
4013 static void graph_single_step_over(RzCore *core, RzAGraph *g) {
4015  g->is_instep = true;
4016  g->need_reload_nodes = true;
4017 }
4018 
4019 static void graph_breakpoint(RzCore *core) {
4020  ut64 addr = core->print->cur_enabled ? core->offset + core->print->cur : core->offset;
4022 }
4023 
4024 static void graph_continue(RzCore *core) {
4025  rz_core_debug_continue(core);
4026 }
4027 static void applyDisMode(RzCore *core) {
4028  switch (disMode) {
4029  case 0:
4030  rz_config_set(core->config, "asm.pseudo", "false");
4031  rz_config_set(core->config, "asm.esil", "false");
4032  break;
4033  case 1:
4034  rz_config_set(core->config, "asm.pseudo", "true");
4035  rz_config_set(core->config, "asm.esil", "false");
4036  break;
4037  case 2:
4038  rz_config_set(core->config, "asm.pseudo", "false");
4039  rz_config_set(core->config, "asm.esil", "true");
4040  break;
4041  }
4042 }
4043 
4044 static void rotateColor(RzCore *core) {
4045  int color = rz_config_get_i(core->config, "scr.color");
4046  if (++color > 2) {
4047  color = 0;
4048  }
4049  rz_config_set_i(core->config, "scr.color", color);
4050 }
4051 
4052 static char *get_graph_string(RzCore *core, RzAGraph *g) {
4053  int c = rz_config_get_i(core->config, "scr.color");
4054  int u = rz_config_get_i(core->config, "scr.utf8");
4055  rz_config_set_i(core->config, "scr.color", 0);
4056  rz_config_set_i(core->config, "scr.utf8", 0);
4057  rz_core_visual_graph(core, g, NULL, false);
4058  char *s = rz_cons_get_buffer_dup();
4059  rz_cons_reset();
4060  rz_config_set_i(core->config, "scr.color", c);
4061  rz_config_set_i(core->config, "scr.utf8", u);
4062  return s;
4063 }
4064 
4065 static void nextword(RzCore *core, RzAGraph *g, const char *word) {
4066  rz_return_if_fail(core && core->graph && g && g->can && word);
4067  if (RZ_STR_ISEMPTY(word)) {
4068  return;
4069  }
4070  RzAGraphHits *gh = &g->ghits;
4071  RzConsCanvas *can = g->can;
4072  if (gh->word_list.len && gh->old_word && !strcmp(word, gh->old_word)) {
4073  if (gh->word_nth >= gh->word_list.len) {
4074  gh->word_nth = 0;
4075  }
4076 
4078  gh->word_nth++;
4079  if (pos) {
4080  can->sx = -pos->x + can->w / 2;
4081  can->sy = -pos->y + can->h / 2;
4082  }
4083  return;
4084  } else {
4085  rz_vector_clear(&gh->word_list);
4086  }
4087  char *s = get_graph_string(core, g);
4088  rz_cons_clear00();
4089  rz_cons_flush();
4090  const size_t MAX_COUNT = 4096;
4091  const char *a = NULL;
4092  size_t count = 0;
4093  int x = 0, y = 0;
4094  for (count = 0; count < MAX_COUNT; count++) {
4095  a = rz_str_str_xy(s, word, a, &x, &y);
4096  if (!a) {
4097  break;
4098  }
4100  if (pos) {
4101  pos->x = x + g->x;
4102  pos->y = y + g->y;
4103  }
4104  }
4105  free(gh->old_word);
4106  gh->old_word = strdup(word);
4107  free(s);
4108  if (!a && count == 0) {
4109  return;
4110  }
4111  nextword(core, g, word);
4112 }
4113 
4114 RZ_API int rz_core_visual_graph(RzCore *core, RzAGraph *g, RzAnalysisFunction *_fcn, int is_interactive) {
4115  if (is_interactive && !rz_cons_is_interactive()) {
4116  eprintf("Interactive graph mode requires scr.interactive=true.\n");
4117  return 0;
4118  }
4119  int o_asmqjmps_letter = core->is_asmqjmps_letter;
4120  int o_vmode = core->vmode;
4121  int exit_graph = false, is_error = false;
4122  int update_seek = false;
4123  struct agraph_refresh_data *grd;
4124  int okey, key;
4126  const char *key_s;
4127  RzConsCanvas *can, *o_can = NULL;
4128  bool graph_allocated = false;
4129  int movspeed;
4130  int ret, invscroll;
4132  if (!hc) {
4133  return false;
4134  }
4135  rz_config_hold_i(hc, "asm.pseudo", "asm.esil", "asm.cmt.right", NULL);
4136 
4137  int h, w = rz_cons_get_size(&h);
4138  can = rz_cons_canvas_new(w, h);
4139  if (!can) {
4140  w = 80;
4141  h = 25;
4142  can = rz_cons_canvas_new(w, h);
4143  if (!can) {
4144  eprintf("Cannot create RzCons.canvas context. Invalid screen "
4145  "size? See scr.columns + scr.rows\n");
4146  rz_config_hold_free(hc);
4147  return false;
4148  }
4149  }
4150  can->linemode = rz_config_get_i(core->config, "graph.linemode");
4151  can->color = rz_config_get_i(core->config, "scr.color");
4152 
4153  if (!g) {
4154  graph_allocated = true;
4155  fcn = _fcn ? _fcn : rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
4156  if (!fcn) {
4158  rz_config_hold_free(hc);
4159  rz_cons_canvas_free(can);
4160  return false;
4161  }
4163  g = rz_agraph_new(can);
4164  if (!g) {
4165  rz_cons_canvas_free(can);
4167  rz_config_hold_free(hc);
4168  return false;
4169  }
4170  g->is_tiny = is_interactive == 2;
4171  g->layout = rz_config_get_i(core->config, "graph.layout");
4172  g->dummy = rz_config_get_i(core->config, "graph.dummy");
4173  g->show_node_titles = rz_config_get_i(core->config, "graph.ntitles");
4174  } else {
4175  o_can = g->can;
4176  }
4177  g->can = can;
4178  g->movspeed = rz_config_get_i(core->config, "graph.scroll");
4179  g->show_node_titles = rz_config_get_i(core->config, "graph.ntitles");
4180  g->show_node_body = rz_config_get_i(core->config, "graph.body");
4181  g->on_curnode_change = (RzANodeCallback)seek_to_node;
4182  g->on_curnode_change_data = core;
4183  g->edgemode = rz_config_get_i(core->config, "graph.edges");
4184  g->hints = rz_config_get_i(core->config, "graph.hints");
4185  g->is_interactive = is_interactive;
4186  bool asm_comments = rz_config_get_i(core->config, "asm.comments");
4187  rz_config_set(core->config, "asm.comments",
4188  rz_str_bool(rz_config_get_i(core->config, "graph.comments")));
4189 
4190  /* we want letters as shortcuts for call/jmps */
4191  core->is_asmqjmps_letter = true;
4192  core->vmode = true;
4193 
4194  grd = RZ_NEW0(struct agraph_refresh_data);
4195  if (!grd) {
4196  rz_cons_canvas_free(can);
4198  rz_config_hold_free(hc);
4199  rz_agraph_free(g);
4200  return false;
4201  }
4202  grd->g = g;
4203  grd->fs = is_interactive == 1;
4204  grd->core = core;
4205  grd->follow_offset = _fcn == NULL;
4206  grd->fcn = fcn != NULL ? &fcn : NULL;
4207  ret = agraph_refresh(grd);
4208  if (!ret || is_interactive != 1) {
4209  rz_cons_newline();
4210  exit_graph = true;
4211  is_error = !ret;
4212  }
4213 
4214  core->cons->event_resize = NULL; // avoid running old event with new data
4215  core->cons->event_data = grd;
4217 
4219 
4220  while (!exit_graph && !is_error && !rz_cons_is_breaked()) {
4221  rz_cons_get_size(&h);
4222  invscroll = rz_config_get_i(core->config, "graph.invscroll");
4223  ret = agraph_refresh(grd);
4224 
4225  if (!ret) {
4226  is_error = true;
4227  break;
4228  }
4229  showcursor(core, false);
4230 
4231  // rz_core_graph_inputhandle()
4232  okey = rz_cons_readchar();
4233  key = rz_cons_arrow_to_hjkl(okey);
4234 
4235  if (core->cons->mouse_event) {
4236  movspeed = rz_config_get_i(core->config, "scr.wheel.speed");
4237  switch (key) {
4238  case 'j':
4239  case 'k':
4240  switch (mousemode) {
4241  case 0: break;
4242  case 1: key = key == 'k' ? 'h' : 'l'; break;
4243  case 2: key = key == 'k' ? 'J' : 'K'; break;
4244  case 3: key = key == 'k' ? 'L' : 'H'; break;
4245  }
4246  break;
4247  }
4248  } else {
4249  movspeed = g->movspeed;
4250  }
4251  const char *cmd;
4252  switch (key) {
4253  case '-':
4254  agraph_set_zoom(g, g->zoom - ZOOM_STEP);
4255  g->force_update_seek = true;
4256  break;
4257  case '+':
4258  agraph_set_zoom(g, g->zoom + ZOOM_STEP);
4259  g->force_update_seek = true;
4260  break;
4261  case '0':
4263  agraph_update_seek(g, get_anode(g->curnode), true);
4264  // update scroll (with minor shift)
4265  break;
4266  case '=': { // TODO: edit
4267  showcursor(core, true);
4268  const char *cmd = rz_config_get(core->config, "cmd.gprompt");
4269  rz_line_set_prompt("cmd.gprompt> ");
4270  core->cons->line->contents = strdup(cmd);
4271  const char *buf = rz_line_readline();
4272  core->cons->line->contents = NULL;
4273  rz_config_set(core->config, "cmd.gprompt", buf);
4274  showcursor(core, false);
4275  } break;
4276  case '|': {
4277  int e = rz_config_get_i(core->config, "graph.layout");
4278  if (++e > 1) {
4279  e = 0;
4280  }
4281  rz_config_set_i(core->config, "graph.layout", e);
4282  g->layout = rz_config_get_i(core->config, "graph.layout");
4283  g->need_update_dim = true;
4284  g->need_set_layout = true;
4285  }
4286  discroll = 0;
4287  agraph_update_seek(g, get_anode(g->curnode), true);
4288  break;
4289  case 'e': {
4290  int e = rz_config_get_i(core->config, "graph.edges");
4291  e++;
4292  if (e > 2) {
4293  e = 0;
4294  }
4295  rz_config_set_i(core->config, "graph.edges", e);
4296  g->edgemode = e;
4297  g->need_update_dim = true;
4298  get_bbupdate(g, core, fcn);
4299  } break;
4300  case '\\':
4301  nextword(core, g, rz_config_get(core->config, "scr.highlight"));
4302  break;
4303  case 'b':
4305  break;
4306  case 'E': {
4307  int e = rz_config_get_i(core->config, "graph.linemode");
4308  e--;
4309  if (e < 0) {
4310  e = 1;
4311  }
4312  rz_config_set_i(core->config, "graph.linemode", e);
4313  g->can->linemode = e;
4314  get_bbupdate(g, core, fcn);
4315  } break;
4316  case 13:
4317  agraph_update_seek(g, get_anode(g->curnode), true);
4318  update_seek = true;
4319  exit_graph = true;
4320  break;
4321  case '>':
4322  if (fcn && rz_cons_yesno('y', "Compute function callgraph? (Y/n)")) {
4324  rz_core_cmd0(core, ".agc* @$FB;.axfg @$FB");
4326  }
4327  break;
4328  case '<':
4329  // rz_core_visual_xrefs (core, true, false);
4330  if (fcn) {
4332  rz_core_cmd0(core, ".axtg $FB");
4334  }
4335  break;
4336  case 'G':
4338  rz_core_cmd0(core, ".dtg*");
4340  break;
4341  case 'V':
4342  if (fcn) {
4344  }
4345  break;
4346  case 'Z':
4347  if (okey == 27) { // shift-tab
4349  }
4350  break;
4351  case 's':
4352  if (!fcn) {
4353  break;
4354  }
4355  key_s = rz_config_get(core->config, "key.s");
4356  if (key_s && *key_s) {
4357  rz_core_cmd0(core, key_s);
4358  } else {
4360  }
4361  discroll = 0;
4362  agraph_update_seek(g, get_anode(g->curnode), true);
4363  break;
4364  case 'S':
4365  if (fcn) {
4367  }
4368  break;
4369  case 'x':
4370  case 'X': {
4371  if (!fcn) {
4372  break;
4373  }
4374  ut64 old_off = core->offset;
4376  if (block) {
4377  rz_core_seek(core, block->addr, false);
4378  }
4379  if ((key == 'x' && !rz_core_visual_xrefs(core, true, true)) ||
4380  (key == 'X' && !rz_core_visual_xrefs(core, false, true))) {
4381  rz_core_seek(core, old_off, false);
4382  }
4383  break;
4384  }
4385  case 9: // tab
4387  discroll = 0;
4388  break;
4389  case '?':
4390  rz_cons_clear00();
4391  rz_cons_printf("Visual Ascii Art graph keybindings:\n"
4392  " :e cmd.gprompt = agft - show tinygraph in one side\n"
4393  " +/-/0 - zoom in/out/default\n"
4394  " ; - add comment in current basic block\n"
4395  " . (dot) - center graph to the current node\n"
4396  " , (comma) - toggle graph.few\n"
4397  " ^ - seek to the first bb of the function\n"
4398  " = - toggle graph.layout\n"
4399  " :cmd - run rizin command\n"
4400  " ' - toggle graph.comments\n"
4401  " \" - toggle graph.refs\n"
4402  " # - toggle graph.hints\n"
4403  " / - highlight text\n"
4404  " \\ - scroll the graph canvas to the next highlight location\n"
4405  " | - set cmd.gprompt\n"
4406  " _ - enter hud selector\n"
4407  " > - show function callgraph (see graph.refs)\n"
4408  " < - show program callgraph (see graph.refs)\n"
4409  " ( - reverse conditional branch of last instruction in bb\n"
4410  " ) - rotate asm.emu and emu.str\n"
4411  " Home/End - go to the top/bottom of the canvas\n"
4412  " Page-UP/DOWN - scroll canvas up/down\n"
4413  " b - visual browse things\n"
4414  " c - toggle graph cursor mode\n"
4415  " C - toggle scr.colors\n"
4416  " d - rename function\n"
4417  " D - toggle the mixed graph+disasm mode\n"
4418  " e - rotate graph.edges (show/hide edges)\n"
4419  " E - rotate graph.linemode (square/diagonal lines)\n"
4420  " F - enter flag selector\n"
4421  " g - go/seek to given offset\n"
4422  " G - debug trace callgraph (generated with dtc)\n"
4423  " hjkl/HJKL - scroll canvas or node depending on graph cursor (uppercase for faster)\n"
4424  " i - select input nodes by index\n"
4425  " I - select output node by index\n"
4426  " m/M - change mouse modes\n"
4427  " n/N - next/previous scr.nkey (function/flag..)\n"
4428  " o([A-Za-z]*) - follow jmp/call identified by shortcut (like ;[oa])\n"
4429  " O - toggle asm.pseudo and asm.esil\n"
4430  " p/P - rotate graph modes (normal, display offsets, minigraph, summary)\n"
4431  " q - back to Visual mode\n"
4432  " r - toggle jmphints/leahints\n"
4433  " R - randomize colors\n"
4434  " s/S - step / step over\n"
4435  " tab - select next node\n"
4436  " TAB - select previous node\n"
4437  " t/f - follow true/false edges\n"
4438  " u/U - undo/redo seek\n"
4439  " V - toggle basicblock / call graphs\n"
4440  " w - toggle between movements speed 1 and graph.scroll\n"
4441  " x/X - jump to xref/ref\n"
4442  " Y - toggle tiny graph\n"
4443  " z - toggle node folding\n"
4444  " Z - toggle basic block folding");
4445  rz_cons_less();
4447  break;
4448  case '"':
4449  rz_config_toggle(core->config, "graph.refs");
4450  break;
4451  case '#':
4452  if (g->mode == RZ_AGRAPH_MODE_COMMENTS) {
4453  g->mode = RZ_AGRAPH_MODE_NORMAL;
4454  } else {
4455  g->mode = RZ_AGRAPH_MODE_COMMENTS;
4456  }
4457  g->need_reload_nodes = true;
4458  discroll = 0;
4459  agraph_update_seek(g, get_anode(g->curnode), true);
4460  // rz_config_toggle (core->config, "graph.hints");
4461  break;
4462  case 'p':
4463  g->mode = next_mode(g->mode);
4464  g->need_reload_nodes = true;
4465  agraph_update_seek(g, get_anode(g->curnode), true);
4466  break;
4467  case 'P':
4468  if (!fcn) {
4469  break;
4470  }
4471  g->mode = prev_mode(g->mode);
4472  g->need_reload_nodes = true;
4473  agraph_update_seek(g, get_anode(g->curnode), true);
4474  break;
4475  case 'o':
4476  goto_asmqjmps(g, core);
4477  break;
4478  case 'g':
4479  showcursor(core, true);
4480  visual_offset(g, core);
4481  showcursor(core, false);
4482  break;
4483  case 'O':
4484  if (!fcn) {
4485  break;
4486  }
4487  disMode = (disMode + 1) % 3;
4488  applyDisMode(core);
4489  g->need_reload_nodes = true;
4490  get_bbupdate(g, core, fcn);
4491  break;
4492  case 'u': {
4493  if (!fcn) {
4494  break;
4495  }
4496  if (!rz_core_seek_undo(core)) {
4497  eprintf("Cannot undo\n");
4498  }
4499  if (rz_config_get_i(core->config, "graph.few")) {
4500  g->need_reload_nodes = true;
4501  }
4502  break;
4503  }
4504  case 'U': {
4505  if (!fcn) {
4506  break;
4507  }
4508  if (!rz_core_seek_redo(core)) {
4509  eprintf("Cannot redo\n");
4510  }
4511  break;
4512  }
4513  case 'r':
4514  if (fcn) {
4515  g->layout = rz_config_get_i(core->config, "graph.layout");
4516  g->need_reload_nodes = true;
4517  }
4518  // TODO: toggle shortcut hotkeys
4520  break;
4521  case '$': {
4522  ut64 dst =
4524  ? core->offset + core->print->cur
4525  : core->offset;
4527  rz_core_seek_to_register(core, "PC", false);
4528  g->need_reload_nodes = true;
4529  break;
4530  }
4531  case 'R':
4532  if (rz_config_get_i(core->config, "scr.randpal")) {
4534  } else {
4536  }
4537  if (!fcn) {
4538  break;
4539  }
4540  g->edgemode = rz_config_get_i(core->config, "graph.edges");
4541  get_bbupdate(g, core, fcn);
4542  break;
4543  case '!':
4545  break;
4546  case '\'':
4547  if (fcn) {
4548  rz_config_toggle(core->config, "graph.comments");
4549  g->need_reload_nodes = true;
4550  }
4551  break;
4552  case ';':
4553  if (fcn) {
4554  showcursor(core, true);
4555  char buf[256];
4556  rz_line_set_prompt("[comment]> ");
4557  if (rz_cons_fgets(buf, sizeof(buf), 0, NULL) > 0) {
4559  }
4560  g->need_reload_nodes = true;
4561  showcursor(core, false);
4562  }
4563  break;
4564  case 'C':
4565  rotateColor(core);
4566  break;
4567  case 'm':
4568  mousemode++;
4569  if (!mousemodes[mousemode]) {
4570  mousemode = 0;
4571  }
4572  break;
4573  case 'M':
4574  mousemode--;
4575  if (mousemode < 0) {
4576  mousemode = 3;
4577  }
4578  break;
4579  case '(': {
4580  if (!fcn) {
4581  break;
4582  }
4583  if (!rz_core_seek_bb_instruction(core, -1)) {
4584  break;
4585  }
4586  ut64 oldseek = core->offset;
4587  core->tmpseek = true;
4588  rz_core_hack(core, "recj");
4589  core->tmpseek = false;
4590  rz_core_seek(core, oldseek, true);
4591  g->need_reload_nodes = true;
4592  break;
4593  }
4594  case ')':
4595  if (fcn) {
4596  rotateAsmemu(core);
4597  g->need_reload_nodes = true;
4598  }
4599  break;
4600  case 'd': {
4601  showcursor(core, true);
4602  rz_core_visual_define(core, "", 0);
4603  get_bbupdate(g, core, fcn);
4604  showcursor(core, false);
4605  } break;
4606  case 'D':
4607  g->is_dis = !g->is_dis;
4608  break;
4609  case 'n':
4610  rz_core_seek_next(core, rz_config_get(core->config, "scr.nkey"), true);
4611  break;
4612  case 'N':
4613  rz_core_seek_prev(core, rz_config_get(core->config, "scr.nkey"), true);
4614  break;
4615  case 'Y':
4617  agraph_update_seek(g, get_anode(g->curnode), true);
4618  break;
4619  case 'z':
4621  discroll = 0;
4622  agraph_update_seek(g, get_anode(g->curnode), true);
4623  break;
4624  case 'v':
4626  break;
4627  case 'J':
4628  // copypaste from 'j'
4629  if (graphCursor) {
4630  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4631  graphNodeMove(g, 'j', speed * 2);
4632  } else {
4633  can->sy -= (5 * movspeed) * (invscroll ? -1 : 1);
4634  }
4635  break;
4636  case 'K':
4637  if (graphCursor) {
4638  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4639  graphNodeMove(g, 'k', speed * 2);
4640  } else {
4641  can->sy += (5 * movspeed) * (invscroll ? -1 : 1);
4642  }
4643  break;
4644  case 'H':
4645  if (graphCursor) {
4646  // move node canvas faster
4647  graphNodeMove(g, 'h', movspeed * 2);
4648  } else {
4649  // scroll canvas faster
4650  if (okey == 27) {
4651  // handle home key
4652  const RzGraphNode *gn = find_near_of(g, NULL, true);
4653  g->update_seek_on = get_anode(gn);
4654  } else {
4655  can->sx += (5 * movspeed) * (invscroll ? -1 : 1);
4656  }
4657  }
4658  break;
4659  case 'L':
4660  if (graphCursor) {
4661  graphNodeMove(g, 'l', movspeed * 2);
4662  } else {
4663  can->sx -= (5 * movspeed) * (invscroll ? -1 : 1);
4664  }
4665  break;
4666  case 'c':
4668  break;
4669  case 'j':
4670  if (g->is_dis) {
4671  rz_core_seek_opcode(core, 1, false);
4672  } else {
4673  if (graphCursor) {
4674  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4675  graphNodeMove(g, 'j', speed);
4676  } else {
4677  // scroll canvas
4678  can->sy -= movspeed * (invscroll ? -1 : 1);
4679  }
4680  }
4681  break;
4682  case 'k':
4683  if (g->is_dis) {
4684  rz_core_seek_opcode(core, -1, false);
4685  } else {
4686  if (graphCursor) {
4687  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4688  graphNodeMove(g, 'k', speed);
4689  } else {
4690  // scroll canvas
4691  can->sy += movspeed * (invscroll ? -1 : 1);
4692  }
4693  }
4694  break;
4695  case 'l':
4696  if (graphCursor) {
4697  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4698  graphNodeMove(g, 'l', speed);
4699  } else {
4700  can->sx -= movspeed * (invscroll ? -1 : 1);
4701  }
4702  break;
4703  case 'h':
4704  if (graphCursor) {
4705  int speed = (okey == 27) ? PAGEKEY_SPEED : movspeed;
4706  graphNodeMove(g, 'h', speed);
4707  } else {
4708  can->sx += movspeed * (invscroll ? -1 : 1);
4709  }
4710  break;
4711  case '^': {
4713  if (fcn) {
4714  rz_core_seek(core, fcn->addr, false);
4715  }
4716  }
4717  agraph_update_seek(g, get_anode(g->curnode), true);
4718  break;
4719  case ',':
4720  rz_config_toggle(core->config, "graph.few");
4721  g->need_reload_nodes = true;
4722  agraph_update_seek(g, get_anode(g->curnode), true);
4723  break;
4724  case '.':
4725  discroll = 0;
4726  agraph_update_seek(g, get_anode(g->curnode), true);
4727  break;
4728  case 'i':
4729  agraph_follow_innodes(g, true);
4730  if (rz_config_get_i(core->config, "graph.few")) {
4731  g->need_reload_nodes = true;
4732  }
4733  break;
4734  case 'I':
4735  agraph_follow_innodes(g, false);
4736  if (rz_config_get_i(core->config, "graph.few")) {
4737  g->need_reload_nodes = true;
4738  }
4739  break;
4740  case 't':
4742  if (rz_config_get_i(core->config, "graph.few")) {
4743  g->need_reload_nodes = true;
4744  }
4745  break;
4746  case 'T':
4747  // XXX WIP agraph_merge_child (g, 0);
4748  break;
4749  case 'f':
4751  if (rz_config_get_i(core->config, "graph.few")) {
4752  g->need_reload_nodes = true;
4753  }
4754  break;
4755  case 'F':
4756  if (okey == 27) {
4757  // handle end key
4758  const RzGraphNode *gn = find_near_of(g, NULL, false);
4759  g->update_seek_on = get_anode(gn);
4760  } else {
4761  // agraph_merge_child (g, 1);
4763  }
4764  break;
4765  case '/':
4766  showcursor(core, true);
4767  rz_core_cmd0(core, "?i highlight;e scr.highlight=`yp`");
4768  showcursor(core, false);
4769  break;
4770  case ':':
4774  if (!g) {
4775  g->need_reload_nodes = true; // maybe too slow and unnecessary sometimes? better be safe and reload
4776  get_bbupdate(g, core, fcn);
4777  }
4778  break;
4779  case 'w':
4781  break;
4782  case '_':
4784  break;
4785  case RZ_CONS_KEY_F1:
4786  cmd = rz_config_get(core->config, "key.f1");
4787  if (cmd && *cmd) {
4788  (void)rz_core_cmd0(core, cmd);
4789  }
4790  break;
4791  case RZ_CONS_KEY_F2:
4792  cmd = rz_config_get(core->config, "key.f2");
4793  if (cmd && *cmd) {
4794  (void)rz_core_cmd0(core, cmd);
4795  } else {
4797  }
4798  break;
4799  case RZ_CONS_KEY_F3:
4800  cmd = rz_config_get(core->config, "key.f3");
4801  if (cmd && *cmd) {
4802  (void)rz_core_cmd0(core, cmd);
4803  }
4804  break;
4805  case RZ_CONS_KEY_F4:
4806  cmd = rz_config_get(core->config, "key.f4");
4807  if (cmd && *cmd) {
4808  (void)rz_core_cmd0(core, cmd);
4809  }
4810  break;
4811  case RZ_CONS_KEY_F5:
4812  cmd = rz_config_get(core->config, "key.f5");
4813  if (cmd && *cmd) {
4814  (void)rz_core_cmd0(core, cmd);
4815  }
4816  break;
4817  case RZ_CONS_KEY_F6:
4818  cmd = rz_config_get(core->config, "key.f6");
4819  if (cmd && *cmd) {
4820  (void)rz_core_cmd0(core, cmd);
4821  }
4822  break;
4823  case RZ_CONS_KEY_F7:
4824  cmd = rz_config_get(core->config, "key.f7");
4825  if (cmd && *cmd) {
4826  (void)rz_core_cmd0(core, cmd);
4827  } else {
4829  }
4830  break;
4831  case RZ_CONS_KEY_F8:
4832  cmd = rz_config_get(core->config, "key.f8");
4833  if (cmd && *cmd) {
4834  (void)rz_core_cmd0(core, cmd);
4835  } else {
4837  }
4838  break;
4839  case RZ_CONS_KEY_F9:
4840  cmd = rz_config_get(core->config, "key.f9");
4841  if (cmd && *cmd) {
4842  (void)rz_core_cmd0(core, cmd);
4843  } else {
4845  }
4846  break;
4847  case RZ_CONS_KEY_F10:
4848  cmd = rz_config_get(core->config, "key.f10");
4849  if (cmd && *cmd) {
4850  (void)rz_core_cmd0(core, cmd);
4851  }
4852  break;
4853  case RZ_CONS_KEY_F11:
4854  cmd = rz_config_get(core->config, "key.f11");
4855  if (cmd && *cmd) {
4856  (void)rz_core_cmd0(core, cmd);
4857  }
4858  break;
4859  case RZ_CONS_KEY_F12:
4860  cmd = rz_config_get(core->config, "key.f12");
4861  if (cmd && *cmd) {
4862  (void)rz_core_cmd0(core, cmd);
4863  }
4864  break;
4865  case -1: // EOF
4866  case ' ':
4867  case 'Q':
4868  case 'q':
4869  if (g->is_callgraph) {
4871  } else {
4872  exit_graph = true;
4873  }
4874  break;
4875  case 27: // ESC
4876  if (rz_cons_readchar() == 91) {
4877  if (rz_cons_readchar() == 90) {
4879  }
4880  }
4881  break;
4882  default:
4883  break;
4884  }
4885  }
4886  rz_vector_fini(&g->ghits.word_list);
4888  rz_config_set(core->config, "asm.comments", rz_str_bool(asm_comments));
4889  core->cons->event_resize = NULL;
4890  core->cons->event_data = NULL;
4891  core->vmode = o_vmode;
4892  core->is_asmqjmps_letter = o_asmqjmps_letter;
4893  core->keep_asmqjmps = false;
4894 
4895  free(grd);
4896  if (graph_allocated) {
4897  rz_agraph_free(g);
4898  } else {
4899  rz_cons_canvas_free(g->can);
4900  g->can = o_can;
4901  }
4903  rz_config_hold_free(hc);
4904  if (update_seek) {
4905  return -1;
4906  }
4907  return !is_error;
4908 }
4909 
4916 RZ_API RzAGraph *create_agraph_from_graph(const RzGraph /*<RzGraphNodeInfo *>*/ *graph) {
4917  rz_return_val_if_fail(graph, NULL);
4918 
4919  RzAGraph *result_agraph = rz_agraph_new(rz_cons_canvas_new(1, 1));
4920  if (!result_agraph) {
4921  return NULL;
4922  }
4923  result_agraph->need_reload_nodes = false;
4924  // Cache lookup to build edges
4925  HtPPOptions pointer_options = { 0 };
4926  HtPP /*<RzGraphNode *node, RzANode *anode>*/ *hashmap = ht_pp_new_opt(&pointer_options);
4927 
4928  if (!hashmap) {
4929  rz_agraph_free(result_agraph);
4930  return NULL;
4931  }
4932  // List of the new RzANodes
4933  RzListIter *iter;
4934  RzGraphNode *node;
4935  // Traverse the list, create new ANode for each Node
4936  rz_list_foreach (graph->nodes, iter, node) {
4937  RzGraphNodeInfo *info = node->data;
4938  RzANode *a_node = rz_agraph_add_node(result_agraph, info->title, info->body);
4939  if (!a_node) {
4940  goto failure;
4941  }
4942  ht_pp_insert(hashmap, node, a_node);
4943  }
4944 
4945  // Traverse the nodes again, now build up the edges
4946  rz_list_foreach (graph->nodes, iter, node) {
4947  RzANode *a_node = ht_pp_find(hashmap, node, NULL);
4948  if (!a_node) {
4949  goto failure; // shouldn't happen in correct graph state
4950  }
4951 
4952  RzListIter *neighbour_iter;
4953  RzGraphNode *neighbour;
4954  rz_list_foreach (node->in_nodes, neighbour_iter, neighbour) {
4955  RzANode *a_neighbour = ht_pp_find(hashmap, neighbour, NULL);
4956  if (!a_neighbour) {
4957  goto failure;
4958  }
4959  rz_agraph_add_edge(result_agraph, a_neighbour, a_node);
4960  }
4961  }
4962 
4963  ht_pp_free(hashmap);
4964  return result_agraph;
4965 failure:
4966  ht_pp_free(hashmap);
4967  rz_agraph_free(result_agraph);
4968  return NULL;
4969 }
size_t len
Definition: 6502dis.c:15
static bool is_offset(const RzAGraph *g)
Definition: agraph.c:113
static void append_shortcut(const RzAGraph *g, char *title, char *nodetitle, int left)
Definition: agraph.c:253
static void fold_asm_trace(RzCore *core, RzAGraph *g)
Definition: agraph.c:2274
RZ_API int rz_core_visual_graph(RzCore *core, RzAGraph *g, RzAnalysisFunction *_fcn, int is_interactive)
Definition: agraph.c:4114
static void free_vertical_nodes_kv(HtPPKv *kv)
Definition: agraph.c:916
static void rotateAsmemu(RzCore *core)
Definition: agraph.c:174
RZ_API RzANode * rz_agraph_add_node_with_color(const RzAGraph *g, const char *title, const char *body, int color)
Definition: agraph.c:3726
RZ_API bool rz_agraph_del_node(const RzAGraph *g, const char *title)
Definition: agraph.c:3769
static void goto_asmqjmps(RzAGraph *g, RzCore *core)
Definition: agraph.c:3954
static char * get_title(ut64 addr)
Definition: agraph.c:200
static void mini_RzANode_print(const RzAGraph *g, const RzANode *n, int cur, bool details)
Definition: agraph.c:265
static void minimize_crossings(const RzAGraph *g)
Definition: agraph.c:786
static int place_nodes_val(const RzAGraph *g, const RzGraphNode *gn, const RzGraphNode *sibl, HtPU *res, int is_left)
Definition: agraph.c:1108
static void original_traverse_l(const RzAGraph *g, HtPU *D, HtPU *P, int from_up)
Definition: agraph.c:1518
#define PAGEKEY_SPEED
Definition: agraph.c:38
static int dist_nodes(const RzAGraph *g, const RzGraphNode *a, const RzGraphNode *b)
Definition: agraph.c:825
static void graph_single_step_in(RzCore *core, RzAGraph *g)
Definition: agraph.c:4007
static void agraph_refresh_oneshot(struct agraph_refresh_data *grd)
Definition: agraph.c:3588
static int RM_listcmp(const struct len_pos_t *a, const struct len_pos_t *b)
Definition: agraph.c:1356
static int ** get_crossing_matrix(const RzGraph *g, const struct layer_t layers[], int maxlayer, int i, int from_up, int *n_rows)
Definition: agraph.c:433
static void place_single(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int va)
Definition: agraph.c:1313
static int hash_get_int(HtPU *ht, const void *key)
Definition: agraph.c:1029
static void agraph_sdb_init(const RzAGraph *g)
Definition: agraph.c:3669
static void set_layout(RzAGraph *g)
Definition: agraph.c:1958
RZ_API void rz_agraph_free(RzAGraph *g)
Definition: agraph.c:3909
static void assign_layers(const RzAGraph *g)
Definition: agraph.c:660
static void agraph_edge_free(AEdge *e)
Definition: agraph.c:1946
static void agraph_print_edges(RzAGraph *g)
Definition: agraph.c:2849
#define graph_foreach_anode(list, it, pos, anode)
Definition: agraph.c:58
RZ_API void rz_agraph_set_title(RzAGraph *g, const char *title)
Definition: agraph.c:3720
RZ_API RzAGraph * rz_agraph_new(RzConsCanvas *can)
Definition: agraph.c:3922
static int agraph_refresh(struct agraph_refresh_data *grd)
Definition: agraph.c:3522
static int cmp_dist(const size_t a, const size_t b)
Definition: agraph.c:1009
static void collect_changes(const RzAGraph *g, int l, const RzGraphNode *b, int from_up, int s, int e, RzList *list, int is_left)
Definition: agraph.c:1364
static void create_dummy_nodes(RzAGraph *g)
Definition: agraph.c:699
RZ_API RzANode * rz_agraph_get_first_node(const RzAGraph *g)
Definition: agraph.c:3846
static bool is_comments(const RzAGraph *g)
Definition: agraph.c:129
RZ_API void rz_agraph_del_edge(const RzAGraph *g, RzANode *a, RzANode *b)
Definition: agraph.c:3880
static int find_edge(const RzGraphEdge *a, const RzGraphEdge *b)
Definition: agraph.c:690
static void visual_offset(RzAGraph *g, RzCore *core)
Definition: agraph.c:3934
RZ_API RzANode * rz_agraph_add_node(const RzAGraph *g, const char *title, const char *body)
Definition: agraph.c:3765
static HtPU * compute_pos(const RzAGraph *g, int is_left, HtPP *v_nodes)
Definition: agraph.c:1164
static RzGraphNode * get_right_dummy(const RzAGraph *g, const RzGraphNode *n)
Definition: agraph.c:1238
#define BORDER_WIDTH
Definition: agraph.c:28
static bool isbbfew(RzAnalysisBlock *curbb, RzAnalysisBlock *bb)
Definition: agraph.c:2313
static void remove_cycles(RzAGraph *g)
Definition: agraph.c:627
static void tiny_RzANode_print(const RzAGraph *g, const RzANode *n, int cur)
Definition: agraph.c:323
static int mode2opts(const RzAGraph *g)
Definition: agraph.c:159
#define MARGIN_TEXT_X
Definition: agraph.c:30
static void agraph_toggle_callgraph(RzAGraph *g)
Definition: agraph.c:3146
static void adjust_class(const RzAGraph *g, int is_left, RzList **classes, HtPU *res, int c)
Definition: agraph.c:1047
static int is_valid_pos(const RzAGraph *g, int l, int pos)
Definition: agraph.c:912
static int get_edge_number(const RzAGraph *g, RzANode *src, RzANode *dst, bool outgoing)
Definition: agraph.c:1738
static void agraph_set_need_reload_nodes(struct agraph_refresh_data *grd)
Definition: agraph.c:3592
static void agraph_print_node(const RzAGraph *g, RzANode *n)
Definition: agraph.c:2743
static void view_cyclic_edge(const RzGraphEdge *e, const RzGraphVisitor *vis)
Definition: agraph.c:594
static int bbcmp(RzAnalysisBlock *a, RzAnalysisBlock *b)
Definition: agraph.c:2214
static int next_mode(int mode)
Definition: agraph.c:133
static int agraph_print(RzAGraph *g, int is_interactive, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:3431
static bool graphCursor
Definition: agraph.c:16
static void delete_dup_edges(RzAGraph *g)
Definition: agraph.c:2295
RZ_API Sdb * rz_agraph_get_sdb(RzAGraph *g)
Definition: agraph.c:3679
static RzGraphNode * agraph_get_title(const RzAGraph *g, RzANode *n, bool in)
Definition: agraph.c:141
static bool reload_nodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:2506
static void place_dummies(const RzAGraph *g)
Definition: agraph.c:1207
static RzGraphNode * get_sibling(const RzAGraph *g, const RzANode *n, int is_left, int is_adjust_class)
Definition: agraph.c:1013
#define ZOOM_DEFAULT
Definition: agraph.c:47
RZ_API void rz_agraph_set_curnode(RzAGraph *g, RzANode *a)
Definition: agraph.c:2696
static ut64 rebase(RzAGraph *g, int v)
Definition: agraph.c:2709
#define get_anode(gn)
Definition: agraph.c:56
static void agraph_print_edges_simple(RzAGraph *g)
Definition: agraph.c:2798
struct ascii_edge_t AEdge
static bool user_node_cb(struct g_cb *user, RZ_UNUSED const void *k, const void *v)
Definition: agraph.c:3801
static HtPP * compute_vertical_nodes(const RzAGraph *g)
Definition: agraph.c:924
static void graph_breakpoint(RzCore *core)
Definition: agraph.c:4019
static void nextword(RzCore *core, RzAGraph *g, const char *word)
Definition: agraph.c:4065
static int is_near_h(const RzANode *n, int x, int y, int is_next)
XXX is wrong.
Definition: agraph.c:2543
static bool check_changes(RzAGraph *g, int is_interactive, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:3364
static const char * mousemodes[]
Definition: agraph.c:17
static void agraph_set_layout(RzAGraph *g)
Definition: agraph.c:2713
static void agraph_node_free(RzANode *n)
Definition: agraph.c:204
static void agraph_next_node(RzAGraph *g)
Definition: agraph.c:3331
static int count_edges(const RzAGraph *g, RzANode *src, RzANode *dst)
Definition: agraph.c:1772
static void graphNodeMove(RzAGraph *g, int dir, int speed)
Definition: agraph.c:3637
RZ_API void rz_agraph_reset(RzAGraph *g)
Definition: agraph.c:3889
static void add_sorted(RzGraphNode *n, RzGraphVisitor *vis)
Definition: agraph.c:649
static void set_dist_nodes(const RzAGraph *g, int l, int cur, int next)
Definition: agraph.c:885
static void place_original(RzAGraph *g)
Definition: agraph.c:1583
static const RzGraphNode * find_near_of(const RzAGraph *g, const RzGraphNode *cur, int is_next)
Definition: agraph.c:2550
static void agraph_toggle_mini(RzAGraph *g)
Definition: agraph.c:3248
static void agraph_follow_true(RzAGraph *g)
Definition: agraph.c:3320
RZ_API void rz_agraph_foreach_edge(RzAGraph *g, RAEdgeCallback cb, void *user)
Definition: agraph.c:3837
static void set_layer_gap(RzAGraph *g)
Definition: agraph.c:1645
static RzList ** compute_classes(const RzAGraph *g, HtPP *v_nodes, int is_left, int *n_classes)
Definition: agraph.c:967
#define BORDER_HEIGHT
Definition: agraph.c:29
#define MINIGRAPH_NODE_TITLE_LEN
Definition: agraph.c:42
#define ZOOM_STEP
Definition: agraph.c:46
static int mousemode
Definition: agraph.c:13
#define MINIGRAPH_NODE_TEXT_CUR
Definition: agraph.c:40
static int first_x_cmp(const void *_a, const void *_b)
Definition: agraph.c:2829
#define NORMALIZE_MOV(x)
Definition: agraph.c:53
#define MIN_NODE_HEIGHT
Definition: agraph.c:35
static void agraph_set_zoom(RzAGraph *g, int v)
Definition: agraph.c:3152
static char * get_node_color(int color, int cur)
Definition: agraph.c:334
RZ_API RzANode * rz_agraph_get_node(const RzAGraph *g, const char *title)
Definition: agraph.c:3852
static char * get_graph_string(RzCore *core, RzAGraph *g)
Definition: agraph.c:4052
#define MINIGRAPH_NODE_MIN_WIDTH
Definition: agraph.c:41
static void agraph_update_title(RzCore *core, RzAGraph *g, RzAnalysisFunction *fcn)
Definition: agraph.c:3350
static int get_bbnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:2331
static int is_near(const RzANode *n, int x, int y, int is_next)
Definition: agraph.c:2535
static void graph_single_step_over(RzCore *core, RzAGraph *g)
Definition: agraph.c:4013
#define HORIZONTAL_NODE_SPACING
Definition: agraph.c:32
static char * get_bb_body(RzCore *core, RzAnalysisBlock *b, int opts, RzAnalysisFunction *fcn, bool emu, ut64 saved_gp, ut8 *saved_arena)
Definition: agraph.c:2167
static void normal_RzANode_print(const RzAGraph *g, const RzANode *n, int cur)
Definition: agraph.c:345
RZ_API void rz_agraph_print(RzAGraph *g)
Definition: agraph.c:3687
static void update_seek(RzConsCanvas *can, RzANode *n, int force)
Definition: agraph.c:2511
static bool agraph_reload_nodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:3178
static void combine_sequences(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int a, int r)
Definition: agraph.c:1430
RZ_API RzAGraph * create_agraph_from_graph(const RzGraph *graph)
Create RzAGraph from generic RzGraph with RzGraphNodeInfo as node data.
Definition: agraph.c:4916
static void applyDisMode(RzCore *core)
Definition: agraph.c:4027
static void place_sequence(const RzAGraph *g, int l, const RzGraphNode *bm, const RzGraphNode *bp, int from_up, int va, int vr)
Definition: agraph.c:1504
static void create_layers(RzAGraph *g)
Definition: agraph.c:743
static bool is_reversed(const RzAGraph *g, const RzGraphEdge *e)
Definition: agraph.c:694
static int RP_listcmp(const struct len_pos_t *a, const struct len_pos_t *b)
Definition: agraph.c:1360
static bool get_cgnodes(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:2446
static bool is_mini(const RzAGraph *g)
Definition: agraph.c:117
static bool is_summary(const RzAGraph *g)
Definition: agraph.c:125
static int adjust_class_val(const RzAGraph *g, const RzGraphNode *gn, const RzGraphNode *sibl, HtPU *res, int is_left)
Definition: agraph.c:1038
static int find_dist(const struct dist_t *a, const struct dist_t *b)
Definition: agraph.c:818
static char * get_body(RzCore *core, ut64 addr, int size, int opts)
Definition: agraph.c:2104
#define BODY_COMMENTS
Definition: agraph.c:51
int tmplayercmp(const void *a, const void *b)
Definition: agraph.c:2794
static bool is_tiny(const RzAGraph *g)
Definition: agraph.c:121
static void place_nodes(const RzAGraph *g, const RzGraphNode *gn, int is_left, HtPP *v_nodes, RzList **classes, HtPU *res, SetP *placed)
Definition: agraph.c:1126
static void agraph_follow_innodes(RzAGraph *g, bool in)
Definition: agraph.c:3258
static void agraph_prev_node(RzAGraph *g)
Definition: agraph.c:3341
static int discroll
Definition: agraph.c:15
static void sdb_set_enc(Sdb *db, const char *key, const char *v, ut32 cas)
Definition: agraph.c:3663
static void update_graph_sizes(RzAGraph *g)
Definition: agraph.c:2588
static int layer_sweep(const RzGraph *g, const struct layer_t layers[], int maxlayer, int i, int from_up)
Definition: agraph.c:549
RZ_API void rz_agraph_add_edge_at(const RzAGraph *g, RzANode *a, RzANode *b, int nth)
Definition: agraph.c:3871
RZ_API void rz_agraph_foreach(RzAGraph *g, RzANodeCallback cb, void *user)
Definition: agraph.c:3829
#define VERTICAL_NODE_SPACING
Definition: agraph.c:33
static HtPPOptions nodes_opt
Definition: agraph.c:3608
RZ_API void rz_agraph_print_json(RzAGraph *g, PJ *pj)
Definition: agraph.c:3694
static bool user_edge_cb(struct g_cb *user, RZ_UNUSED const void *k, const void *v)
Definition: agraph.c:3811
static void agraph_toggle_speed(RzAGraph *g, RzCore *core)
Definition: agraph.c:3596
static void agraph_print_nodes(const RzAGraph *g)
Definition: agraph.c:2758
static void move_current_node(RzAGraph *g, int xdiff, int ydiff)
Definition: agraph.c:3196
static void check_function_modified(RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:3514
static void seek_to_node(RzANode *n, RzCore *core)
Definition: agraph.c:3991
static void rotateColor(RzCore *core)
Definition: agraph.c:4044
static void graph_continue(RzCore *core)
Definition: agraph.c:4024
#define MARGIN_TEXT_Y
Definition: agraph.c:31
RZ_API void rz_agraph_add_edge(const RzAGraph *g, RzANode *a, RzANode *b)
Definition: agraph.c:3862
static void free_nodes_kv(HtPPKv *kv)
Definition: agraph.c:3601
static void adjust_directions(const RzAGraph *g, int i, int from_up, HtPU *D, HtPU *P)
Definition: agraph.c:1259
static void get_bbupdate(RzAGraph *g, RzCore *core, RzAnalysisFunction *fcn)
Definition: agraph.c:2218
#define MININODE_MIN_WIDTH
Definition: agraph.c:44
static void agraph_init(RzAGraph *g)
Definition: agraph.c:3619
static void showcursor(RzCore *core, int x)
Definition: agraph.c:188
static void view_dummy(const RzGraphEdge *e, const RzGraphVisitor *vis)
Definition: agraph.c:606
#define W(x)
Definition: agraph.c:110
static void fix_back_edge_dummy_nodes(RzAGraph *g, RzANode *from, RzANode *to)
Definition: agraph.c:1698
static void update_node_dimension(const RzGraph *g, int is_mini, int zoom, int edgemode, bool callgraph, int layout)
Definition: agraph.c:212
#define MIN_NODE_WIDTH
Definition: agraph.c:34
#define G(x, y)
Definition: agraph.c:109
#define BODY_SUMMARY
Definition: agraph.c:50
static int prev_mode(int mode)
Definition: agraph.c:137
static void agraph_update_seek(RzAGraph *g, RzANode *n, int force)
Definition: agraph.c:2738
static int place_nodes_sel_p(int newval, int oldval, int is_first, int is_left)
Definition: agraph.c:1115
#define BODY_OFFSETS
Definition: agraph.c:49
static void agraph_toggle_tiny(RzAGraph *g)
Definition: agraph.c:3240
static void backedge_info(RzAGraph *g)
Definition: agraph.c:1776
#define MINIGRAPH_NODE_CENTER_X
Definition: agraph.c:43
static void agraph_follow_false(RzAGraph *g)
Definition: agraph.c:3325
#define DEFAULT_SPEED
Definition: agraph.c:37
#define TITLE_LEN
Definition: agraph.c:36
static int disMode
Definition: agraph.c:14
static void follow_nth(RzAGraph *g, int nth)
Definition: agraph.c:3183
RZ_API bool rz_analysis_function_was_modified(RzAnalysisFunction *fcn)
Definition: function.c:366
#define e(frag)
lzma_index ** i
Definition: index.h:629
lzma_index * src
Definition: index.h:567
RZ_API ut8 * rz_reg_arena_dup(RzReg *reg, const ut8 *source)
Definition: arena.c:301
RZ_API ut8 * rz_reg_arena_peek(RzReg *reg)
Definition: arena.c:280
RZ_API void rz_reg_arena_poke(RzReg *reg, const ut8 *ret)
Definition: arena.c:293
ut16 val
Definition: armass64_const.h:6
static bool err
Definition: armass.c:435
RZ_API int sdb_array_add(Sdb *s, const char *key, const char *val, ut32 cas)
Definition: array.c:224
RZ_API int sdb_array_insert(Sdb *s, const char *key, int idx, const char *val, ut32 cas)
Definition: array.c:139
RZ_API int sdb_array_remove(Sdb *s, const char *key, const char *val, ut32 cas)
Definition: array.c:436
RZ_API char * sdb_encode(const ut8 *bin, int len)
Definition: base64.c:18
static RzList * classes(RzBinFile *bf)
Definition: bin_dex.c:71
RzBinInfo * info(RzBinFile *bf)
Definition: bin_ne.c:86
RZ_API RzAnalysisBlock * rz_analysis_get_block_at(RzAnalysis *analysis, ut64 addr)
Definition: block.c:90
RZ_API RzAnalysisBlock * rz_analysis_find_most_relevant_block_in(RzAnalysis *analysis, ut64 off)
Definition: block.c:997
#define D
Definition: block.c:38
const lzma_allocator const uint8_t * in
Definition: block.h:527
RZ_IPI void rz_core_agraph_print_interactive(RzCore *core)
Definition: cagraph.c:88
RZ_IPI void rz_core_agraph_reset(RzCore *core)
Definition: cagraph.c:8
RZ_API bool rz_core_analysis_function_add(RzCore *core, const char *name, ut64 addr, bool analyze_recursively)
Definition: canalysis.c:5298
RZ_IPI char * rz_core_analysis_function_signature(RzCore *core, RzOutputMode mode, char *fcn_name)
Definition: canalysis.c:5382
RZ_API void rz_cons_canvas_print_region(RzConsCanvas *c)
Definition: canvas.c:410
RZ_API void rz_cons_canvas_fill(RzConsCanvas *c, int x, int y, int w, int h, char ch)
Definition: canvas.c:551
RZ_API int rz_cons_canvas_resize(RzConsCanvas *c, int w, int h)
Definition: canvas.c:432
RZ_API void rz_cons_canvas_line(RzConsCanvas *c, int x, int y, int x2, int y2, RzCanvasLineStyle *style)
Definition: canvas.c:570
RZ_API RzConsCanvas * rz_cons_canvas_new(int w, int h)
Definition: canvas.c:223
RZ_API void rz_cons_canvas_box(RzConsCanvas *c, int x, int y, int w, int h, const char *color)
Definition: canvas.c:486
RZ_API void rz_cons_canvas_free(RzConsCanvas *c)
Definition: canvas.c:150
RZ_API void rz_cons_canvas_line_square_defined(RzConsCanvas *c, int x, int y, int x2, int y2, RzCanvasLineStyle *style, int bendpoint, int isvert)
Definition: canvas_line.c:401
RZ_API void rz_cons_canvas_line_back_edge(RzConsCanvas *c, int x, int y, int x2, int y2, RzCanvasLineStyle *style, int ybendpoint1, int xbendpoint, int ybendpoint2, int isvert)
Definition: canvas_line.c:454
RZ_API void rz_core_debug_breakpoint_toggle(RZ_NONNULL RzCore *core, ut64 addr)
Toggle breakpoint.
Definition: cdebug.c:235
RZ_IPI void rz_core_debug_continue(RzCore *core)
Definition: cdebug.c:81
RZ_IPI void rz_core_debug_single_step_in(RzCore *core)
Definition: cdebug.c:193
RZ_IPI void rz_core_debug_single_step_over(RzCore *core)
Definition: cdebug.c:207
RZ_API int rz_core_cmd0(RzCore *core, const char *cmd)
Definition: cmd.c:5428
RZ_API char * rz_core_cmd_strf(RzCore *core, const char *fmt,...)
Definition: cmd.c:5472
RZ_API char * rz_core_cmd_str(RzCore *core, const char *cmd)
Executes a rizin command and returns the stdout as a string.
Definition: cmd.c:5513
#define P
RZ_API void rz_core_theme_nextpal(RzCore *core, RzConsPalSeekMode mode)
Definition: cmd_eval.c:148
RZ_API void rz_core_gadget_print(RzCore *core)
Prints or displays the print gadgets while in visual mode.
Definition: cmd_print.c:1185
RZ_IPI bool rz_core_seek_to_register(RzCore *core, const char *regname, bool is_silent)
Definition: cmd_seek.c:22
RZ_IPI int rz_core_seek_opcode(RzCore *core, int n, bool silent)
Definition: cmd_seek.c:86
RZ_API ut64 rz_config_get_i(RzConfig *cfg, RZ_NONNULL const char *name)
Definition: config.c:119
RZ_API RzConfigNode * rz_config_set(RzConfig *cfg, RZ_NONNULL const char *name, const char *value)
Definition: config.c:267
RZ_API RzConfigNode * rz_config_set_i(RzConfig *cfg, RZ_NONNULL const char *name, const ut64 i)
Definition: config.c:419
RZ_API RZ_BORROW const char * rz_config_get(RzConfig *cfg, RZ_NONNULL const char *name)
Definition: config.c:75
RZ_API bool rz_config_toggle(RzConfig *cfg, RZ_NONNULL const char *name)
Definition: config.c:97
RZ_API void rz_cons_clear00(void)
Definition: cons.c:778
RZ_API int rz_cons_get_size(int *rows)
Definition: cons.c:1446
RZ_API RZ_OWN char * rz_cons_get_buffer_dup(void)
Return a newly allocated buffer containing what's currently in RzCons buffer.
Definition: cons.c:827
RZ_API bool rz_cons_enable_mouse(const bool enable)
Definition: cons.c:500
RZ_API RzCons * rz_cons_singleton(void)
Definition: cons.c:300
RZ_API void rz_cons_clear_line(int std_err)
Definition: cons.c:756
RZ_API void rz_cons_strcat(const char *str)
Definition: cons.c:1263
RZ_API void rz_cons_newline(void)
Definition: cons.c:1274
RZ_API void rz_cons_break_pop(void)
Definition: cons.c:361
RZ_API void rz_cons_break_push(RzConsBreak cb, void *user)
Definition: cons.c:357
RZ_API int rz_cons_printf(const char *format,...)
Definition: cons.c:1202
RZ_API bool rz_cons_is_interactive(void)
Definition: cons.c:365
RZ_API void rz_cons_visual_flush(void)
Definition: cons.c:1067
RZ_API void rz_cons_show_cursor(int cursor)
Definition: cons.c:1581
RZ_API void rz_cons_flush(void)
Definition: cons.c:959
RZ_API void rz_cons_break_end(void)
Definition: cons.c:427
RZ_API bool rz_cons_is_breaked(void)
Definition: cons.c:373
RZ_API void rz_cons_gotoxy(int x, int y)
Definition: cons.c:724
RZ_API void rz_cons_reset(void)
Definition: cons.c:804
#define RZ_API
RZ_IPI bool rz_core_seek_bb_instruction(RzCore *core, int index)
Definition: seek.c:453
#define INT_MAX
Definition: cp-demangle.c:131
RZ_API bool rz_core_reg_set_by_role_or_name(RzCore *core, const char *name, ut64 num)
set on rz_core_reg_default()
Definition: creg.c:39
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
#define w
Definition: crypto_rc6.c:13
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags cmd
Definition: sflib.h:79
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len key
Definition: sflib.h:118
uint32_t ut32
RZ_API int rz_line_hist_cmd_down(RzLine *line)
Definition: dietline.c:346
RZ_API const char * rz_line_readline(void)
Definition: dietline.c:913
RZ_API int rz_line_hist_cmd_up(RzLine *line)
Definition: dietline.c:316
RZ_API int rz_line_set_hist_callback(RzLine *line, RzLineHistoryUpCb up, RzLineHistoryDownCb down)
Definition: dietline.c:292
const char * k
Definition: dsignal.c:11
const char * v
Definition: dsignal.c:12
static states step(struct re_guts *, sopno, sopno, states, int, states)
Definition: engine.c:888
struct @667 g
int max
Definition: enough.c:225
void cleanup(void)
Definition: enough.c:244
RZ_DEPRECATE RZ_API RzAnalysisFunction * rz_analysis_get_fcn_in(RzAnalysis *analysis, ut64 addr, int type)
Definition: fcn.c:1687
RZ_API void rz_analysis_function_update_analysis(RzAnalysisFunction *fcn)
Definition: fcn.c:2435
RZ_API char * sdb_fmt(const char *fmt,...)
Definition: fmt.c:26
RZ_API bool rz_core_hack(RzCore *core, const char *op)
Write/Modify instructions at current offset based on op.
Definition: hack.c:280
RZ_API void rz_config_hold_restore(RzConfigHold *h)
Restore whatever config options were previously saved in h.
Definition: hold.c:132
RZ_API RzConfigHold * rz_config_hold_new(RzConfig *cfg)
Create an opaque object to save/restore some configuration options.
Definition: hold.c:116
RZ_API bool rz_config_hold_i(RzConfigHold *h,...)
Save the current values of a list of config options that have integer values.
Definition: hold.c:81
RZ_API void rz_config_hold_free(RzConfigHold *h)
Free a RzConfigHold object h.
Definition: hold.c:152
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
static void freefn(HtName_(Ht) *ht, HT_(Kv) *kv)
Definition: ht_inc.c:46
static ut32 hashfn(HtName_(Ht) *ht, const KEY_TYPE k)
Definition: ht_inc.c:22
RZ_API const KEY_TYPE bool * found
Definition: ht_inc.h:130
RZ_API void rz_cons_message(RZ_NONNULL const char *msg)
Definition: hud.c:321
static char * rp[]
Definition: i8080dis.c:36
RZ_API char * rz_cons_input(const char *msg)
Definition: input.c:696
RZ_API int rz_cons_arrow_to_hjkl(int ch)
Definition: input.c:78
RZ_API int rz_cons_any_key(const char *msg)
Definition: input.c:393
RZ_API int rz_cons_fgets(char *buf, int len, int argc, const char **argv)
Definition: input.c:339
RZ_API bool rz_cons_yesno(int def, const char *fmt,...)
Definition: input.c:666
RZ_API int rz_cons_readchar(void)
Definition: input.c:619
static RzSocket * gs
Definition: io_winedbg.c:11
voidpf void uLong size
Definition: ioapi.h:138
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
snprintf
Definition: kernel.h:364
RZ_API void rz_cons_less(void)
Definition: less.c:171
uint8_t ut8
Definition: lh5801.h:11
void * p
Definition: libc.cpp:67
RZ_API ut64 rz_core_get_asmqjmps(RzCore *core, const char *str)
Definition: core.c:203
RZ_API char * rz_core_add_asmqjmp(RzCore *core, ut64 addr)
Definition: core.c:238
static void list(RzEgg *egg)
Definition: rz-gg.c:52
RZ_API RZ_BORROW RzListIter * rz_list_find(RZ_NONNULL const RzList *list, const void *p, RZ_NONNULL RzListComparator cmp)
Returns RzListIter element which matches via the RzListComparator.
Definition: list.c:620
RZ_API RZ_BORROW RzListIter * rz_list_contains(RZ_NONNULL const RzList *list, RZ_NONNULL const void *ptr)
Returns the RzListIter of the given pointer, if found.
Definition: list.c:592
RZ_API RZ_BORROW RzListIter * rz_list_prepend(RZ_NONNULL RzList *list, void *data)
Appends at the beginning of the list a new element.
Definition: list.c:316
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API void rz_list_delete(RZ_NONNULL RzList *list, RZ_NONNULL RzListIter *iter)
Removes an entry in the list by using the RzListIter pointer.
Definition: list.c:162
RZ_API void * rz_list_iter_get_data(RzListIter *list)
returns the value stored in the list element
Definition: list.c:42
RZ_API bool rz_list_delete_data(RZ_NONNULL RzList *list, void *ptr)
Deletes an entry in the list by searching for a pointer.
Definition: list.c:148
RZ_API RZ_OWN void * rz_list_pop(RZ_NONNULL RzList *list)
Removes and returns the last element of the list.
Definition: list.c:376
RZ_API RZ_OWN RzList * rz_list_new(void)
Returns a new initialized RzList pointer (free method is not initialized)
Definition: list.c:235
RZ_API void rz_list_sort(RZ_NONNULL RzList *list, RZ_NONNULL RzListComparator cmp)
Sorts via merge sort or via insertion sort a list.
Definition: list.c:743
RZ_API RZ_BORROW void * rz_list_first(RZ_NONNULL const RzList *list)
Returns the first element of the list.
Definition: list.c:77
RZ_API RZ_BORROW void * rz_list_get_n(RZ_NONNULL const RzList *list, ut32 n)
Returns the N-th element of the list.
Definition: list.c:574
RZ_API RZ_BORROW RzListIter * rz_list_push(RZ_NONNULL RzList *list, void *item)
Alias for rz_list_append.
Definition: list.c:60
RZ_API ut32 rz_list_length(RZ_NONNULL const RzList *list)
Returns the length of the list.
Definition: list.c:109
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API RZ_BORROW RzListIter * rz_list_add_sorted(RZ_NONNULL RzList *list, void *data, RZ_NONNULL RzListComparator cmp)
Adds an element to a sorted list via the RzListComparator.
Definition: list.c:518
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
RZ_API void rz_list_purge(RZ_NONNULL RzList *list)
Empties the list without freeing the list pointer.
Definition: list.c:120
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
RZ_API void rz_line_set_prompt(const char *prompt)
Definition: line.c:56
return strdup("=SP r13\n" "=LR r14\n" "=PC r15\n" "=A0 r0\n" "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" "=CF cf\n" "=SN or0\n" "gpr lr .32 56 0\n" "gpr pc .32 60 0\n" "gpr cpsr .32 64 0 ____tfiae_________________qvczn\n" "gpr or0 .32 68 0\n" "gpr tf .1 64.5 0 thumb\n" "gpr ef .1 64.9 0 endian\n" "gpr jf .1 64.24 0 java\n" "gpr qf .1 64.27 0 sticky_overflow\n" "gpr vf .1 64.28 0 overflow\n" "gpr cf .1 64.29 0 carry\n" "gpr zf .1 64.30 0 zero\n" "gpr nf .1 64.31 0 negative\n" "gpr itc .4 64.10 0 if_then_count\n" "gpr gef .4 64.16 0 great_or_equal\n" "gpr r0 .32 0 0\n" "gpr r1 .32 4 0\n" "gpr r2 .32 8 0\n" "gpr r3 .32 12 0\n" "gpr r4 .32 16 0\n" "gpr r5 .32 20 0\n" "gpr r6 .32 24 0\n" "gpr r7 .32 28 0\n" "gpr r8 .32 32 0\n" "gpr r9 .32 36 0\n" "gpr r10 .32 40 0\n" "gpr r11 .32 44 0\n" "gpr r12 .32 48 0\n" "gpr r13 .32 52 0\n" "gpr r14 .32 56 0\n" "gpr r15 .32 60 0\n" "gpr r16 .32 64 0\n" "gpr r17 .32 68 0\n")
static const char struct stat static buf struct stat static buf static vhangup int options
Definition: sflib.h:145
char * dst
Definition: lz4.h:724
@ RZ_ABS
RZ_API bool rz_meta_set_string(RzAnalysis *a, RzAnalysisMetaType type, ut64 addr, const char *s)
Definition: meta.c:141
int x
Definition: mipsasm.c:20
int n
Definition: mipsasm.c:19
int idx
Definition: setup.py:197
RZ_API int sdb_num_set(Sdb *s, const char *key, ut64 v, ut32 cas)
Definition: num.c:25
RZ_API int sdb_bool_set(Sdb *db, const char *str, bool v, ut32 cas)
Definition: num.c:72
RZ_API void rz_cons_pal_random(void)
Definition: pal.c:270
RZ_API bool rz_core_visual_panels_root(RzCore *core, RzPanelsRoot *panels_root)
Definition: panels.c:5947
#define min(a, b)
Definition: qsort.h:83
RZ_API ut64 rz_reg_getv(RzReg *reg, const char *name)
Definition: reg.c:332
RZ_API RzRegItem * rz_reg_get(RzReg *reg, const char *name, int type)
Definition: reg.c:344
RZ_API const char * rz_reg_get_name(RzReg *reg, int role)
Definition: reg.c:147
#define eprintf(x, y...)
Definition: rlcc.c:7
static RzSocket * s
Definition: rtr.c:28
RZ_API ut64 rz_reg_get_value(RzReg *reg, RzRegItem *item)
Definition: rvalue.c:114
#define RZ_AGRAPH_MODE_NORMAL
Definition: rz_agraph.h:35
#define RZ_AGRAPH_MODE_COMMENTS
Definition: rz_agraph.h:40
void(* RAEdgeCallback)(RzANode *from, RzANode *to, void *user)
Definition: rz_agraph.h:44
#define RZ_AGRAPH_MODE_MINI
Definition: rz_agraph.h:37
#define RZ_AGRAPH_MODE_SUMMARY
Definition: rz_agraph.h:39
void(* RzANodeCallback)(RzANode *n, void *user)
Definition: rz_agraph.h:43
#define RZ_AGRAPH_MODE_MAX
Definition: rz_agraph.h:41
#define RZ_AGRAPH_MODE_TINY
Definition: rz_agraph.h:38
#define RZ_AGRAPH_MODE_OFFSET
Definition: rz_agraph.h:36
@ RZ_ANALYSIS_DIFF_TYPE_MATCH
Definition: rz_analysis.h:206
@ RZ_ANALYSIS_DIFF_TYPE_UNMATCH
Definition: rz_analysis.h:207
@ RZ_META_TYPE_COMMENT
Definition: rz_analysis.h:295
#define rz_return_if_fail(expr)
Definition: rz_assert.h:100
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
#define RZ_CONS_KEY_F12
Definition: rz_cons.h:589
#define RZ_CONS_KEY_F11
Definition: rz_cons.h:588
#define RZ_CONS_KEY_F10
Definition: rz_cons.h:587
#define Color_RESET
Definition: rz_cons.h:617
#define RZ_CONS_KEY_F6
Definition: rz_cons.h:583
#define RZ_CONS_KEY_F7
Definition: rz_cons.h:584
#define RZ_CONS_KEY_F3
Definition: rz_cons.h:580
#define RZ_CONS_KEY_F2
Definition: rz_cons.h:579
void(* RzConsEvent)(void *)
Definition: rz_cons.h:346
#define RZ_CONS_KEY_F1
Definition: rz_cons.h:578
#define DOT_STYLE_CONDITIONAL
Definition: rz_cons.h:784
#define DOT_STYLE_NORMAL
Definition: rz_cons.h:783
#define RZ_CONS_KEY_F5
Definition: rz_cons.h:582
#define RZ_EDGES_X_INC
Definition: rz_cons.h:994
#define RZ_CONS_KEY_F4
Definition: rz_cons.h:581
@ RZ_LINE_PROMPT_DEFAULT
Definition: rz_cons.h:1037
@ RZ_LINE_PROMPT_OFFSET
Definition: rz_cons.h:1038
#define Color_YELLOW
Definition: rz_cons.h:631
#define RZ_CONS_KEY_F9
Definition: rz_cons.h:586
#define DOT_STYLE_BACKEDGE
Definition: rz_cons.h:785
#define UTF_CIRCLE
Definition: rz_cons.h:418
@ LINE_NOSYM_HORIZ
Definition: rz_cons.h:775
@ LINE_NOSYM_VERT
Definition: rz_cons.h:774
@ LINE_TRUE
Definition: rz_cons.h:771
@ LINE_NONE
Definition: rz_cons.h:770
@ LINE_FALSE
Definition: rz_cons.h:772
@ LINE_UNCJMP
Definition: rz_cons.h:773
#define RZ_CONS_KEY_F8
Definition: rz_cons.h:585
#define RZ_CORE_ASMQJMPS_LEN_LETTERS
Definition: rz_core.h:186
RZ_API RzGraph * rz_graph_new(void)
Definition: graph.c:108
RZ_API void rz_graph_del_node(RzGraph *g, RzGraphNode *n)
Definition: graph.c:177
void(* RzGraphEdgeCallback)(const RzGraphEdge *e, RzGraphVisitor *vis)
Definition: rz_graph.h:41
RZ_API const RzList * rz_graph_all_neighbours(const RzGraph *g, const RzGraphNode *n)
Definition: graph.c:261
RZ_API void rz_graph_dfs(RzGraph *g, RzGraphVisitor *vis)
Definition: graph.c:299
RZ_API void rz_graph_del_edge(RzGraph *g, RzGraphNode *from, RzGraphNode *to)
Definition: graph.c:232
RZ_API RzGraphNode * rz_graph_nth_neighbour(const RzGraph *g, const RzGraphNode *n, int nth)
Definition: graph.c:251
RZ_API const RzList * rz_graph_get_nodes(const RzGraph *g)
Definition: graph.c:265
RZ_API const RzList * rz_graph_get_neighbours(const RzGraph *g, const RzGraphNode *n)
Definition: graph.c:245
RZ_API void rz_graph_reset(RzGraph *g)
Definition: graph.c:141
RZ_API const RzList * rz_graph_innodes(const RzGraph *g, const RzGraphNode *n)
Definition: graph.c:256
RZ_API void rz_graph_add_edge(RzGraph *g, RzGraphNode *from, RzGraphNode *to)
Definition: graph.c:199
RZ_API void rz_graph_add_edge_at(RzGraph *g, RzGraphNode *from, RzGraphNode *to, int nth)
Definition: graph.c:203
RZ_API void rz_graph_free(RzGraph *g)
Definition: graph.c:124
RZ_API RzGraphNode * rz_graph_add_node(RzGraph *g, void *data)
Definition: graph.c:153
void(* RzGraphNodeCallback)(RzGraphNode *n, RzGraphVisitor *vis)
Definition: rz_graph.h:40
RZ_API bool rz_io_is_valid_offset(RzIO *io, ut64 offset, int hasperm)
Definition: ioutils.c:20
void(* RzListFree)(void *ptr)
Definition: rz_list.h:11
int(* RzListComparator)(const void *value, const void *list_data)
Definition: rz_list.h:33
RZ_API ut64 rz_num_get(RzNum *num, const char *str)
Definition: unum.c:172
RZ_API PJ * pj_ki(PJ *j, const char *k, int d)
Definition: pj.c:149
RZ_API PJ * pj_k(PJ *j, const char *k)
Definition: pj.c:104
RZ_API PJ * pj_end(PJ *j)
Definition: pj.c:87
RZ_API PJ * pj_i(PJ *j, int d)
Definition: pj.c:284
RZ_API PJ * pj_o(PJ *j)
Definition: pj.c:75
RZ_API PJ * pj_ks(PJ *j, const char *k, const char *v)
Definition: pj.c:170
RZ_API PJ * pj_a(PJ *j)
Definition: pj.c:81
@ RZ_REG_NAME_PC
Definition: rz_reg.h:43
RZ_API char * rz_str_trim_lines(char *str)
Definition: str_trim.c:60
#define RZ_STR_ISNOTEMPTY(x)
Definition: rz_str.h:68
RZ_API const char * rz_str_str_xy(const char *s, const char *word, const char *prev, int *x, int *y)
Definition: str.c:3964
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_str_append(char *ptr, const char *string)
Definition: str.c:1063
RZ_API const char * rz_str_bool(int b)
Definition: str.c:3896
#define RZ_STR_ISEMPTY(x)
Definition: rz_str.h:67
RZ_API char * rz_str_ansi_crop(const char *str, unsigned int x, unsigned int y, unsigned int x2, unsigned int y2)
Definition: str.c:2174
RZ_API char * rz_str_replace(char *str, const char *key, const char *val, int g)
Definition: str.c:1110
RZ_API char * rz_str_trunc_ellipsis(const char *str, int len)
Definition: str.c:883
RZ_API void rz_str_trim(RZ_NONNULL RZ_INOUT char *str)
Removes whitespace characters (space, tab, newline etc.) from the beginning and end of a string.
Definition: str_trim.c:190
RZ_API int rz_str_bounds(const char *str, int *h)
Definition: str.c:3124
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_UNUSED
Definition: rz_types.h:73
#define RZ_NEW(x)
Definition: rz_types.h:285
#define RZ_NEWS0(x, y)
Definition: rz_types.h:282
@ RZ_OUTPUT_MODE_STANDARD
Definition: rz_types.h:39
#define RZ_FREE(x)
Definition: rz_types.h:369
#define PFMT64x
Definition: rz_types.h:393
#define RZ_MIN(x, y)
#define RZ_MAX(x, y)
#define UT64_MAX
Definition: rz_types_base.h:86
static void * rz_vector_index_ptr(RzVector *vec, size_t index)
Definition: rz_vector.h:88
RZ_API void * rz_vector_push(RzVector *vec, void *x)
Definition: vector.c:197
RZ_API void rz_vector_fini(RzVector *vec)
Definition: vector.c:61
RZ_API void rz_vector_clear(RzVector *vec)
Definition: vector.c:68
RZ_API void rz_vector_init(RzVector *vec, size_t elem_size, RzVectorFree free, void *free_user)
Definition: vector.c:33
#define islower(c)
Definition: safe-ctype.h:135
#define isalpha(c)
Definition: safe-ctype.h:125
RZ_API int sdb_set_owned(Sdb *s, const char *key, char *val, ut32 cas)
Definition: sdb.c:607
RZ_API int sdb_set(Sdb *s, const char *key, const char *val, ut32 cas)
Definition: sdb.c:611
RZ_API Sdb * sdb_new0(void)
Definition: sdb.c:43
RZ_API char * sdb_get(Sdb *s, const char *key, ut32 *cas)
Definition: sdb.c:290
RZ_API bool sdb_free(Sdb *s)
Definition: sdb.c:206
RZ_API void sdb_reset(Sdb *s)
Definition: sdb.c:433
RZ_API const char * sdb_const_get(Sdb *s, const char *key, ut32 *cas)
Definition: sdb.c:279
RZ_API ut32 sdb_hash(const char *key)
Definition: util.c:22
RZ_API bool rz_core_seek_redo(RzCore *core)
Definition: seek.c:316
RZ_API bool rz_core_seek_and_save(RzCore *core, ut64 addr, bool rb)
Save currently marked state in seek history and seek to addr .
Definition: seek.c:101
RZ_API bool rz_core_seek_next(RzCore *core, const char *type, bool save)
Seek to the next type of item from current offset.
Definition: seek.c:203
RZ_API bool rz_core_seek_prev(RzCore *core, const char *type, bool save)
Seek to the previous type of item from current offset.
Definition: seek.c:241
RZ_API bool rz_core_seek_undo(RzCore *core)
Definition: seek.c:301
RZ_API bool rz_core_seek(RzCore *core, ut64 addr, bool rb)
Seek to addr.
Definition: seek.c:116
RZ_API void set_p_free(SetP *p)
Definition: set.c:24
RZ_API bool set_p_contains(SetP *s, const void *u)
Definition: set.c:16
RZ_API void set_p_add(SetP *s, const void *u)
Definition: set.c:12
HtPP SetP
Definition: set.h:14
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr from
Definition: sfsocketcall.h:123
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr socklen_t static fromlen const void const struct sockaddr to
Definition: sfsocketcall.h:125
static int
Definition: sfsocketcall.h:114
#define d(i)
Definition: sha256.c:44
#define b(i)
Definition: sha256.c:42
#define f(i)
Definition: sha256.c:46
#define c(i)
Definition: sha256.c:43
#define a(i)
Definition: sha256.c:41
#define h(i)
Definition: sha256.c:48
RzAnalysisFunction ** fcn
Definition: agraph.c:99
RzAGraph * g
Definition: agraph.c:98
RzCore * core
Definition: agraph.c:97
int is_reversed
Definition: agraph.c:84
RzList * x
Definition: agraph.c:83
RzANode * to
Definition: agraph.c:82
RzList * y
Definition: agraph.c:83
RzANode * from
Definition: agraph.c:81
Definition: agraph.c:67
const RzGraphNode * from
Definition: agraph.c:68
int dist
Definition: agraph.c:70
const RzGraphNode * to
Definition: agraph.c:69
Definition: agraph.c:73
RzANodeCallback node_cb
Definition: agraph.c:75
RzAGraph * graph
Definition: agraph.c:74
void * data
Definition: agraph.c:77
RAEdgeCallback edge_cb
Definition: agraph.c:76
Definition: heap-inl.h:40
Definition: dis.h:35
Definition: agraph.c:87
int width
Definition: agraph.c:92
int position
Definition: agraph.c:90
int n_nodes
Definition: agraph.c:88
int height
Definition: agraph.c:91
RzGraphNode ** nodes
Definition: agraph.c:89
int gap
Definition: agraph.c:93
int pos
Definition: agraph.c:64
int len
Definition: agraph.c:63
Definition: rz_pj.h:12
ut8 * parent_reg_arena
Definition: rz_analysis.h:877
RzAnalysisSwitchOp * switch_op
Definition: rz_analysis.h:874
Generic drawable graph node.
ut8 * last_disasm_reg
Definition: rz_analysis.h:569
bool need_reload_nodes
Definition: rz_agraph.h:68
char * title
Definition: rz_agraph.h:10
RzGraphNode * gnode
Definition: rz_agraph.h:9
int is_debugger
Definition: rz_bin.h:350
RzConsPrintablePalette pal
Definition: rz_cons.h:491
struct rz_line_t * line
Definition: rz_cons.h:553
RzConsEvent event_resize
Definition: rz_cons.h:522
void * event_data
Definition: rz_cons.h:523
bool use_utf8
Definition: rz_cons.h:559
RzConsContext * context
Definition: rz_cons.h:502
int mouse_event
Definition: rz_cons.h:524
RzVector word_list
Definition: rz_agraph.h:31
bool vmode
Definition: rz_core.h:309
RzCons * cons
Definition: rz_core.h:312
bool scr_gadgets
Definition: rz_core.h:385
RzBin * bin
Definition: rz_core.h:298
ut64 offset
Definition: rz_core.h:301
RzAnalysis * analysis
Definition: rz_core.h:322
RzDebug * dbg
Definition: rz_core.h:329
RzIO * io
Definition: rz_core.h:313
bool keep_asmqjmps
Definition: rz_core.h:350
RzAGraph * graph
Definition: rz_core.h:333
RzCoreTaskScheduler tasks
Definition: rz_core.h:362
bool tmpseek
Definition: rz_core.h:308
RzPanelsRoot * panels_root
Definition: rz_core.h:334
RzPrint * print
Definition: rz_core.h:327
bool is_asmqjmps_letter
Definition: rz_core.h:349
RzConfig * config
Definition: rz_core.h:300
RzReg * reg
Definition: rz_debug.h:286
RzGraphNode * to
Definition: rz_graph.h:21
RzGraphNode * from
Definition: rz_graph.h:20
void * data
Definition: rz_graph.h:12
unsigned int idx
Definition: rz_graph.h:11
RzList * out_nodes
Definition: rz_graph.h:13
RzList * in_nodes
Definition: rz_graph.h:14
RzList * nodes
Definition: rz_graph.h:29
void(* finish_node)(RzGraphNode *n, struct rz_graph_visitor_t *vis)
Definition: rz_graph.h:34
void(* back_edge)(const RzGraphEdge *e, struct rz_graph_visitor_t *vis)
Definition: rz_graph.h:36
void(* tree_edge)(const RzGraphEdge *e, struct rz_graph_visitor_t *vis)
Definition: rz_graph.h:35
void(* fcross_edge)(const RzGraphEdge *e, struct rz_graph_visitor_t *vis)
Definition: rz_graph.h:37
RzLinePromptType prompt_type
Definition: rz_cons.h:1116
char * contents
Definition: rz_cons.h:1111
struct rz_list_iter_t * n
Definition: rz_list.h:15
void * data
Definition: rz_list.h:14
RzListIter * head
Definition: rz_list.h:19
RzListFree free
Definition: rz_list.h:21
bool sorted
Definition: rz_list.h:23
ut32 length
Definition: rz_list.h:22
bool cur_enabled
Definition: rz_print.h:130
size_t len
Definition: rz_vector.h:47
Definition: sdb.h:63
RzCanvasLineStyle style
Definition: agraph.c:2791
int maxx
Definition: agraph.c:2781
int revedgectr
Definition: agraph.c:2779
int layer
Definition: agraph.c:2777
int edgectr
Definition: agraph.c:2778
int minx
Definition: agraph.c:2780
int pos
Definition: main.c:11
RZ_API void rz_core_task_enqueue_oneshot(RzCoreTaskScheduler *scheduler, RzCoreTaskOneShot func, void *user)
Definition: task.c:391
RZ_API RzDebugTracepoint * rz_debug_trace_get(RzDebug *dbg, ut64 addr)
Definition: trace.c:200
static int color
Definition: visual.c:20
RZ_API void rz_core_visual_toggle_decompiler_disasm(RzCore *core, bool for_graph, bool reset)
Definition: visual.c:127
RZ_API void rz_core_visual_prompt_input(RzCore *core)
Definition: visual.c:733
RZ_API void rz_core_visual_browse(RzCore *core, const char *input)
Definition: visual.c:2067
RZ_API void rz_core_print_scrollbar(RzCore *core)
Definition: visual.c:3541
RZ_API int rz_core_visual_xrefs(RzCore *core, bool xref_to, bool fcnInsteadOfAddr)
Definition: visual.c:1312
RZ_API void rz_core_visual_toggle_hints(RzCore *core)
Definition: visual.c:109
static int zoom
Definition: visual.c:22
RZ_API int rz_line_hist_offset_up(RzLine *line)
Definition: visual.c:1222
RZ_API int rz_line_hist_offset_down(RzLine *line)
Definition: visual.c:1235
RZ_API void rz_core_visual_define(RzCore *core, const char *args, int distance)
Definition: vmenus.c:3149
RZ_API void rz_core_visual_analysis(RzCore *core, const char *input)
Definition: vmenus.c:2739
RZ_API int rz_core_visual_trackflags(RzCore *core)
Definition: vmenus.c:1485
RZ_API bool rz_core_visual_hudstuff(RzCore *core)
Definition: vmenus.c:539
static st64 delta
Definition: vmenus.c:2425
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
RZ_API RzList * rz_analysis_function_get_xrefs_from(RzAnalysisFunction *fcn)
Definition: xrefs.c:297
static const char * cb[]
Definition: z80_tab.h:176
static unsigned char * obuf
Definition: z80asm.c:36
static int addr
Definition: z80asm.c:58