1 /* radare - LGPL - Copyright 2009-2020 - pancake */
2 
3 #include <r_core.h>
4 #include <r_cons.h>
5 
6 #define NPF 5
7 #define PIDX (R_ABS (core->printidx % NPF))
8 
9 static void visual_refresh(RCore *core);
10 
11 static int obs = 0;
12 static int blocksize = 0;
13 static bool autoblocksize = true;
14 static int disMode = 0;
15 static int hexMode = 0;
16 static int printMode = 0;
17 static bool snowMode = false;
18 static RList *snows = NULL;
19 static int color = 1;
20 static int debug = 1;
21 static int zoom = 0;
22 
23 typedef struct {
24 	int x;
25 	int y;
26 } Snow;
27 
28 #define KEY_ALTQ 0xc5
29 
30 static const char *printfmtSingle[NPF] = {
31 	"xc",  // HEXDUMP
32 	"pd $r",  // ASSEMBLY
33 	"pxw 64@r:SP;dr=;pd $r",  // DEBUGGER
34 	"prc", // OVERVIEW
35 	"pss", // PC//  copypasteable views
36 };
37 
38 static const char *printfmtColumns[NPF] = {
39 	"pCx",  // HEXDUMP // + pCw
40 	"pCd $r-1",  // ASSEMBLY
41 	"pCD",  // DEBUGGER
42 	"pCA", // OVERVIEW
43 	"pCc", // PC//  copypasteable views
44 };
45 
46 // to print the stack in the debugger view
47 #define PRINT_HEX_FORMATS 10
48 #define PRINT_3_FORMATS 2
49 #define PRINT_4_FORMATS 9
50 #define PRINT_5_FORMATS 8
51 
52 static int currentFormat = 0;
53 static int current0format = 0;
54 static const char *printHexFormats[PRINT_HEX_FORMATS] = {
55 	"px", "pxa", "pxr", "prx", "pxb", "pxh", "pxw", "pxq", "pxd", "pxr",
56 };
57 static int current3format = 0;
58 static const char *print3Formats[PRINT_3_FORMATS] = { //  not used at all. its handled by the pd format
59 	"pxw 64@r:SP;dr=;pd $r", // DEBUGGER
60 	"pCD"
61 };
62 static int current4format = 0;
63 static const char *print4Formats[PRINT_4_FORMATS] = {
64 	"prc", "p2", "prc=a", "pxAv", "pxx", "p=e $r-2", "pq 64", "pk 64", "pri",
65 };
66 static int current5format = 0;
67 static const char *print5Formats[PRINT_5_FORMATS] = {
68 	"pca", "pcA", "p8", "pcc", "pss", "pcp", "pcd", "pcj"
69 };
70 
r_core_visual_applyHexMode(RCore * core,int hexMode)71 R_API void r_core_visual_applyHexMode(RCore *core, int hexMode) {
72 	currentFormat = R_ABS(hexMode) % PRINT_HEX_FORMATS;
73 	switch (currentFormat) {
74 	case 0: /* px */
75 	case 3: /* prx */
76 	case 6: /* pxw */
77 	case 9: /* pxr */
78 		r_config_set (core->config, "hex.compact", "false");
79 		r_config_set (core->config, "hex.comments", "true");
80 		break;
81 	case 1: /* pxa */
82 	case 4: /* pxb */
83 	case 7: /* pxq */
84 		r_config_set (core->config, "hex.compact", "true");
85 		r_config_set (core->config, "hex.comments", "true");
86 		break;
87 	case 2: /* pxr */
88 	case 5: /* pxh */
89 	case 8: /* pxd */
90 		r_config_set (core->config, "hex.compact", "false");
91 		r_config_set (core->config, "hex.comments", "false");
92 		break;
93 	}
94 }
95 
r_core_visual_toggle_decompiler_disasm(RCore * core,bool for_graph,bool reset)96 R_API void r_core_visual_toggle_decompiler_disasm(RCore *core, bool for_graph, bool reset) {
97 	static RConfigHold *hold = NULL; // should be a tab-specific var
98 	if (hold) {
99 		r_config_hold_restore (hold);
100 		r_config_hold_free (hold);
101 		hold = NULL;
102 		return;
103 	}
104 	if (reset) {
105 		return;
106 	}
107 	hold = r_config_hold_new (core->config);
108 	r_config_hold (hold, "asm.hint.pos", "asm.cmt.col", "asm.offset", "asm.lines",
109 	"asm.indent", "asm.bytes", "asm.comments", "asm.dwarf", "asm.usercomments", "asm.instr", NULL);
110 	if (for_graph) {
111 		r_config_set (core->config, "asm.hint.pos", "-2");
112 		r_config_set (core->config, "asm.lines", "false");
113 		r_config_set (core->config, "asm.indent", "false");
114 	} else {
115 		r_config_set (core->config, "asm.hint.pos", "0");
116 		r_config_set (core->config, "asm.indent", "true");
117 		r_config_set (core->config, "asm.lines", "true");
118 	}
119 	r_config_set (core->config, "asm.cmt.col", "0");
120 	r_config_set (core->config, "asm.offset", "false");
121 	r_config_set (core->config, "asm.dwarf", "true");
122 	r_config_set (core->config, "asm.bytes", "false");
123 	r_config_set (core->config, "asm.comments", "false");
124 	r_config_set (core->config, "asm.usercomments", "true");
125 	r_config_set (core->config, "asm.instr", "false");
126 }
127 
setcursor(RCore * core,bool cur)128 static void setcursor(RCore *core, bool cur) {
129 	int flags = core->print->flags; // wtf
130 	if (core->print->cur_enabled) {
131 		flags |= R_PRINT_FLAGS_CURSOR;
132 	} else {
133 		flags &= ~(R_PRINT_FLAGS_CURSOR);
134 	}
135 	core->print->cur_enabled = cur;
136 	if (core->print->cur == -1) {
137 		core->print->cur = 0;
138 	}
139 	r_print_set_flags (core->print, flags);
140 	core->print->col = core->print->cur_enabled? 1: 0;
141 }
142 
r_core_visual_applyDisMode(RCore * core,int disMode)143 R_API void r_core_visual_applyDisMode(RCore *core, int disMode) {
144 	currentFormat = R_ABS(disMode) % 5;
145 	switch (currentFormat) {
146 	case 0:
147 		r_config_set (core->config, "asm.pseudo", "false");
148 		r_config_set (core->config, "asm.bytes", "true");
149 		r_config_set (core->config, "asm.esil", "false");
150 		r_config_set (core->config, "emu.str", "false");
151 		r_config_set (core->config, "asm.emu", "false");
152 		break;
153 	case 1:
154 		r_config_set (core->config, "asm.pseudo", "false");
155 		r_config_set (core->config, "asm.bytes", "true");
156 		r_config_set (core->config, "asm.esil", "false");
157 		r_config_set (core->config, "asm.emu", "false");
158 		r_config_set (core->config, "emu.str", "true");
159 		break;
160 	case 2:
161 		r_config_set (core->config, "asm.pseudo", "true");
162 		r_config_set (core->config, "asm.bytes", "true");
163 		r_config_set (core->config, "asm.esil", "true");
164 		r_config_set (core->config, "emu.str", "true");
165 		r_config_set (core->config, "asm.emu", "true");
166 		break;
167 	case 3:
168 		r_config_set (core->config, "asm.pseudo", "false");
169 		r_config_set (core->config, "asm.bytes", "false");
170 		r_config_set (core->config, "asm.esil", "false");
171 		r_config_set (core->config, "asm.emu", "false");
172 		r_config_set (core->config, "emu.str", "true");
173 		break;
174 	case 4:
175 		r_config_set (core->config, "asm.pseudo", "true");
176 		r_config_set (core->config, "asm.bytes", "false");
177 		r_config_set (core->config, "asm.esil", "false");
178 		r_config_set (core->config, "asm.emu", "false");
179 		r_config_set (core->config, "emu.str", "true");
180 		break;
181 	}
182 }
183 
nextPrintCommand(void)184 static void nextPrintCommand(void) {
185 	current0format++;
186 	current0format %= PRINT_HEX_FORMATS;
187 	currentFormat = current0format;
188 }
189 
prevPrintCommand(void)190 static void prevPrintCommand(void) {
191 	current0format--;
192 	if (current0format < 0) {
193 		current0format = 0;
194 	}
195 	currentFormat = current0format;
196 }
197 
stackPrintCommand(RCore * core)198 static const char *stackPrintCommand(RCore *core) {
199 	if (current0format == 0) {
200 		if (r_config_get_i (core->config, "dbg.slow")) {
201 			return "pxr";
202 		}
203 		if (r_config_get_i (core->config, "stack.bytes")) {
204 			return "px";
205 		}
206 		switch (core->rasm->bits) {
207 		case 64: return "pxq"; break;
208 		case 32: return "pxw"; break;
209 		}
210 		return "px";
211 	}
212 	return printHexFormats[current0format % PRINT_HEX_FORMATS];
213 }
214 
__core_visual_print_command(RCore * core)215 static const char *__core_visual_print_command(RCore *core) {
216 	if (core->visual.tabs) {
217 		RCoreVisualTab *tab = r_list_get_n (core->visual.tabs, core->visual.tab);
218 		if (tab && tab->name[0] == ':') {
219 			return tab->name + 1;
220 		}
221 	}
222 	if (r_config_get_i (core->config, "scr.dumpcols")) {
223 		free (core->stkcmd);
224 		core->stkcmd = r_str_new (stackPrintCommand (core));
225 		return printfmtColumns[PIDX];
226 	}
227 	return printfmtSingle[PIDX];
228 }
229 
__core_visual_gogo(RCore * core,int ch)230 static bool __core_visual_gogo(RCore *core, int ch) {
231 	RIOMap *map;
232 	int ret = -1;
233 	switch (ch) {
234 	case 'g':
235 		if (core->io->va) {
236 			RIOMap *map = r_io_map_get (core->io, core->offset);
237 			if (!map && !r_pvector_empty (&core->io->maps)) {
238 				map = r_pvector_at (&core->io->maps, r_pvector_len (&core->io->maps) - 1);
239 			}
240 			if (map) {
241 				r_core_seek (core, r_io_map_begin (map), true);
242 			}
243 		} else {
244 			r_core_seek (core, 0, true);
245 		}
246 		r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
247 		return true;
248 	case 'G':
249 		map = r_io_map_get (core->io, core->offset);
250 		if (!map && !r_pvector_empty (&core->io->maps)) {
251 			map = r_pvector_at (&core->io->maps, 0);
252 		}
253 		if (map) {
254 			RPrint *p = core->print;
255 			int scr_rows;
256 			if (!p->consbind.get_size) {
257 				break;
258 			}
259 			(void)p->consbind.get_size (&scr_rows);
260 			ut64 scols = r_config_get_i (core->config, "hex.cols");
261 			ret = r_core_seek (core, r_io_map_end (map) - (scr_rows - 2) * scols, true);
262 		}
263 		if (ret != -1) {
264 			r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
265 		}
266 		return true;
267 	}
268 	return false;
269 }
270 
271 static const char *help_visual[] = {
272 	"?", "full help",
273 	"!", "enter panels",
274 	"a", "code analysis",
275 	"c", "toggle cursor",
276 	"d", "debugger / emulator",
277 	"e", "toggle configurations",
278 	"i", "insert / write",
279 	"m", "moving around (seeking)",
280 	"p", "print commands and modes",
281 	"v", "view management",
282 	NULL
283 };
284 
285 static const char *help_msg_visual[] = {
286 	"?", "show visual mode help (short)",
287 	"??", "show visual mode help (full)",
288 	"$", "set the program counter to the current offset + cursor",
289 	"&", "rotate asm.bits between 8, 16, 32 and 64 applying hints",
290 	"%", "in cursor mode finds matching pair, otherwise toggle autoblocksz",
291 	"^", "seek to the beginning of the function",
292 	"!", "swap into visual panels mode",
293 	"TAB", "switch to the next print mode (or element in cursor mode)",
294 	"_", "enter the flag/comment/functions/.. hud (same as VF_)",
295 	"=", "set cmd.vprompt (top row)",
296 	"|", "set cmd.cprompt (right column)",
297 	".", "seek to program counter",
298 	"#", "toggle decompiler comments in disasm (see pdd* from r2dec)",
299 	"\\", "toggle visual split mode",
300 	"\"", "toggle the column mode (uses pC..)",
301 	"/", "in cursor mode search in current block",
302 	"(", "toggle snow",
303 	")", "toggle emu.str",
304 	":cmd", "run radare command",
305 	";[-]cmt", "add/remove comment",
306 	"0", "seek to beginning of current function",
307 	"[1-9]", "follow jmp/call identified by shortcut (like ;[1])",
308 	",file", "add a link to the text file",
309 	"/*+-[]", "change block size, [] = resize hex.cols",
310 	"<,>", "seek aligned to block size (in cursor slurp or dump files)",
311 	"a/A", "(a)ssemble code, visual (A)ssembler",
312 	"b", "browse evals, symbols, flags, mountpoints, evals, classes, ...",
313 	"B", "toggle breakpoint",
314 	"c/C", "toggle (c)ursor and (C)olors",
315 	"d[f?]", "define function, data, code, ..",
316 	"D", "enter visual diff mode (set diff.from/to)",
317 	"f/F", "set/unset or browse flags. f- to unset, F to browse, ..",
318 	"hjkl", "move around (or HJKL) (left-down-up-right)",
319 	"i", "insert hex or string (in hexdump) use tab to toggle",
320 	"I", "insert hexpair block ",
321 	"mK/'K", "mark/go to Key (any key)",
322 	"n/N", "seek next/prev function/flag/hit (scr.nkey)",
323 	"g", "go/seek to given offset (g[g/G]<enter> to seek begin/end of file)",
324 	"O", "toggle asm.pseudo and asm.esil",
325 	"p/P", "rotate print modes (hex, disasm, debug, words, buf)",
326 	"q", "back to radare shell",
327 	"r", "toggle callhints/jmphints/leahints",
328 	"R", "randomize color palette (ecr)",
329 	"sS", "step / step over",
330 	"tT", "tt new tab, t[1-9] switch to nth tab, t= name tab, t- close tab",
331 	"uU", "undo/redo seek",
332 	"v", "visual function/vars code analysis mode",
333 	"V", "(V)iew interactive ascii art graph (agfv)",
334 	"wW", "seek cursor to next/prev word",
335 	"xX", "show xrefs/refs of current function from/to data/code",
336 	"yY", "copy and paste selection",
337 	"z", "fold/unfold comments in disassembly",
338 	"Z", "shift-tab rotate print modes", // ctoggle zoom mode",
339 	"Enter", "follow address of jump/call",
340 	NULL
341 };
342 
343 static const char *help_msg_visual_fn[] = {
344 	"F2", "toggle breakpoint",
345 	"F4", "run to cursor",
346 	"F7", "single step",
347 	"F8", "step over",
348 	"F9", "continue",
349 	NULL
350 };
351 
352 static bool splitView = false;
353 static ut64 splitPtr = UT64_MAX;
354 
355 #undef USE_THREADS
356 #define USE_THREADS 1
357 
358 #if USE_THREADS
359 
printSnow(RCore * core)360 static void printSnow(RCore *core) {
361 	if (!snows) {
362 		snows = r_list_newf (free);
363 	}
364 	int i, h, w = r_cons_get_size (&h);
365 	int amount = r_num_rand (4);
366 	if (amount > 0) {
367 		for (i = 0; i < amount; i++) {
368 			Snow *snow = R_NEW (Snow);
369 			snow->x = r_num_rand (w);
370 			snow->y = 0;
371 			r_list_append (snows, snow);
372 		}
373 	}
374 	RListIter *iter, *iter2;
375 	Snow *snow;
376 	r_list_foreach_safe (snows, iter, iter2, snow) {
377 		int pos = (r_num_rand (3)) - 1;
378 		snow->x += pos;
379 		snow->y++;
380 		if (snow->x >= w) {
381 			r_list_delete (snows, iter);
382 			continue;
383 		}
384 		if (snow->y > h) {
385 			r_list_delete (snows, iter);
386 			continue;
387 		}
388 		r_cons_gotoxy (snow->x, snow->y);
389 		r_cons_printf ("*");
390 	}
391 	// r_cons_gotoxy (10 , 10);
392 	r_cons_flush ();
393 }
394 #endif
395 
rotateAsmBits(RCore * core)396 static void rotateAsmBits(RCore *core) {
397 	RAnalHint *hint = r_anal_hint_get (core->anal, core->offset);
398 	int bits = hint? hint->bits : r_config_get_i (core->config, "asm.bits");
399 	int retries = 4;
400 	while (retries > 0) {
401 		int nb = bits == 64 ? 8:
402 			bits == 32 ? 64:
403 			bits == 16 ? 32:
404 			bits == 8 ? 16: bits;
405 		if ((core->rasm->cur->bits & nb) == nb) {
406 			r_core_cmdf (core, "ahb %d", nb);
407 			break;
408 		}
409 		bits = nb;
410 		retries--;
411 	}
412 	r_anal_hint_free (hint);
413 }
414 
rotateAsmemu(RCore * core)415 static const char *rotateAsmemu(RCore *core) {
416 	const bool isEmuStr = r_config_get_i (core->config, "emu.str");
417 	const bool isEmu = r_config_get_i (core->config, "asm.emu");
418 	if (isEmu) {
419 		if (isEmuStr) {
420 			r_config_set (core->config, "emu.str", "false");
421 		} else {
422 			r_config_set (core->config, "asm.emu", "false");
423 		}
424 	} else {
425 		r_config_set (core->config, "emu.str", "true");
426 	}
427 	return "pd";
428 }
429 
r_core_visual_showcursor(RCore * core,int x)430 R_API void r_core_visual_showcursor(RCore *core, int x) {
431 	if (core && core->vmode) {
432 		r_cons_show_cursor (x);
433 		r_cons_enable_mouse (r_config_get_i (core->config, "scr.wheel"));
434 	} else {
435 		r_cons_enable_mouse (false);
436 	}
437 	r_cons_flush ();
438 }
439 
printFormat(RCore * core,const int next)440 static void printFormat(RCore *core, const int next) {
441 	switch (core->printidx) {
442 	case R_CORE_VISUAL_MODE_PX: // 0 // xc
443 		hexMode += next;
444 		r_core_visual_applyHexMode (core, hexMode);
445 		printfmtSingle[0] = printHexFormats[R_ABS(hexMode) % PRINT_HEX_FORMATS];
446 		break;
447 	case R_CORE_VISUAL_MODE_PD: // pd
448 		disMode += next;
449 		r_core_visual_applyDisMode (core, disMode);
450 		printfmtSingle[1] = rotateAsmemu (core);
451 		break;
452 	case R_CORE_VISUAL_MODE_DB: // debugger
453 		disMode += next;
454 		r_core_visual_applyDisMode (core, disMode);
455 		printfmtSingle[1] = rotateAsmemu (core);
456 		current3format += next;
457 		currentFormat = R_ABS (current3format) % PRINT_3_FORMATS;
458 		printfmtSingle[2] = print3Formats[currentFormat];
459 		break;
460 	case R_CORE_VISUAL_MODE_OV: // overview
461 		current4format += next;
462 		currentFormat = R_ABS (current4format) % PRINT_4_FORMATS;
463 		printfmtSingle[3] = print4Formats[currentFormat];
464 		break;
465 	case R_CORE_VISUAL_MODE_CD: // code
466 		current5format += next;
467 		currentFormat = R_ABS (current5format) % PRINT_5_FORMATS;
468 		printfmtSingle[4] = print5Formats[currentFormat];
469 		break;
470 	}
471 }
472 
nextPrintFormat(RCore * core)473 static inline void nextPrintFormat(RCore *core) {
474 	printFormat (core, 1);
475 }
476 
prevPrintFormat(RCore * core)477 static inline void prevPrintFormat(RCore *core) {
478 	printFormat (core, -1);
479 }
480 
r_core_visual_hud(RCore * core)481 R_API int r_core_visual_hud(RCore *core) {
482 	const char *c = r_config_get (core->config, "hud.path");
483 	char *f = r_str_newf (R_JOIN_3_PATHS ("%s", R2_HUD, "main"),
484 		r_sys_prefix (NULL));
485 	int use_color = core->print->flags & R_PRINT_FLAGS_COLOR;
486 	char *homehud = r_str_home (R2_HOME_HUD);
487 	char *res = NULL;
488 	char *p = 0;
489 	r_cons_singleton ()->context->color_mode = use_color;
490 
491 	r_core_visual_showcursor (core, true);
492 	if (c && *c && r_file_exists (c)) {
493 		res = r_cons_hud_file (c);
494 	}
495 	if (!res && homehud) {
496 		res = r_cons_hud_file (homehud);
497 	}
498 	if (!res && r_file_exists (f)) {
499 		res = r_cons_hud_file (f);
500 	}
501 	if (!res) {
502 		r_cons_message ("Cannot find hud file");
503 	}
504 
505 	r_cons_clear ();
506 	if (res) {
507 		p = strchr (res, ';');
508 		r_cons_println (res);
509 		r_cons_flush ();
510 		if (p) {
511 			r_core_cmd0 (core, p + 1);
512 		}
513 		free (res);
514 	}
515 	r_core_visual_showcursor (core, false);
516 	r_cons_flush ();
517 	free (homehud);
518 	free (f);
519 	return (int) (size_t) p;
520 }
521 
r_core_visual_jump(RCore * core,ut8 ch)522 R_API void r_core_visual_jump(RCore *core, ut8 ch) {
523 	char chbuf[2];
524 	ut64 off;
525 	chbuf[0] = ch;
526 	chbuf[1] = '\0';
527 	off = r_core_get_asmqjmps (core, chbuf);
528 	if (off != UT64_MAX) {
529 		int delta = R_ABS ((st64) off - (st64) core->offset);
530 		r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
531 		if (core->print->cur_enabled && delta < 100) {
532 			core->print->cur = delta;
533 		} else {
534 			r_core_visual_seek_animation (core, off);
535 			core->print->cur = 0;
536 		}
537 		r_core_block_read (core);
538 	}
539 }
540 
541 // TODO: merge with r_cons_cmd_help
r_core_visual_append_help(RStrBuf * p,const char * title,const char ** help)542 R_API void r_core_visual_append_help(RStrBuf *p, const char *title, const char **help) {
543 	RCons *cons = r_cons_singleton ();
544 	bool use_color = cons->context->color_mode;
545 	const char
546 		*pal_input_color = use_color ? cons->context->pal.input : "",
547 		*pal_args_color = use_color ? cons->context->pal.args : "",
548 		*pal_help_color = use_color ? cons->context->pal.help : "",
549 		*pal_reset = use_color ? cons->context->pal.reset : "";
550 	int i, max_length = 0, padding = 0;
551 	const char *help_cmd = NULL, *help_desc = NULL;
552 
553 	// calculate padding for description text in advance
554 	for (i = 0; help[i]; i += 2) {
555 		max_length = R_MAX (max_length, strlen (help[i]));
556 	}
557 
558 	/* Usage header */
559 	r_strbuf_appendf (p, "%s%s%s\n",
560 		pal_args_color, title, pal_reset);
561 
562 	/* Body of help text, indented */
563 	for (i = 0; help[i]; i += 2) {
564 		help_cmd  = help[i + 0];
565 		help_desc = help[i + 1];
566 
567 		padding = max_length - (strlen (help[i]));
568 		r_strbuf_appendf (p, "| %s%s%*s  %s%s%s\n",
569 			pal_input_color, help_cmd,
570 			padding, "",
571 			pal_help_color, help_desc, pal_reset);
572 	}
573 }
574 
visual_help(RCore * core)575 static int visual_help(RCore *core) {
576 	int ret = 0;
577 	RStrBuf *p, *q;
578 repeat:
579 	p = r_strbuf_new (NULL);
580 	q = r_strbuf_new (NULL);
581 	if (!p) {
582 		return 0;
583 	}
584 	r_cons_clear00 ();
585 	r_core_visual_append_help (q, "Visual Mode Help (short)", help_visual);
586 	r_cons_printf ("%s", r_strbuf_get (q));
587 	r_cons_flush ();
588 	switch (r_cons_readchar ()) {
589 	case 'q':
590 		r_strbuf_free (p);
591 		r_strbuf_free (q);
592 		return ret;
593 	case '!':
594 		r_core_panels_root (core, core->panels_root);
595 		break;
596 	case '?':
597 		r_core_visual_append_help (p, "Visual Mode Help (full)", help_msg_visual);
598 		r_core_visual_append_help (p, "Function Keys Defaults  # Use `e key.` to owerwrite", help_msg_visual_fn);
599 		ret = r_cons_less_str (r_strbuf_get (p), "?");
600 		break;
601 	case 'v':
602 		r_strbuf_appendf (p, "Visual Views:\n\n");
603 		r_strbuf_appendf (p,
604 			" \\     toggle horizonal split mode\n"
605 			" tt     create a new tab (same as t+)\n"
606 			" t=     give a name to the current tab\n"
607 			" t-     close current tab\n"
608 			" th     select previous tab (same as tj)\n"
609 			" tl     select next tab (same as tk)\n"
610 			" t[1-9] select nth tab\n"
611 			" C   -> rotate scr.color=0,1,2,3\n"
612 			" R   -> rotate color theme with ecr command which honors scr.randpal\n"
613 		);
614 		ret = r_cons_less_str (r_strbuf_get (p), "?");
615 		break;
616 	case 'p':
617 		r_strbuf_appendf (p, "Visual Print Modes:\n\n");
618 		r_strbuf_appendf (p,
619 			" pP  -> change to the next/previous print mode (hex, dis, ..)\n"
620 			" TAB -> rotate between all the configurations for the current print mode\n"
621 		);
622 		ret = r_cons_less_str (r_strbuf_get (p), "?");
623 		break;
624 	case 'e':
625 		r_strbuf_appendf (p, "Visual Evals:\n\n");
626 		r_strbuf_appendf (p,
627 			" E      toggle asm.leahints\n"
628 			" &      rotate asm.bits=16,32,64\n"
629 		);
630 		ret = r_cons_less_str (r_strbuf_get (p), "?");
631 		break;
632 	case 'c':
633 		setcursor (core, !core->print->cur_enabled);
634 		r_strbuf_free (p);
635 		return ret;
636 	case 'i':
637 		r_strbuf_appendf (p, "Visual Insertion Help:\n\n");
638 		r_strbuf_appendf (p,
639 			" i   -> insert bits, bytes or text depending on view\n"
640 			" a   -> assemble instruction and write the bytes in the current offset\n"
641 			" A   -> visual assembler\n"
642 			" +   -> increment value of byte\n"
643 			" -   -> decrement value of byte\n"
644 		);
645 		ret = r_cons_less_str (r_strbuf_get (p), "?");
646 		break;
647 	case 'd':
648 		r_strbuf_appendf (p, "Visual Debugger Help:\n\n");
649 		r_strbuf_appendf (p,
650 			" $   -> set the program counter (PC register)\n"
651 			" s   -> step in\n"
652 			" S   -> step over\n"
653 			" B   -> toggle breakpoint\n"
654 			" :dc -> continue\n"
655 		);
656 		ret = r_cons_less_str (r_strbuf_get (p), "?");
657 		break;
658 	case 'm':
659 		r_strbuf_appendf (p, "Visual Moving Around:\n\n");
660 		r_strbuf_appendf (p,
661 			" g        type flag/offset/register name to seek\n"
662 			" hl       seek to the next/previous byte\n"
663 			" jk       seek to the next row (core.offset += hex.cols)\n"
664 			" JK       seek one page down\n"
665 			" ^        seek to the beginning of the current map\n"
666 			" $        seek to the end of the current map\n"
667 			" c        toggle cursor mode (use hjkl to move and HJKL to select a range)\n"
668 			" mK/'K    mark/go to Key (any key)\n"
669 		);
670 		ret = r_cons_less_str (r_strbuf_get (p), "?");
671 		break;
672 	case 'a':
673 		r_strbuf_appendf (p, "Visual Analysis:\n\n");
674 		r_strbuf_appendf (p,
675 			" df -> define function\n"
676 			" du -> undefine function\n"
677 			" dc -> define as code\n"
678 			" dw -> define as dword (32bit)\n"
679 			" dw -> define as qword (64bit)\n"
680 			" dd -> define current block or selected bytes as data\n"
681 			" V  -> view graph (same as press the 'space' key)\n"
682 		);
683 		ret = r_cons_less_str (r_strbuf_get (p), "?");
684 		break;
685 	}
686 	r_strbuf_free (p);
687 	r_strbuf_free (q);
688 	goto repeat;
689 }
690 
prompt_read(const char * p,char * buf,int buflen)691 static void prompt_read(const char *p, char *buf, int buflen) {
692 	if (!buf || buflen < 1) {
693 		return;
694 	}
695 	*buf = 0;
696 	r_line_set_prompt (p);
697 	r_core_visual_showcursor (NULL, true);
698 	r_cons_fgets (buf, buflen, 0, NULL);
699 	r_core_visual_showcursor (NULL, false);
700 }
701 
reset_print_cur(RPrint * p)702 static void reset_print_cur(RPrint *p) {
703 	p->cur = 0;
704 	p->ocur = -1;
705 }
706 
__holdMouseState(RCore * core)707 static bool __holdMouseState(RCore *core) {
708 	bool m = r_cons_singleton ()->mouse;
709 	r_cons_enable_mouse (false);
710 	return m;
711 }
712 
backup_current_addr(RCore * core,ut64 * addr,ut64 * bsze,ut64 * newaddr)713 static void backup_current_addr(RCore *core, ut64 *addr, ut64 *bsze, ut64 *newaddr) {
714 	*addr = core->offset;
715 	*bsze = core->blocksize;
716 	if (core->print->cur_enabled) {
717 		if (core->print->ocur != -1) {
718 			int newsz = core->print->cur - core->print->ocur;
719 			*newaddr = core->offset + core->print->ocur;
720 			r_core_block_size (core, newsz);
721 		} else {
722 			*newaddr = core->offset + core->print->cur;
723 		}
724 		r_core_seek (core, *newaddr, true);
725 	}
726 }
727 
restore_current_addr(RCore * core,ut64 addr,ut64 bsze,ut64 newaddr)728 static void restore_current_addr(RCore *core, ut64 addr, ut64 bsze, ut64 newaddr) {
729 	bool restore_seek = true;
730 	if (core->offset != newaddr) {
731 		bool cursor_moved = false;
732 		// when new address is in the screen bounds, just move
733 		// the cursor if enabled and restore seek
734 		if (core->print->cur != -1 && core->print->screen_bounds > 1) {
735 			if (core->offset >= addr &&
736 			    core->offset < core->print->screen_bounds) {
737 				core->print->ocur = -1;
738 				core->print->cur = core->offset - addr;
739 				cursor_moved = true;
740 			}
741 		}
742 
743 		if (!cursor_moved) {
744 			restore_seek = false;
745 			reset_print_cur (core->print);
746 		}
747 	}
748 
749 	if (core->print->cur_enabled) {
750 		if (restore_seek) {
751 			r_core_seek (core, addr, true);
752 			r_core_block_size (core, bsze);
753 		}
754 	}
755 }
756 
r_core_visual_prompt_input(RCore * core)757 R_API void r_core_visual_prompt_input(RCore *core) {
758 	ut64 addr, bsze, newaddr = 0LL;
759 	int ret, h;
760 	(void) r_cons_get_size (&h);
761 	bool mouse_state = __holdMouseState(core);
762 	r_cons_gotoxy (0, h);
763 	r_cons_reset_colors ();
764 	//r_cons_printf ("\nPress <enter> to return to Visual mode.\n");
765 	r_cons_show_cursor (true);
766 	core->vmode = false;
767 
768 	backup_current_addr (core, &addr, &bsze, &newaddr);
769 	do {
770 		ret = r_core_visual_prompt (core);
771 	} while (ret);
772 	restore_current_addr (core, addr, bsze, newaddr);
773 
774 	r_cons_show_cursor (false);
775 	core->vmode = true;
776 	r_cons_enable_mouse (mouse_state && r_config_get_i (core->config, "scr.wheel"));
777 	r_cons_show_cursor (true);
778 }
779 
r_core_visual_prompt(RCore * core)780 R_API int r_core_visual_prompt(RCore *core) {
781 	char buf[1024];
782 	int ret;
783 	if (PIDX != 2) {
784 		core->seltab = 0;
785 	}
786 #if __UNIX__
787 	r_line_set_prompt (Color_RESET ":> ");
788 #else
789 	r_line_set_prompt (":> ");
790 #endif
791 	r_core_visual_showcursor (core, true);
792 	r_cons_fgets (buf, sizeof (buf), 0, NULL);
793 	if (!strcmp (buf, "q")) {
794 		ret = false;
795 	} else if (*buf) {
796 		r_line_hist_add (buf);
797 		r_core_cmd (core, buf, 0);
798 		r_cons_echo (NULL);
799 		r_cons_flush ();
800 		ret = true;
801 		if (r_config_get_i (core->config, "cfg.debug")) {
802 			r_core_cmd (core, ".dr*", 0);
803 		}
804 	} else {
805 		ret = false;
806 		//r_cons_any_key (NULL);
807 		r_cons_clear00 ();
808 		r_core_visual_showcursor (core, false);
809 	}
810 	return ret;
811 }
812 
visual_single_step_in(RCore * core)813 static void visual_single_step_in(RCore *core) {
814 	if (r_config_get_i (core->config, "cfg.debug")) {
815 		if (core->print->cur_enabled) {
816 			// dcu 0xaddr
817 			r_core_cmdf (core, "dcu 0x%08"PFMT64x, core->offset + core->print->cur);
818 			core->print->cur_enabled = 0;
819 		} else {
820 			r_core_cmd (core, "ds", 0);
821 			r_core_cmd (core, ".dr*", 0);
822 		}
823 	} else {
824 		r_core_cmd (core, "aes", 0);
825 		r_core_cmd (core, ".ar*", 0);
826 	}
827 }
828 
__core_visual_step_over(RCore * core)829 static void __core_visual_step_over(RCore *core) {
830 	bool io_cache = r_config_get_i (core->config, "io.cache");
831 	r_config_set_i (core->config, "io.cache", false);
832 	if (r_config_get_i (core->config, "cfg.debug")) {
833 		if (core->print->cur_enabled) {
834 			r_core_cmd (core, "dcr", 0);
835 			core->print->cur_enabled = 0;
836 		} else {
837 			r_core_cmd (core, "dso", 0);
838 			r_core_cmd (core, ".dr*", 0);
839 		}
840 	} else {
841 		r_core_cmd (core, "aeso", 0);
842 		r_core_cmd (core, ".ar*", 0);
843 	}
844 	r_config_set_i (core->config, "io.cache", io_cache);
845 }
846 
visual_breakpoint(RCore * core)847 static void visual_breakpoint(RCore *core) {
848 	r_core_cmd (core, "dbs $$", 0);
849 }
850 
visual_continue(RCore * core)851 static void visual_continue(RCore *core) {
852 	if (r_config_get_i (core->config, "cfg.debug")) {
853 		r_core_cmd (core, "dc", 0);
854 	} else {
855 		r_core_cmd (core, "aec;.ar*", 0);
856 	}
857 }
858 
visual_nkey(RCore * core,int ch)859 static int visual_nkey(RCore *core, int ch) {
860 	const char *cmd;
861 	ut64 oseek = UT64_MAX;
862 	if (core->print->ocur == -1) {
863 		oseek = core->offset;
864 		r_core_seek (core, core->offset + core->print->cur, false);
865 	}
866 
867 	switch (ch) {
868 	case R_CONS_KEY_F1:
869 		cmd = r_config_get (core->config, "key.f1");
870 		if (cmd && *cmd) {
871 			ch = r_core_cmd0 (core, cmd);
872 		} else {
873 			visual_help (core);
874 		}
875 		break;
876 	case R_CONS_KEY_F2:
877 		cmd = r_config_get (core->config, "key.f2");
878 		if (cmd && *cmd) {
879 			ch = r_core_cmd0 (core, cmd);
880 		} else {
881 			visual_breakpoint (core);
882 		}
883 		break;
884 	case R_CONS_KEY_F3:
885 		cmd = r_config_get (core->config, "key.f3");
886 		if (cmd && *cmd) {
887 			ch = r_core_cmd0 (core, cmd);
888 		}
889 		break;
890 	case R_CONS_KEY_F4:
891 		cmd = r_config_get (core->config, "key.f4");
892 		if (cmd && *cmd) {
893 			ch = r_core_cmd0 (core, cmd);
894 		} else {
895 			if (core->print->cur_enabled) {
896 				// dcu 0xaddr
897 				r_core_cmdf (core, "dcu 0x%08"PFMT64x, core->offset + core->print->cur);
898 				core->print->cur_enabled = 0;
899 			}
900 		}
901 		break;
902 	case R_CONS_KEY_F5:
903 		cmd = r_config_get (core->config, "key.f5");
904 		if (cmd && *cmd) {
905 			ch = r_core_cmd0 (core, cmd);
906 		}
907 		break;
908 	case R_CONS_KEY_F6:
909 		cmd = r_config_get (core->config, "key.f6");
910 		if (cmd && *cmd) {
911 			ch = r_core_cmd0 (core, cmd);
912 		}
913 		break;
914 	case R_CONS_KEY_F7:
915 		cmd = r_config_get (core->config, "key.f7");
916 		if (cmd && *cmd) {
917 			ch = r_core_cmd0 (core, cmd);
918 		} else {
919 			visual_single_step_in (core);
920 		}
921 		break;
922 	case R_CONS_KEY_F8:
923 		cmd = r_config_get (core->config, "key.f8");
924 		if (cmd && *cmd) {
925 			ch = r_core_cmd0 (core, cmd);
926 		} else {
927 			__core_visual_step_over (core);
928 		}
929 		break;
930 	case R_CONS_KEY_F9:
931 		cmd = r_config_get (core->config, "key.f9");
932 		if (cmd && *cmd) {
933 			ch = r_core_cmd0 (core, cmd);
934 		} else {
935 			visual_continue (core);
936 		}
937 		break;
938 	case R_CONS_KEY_F10:
939 		cmd = r_config_get (core->config, "key.f10");
940 		if (cmd && *cmd) {
941 			ch = r_core_cmd0 (core, cmd);
942 		}
943 		break;
944 	case R_CONS_KEY_F11:
945 		cmd = r_config_get (core->config, "key.f11");
946 		if (cmd && *cmd) {
947 			ch = r_core_cmd0 (core, cmd);
948 		}
949 		break;
950 	case R_CONS_KEY_F12:
951 		cmd = r_config_get (core->config, "key.f12");
952 		if (cmd && *cmd) {
953 			ch = r_core_cmd0 (core, cmd);
954 		}
955 		break;
956 	}
957 	if (oseek != UT64_MAX) {
958 		r_core_seek (core, oseek, false);
959 	}
960 	return ch;
961 }
962 
setdiff(RCore * core)963 static void setdiff(RCore *core) {
964 	char from[64], to[64];
965 	prompt_read ("diff from: ", from, sizeof (from));
966 	r_config_set (core->config, "diff.from", from);
967 	prompt_read ("diff to: ", to, sizeof (to));
968 	r_config_set (core->config, "diff.to", to);
969 }
970 
findPair(RCore * core)971 static void findPair(RCore *core) {
972 	ut8 buf[256];
973 	int i, len, d = core->print->cur + 1;
974 	int delta = 0;
975 	const ut8 *p, *q = NULL;
976 	const char *keys = "{}[]()<>";
977 	ut8 ch = core->block[core->print->cur];
978 
979 	p = (const ut8 *) strchr (keys, ch);
980 	if (p) {
981 		char p_1 = 0;
982 		if ((const char *) p > keys) {
983 			p_1 = p[-1];
984 		}
985 		delta = (size_t) (p - (const ut8 *) keys);
986 		ch = (delta % 2 && p != (const ut8 *) keys)? p_1: p[1];
987 	}
988 	len = 1;
989 	buf[0] = ch;
990 
991 	if (p && (delta % 2)) {
992 		for (i = d - 1; i >= 0; i--) {
993 			if (core->block[i] == ch) {
994 				q = core->block + i;
995 				break;
996 			}
997 		}
998 	} else {
999 		q = r_mem_mem (core->block + d, core->blocksize - d,
1000 			(const ut8 *) buf, len);
1001 		if (!q) {
1002 			q = r_mem_mem (core->block, R_MIN (core->blocksize, d),
1003 				(const ut8 *) buf, len);
1004 		}
1005 	}
1006 	if (q) {
1007 		core->print->cur = (int) (size_t) (q - core->block);
1008 		core->print->ocur = -1;
1009 		r_core_visual_showcursor (core, true);
1010 	}
1011 }
1012 
findNextWord(RCore * core)1013 static void findNextWord(RCore *core) {
1014 	int i, d = core->print->cur_enabled? core->print->cur: 0;
1015 	for (i = d + 1; i < core->blocksize; i++) {
1016 		switch (core->block[i]) {
1017 		case ' ':
1018 		case '.':
1019 		case '\t':
1020 		case '\n':
1021 			if (core->print->cur_enabled) {
1022 				core->print->cur = i + 1;
1023 				core->print->ocur = -1;
1024 				r_core_visual_showcursor (core, true);
1025 			} else {
1026 				r_core_seek (core, core->offset + i + 1, true);
1027 			}
1028 			return;
1029 		}
1030 	}
1031 }
1032 
isSpace(char ch)1033 static int isSpace(char ch) {
1034 	switch (ch) {
1035 	case ' ':
1036 	case '.':
1037 	case ',':
1038 	case '\t':
1039 	case '\n':
1040 		return 1;
1041 	}
1042 	return 0;
1043 }
1044 
findPrevWord(RCore * core)1045 static void findPrevWord(RCore *core) {
1046 	int i = core->print->cur_enabled? core->print->cur: 0;
1047 	while (i > 1) {
1048 		if (isSpace (core->block[i])) {
1049 			i--;
1050 		} else if (isSpace (core->block[i - 1])) {
1051 			i -= 2;
1052 		} else {
1053 			break;
1054 		}
1055 	}
1056 	for (; i >= 0; i--) {
1057 		if (isSpace (core->block[i])) {
1058 			if (core->print->cur_enabled) {
1059 				core->print->cur = i + 1;
1060 				core->print->ocur = -1;
1061 				r_core_visual_showcursor (core, true);
1062 			}
1063 			break;
1064 		}
1065 	}
1066 }
1067 
1068 // TODO: integrate in '/' command with search.inblock ?
visual_search(RCore * core)1069 static void visual_search(RCore *core) {
1070 	const ut8 *p;
1071 	int len, d = core->print->cur;
1072 	char str[128], buf[sizeof (str) * 2 + 1];
1073 
1074 	r_line_set_prompt ("search byte/string in block: ");
1075 	r_cons_fgets (str, sizeof (str), 0, NULL);
1076 	len = r_hex_str2bin (str, (ut8 *) buf);
1077 	if (*str == '"') {
1078 		r_str_ncpy (buf, str + 1, sizeof (buf));
1079 		len = strlen (buf);
1080 		char *e = buf + len - 1;
1081 		if (e > buf && *e == '"') {
1082 			*e = 0;
1083 			len--;
1084 		}
1085 	} else if (len < 1) {
1086 		r_str_ncpy (buf, str, sizeof (buf));
1087 		len = strlen (buf);
1088 	}
1089 	p = r_mem_mem (core->block + d, core->blocksize - d,
1090 		(const ut8 *) buf, len);
1091 	if (p) {
1092 		core->print->cur = (int) (size_t) (p - core->block);
1093 		if (len > 1) {
1094 			core->print->ocur = core->print->cur + len - 1;
1095 		} else {
1096 			core->print->ocur = -1;
1097 		}
1098 		r_core_visual_showcursor (core, true);
1099 		eprintf ("Found in offset 0x%08"PFMT64x" + %d\n", core->offset, core->print->cur);
1100 		r_cons_any_key (NULL);
1101 	} else {
1102 		eprintf ("Cannot find bytes.\n");
1103 		r_cons_any_key (NULL);
1104 		r_cons_clear00 ();
1105 	}
1106 }
1107 
r_core_visual_show_char(RCore * core,char ch)1108 R_API void r_core_visual_show_char(RCore *core, char ch) {
1109 	if (r_config_get_i (core->config, "scr.feedback") < 2) {
1110 		return;
1111 	}
1112 	if (!IS_PRINTABLE (ch)) {
1113 		return;
1114 	}
1115 	r_cons_gotoxy (1, 2);
1116 	r_cons_printf (".---.\n");
1117 	r_cons_printf ("| %c |\n", ch);
1118 	r_cons_printf ("'---'\n");
1119 	r_cons_flush ();
1120 	r_sys_sleep (1);
1121 }
1122 
r_core_visual_seek_animation(RCore * core,ut64 addr)1123 R_API void r_core_visual_seek_animation(RCore *core, ut64 addr) {
1124 	r_core_seek (core, addr, true);
1125 	if (r_config_get_i (core->config, "scr.feedback") < 1) {
1126 		return;
1127 	}
1128 	if (core->offset == addr) {
1129 		return;
1130 	}
1131 	r_cons_gotoxy (1, 2);
1132 	if (addr > core->offset) {
1133 		r_cons_printf (".----.\n");
1134 		r_cons_printf ("| \\/ |\n");
1135 		r_cons_printf ("'----'\n");
1136 	} else {
1137 		r_cons_printf (".----.\n");
1138 		r_cons_printf ("| /\\ |\n");
1139 		r_cons_printf ("'----'\n");
1140 	}
1141 	r_cons_flush ();
1142 	r_sys_usleep (90000);
1143 }
1144 
setprintmode(RCore * core,int n)1145 static void setprintmode(RCore *core, int n) {
1146 	RAsmOp op;
1147 
1148 	if (n > 0) {
1149 		core->printidx = R_ABS ((core->printidx + 1) % NPF);
1150 	} else {
1151 		if (core->printidx) {
1152 			core->printidx--;
1153 		} else {
1154 			core->printidx = NPF - 1;
1155 		}
1156 	}
1157 	switch (core->printidx) {
1158 	case R_CORE_VISUAL_MODE_PD:
1159 	case R_CORE_VISUAL_MODE_DB:
1160 		r_asm_op_init (&op);
1161 		r_asm_disassemble (core->rasm, &op, core->block, R_MIN (32, core->blocksize));
1162 		r_asm_op_fini (&op);
1163 		break;
1164 	default:
1165 		break;
1166 	}
1167 }
1168 
1169 #define OPDELTA 32
prevop_addr(RCore * core,ut64 addr)1170 static ut64 prevop_addr(RCore *core, ut64 addr) {
1171 	ut8 buf[OPDELTA * 2];
1172 	ut64 target, base;
1173 	RAnalBlock *bb;
1174 	RAnalOp op;
1175 	int len, ret, i;
1176 	int minop = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MIN_OP_SIZE);
1177 	int maxop = r_anal_archinfo (core->anal, R_ANAL_ARCHINFO_MAX_OP_SIZE);
1178 
1179 	if (minop == maxop) {
1180 		if (minop == -1) {
1181 			return addr - 4;
1182 		}
1183 		return addr - minop;
1184 	}
1185 
1186 	// let's see if we can use anal info to get the previous instruction
1187 	// TODO: look in the current basicblock, then in the current function
1188 	// and search in all functions only as a last chance, to try to speed
1189 	// up the process.
1190 	bb = r_anal_bb_from_offset (core->anal, addr - minop);
1191 	if (bb) {
1192 		ut64 res = r_anal_bb_opaddr_at (bb, addr - minop);
1193 		if (res != UT64_MAX) {
1194 			return res;
1195 		}
1196 	}
1197 	// if we anal info didn't help then fallback to the dumb solution.
1198 	int midflags = r_config_get_i (core->config, "asm.flags.middle");
1199 	target = addr;
1200 	base = target > OPDELTA ? target - OPDELTA : 0;
1201 	r_io_read_at (core->io, base, buf, sizeof (buf));
1202 	for (i = 0; i < sizeof (buf); i++) {
1203 		ret = r_anal_op (core->anal, &op, base + i,
1204 			buf + i, sizeof (buf) - i, R_ANAL_OP_MASK_BASIC);
1205 		if (ret) {
1206 			len = op.size;
1207 			if (len < 1) {
1208 				len = 1;
1209 			}
1210 			r_anal_op_fini (&op); // XXX
1211 			if (midflags >= R_MIDFLAGS_REALIGN) {
1212 				int skip_bytes = r_core_flag_in_middle (core, base + i, len, &midflags);
1213 				if (skip_bytes && base + i + skip_bytes < target) {
1214 					i += skip_bytes - 1;
1215 					continue;
1216 				}
1217 			}
1218 		} else {
1219 			len = 1;
1220 		}
1221 		if (target <= base + i + len) {
1222 			return base + i;
1223 		}
1224 		i += len - 1;
1225 	}
1226 	return target > 4 ? target - 4 : 0;
1227 }
1228 
1229 //  Returns true if we can use analysis to find the previous operation address,
1230 //  sets prev_addr to the value of the instruction numinstrs back.
1231 //  If we can't use the anal, then set prev_addr to UT64_MAX and return false;
r_core_prevop_addr(RCore * core,ut64 start_addr,int numinstrs,ut64 * prev_addr)1232 R_API bool r_core_prevop_addr(RCore *core, ut64 start_addr, int numinstrs, ut64 *prev_addr) {
1233 	RAnalBlock *bb;
1234 	int i;
1235 	// Check that we're in a bb, otherwise this prevop stuff won't work.
1236 	bb = r_anal_bb_from_offset (core->anal, start_addr);
1237 	if (bb) {
1238 		if (r_anal_bb_opaddr_at (bb, start_addr) != UT64_MAX) {
1239 			// Do some anal looping.
1240 			for (i = 0; i < numinstrs; i++) {
1241 				*prev_addr = prevop_addr (core, start_addr);
1242 				start_addr = *prev_addr;
1243 			}
1244 			return true;
1245 		}
1246 	}
1247 	// Dang! not in a bb, return false and fallback to other methods.
1248 	*prev_addr = UT64_MAX;
1249 	return false;
1250 }
1251 
1252 //  Like r_core_prevop_addr(), but also uses fallback from prevop_addr() if
1253 //  no anal info is available.
r_core_prevop_addr_force(RCore * core,ut64 start_addr,int numinstrs)1254 R_API ut64 r_core_prevop_addr_force(RCore *core, ut64 start_addr, int numinstrs) {
1255 	int i;
1256 	for (i = 0; i < numinstrs; i++) {
1257 		start_addr = prevop_addr (core, start_addr);
1258 	}
1259 	return start_addr;
1260 }
1261 
r_line_hist_offset_up(RLine * line)1262 R_API int r_line_hist_offset_up(RLine *line) {
1263 	RCore *core = line->user;
1264 	RIOUndo *undo = &core->io->undo;
1265 	if (line->offset_hist_index <= -undo->undos) {
1266 		return false;
1267 	}
1268 	line->offset_hist_index--;
1269 	ut64 off = undo->seek[undo->idx + line->offset_hist_index].off;
1270 	RFlagItem *f = r_flag_get_at (core->flags, off, false);
1271 	char *command;
1272 	if (f && f->offset == off && f->offset > 0) {
1273 		command = r_str_newf ("%s", f->name);
1274 	} else {
1275 		command = r_str_newf ("0x%"PFMT64x, off);
1276 	}
1277 	strncpy (line->buffer.data, command, R_LINE_BUFSIZE - 1);
1278 	line->buffer.index = line->buffer.length = strlen (line->buffer.data);
1279 	free (command);
1280 	return true;
1281 }
1282 
r_line_hist_offset_down(RLine * line)1283 R_API int r_line_hist_offset_down(RLine *line) {
1284 	RCore *core = line->user;
1285 	RIOUndo *undo = &core->io->undo;
1286 	if (line->offset_hist_index >= undo->redos) {
1287 		return false;
1288 	}
1289 	line->offset_hist_index++;
1290 	if (line->offset_hist_index == undo->redos) {
1291 		line->buffer.data[0] = '\0';
1292 		line->buffer.index = line->buffer.length = 0;
1293 		return false;
1294 	}
1295 	ut64 off = undo->seek[undo->idx + line->offset_hist_index].off;
1296 	RFlagItem *f = r_flag_get_at (core->flags, off, false);
1297 	char *command;
1298 	if (f && f->offset == off && f->offset > 0) {
1299 		command = r_str_newf ("%s", f->name);
1300 	} else {
1301 		command = r_str_newf ("0x%"PFMT64x, off);
1302 	}
1303 	strncpy (line->buffer.data, command, R_LINE_BUFSIZE - 1);
1304 	line->buffer.index = line->buffer.length = strlen (line->buffer.data);
1305 	free (command);
1306 	return true;
1307 }
1308 
r_core_visual_offset(RCore * core)1309 R_API void r_core_visual_offset(RCore *core) {
1310 	ut64 addr, bsze, newaddr = 0LL;
1311 	char buf[256];
1312 
1313 	backup_current_addr (core, &addr, &bsze, &newaddr);
1314 	core->cons->line->prompt_type = R_LINE_PROMPT_OFFSET;
1315 	r_line_set_hist_callback (core->cons->line,
1316 		&r_line_hist_offset_up,
1317 		&r_line_hist_offset_down);
1318 	r_line_set_prompt ("[offset]> ");
1319 	strcpy (buf, "s ");
1320 	if (r_cons_fgets (buf + 2, sizeof (buf) - 2, 0, NULL) > 0) {
1321 		if (!strcmp (buf + 2, "g") || !strcmp (buf + 2, "G")) {
1322 			__core_visual_gogo (core, buf[2]);
1323 		} else {
1324 			if (buf[2] == '.') {
1325 				buf[1] = '.';
1326 			}
1327 			r_core_cmd0 (core, buf);
1328 			restore_current_addr (core, addr, bsze, newaddr);
1329 		}
1330 	}
1331 	r_line_set_hist_callback (core->cons->line, &r_line_hist_cmd_up, &r_line_hist_cmd_down);
1332 	core->cons->line->prompt_type = R_LINE_PROMPT_DEFAULT;
1333 }
1334 
r_core_visual_prevopsz(RCore * core,ut64 addr)1335 R_API int r_core_visual_prevopsz(RCore *core, ut64 addr) {
1336 	ut64 prev_addr = prevop_addr (core, addr);
1337 	return addr - prev_addr;
1338 }
1339 
addComment(RCore * core,ut64 addr)1340 static void addComment(RCore *core, ut64 addr) {
1341 	char buf[1024];
1342 	r_cons_printf ("Enter comment for reference:\n");
1343 	r_core_visual_showcursor (core, true);
1344 	r_cons_flush ();
1345 	r_cons_set_raw (false);
1346 	r_line_set_prompt (":> ");
1347 	r_cons_enable_mouse (false);
1348 	if (r_cons_fgets (buf, sizeof (buf), 0, NULL) < 0) {
1349 		buf[0] = '\0';
1350 	}
1351 	r_core_cmdf (core, "\"CC %s\"@0x%08"PFMT64x, buf, addr);
1352 	r_core_visual_showcursor (core, false);
1353 	r_cons_set_raw (true);
1354 }
1355 
follow_ref(RCore * core,RList * xrefs,int choice,int xref)1356 static int follow_ref(RCore *core, RList *xrefs, int choice, int xref) {
1357 	RAnalRef *refi = r_list_get_n (xrefs, choice);
1358 	if (refi) {
1359 		if (core->print->cur_enabled) {
1360 			core->print->cur = 0;
1361 		}
1362 		ut64 addr = refi->addr;
1363 		r_io_sundo_push (core->io, core->offset, -1);
1364 		r_core_seek (core, addr, true);
1365 		return 1;
1366 	}
1367 	return 0;
1368 }
1369 
1370 static const char *help_msg_visual_xref[] = {
1371 	"j/k",	"select next or previous item (use arrows)",
1372 	"J/K",	"scroll by 10 refs",
1373 	"g/G",	"scroll to top / bottom",
1374 	"p/P",	"rotate between various print modes",
1375 	":",	"run r2 command",
1376 	"/",	"highlight given word",
1377 	"?",	"show this help message",
1378 	"x/<",	"show xrefs",
1379 	"X/>",	"show refs",
1380 	"l/Space/Enter",	"seek to ref or xref",
1381 	"Tab",	"toggle between address and function references",
1382 	"h/q/Q",	"quit xref mode",
1383 	NULL
1384 };
1385 
r_core_visual_refs(RCore * core,bool xref,bool fcnInsteadOfAddr)1386 R_API int r_core_visual_refs(RCore *core, bool xref, bool fcnInsteadOfAddr) {
1387 	ut64 cur_ref_addr = UT64_MAX;
1388 	int ret = 0;
1389 	char ch;
1390 	int count = 0;
1391 	RList *xrefs = NULL;
1392 	RAnalRef *refi;
1393 	RListIter *iter;
1394 	int skip = 0;
1395 	int idx = 0;
1396 	char cstr[32];
1397 	ut64 addr = core->offset;
1398 	bool xrefsMode = fcnInsteadOfAddr;
1399 	int lastPrintMode = 3;
1400 	if (core->print->cur_enabled) {
1401 		addr += core->print->cur;
1402 	}
1403 repeat:
1404 	r_list_free (xrefs);
1405 	if (xrefsMode) {
1406 		RAnalFunction *fun = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
1407 		if (fun) {
1408 			if (xref) { //  function xrefs
1409 				xrefs = r_anal_xrefs_get (core->anal, addr);
1410 				//XXX xrefs = r_anal_fcn_get_xrefs (core->anal, fun);
1411 				// this function is buggy so we must get the xrefs of the addr
1412 			} else { // functon refs
1413 				xrefs = r_anal_function_get_refs (fun);
1414 			}
1415 		} else {
1416 			xrefs = NULL;
1417 		}
1418 	} else {
1419 		if (xref) { // address xrefs
1420 			xrefs = r_anal_xrefs_get (core->anal, addr);
1421 		} else { // address refs
1422 			xrefs = r_anal_refs_get (core->anal, addr);
1423 		}
1424 	}
1425 
1426 	r_cons_clear00 ();
1427 	r_cons_gotoxy (1, 1);
1428 	{
1429 		char *address = (core->dbg->bits & R_SYS_BITS_64)
1430 			? r_str_newf ("0x%016"PFMT64x, addr)
1431 			: r_str_newf ("0x%08"PFMT64x, addr);
1432 		r_cons_printf ("[%s%srefs]> %s # (TAB/jk/q/?) ",
1433 				xrefsMode? "fcn.": "addr.", xref ? "x": "", address);
1434 		free (address);
1435 	}
1436 	if (!xrefs || r_list_empty (xrefs)) {
1437 		r_list_free (xrefs);
1438 		xrefs = NULL;
1439 		r_cons_printf ("\n\n(no %srefs)\n", xref ? "x": "");
1440 	} else {
1441 		int h, w = r_cons_get_size (&h);
1442 		bool asm_bytes = r_config_get_i (core->config, "asm.bytes");
1443 		r_config_set_i (core->config, "asm.bytes", false);
1444 		r_core_cmd0 (core, "fd");
1445 
1446 		int maxcount = 9;
1447 		int rows, cols = r_cons_get_size (&rows);
1448 		count = 0;
1449 		char *dis = NULL;
1450 		rows -= 4;
1451 		idx = 0;
1452 		ut64 curat = UT64_MAX;
1453 		r_list_foreach (xrefs, iter, refi) {
1454 			if (idx - skip > maxcount) {
1455 				r_cons_printf ("...");
1456 				break;
1457 			}
1458 			if (!iter->n && idx < skip) {
1459 				skip = idx;
1460 			}
1461 			if (idx >= skip) {
1462 				if (count > maxcount) {
1463 					strcpy (cstr, "?");
1464 				} else {
1465 					snprintf (cstr, sizeof (cstr), "%d", count);
1466 				}
1467 				if (idx == skip) {
1468 					cur_ref_addr = refi->addr;
1469 				}
1470 				RAnalFunction *fun = r_anal_get_fcn_in (core->anal, refi->addr, R_ANAL_FCN_TYPE_NULL);
1471 				char *name;
1472 				if (fun) {
1473 					name = strdup (fun->name);
1474 				} else {
1475 					RFlagItem *f = r_flag_get_at (core->flags, refi->addr, true);
1476 					if (f) {
1477 						name = r_str_newf ("%s + %" PFMT64d, f->name, refi->addr - f->offset);
1478 					} else {
1479 						name = strdup ("unk");
1480 					}
1481 				}
1482 				if (w > 45) {
1483 					if (strlen (name) > w -45) {
1484 						name[w - 45] = 0;
1485 					}
1486 				} else {
1487 					name[0] = 0;
1488 				}
1489 				char *cmt = r_core_cmd_strf (core, "CC.@0x%08"PFMT64x, refi->addr);
1490 				r_str_trim (cmt);
1491 				r_cons_printf (" %d [%s] 0x%08"PFMT64x" 0x%08"PFMT64x " %s %sref (%s) ; %s\n",
1492 					idx, cstr, refi->at, refi->addr,
1493 					r_anal_xrefs_type_tostring (refi->type),
1494 					xref ? "x":"", name, cmt);
1495 				free (cmt);
1496 				free (name);
1497 				if (idx == skip) {
1498 					free (dis);
1499 					curat = refi->addr;
1500 					char *res = r_core_cmd_strf (core, "pd 4 @ 0x%08"PFMT64x"@e:asm.flags.limit=1", refi->at);
1501 					// TODO: show disasm with context. not seek addr
1502 					// dis = r_core_cmd_strf (core, "pd $r-4 @ 0x%08"PFMT64x, refi->addr);
1503 					dis = NULL;
1504 					res = r_str_appendf (res, "; ---------------------------\n");
1505 					switch (printMode) {
1506 					case 0:
1507 						dis = r_core_cmd_strf (core, "pd $r-4 @ 0x%08"PFMT64x, refi->addr);
1508 						break;
1509 					case 1:
1510 						dis = r_core_cmd_strf (core, "pd @ 0x%08"PFMT64x"-32", refi->addr);
1511 						break;
1512 					case 2:
1513 						dis = r_core_cmd_strf (core, "px @ 0x%08"PFMT64x, refi->addr);
1514 						break;
1515 					case 3:
1516 						dis = r_core_cmd_strf (core, "pds @ 0x%08"PFMT64x, refi->addr);
1517 						break;
1518 					}
1519 					if (dis) {
1520 						res = r_str_append (res, dis);
1521 						free (dis);
1522 					}
1523 					dis = res;
1524 				}
1525 				if (++count >= rows) {
1526 					r_cons_printf ("...");
1527 					break;
1528 				}
1529 			}
1530 			idx++;
1531 		}
1532 		if (dis) {
1533 			if (count < rows) {
1534 				r_cons_newline ();
1535 			}
1536 			int i = count;
1537 			for (; i < 9; i++)  {
1538 				r_cons_newline ();
1539 			}
1540 			/* prepare highlight */
1541 			char *cmd = strdup (r_config_get (core->config, "scr.highlight"));
1542 			char *ats = r_str_newf ("%"PFMT64x, curat);
1543 			if (ats && !*cmd) {
1544 				(void) r_config_set (core->config, "scr.highlight", ats);
1545 			}
1546 			/* print disasm */
1547 			char *d = r_str_ansi_crop (dis, 0, 0, cols, rows - 9);
1548 			if (d) {
1549 				r_cons_printf ("%s", d);
1550 				free (d);
1551 			}
1552 			/* flush and restore highlight */
1553 			r_cons_flush ();
1554 			r_config_set (core->config, "scr.highlight", cmd);
1555 			free (ats);
1556 			free (cmd);
1557 			free (dis);
1558 			dis = NULL;
1559 		}
1560 		r_config_set_i (core->config, "asm.bytes", asm_bytes);
1561 	}
1562 	r_cons_flush ();
1563 	r_cons_enable_mouse (r_config_get_i (core->config, "scr.wheel"));
1564 	ch = r_cons_readchar ();
1565 	ch = r_cons_arrow_to_hjkl (ch);
1566 	if (ch == ':') {
1567 		r_core_visual_prompt_input (core);
1568 		goto repeat;
1569 	} else if (ch == '?') {
1570 		r_cons_clear00 ();
1571 		RStrBuf *rsb = r_strbuf_new ("");
1572 		r_core_visual_append_help (rsb, "Xrefs Visual Analysis Mode (Vv + x) Help", help_msg_visual_xref);
1573 		ret = r_cons_less_str (r_strbuf_get (rsb), "?");
1574 		r_strbuf_free (rsb);
1575 		goto repeat;
1576 	} else if (ch == 9) { // TAB
1577 		xrefsMode = !xrefsMode;
1578 		r_core_visual_toggle_decompiler_disasm (core, false, true);
1579 		goto repeat;
1580 	} else if (ch == 'p') {
1581 		r_core_visual_toggle_decompiler_disasm (core, false, true);
1582 		printMode++;
1583 		if (printMode > lastPrintMode) {
1584 			printMode = 0;
1585 		}
1586 		goto repeat;
1587 	} else if (ch == 'P') {
1588 		r_core_visual_toggle_decompiler_disasm (core, false, true);
1589 		printMode--;
1590 		if (printMode < 0) {
1591 			printMode = lastPrintMode;
1592 		}
1593 		goto repeat;
1594 	} else if (ch == '/') {
1595 		r_core_cmd0 (core, "?i highlight;e scr.highlight=`yp`");
1596 		goto repeat;
1597 	} else if (ch == 'x' || ch == '<') {
1598 		xref = true;
1599 		xrefsMode = !xrefsMode;
1600 		goto repeat;
1601 	} else if (ch == 'X' || ch == '>') {
1602 		xref = false;
1603 		xrefsMode = !xrefsMode;
1604 		goto repeat;
1605 	} else if (ch == 'g') {
1606 		skip = 0;
1607 		goto repeat;
1608 	} else if (ch == 'G') {
1609 		skip = 9999;
1610 		goto repeat;
1611 	} else if (ch == ';') {
1612 		addComment (core, cur_ref_addr);
1613 		goto repeat;
1614 	} else if (ch == '.') {
1615 		skip = 0;
1616 		goto repeat;
1617 	} else if (ch == 'j') {
1618 		skip++;
1619 		goto repeat;
1620 	} else if (ch == 'J') {
1621 		skip += 10;
1622 		goto repeat;
1623 	} else if (ch == 'k') {
1624 		skip--;
1625 		if (skip < 0) {
1626 			skip = 0;
1627 		}
1628 		goto repeat;
1629 	} else if (ch == 'K') {
1630 		skip = (skip < 10) ? 0: skip - 10;
1631 		goto repeat;
1632 	} else if (ch == ' ' || ch == '\n' || ch == '\r' || ch == 'l') {
1633 		ret = follow_ref (core, xrefs, skip, xref);
1634 	} else if (IS_DIGIT (ch)) {
1635 		ret = follow_ref (core, xrefs, ch - 0x30, xref);
1636 	} else if (ch != 'q' && ch != 'Q' && ch != 'h') {
1637 		goto repeat;
1638 	}
1639 	r_list_free (xrefs);
1640 
1641 	return ret;
1642 }
1643 
1644 #if __WINDOWS__
SetWindow(int Width,int Height)1645 void SetWindow(int Width, int Height) {
1646 	COORD coord;
1647 	coord.X = Width;
1648 	coord.Y = Height;
1649 
1650 	SMALL_RECT Rect;
1651 	Rect.Top = 0;
1652 	Rect.Left = 0;
1653 	Rect.Bottom = Height - 1;
1654 	Rect.Right = Width - 1;
1655 
1656 	HANDLE Handle = GetStdHandle (STD_OUTPUT_HANDLE);
1657 	SetConsoleScreenBufferSize (Handle, coord);
1658 	SetConsoleWindowInfo (Handle, TRUE, &Rect);
1659 }
1660 #endif
1661 
1662 // unnecesarily public
getcommapath(RCore * core)1663 char *getcommapath(RCore *core) {
1664 	char *cwd;
1665 	const char *dir = r_config_get (core->config, "dir.projects");
1666 	const char *prj = r_config_get (core->config, "prj.name");
1667 	if (dir && *dir && prj && *prj) {
1668 		char *abspath = r_file_abspath (dir);
1669 		/* use prjdir as base directory for comma-ent files */
1670 		cwd = r_str_newf ("%s"R_SYS_DIR "%s.d", abspath, prj);
1671 		free (abspath);
1672 	} else {
1673 		/* use cwd as base directory for comma-ent files */
1674 		cwd = r_sys_getdir ();
1675 	}
1676 	return cwd;
1677 }
1678 
visual_comma(RCore * core)1679 static void visual_comma(RCore *core) {
1680 	bool mouse_state = __holdMouseState (core);
1681 	ut64 addr = core->offset + (core->print->cur_enabled? core->print->cur: 0);
1682 	char *comment, *cwd, *cmtfile;
1683 	const char *prev_cmt = r_meta_get_string (core->anal, R_META_TYPE_COMMENT, addr);
1684 	comment = prev_cmt ? strdup (prev_cmt) : NULL;
1685 	cmtfile = r_str_between (comment, ",(", ")");
1686 	cwd = getcommapath (core);
1687 	if (!cmtfile) {
1688 		char *fn;
1689 		fn = r_cons_input ("<comment-file> ");
1690 		if (fn && *fn) {
1691 			cmtfile = strdup (fn);
1692 			if (!comment || !*comment) {
1693 				comment = r_str_newf (",(%s)", fn);
1694 				r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, comment);
1695 			} else {
1696 				// append filename in current comment
1697 				char *nc = r_str_newf ("%s ,(%s)", comment, fn);
1698 				r_meta_set_string (core->anal, R_META_TYPE_COMMENT, addr, nc);
1699 				free (nc);
1700 			}
1701 		}
1702 		free (fn);
1703 	}
1704 	if (cmtfile) {
1705 		char *cwf = r_str_newf ("%s"R_SYS_DIR "%s", cwd, cmtfile);
1706 		char *odata = r_file_slurp (cwf, NULL);
1707 		if (!odata) {
1708 			eprintf ("Could not open '%s'.\n", cwf);
1709 			free (cwf);
1710 			goto beach;
1711 		}
1712 		char *data = r_core_editor (core, NULL, odata);
1713 		r_file_dump (cwf, (const ut8 *) data, -1, 0);
1714 		free (data);
1715 		free (odata);
1716 		free (cwf);
1717 	} else {
1718 		eprintf ("No commafile found.\n");
1719 	}
1720 beach:
1721 	free (comment);
1722 	r_cons_enable_mouse (mouse_state && r_config_get_i (core->config, "scr.wheel"));
1723 }
1724 
isDisasmPrint(int mode)1725 static bool isDisasmPrint(int mode) {
1726 	return (mode == R_CORE_VISUAL_MODE_PD || mode == R_CORE_VISUAL_MODE_DB);
1727 }
1728 
cursor_ocur(RCore * core,bool use_ocur)1729 static void cursor_ocur(RCore *core, bool use_ocur) {
1730 	RPrint *p = core->print;
1731 	if (use_ocur && p->ocur == -1) {
1732 		p->ocur = p->cur;
1733 	} else if (!use_ocur) {
1734 		p->ocur = -1;
1735 	}
1736 }
1737 
nextOpcode(RCore * core)1738 static void nextOpcode(RCore *core) {
1739 	RAnalOp *aop = r_core_anal_op (core, core->offset + core->print->cur, R_ANAL_OP_MASK_BASIC);
1740 	RPrint *p = core->print;
1741 	if (aop) {
1742 		p->cur += aop->size;
1743 		r_anal_op_free (aop);
1744 	} else {
1745 		p->cur += 4;
1746 	}
1747 }
1748 
prevOpcode(RCore * core)1749 static void prevOpcode(RCore *core) {
1750 	RPrint *p = core->print;
1751 	ut64 addr, oaddr = core->offset + core->print->cur;
1752 	if (r_core_prevop_addr (core, oaddr, 1, &addr)) {
1753 		const int delta = oaddr - addr;
1754 		p->cur -= delta;
1755 	} else {
1756 		p->cur -= 4;
1757 	}
1758 }
1759 
cursor_nextrow(RCore * core,bool use_ocur)1760 static void cursor_nextrow(RCore *core, bool use_ocur) {
1761 	RPrint *p = core->print;
1762 	ut32 roff, next_roff;
1763 	int row, sz, delta;
1764 	RAsmOp op;
1765 
1766 	cursor_ocur (core, use_ocur);
1767 	if (PIDX == 1) { // DISASM
1768 		nextOpcode (core);
1769 		return;
1770 	}
1771 
1772 	if (PIDX == 7 || !strcmp ("prc", r_config_get (core->config, "cmd.visual"))) {
1773 		p->cur += r_config_get_i (core->config, "hex.cols");
1774 		return;
1775 	}
1776 	if (splitView) {
1777 		int w = r_config_get_i (core->config, "hex.cols");
1778 		if (w < 1) {
1779 			w = 16;
1780 		}
1781 		if (core->seltab == 0) {
1782 			splitPtr += w;
1783 		} else {
1784 			core->offset += w;
1785 		}
1786 		return;
1787 	}
1788 	if (PIDX == R_CORE_VISUAL_MODE_DB) {
1789 		const int cols = core->dbg->regcols;
1790 		int w = r_config_get_i (core->config, "hex.cols");
1791 		switch (core->seltab) {
1792 		case 0:
1793 			if (w < 1) {
1794 				w = 16;
1795 			}
1796 			r_config_set_i (core->config, "stack.delta",
1797 					r_config_get_i (core->config, "stack.delta") - w);
1798 			return;
1799 		case 1:
1800 			p->cur += cols > 0? cols: 3;
1801 			return;
1802 		default:
1803 			nextOpcode (core);
1804 			return;
1805 		}
1806 	}
1807 	if (p->row_offsets) {
1808 		// FIXME: cache the current row
1809 		row = r_print_row_at_off (p, p->cur);
1810 		roff = r_print_rowoff (p, row);
1811 		if (roff == -1) {
1812 			p->cur++;
1813 			return;
1814 		}
1815 		next_roff = r_print_rowoff (p, row + 1);
1816 		if (next_roff == UT32_MAX) {
1817 			p->cur++;
1818 			return;
1819 		}
1820 		if (next_roff > core->blocksize) {
1821 			p->cur += 32; // XXX workaround to "fix" cursor nextrow far away scrolling issue
1822 			return;
1823 		}
1824 		if (next_roff + 32 < core->blocksize) {
1825 			sz = r_asm_disassemble (core->rasm, &op,
1826 				core->block + next_roff, 32);
1827 			if (sz < 1) {
1828 				sz = 1;
1829 			}
1830 		} else {
1831 			sz = 1;
1832 		}
1833 		delta = p->cur - roff;
1834 		p->cur = next_roff + R_MIN (delta, sz - 1);
1835 	} else {
1836 		p->cur += R_MAX (1, p->cols);
1837 	}
1838 }
1839 
cursor_prevrow(RCore * core,bool use_ocur)1840 static void cursor_prevrow(RCore *core, bool use_ocur) {
1841 	RPrint *p = core->print;
1842 	ut32 roff, prev_roff;
1843 	int row;
1844 
1845 	cursor_ocur (core, use_ocur);
1846 	if (PIDX == 1) { // DISASM
1847 		prevOpcode (core);
1848 		return;
1849 	}
1850 
1851 	if (PIDX == 7 || !strcmp ("prc", r_config_get (core->config, "cmd.visual"))) {
1852 		int cols = r_config_get_i (core->config, "hex.cols");
1853 		p->cur -= R_MAX (cols, 0);
1854 		return;
1855 	}
1856 
1857 	if (splitView) {
1858 		int w = r_config_get_i (core->config, "hex.cols");
1859 		if (w < 1) {
1860 			w = 16;
1861 		}
1862 		if (core->seltab == 0) {
1863 			splitPtr -= w;
1864 		} else {
1865 			core->offset -= w;
1866 		}
1867 		return;
1868 	}
1869 	if (PIDX == R_CORE_VISUAL_MODE_DB) {
1870 		switch (core->seltab) {
1871 		case 0:
1872 			{
1873 				int w = r_config_get_i (core->config, "hex.cols");
1874 				if (w < 1) {
1875 					w = 16;
1876 				}
1877 				r_config_set_i (core->config, "stack.delta",
1878 						r_config_get_i (core->config, "stack.delta") + w);
1879 			}
1880 			return;
1881 		case 1:
1882 			{
1883 				const int cols = core->dbg->regcols;
1884 				p->cur -= cols > 0? cols: 4;
1885 				return;
1886 			}
1887 		default:
1888 			prevOpcode (core);
1889 			return;
1890 		}
1891 	}
1892 	if (p->row_offsets) {
1893 		int delta, prev_sz;
1894 
1895 		// FIXME: cache the current row
1896 		row = r_print_row_at_off (p, p->cur);
1897 		roff = r_print_rowoff (p, row);
1898 		if (roff == UT32_MAX) {
1899 			p->cur--;
1900 			return;
1901 		}
1902 		prev_roff = row > 0? r_print_rowoff (p, row - 1): UT32_MAX;
1903 		delta = p->cur - roff;
1904 		if (prev_roff == UT32_MAX) {
1905 			ut64 prev_addr = prevop_addr (core, core->offset + roff);
1906 			if (prev_addr > core->offset) {
1907 				prev_roff = 0;
1908 				prev_sz = 1;
1909 			} else {
1910 				RAsmOp op;
1911 				prev_roff = 0;
1912 				r_core_seek (core, prev_addr, true);
1913 				prev_sz = r_asm_disassemble (core->rasm, &op,
1914 					core->block, 32);
1915 			}
1916 		} else {
1917 			prev_sz = roff - prev_roff;
1918 		}
1919 		int res = R_MIN (delta, prev_sz - 1);
1920 		ut64 cur = prev_roff + res;
1921 		if (cur == p->cur) {
1922 			if (p->cur > 0) {
1923 				p->cur--;
1924 			}
1925 		} else {
1926 			p->cur = prev_roff + delta; //res;
1927 		}
1928 	} else {
1929 		p->cur -= p->cols;
1930 	}
1931 }
1932 
cursor_left(RCore * core,bool use_ocur)1933 static void cursor_left(RCore *core, bool use_ocur) {
1934 	if (PIDX == 2) {
1935 		if (core->seltab == 1) {
1936 			core->print->cur--;
1937 			return;
1938 		}
1939 	}
1940 	cursor_ocur (core, use_ocur);
1941 	core->print->cur--;
1942 }
1943 
cursor_right(RCore * core,bool use_ocur)1944 static void cursor_right(RCore *core, bool use_ocur) {
1945 	if (PIDX == 2) {
1946 		if (core->seltab == 1) {
1947 			core->print->cur++;
1948 			return;
1949 		}
1950 	}
1951 	cursor_ocur (core, use_ocur);
1952 	core->print->cur++;
1953 }
1954 
fix_cursor(RCore * core)1955 static bool fix_cursor(RCore *core) {
1956 	RPrint *p = core->print;
1957 	int offscreen = (core->cons->rows - 3) * p->cols;
1958 	bool res = false;
1959 
1960 	if (!core->print->cur_enabled) {
1961 		return false;
1962 	}
1963 	if (core->print->screen_bounds > 1) {
1964 		bool off_is_visible = core->offset < core->print->screen_bounds;
1965 		bool cur_is_visible = core->offset + p->cur < core->print->screen_bounds;
1966 		bool is_close = core->offset + p->cur < core->print->screen_bounds + 32;
1967 
1968 		if ((!cur_is_visible && !is_close) || (!cur_is_visible && p->cur == 0)) {
1969 			// when the cursor is not visible and it's far from the
1970 			// last visible byte, just seek there.
1971 			r_core_seek_delta (core, p->cur);
1972 			reset_print_cur (p);
1973 		} else if ((!cur_is_visible && is_close) || !off_is_visible) {
1974 			RAsmOp op;
1975 			int sz = r_asm_disassemble (core->rasm,
1976 				&op, core->block, 32);
1977 			if (sz < 1) {
1978 				sz = 1;
1979 			}
1980 			r_core_seek_delta (core, sz);
1981 			p->cur = R_MAX (p->cur - sz, 0);
1982 			if (p->ocur != -1) {
1983 				p->ocur = R_MAX (p->ocur - sz, 0);
1984 			}
1985 			res |= off_is_visible;
1986 		}
1987 	} else if (core->print->cur >= offscreen) {
1988 		r_core_seek (core, core->offset + p->cols, true);
1989 		p->cur -= p->cols;
1990 		if (p->ocur != -1) {
1991 			p->ocur -= p->cols;
1992 		}
1993 	}
1994 
1995 	if (p->cur < 0) {
1996 		int sz = p->cols;
1997 		if (isDisasmPrint (core->printidx)) {
1998 			sz = r_core_visual_prevopsz (core, core->offset + p->cur);
1999 			if (sz < 1) {
2000 				sz = 1;
2001 			}
2002 		}
2003 		r_core_seek_delta (core, -sz);
2004 		p->cur += sz;
2005 		if (p->ocur != -1) {
2006 			p->ocur += sz;
2007 		}
2008 	}
2009 	return res;
2010 }
2011 
2012 static bool __ime = false;
2013 static int __nib = -1;
2014 
insert_mode_enabled(RCore * core)2015 static bool insert_mode_enabled(RCore *core) {
2016 	if (!__ime) {
2017 		return false;
2018 	}
2019 	char ch = (ut8)r_cons_readchar ();
2020 	if ((ut8)ch == KEY_ALTQ) {
2021 		(void)r_cons_readchar ();
2022 		__ime = false;
2023 		return true;
2024 	}
2025 	char arrows = r_cons_arrow_to_hjkl (ch);
2026 	switch (ch) {
2027 	case 127:
2028 		core->print->cur = R_MAX (0, core->print->cur - 1);
2029 		return true;
2030 	case 9: // tab "tab" TAB
2031 		core->print->col = core->print->col == 1? 2: 1;
2032 		break;
2033 	}
2034 	if (ch != 'h' && arrows == 'h') {
2035 		core->print->cur = R_MAX (0, core->print->cur - 1);
2036 		return true;
2037 	} else if (ch != 'l' && arrows == 'l') {
2038 		core->print->cur = core->print->cur + 1;
2039 		return true;
2040 	} else if (ch != 'j' && arrows == 'j') {
2041 		cursor_nextrow (core, false);
2042 		return true;
2043 	} else if (ch != 'k' && arrows == 'k') {
2044 		cursor_prevrow (core, false);
2045 		return true;
2046 	}
2047 	if (core->print->col == 2) {
2048 		/* ascii column */
2049 		if (IS_PRINTABLE (ch)) {
2050 			r_core_cmdf (core, "\"w %c\" @ $$+%d", ch, core->print->cur);
2051 			core->print->cur++;
2052 		}
2053 		return true;
2054 	}
2055 	ch = arrows;
2056 	/* hex column */
2057 	switch (ch) {
2058 	case '0':
2059 	case '1':
2060 	case '2':
2061 	case '3':
2062 	case '4':
2063 	case '5':
2064 	case '6':
2065 	case '7':
2066 	case '8':
2067 	case '9':
2068 	case 'a':
2069 	case 'b':
2070 	case 'c':
2071 	case 'd':
2072 	case 'e':
2073 	case 'f':
2074 		if (__nib != -1) {
2075 			r_core_cmdf (core, "wx %c%c @ $$+%d", __nib, ch, core->print->cur);
2076 			core->print->cur++;
2077 			__nib = -1;
2078 		} else {
2079 			r_core_cmdf (core, "wx %c. @ $$+%d", ch, core->print->cur);
2080 			__nib = ch;
2081 		}
2082 		break;
2083 	case 'r':
2084 		r_core_cmdf (core, "r-1 @ 0x%08"PFMT64x, core->offset + core->print->cur);
2085 		break;
2086 	case 'R':
2087 		r_core_cmdf (core, "r+1 @ 0x%08"PFMT64x, core->offset + core->print->cur);
2088 		break;
2089 	case 'h':
2090 		core->print->cur = R_MAX (0, core->print->cur - 1);
2091 		break;
2092 	case 'l':
2093 		core->print->cur = core->print->cur + 1;
2094 		break;
2095 	case 'j':
2096 		cursor_nextrow (core, false);
2097 		break;
2098 	case 'k':
2099 		cursor_prevrow (core, false);
2100 		break;
2101 	case 'Q':
2102 	case 'q':
2103 		__ime = false;
2104 		break;
2105 	case '?':
2106 		r_cons_less_str ("\nVisual Insert Mode:\n\n"
2107 			" tab          - toggle between ascii and hex columns\n"
2108 			" q (or alt-q) - quit insert mode\n"
2109 			"\nHex column:\n"
2110 			" r            - remove byte in cursor\n"
2111 			" R            - insert byte in cursor\n"
2112 			" [0-9a-f]     - insert hexpairs in hex column\n"
2113 			" hjkl         - move around\n"
2114 			"\nAscii column:\n"
2115 			" arrows       - move around\n"
2116 			" alt-q        - quit insert mode\n"
2117 			, "?");
2118 		break;
2119 	}
2120 	return true;
2121 }
2122 
r_core_visual_browse(RCore * core,const char * input)2123 R_API void r_core_visual_browse(RCore *core, const char *input) {
2124 	const char *browsemsg = \
2125 		"Browse stuff:\n"
2126 		"-------------\n"
2127 		" _  hud mode (V_)\n"
2128 		" 1  bit editor (vd1)\n"
2129 		" b  blocks\n"
2130 		" a  anal classes\n"
2131 		" c  classes\n"
2132 		" C  comments\n"
2133 		" d  debug traces\n"
2134 		" e  eval var configurations\n"
2135 		" E  esil debugger mode\n"
2136 		" f  flags\n"
2137 		" F  functions\n"
2138 		" g  graph\n"
2139 		" h  history\n"
2140 		" i  imports\n"
2141 		" l  chat logs (previously VT)\n"
2142 		" m  maps\n"
2143 		" M  mountpoints\n"
2144 		" p  pids/threads\n"
2145 		" q  quit\n"
2146 		" r  ROP gadgets\n"
2147 		" s  symbols\n"
2148 		" t  types\n"
2149 		" T  themes\n"
2150 		" v  vars\n"
2151 		" x  xrefs\n"
2152 		" X  refs\n"
2153 		" z  browse function zignatures\n"
2154 		" :  run command\n"
2155 	;
2156 	for (;;) {
2157 		r_cons_clear00 ();
2158 		r_cons_printf ("%s\n", browsemsg);
2159 		r_cons_flush ();
2160 		char ch = 0;
2161 		if (input && *input) {
2162 			ch = *input;
2163 			input++;
2164 		} else {
2165 			ch = r_cons_readchar ();
2166 		}
2167 		ch = r_cons_arrow_to_hjkl (ch);
2168 		switch (ch) {
2169 		case '1':
2170 			r_core_visual_bit_editor (core);
2171 			break;
2172 		case 'M':
2173 			if (!r_list_empty (core->fs->roots)) {
2174 				r_core_visual_mounts (core);
2175 			}
2176 			break;
2177 		case 'z': // "vbz"
2178 			if (r_core_visual_view_zigns (core)) {
2179 				return;
2180 			}
2181 			break;
2182 		case 'g': // "vbg"
2183 			if (r_core_visual_view_graph (core)) {
2184 				return;
2185 			}
2186 			break;
2187 		case 'r': // "vbr"
2188 			r_core_visual_view_rop (core);
2189 			break;
2190 		case 'f': // "vbf"
2191 			r_core_visual_trackflags (core);
2192 			break;
2193 		case 'F': // "vbF"
2194 			r_core_visual_anal (core, NULL);
2195 			// r_core_cmd0 (core, "s $(afl~...)");
2196 			break;
2197 		case 'd': // "vbd"
2198 			r_core_visual_debugtraces (core, NULL);
2199 			break;
2200 		case 'v': // "vbv"
2201 			r_core_visual_anal (core, "v");
2202 			break;
2203 		case 'e': // "vbe"
2204 			r_core_visual_config (core);
2205 			break;
2206 		case 'E': // "vbe"
2207 			r_core_visual_esil (core);
2208 			break;
2209 		case 'c': // "vbc"
2210 			r_core_visual_classes (core);
2211 			break;
2212 		case 'a': // "vba"
2213 			r_core_visual_anal_classes (core);
2214 			break;
2215 		case 'C': // "vbC"
2216 			r_core_visual_comments (core);
2217 			//r_core_cmd0 (core, "s $(CC~...)");
2218 			break;
2219 		case 't': // "vbt"
2220 			r_core_visual_types (core);
2221 			break;
2222 		case 'T': // "vbT"
2223 			r_core_cmd0 (core, "eco $(eco~...)");
2224 			break;
2225 		case 'l': // previously VT
2226 			if (r_sandbox_enable (0)) {
2227 				eprintf ("sandbox not enabled\n");
2228 			} else {
2229 				if (r_cons_is_interactive ()) {
2230 					r_core_cmd0 (core, "TT");
2231 				}
2232 			}
2233 			break;
2234 		case 'p':
2235 			r_core_cmd0 (core, "dpt=$(dpt~[1-])");
2236 			break;
2237 		case 'b':
2238 			r_core_cmd0 (core, "s $(afb~...)");
2239 			break;
2240 		case 'i':
2241 			// XXX ii shows index first and iiq shows no offset :(
2242 			r_core_cmd0 (core, "s $(ii~...)");
2243 			break;
2244 		case 's':
2245 			r_core_cmd0 (core, "s $(isq~...)");
2246 			break;
2247 		case 'm':
2248 			r_core_cmd0 (core, "s $(dm~...)");
2249 			break;
2250 		case 'x':
2251 			r_core_visual_refs (core, true, true);
2252 			break;
2253 		case 'X':
2254 			r_core_visual_refs (core, false, true);
2255 			break;
2256 		case 'h': // seek history
2257 			r_core_cmdf (core, "s!~...");
2258 			break;
2259 		case '_':
2260 			r_core_visual_hudstuff (core);
2261 			break;
2262 		case ':':
2263 			r_core_visual_prompt_input (core);
2264 			break;
2265 		case 127: // backspace
2266 		case 'q':
2267 			return;
2268 		}
2269 	}
2270 }
2271 
2272 #include "visual_tabs.inc"
2273 
isNumber(RCore * core,int ch)2274 static bool isNumber(RCore *core, int ch) {
2275 	if (ch > '0' && ch <= '9') {
2276 		return true;
2277 	}
2278 	if (core->print->cur_enabled) {
2279 		return ch == '0';
2280 	}
2281 	return false;
2282 }
2283 
2284 static char numbuf[32] = {0};
2285 static int numbuf_i = 0;
2286 
numbuf_append(int ch)2287 static void numbuf_append(int ch) {
2288 	if (numbuf_i >= sizeof (numbuf) - 1) {
2289 		numbuf_i = 0;
2290 	}
2291 	numbuf[numbuf_i++] = ch;
2292 	numbuf[numbuf_i] = 0;
2293 }
2294 
numbuf_pull(void)2295 static int numbuf_pull(void) {
2296 	int distance = 1;
2297 	if (numbuf_i) {
2298 		numbuf[numbuf_i] = 0;
2299 		distance = atoi (numbuf);
2300 		if (!distance) {
2301 			distance = 1;
2302 		}
2303 		numbuf_i = 0;
2304 	}
2305 	return distance;
2306 }
2307 
canWrite(RCore * core,ut64 addr)2308 static bool canWrite(RCore *core, ut64 addr) {
2309 	if (r_config_get_i (core->config, "io.cache")) {
2310 		return true;
2311 	}
2312 	RIOMap *map = r_io_map_get (core->io, addr);
2313 	return (map && (map->perm & R_PERM_W));
2314 }
2315 
toggle_bb(RCore * core,ut64 addr)2316 static bool toggle_bb(RCore *core, ut64 addr) {
2317 	RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_NULL);
2318 	if (fcn) {
2319 		RAnalBlock *bb = r_anal_fcn_bbget_in (core->anal, fcn, addr);
2320 		if (bb) {
2321 			bb->folded = !bb->folded;
2322 		} else {
2323 			r_warn_if_reached ();
2324 		}
2325 		return true;
2326 	}
2327 	return false;
2328 }
2329 
process_get_click(RCore * core,int ch)2330 static int process_get_click(RCore *core, int ch) {
2331 	int x, y;
2332 	if (r_cons_get_click (&x, &y)) {
2333 		if (y == 1) {
2334 			if (x < 13) {
2335 				ch = '_';
2336 			} else if (x < 20) {
2337 				ch = 'p';
2338 			} else if (x < 24) {
2339 				ch = 9;
2340 			}
2341 		} else if (y == 2) {
2342 			if (x < 2) {
2343 				visual_closetab (core);
2344 			} else if (x < 5) {
2345 				visual_newtab (core);
2346 			} else {
2347 				visual_nexttab (core);
2348 			}
2349 			return 0;
2350 		} else {
2351 			ch = 0; //'c';
2352 		}
2353 	}
2354 	return ch;
2355 }
2356 
r_core_visual_cmd(RCore * core,const char * arg)2357 R_API int r_core_visual_cmd(RCore *core, const char *arg) {
2358 	ut8 och = arg[0];
2359 	RAsmOp op;
2360 	ut64 offset = core->offset;
2361 	char buf[4096];
2362 	const char *key_s;
2363 	int i, cols = core->print->cols;
2364 	int wheelspeed;
2365 	int ch = och;
2366 	if ((ut8)ch == KEY_ALTQ) {
2367 		r_cons_readchar ();
2368 		ch = 'q';
2369 	}
2370 	ch = r_cons_arrow_to_hjkl (ch);
2371 	ch = visual_nkey (core, ch);
2372 	if (ch < 2) {
2373 		ch = process_get_click (core, ch);
2374 		if (!ch) {
2375 			return 1;
2376 		}
2377 	}
2378 	if (r_cons_singleton ()->mouse_event) {
2379 		wheelspeed = r_config_get_i (core->config, "scr.wheel.speed");
2380 	} else {
2381 		wheelspeed = 1;
2382 	}
2383 
2384 	if (ch == 'l' && och == 6) {
2385 		ch = 'J';
2386 	} else if (ch == 'h' && och == 2) {
2387 		ch = 'K';
2388 	}
2389 
2390 	// do we need hotkeys for data references? not only calls?
2391 	// '0' is handled to seek at the beginning of the function
2392 	// unless the cursor is set, then, the 0 is captured here
2393 	if (isNumber (core, ch)) {
2394 		// only in disasm and debug prints..
2395 		if (isDisasmPrint (core->printidx)) {
2396 			if (r_config_get_i (core->config, "asm.hints") && (r_config_get_i (core->config, "asm.hint.jmp")
2397 			|| r_config_get_i (core->config, "asm.hint.lea") || r_config_get_i (core->config, "asm.hint.emu")
2398 			|| r_config_get_i (core->config, "asm.hint.call"))) {
2399 				r_core_visual_jump (core, ch);
2400 			} else {
2401 				numbuf_append (ch);
2402 			}
2403 		} else {
2404 			numbuf_append (ch);
2405 		}
2406 	} else {
2407 		switch (ch) {
2408 #if __WINDOWS__
2409 		case 0xf5:
2410 			SetWindow (81, 25);
2411 			break;
2412 		case 0xcf5:
2413 			SetWindow (81, 40);
2414 			break;
2415 #endif
2416 		case 0x0d: // "enter" "\\n" "newline"
2417 			if (r_config_get_i (core->config, "scr.cursor")) {
2418 				r_cons_set_click (core->cons->cpos.x, core->cons->cpos.y);
2419 				char buf[10];
2420 				int ch = process_get_click (core, 0);
2421 				buf[0] = ch;
2422 				buf[1] = 0;
2423 				r_core_visual_cmd(core, buf);
2424 			} else {
2425 			RAnalOp *op;
2426 			int wheel = r_config_get_i (core->config, "scr.wheel");
2427 			if (wheel) {
2428 				r_cons_enable_mouse (true);
2429 			}
2430 			do {
2431 				op = r_core_anal_op (core, core->offset + core->print->cur, R_ANAL_OP_MASK_BASIC);
2432 				if (op) {
2433 					if (op->type == R_ANAL_OP_TYPE_JMP ||
2434 					op->type == R_ANAL_OP_TYPE_CJMP ||
2435 					op->type == R_ANAL_OP_TYPE_CALL ||
2436 					op->type == R_ANAL_OP_TYPE_CCALL) {
2437 						if (core->print->cur_enabled) {
2438 							int delta = R_ABS ((st64) op->jump - (st64) offset);
2439 							if (op->jump < core->offset || op->jump >= core->print->screen_bounds) {
2440 								r_io_sundo_push (core->io, offset, r_print_get_cursor (core->print));
2441 								r_core_visual_seek_animation (core, op->jump);
2442 								core->print->cur = 0;
2443 							} else {
2444 								r_io_sundo_push (core->io, offset, r_print_get_cursor (core->print));
2445 								core->print->cur = delta;
2446 							}
2447 						} else {
2448 							r_io_sundo_push (core->io, offset, 0);
2449 							r_core_visual_seek_animation (core, op->jump);
2450 						}
2451 					}
2452 				}
2453 				r_anal_op_free (op);
2454 			} while (--wheelspeed > 0);
2455 		}
2456 		break;
2457 		case 'o': // tab TAB
2458 			nextPrintFormat (core);
2459 			break;
2460 		case 'O': // tab TAB
2461 		case 9: // tab TAB
2462 			r_core_visual_toggle_decompiler_disasm (core, false, true);
2463 			if (splitView) {
2464 				// this split view is kind of useless imho, we should kill it or merge it into tabs
2465 				core->print->cur = 0;
2466 				core->curtab = 0;
2467 				core->seltab++;
2468 				if (core->seltab > 1) {
2469 					core->seltab = 0;
2470 				}
2471 			} else {
2472 				if (core->print->cur_enabled) {
2473 					core->curtab = 0;
2474 					if (core->printidx == R_CORE_VISUAL_MODE_DB) {
2475 						core->print->cur = 0;
2476 						core->seltab++;
2477 						if (core->seltab > 2) {
2478 							core->seltab = 0;
2479 						}
2480 					} else {
2481 						core->seltab = 0;
2482 						ut64 f = r_config_get_i (core->config, "diff.from");
2483 						ut64 t = r_config_get_i (core->config, "diff.to");
2484 						if (f == t && f == 0) {
2485 							core->print->col = core->print->col == 1? 2: 1;
2486 						}
2487 					}
2488 				} else {
2489 					prevPrintFormat (core);
2490 				}
2491 			}
2492 			break;
2493 		case '&':
2494 			rotateAsmBits (core);
2495 			break;
2496 		case 'a':
2497 		{
2498 			{
2499 				ut64 addr = core->offset;
2500 				if (PIDX == 2) {
2501 					if (core->seltab == 0) {
2502 						addr = r_debug_reg_get (core->dbg, "SP");
2503 					}
2504 				}
2505 				if (!canWrite (core, addr)) {
2506 					r_cons_printf ("\nFile has been opened in read-only mode. Use -w flag, oo+ or e io.cache=true\n");
2507 					r_cons_any_key (NULL);
2508 					return true;
2509 				}
2510 			}
2511 			r_cons_printf ("Enter assembler opcodes separated with ';':\n");
2512 			r_core_visual_showcursor (core, true);
2513 			r_cons_flush ();
2514 			r_cons_set_raw (false);
2515 			strcpy (buf, "\"wa ");
2516 			r_line_set_prompt (":> ");
2517 			r_cons_enable_mouse (false);
2518 			if (r_cons_fgets (buf + 4, sizeof (buf) - 4, 0, NULL) < 0) {
2519 				buf[0] = '\0';
2520 			}
2521 			strcat (buf, "\"");
2522 			int wheel = r_config_get_i (core->config, "scr.wheel");
2523 			if (wheel) {
2524 				r_cons_enable_mouse (true);
2525 			}
2526 			if (*buf) {
2527 				if (core->print->cur_enabled) {
2528 					int t = core->offset + core->print->cur;
2529 					r_core_seek (core, t, false);
2530 				}
2531 				r_core_cmd (core, buf, true);
2532 				if (core->print->cur_enabled) {
2533 					int t = core->offset - core->print->cur;
2534 					r_core_seek (core, t, true);
2535 				}
2536 			}
2537 			r_core_visual_showcursor (core, false);
2538 			r_cons_set_raw (true);
2539 		}
2540 		break;
2541 		case '=':
2542 		{ // TODO: edit
2543 			r_core_visual_showcursor (core, true);
2544 			const char *buf = NULL;
2545 			#define I core->cons
2546 			const char *cmd = r_config_get (core->config, "cmd.vprompt");
2547 			r_line_set_prompt ("cmd.vprompt> ");
2548 			I->line->contents = strdup (cmd);
2549 			buf = r_line_readline ();
2550 			I->line->contents = NULL;
2551 			(void)r_config_set (core->config, "cmd.vprompt", buf);
2552 			r_core_visual_showcursor (core, false);
2553 		}
2554 		break;
2555 		case '|':
2556 		{ // TODO: edit
2557 			r_core_visual_showcursor (core, true);
2558 			const char *buf = NULL;
2559 			#define I core->cons
2560 			const char *cmd = r_config_get (core->config, "cmd.cprompt");
2561 			r_line_set_prompt ("cmd.cprompt> ");
2562 			I->line->contents = strdup (cmd);
2563 			buf = r_line_readline ();
2564 			if (buf && !strcmp (buf, "|")) {
2565 				R_FREE (I->line->contents);
2566 				core->print->cur_enabled = true;
2567 				core->print->cur = 0;
2568 				(void)r_config_set (core->config, "cmd.cprompt", "p=e $r-2");
2569 			} else {
2570 				R_FREE (I->line->contents);
2571 				(void)r_config_set (core->config, "cmd.cprompt", r_str_get (buf));
2572 			}
2573 			r_core_visual_showcursor (core, false);
2574 		}
2575 		break;
2576 		case '!':
2577 			r_core_panels_root (core, core->panels_root);
2578 			break;
2579 		case 'g':
2580 			r_core_visual_showcursor (core, true);
2581 			r_core_visual_offset (core);
2582 			r_core_visual_showcursor (core, false);
2583 			break;
2584 		case 'G':
2585 			__core_visual_gogo (core, 'G');
2586 			break;
2587 		case 'A':
2588 		{
2589 			const int oce = core->print->cur_enabled;
2590 			const int oco = core->print->ocur;
2591 			const int occ = core->print->cur;
2592 			ut64 off = oce? core->offset + core->print->cur: core->offset;
2593 			core->print->cur_enabled = 0;
2594 			r_cons_enable_mouse (false);
2595 			r_core_visual_asm (core, off);
2596 			core->print->cur_enabled = oce;
2597 			core->print->cur = occ;
2598 			core->print->ocur = oco;
2599 			if (r_config_get_i (core->config, "scr.wheel")) {
2600 				r_cons_enable_mouse (true);
2601 			}
2602 		}
2603 		break;
2604 		case '\\':
2605 			if (splitPtr == UT64_MAX) {
2606 				splitPtr = core->offset;
2607 			}
2608 			splitView = !splitView;
2609 			setcursor (core, splitView);
2610 			break;
2611 		case 'c':
2612 			setcursor (core, !core->print->cur_enabled);
2613 			break;
2614 		case '$':
2615 			if (core->print->cur_enabled) {
2616 				r_core_cmdf (core, "dr PC=$$+%d", core->print->cur);
2617 			} else {
2618 				r_core_cmd0 (core, "dr PC=$$");
2619 			}
2620 			break;
2621 		case '@':
2622 			if (core->print->cur_enabled) {
2623 				char buf[128];
2624 				prompt_read ("cursor at:", buf, sizeof (buf));
2625 				core->print->cur = (st64) r_num_math (core->num, buf);
2626 			}
2627 			break;
2628 		case 'C':
2629 			if (++color > 2) {
2630 				color = 0;
2631 			}
2632 			r_config_set_i (core->config, "scr.color", color);
2633 			break;
2634 		case 'd': {
2635 			bool mouse_state = __holdMouseState (core);
2636 			r_core_visual_showcursor (core, true);
2637 			int distance = numbuf_pull ();
2638 			r_core_visual_define (core, arg + 1, distance - 1);
2639 			r_core_visual_showcursor (core, false);
2640 			r_cons_enable_mouse (mouse_state && r_config_get_i(core->config, "scr.wheel"));
2641 		}
2642 			break;
2643 		case 'D':
2644 			setdiff (core);
2645 			break;
2646 		case 'f':
2647 		{
2648 			bool mouse_state = __holdMouseState (core);
2649 			int range, min, max;
2650 			char name[256], *n;
2651 			r_line_set_prompt ("flag name: ");
2652 			r_core_visual_showcursor (core, true);
2653 			if (r_cons_fgets (name, sizeof (name), 0, NULL) >= 0 && *name) {
2654 				n = name;
2655 				r_str_trim (n);
2656 				if (core->print->ocur != -1) {
2657 					min = R_MIN (core->print->cur, core->print->ocur);
2658 					max = R_MAX (core->print->cur, core->print->ocur);
2659 				} else {
2660 					min = max = core->print->cur;
2661 				}
2662 				range = max - min + 1;
2663 				if (!strcmp (n, "-")) {
2664 					r_flag_unset_off (core->flags, core->offset + core->print->cur);
2665 				} else if (*n == '.') {
2666 					if (n[1] == '-') {
2667 						//unset
2668 						r_core_cmdf (core, "f.-%s@0x%"PFMT64x, n + 1, core->offset + min);
2669 					} else {
2670 						r_core_cmdf (core, "f.%s@0x%"PFMT64x, n + 1, core->offset + min);
2671 					}
2672 				} else if (*n == '-') {
2673 					if (*n) {
2674 						r_flag_unset_name (core->flags, n + 1);
2675 					}
2676 				} else {
2677 					if (range < 1) {
2678 						range = 1;
2679 					}
2680 					if (*n) {
2681 						r_flag_set (core->flags, n,
2682 							core->offset + min, range);
2683 					}
2684 				}
2685 			}
2686 			r_cons_enable_mouse (mouse_state && r_config_get_i(core->config, "scr.wheel"));
2687 		}
2688 			r_core_visual_showcursor (core, false);
2689 			break;
2690 		case ',':
2691 			visual_comma (core);
2692 			break;
2693 		case 't':
2694 			{
2695 				r_cons_gotoxy (0, 0);
2696 				if (core->visual.tabs) {
2697 					r_cons_printf ("[tnp:=+-] ");
2698 				} else {
2699 					r_cons_printf ("[t] ");
2700 				}
2701 				r_cons_flush();
2702 				int ch = r_cons_readchar ();
2703 				if (isdigit (ch)) {
2704 					visual_nthtab (core, ch - '0' - 1);
2705 				}
2706 				switch (ch) {
2707 				case 'h':
2708 				case 'k':
2709 				case 'p':
2710 					visual_prevtab (core);
2711 					break;
2712 				case 9: // t-TAB
2713 				case 'l':
2714 				case 'j':
2715 				case 'n':
2716 					visual_nexttab (core);
2717 					break;
2718 				case '=':
2719 					visual_tabname (core);
2720 					break;
2721 				case '-':
2722 					visual_closetab (core);
2723 					break;
2724 				case ':':
2725 					{
2726 						RCoreVisualTab *tab = visual_newtab (core);
2727 						if (tab) {
2728 							tab->name[0] = ':';
2729 							r_cons_fgets (tab->name + 1, sizeof (tab->name) - 1, 0, NULL);
2730 						}
2731 					}
2732 					break;
2733 				case '+':
2734 				case 't':
2735 				case 'a':
2736 					visual_newtab (core);
2737 					break;
2738 				}
2739 			}
2740 			break;
2741 		case 'T':
2742 			visual_closetab (core);
2743 			break;
2744 		case 'n':
2745 			r_core_seek_next (core, r_config_get (core->config, "scr.nkey"));
2746 			break;
2747 		case 'N':
2748 			r_core_seek_previous (core, r_config_get (core->config, "scr.nkey"));
2749 			break;
2750 		case 'i':
2751 		case 'I':
2752 			{
2753 			ut64 oaddr = core->offset;
2754 			int delta = (core->print->ocur != -1)? R_MIN (core->print->cur, core->print->ocur): core->print->cur;
2755 			ut64 addr = core->offset + delta;
2756 			if (PIDX == 0) {
2757 				if (strstr (printfmtSingle[0], "pxb")) {
2758 					r_core_visual_define (core, "1", 1);
2759 					return true;
2760 				}
2761 				if (core->print->ocur == -1) {
2762 					__ime = true;
2763 					core->print->cur_enabled = true;
2764 					return true;
2765 				}
2766 			} else if (PIDX == 2) {
2767 				if (core->seltab == 0) {
2768 					addr = r_debug_reg_get (core->dbg, "SP") + delta;
2769 				} else if (core->seltab == 1) {
2770 					char buf[128];
2771 					prompt_read ("new-reg-value> ", buf, sizeof (buf));
2772 					if (*buf) {
2773 						const char *creg = core->dbg->creg;
2774 						if (creg) {
2775 							r_core_cmdf (core, "dr %s = %s\n", creg, buf);
2776 						}
2777 					}
2778 					return true;
2779 				}
2780 			}
2781 			if (!canWrite (core, addr)) {
2782 				r_cons_printf ("\nFile has been opened in read-only mode. Use -w flag, oo+ or e io.cache=true\n");
2783 				r_cons_any_key (NULL);
2784 				return true;
2785 			}
2786 			r_core_visual_showcursor (core, true);
2787 			r_cons_flush ();
2788 			r_cons_set_raw (0);
2789 			if (ch == 'I') {
2790 				strcpy (buf, "wow ");
2791 				r_line_set_prompt ("insert hexpair block: ");
2792 				if (r_cons_fgets (buf + 4, sizeof (buf) - 4, 0, NULL) < 0) {
2793 					buf[0] = '\0';
2794 				}
2795 				char *p = strdup (buf);
2796 				int cur = core->print->cur;
2797 				if (cur >= core->blocksize) {
2798 					cur = core->print->cur - 1;
2799 				}
2800 				snprintf (buf, sizeof (buf), "%s @ $$0!%i", p,
2801 					core->blocksize - cur);
2802 				r_core_cmd (core, buf, 0);
2803 				free (p);
2804 				break;
2805 			}
2806 			if (core->print->col == 2) {
2807 				strcpy (buf, "\"w ");
2808 				r_line_set_prompt ("insert string: ");
2809 				if (r_cons_fgets (buf + 3, sizeof (buf) - 3, 0, NULL) < 0) {
2810 					buf[0] = '\0';
2811 				}
2812 				strcat (buf, "\"");
2813 			} else {
2814 				r_line_set_prompt ("insert hex: ");
2815 				if (core->print->ocur != -1) {
2816 					int bs = R_ABS (core->print->cur - core->print->ocur) + 1;
2817 					core->blocksize = bs;
2818 					strcpy (buf, "wow ");
2819 				} else {
2820 					strcpy (buf, "wx ");
2821 				}
2822 				if (r_cons_fgets (buf + strlen (buf), sizeof (buf) - strlen (buf), 0, NULL) < 0) {
2823 					buf[0] = '\0';
2824 				}
2825 			}
2826 			if (core->print->cur_enabled) {
2827 				r_core_seek (core, addr, false);
2828 			}
2829 			r_core_cmd (core, buf, 1);
2830 			if (core->print->cur_enabled) {
2831 				r_core_seek (core, addr, true);
2832 			}
2833 			r_cons_set_raw (1);
2834 			r_core_visual_showcursor (core, false);
2835 			r_core_seek (core, oaddr, true);
2836 			}
2837 			break;
2838 		case 'R':
2839 			if (r_config_get_i (core->config, "scr.randpal")) {
2840 				r_core_cmd0 (core, "ecr");
2841 			} else {
2842 				r_core_cmd0 (core, "ecn");
2843 			}
2844 			break;
2845 		case 'e':
2846 			r_core_visual_config (core);
2847 			break;
2848 		case '^':
2849 			  {
2850 				  RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
2851 				  if (fcn) {
2852 					  r_core_seek (core, fcn->addr, false);
2853 				  } else {
2854 					  __core_visual_gogo (core, 'g');
2855 				  }
2856 			  }
2857 			  break;
2858 		case 'E':
2859 			r_core_visual_colors (core);
2860 			break;
2861 		case 'x':
2862 			r_core_visual_refs (core, true, false);
2863 			break;
2864 		case 'X':
2865 			r_core_visual_refs (core, false, false);
2866 			break;
2867 		case 'r':
2868 			// TODO: toggle shortcut hotkeys
2869 			if (r_config_get_i (core->config, "asm.hint.call")) {
2870 				r_core_cmd0 (core, "e!asm.hint.call");
2871 				r_core_cmd0 (core, "e asm.hint.jmp=true");
2872 			} else if (r_config_get_i (core->config, "asm.hint.jmp")) {
2873 				r_core_cmd0 (core, "e!asm.hint.jmp");
2874 				r_core_cmd0 (core, "e asm.hint.emu=true");
2875 			} else if (r_config_get_i (core->config, "asm.hint.emu")) {
2876 				r_core_cmd0 (core, "e!asm.hint.emu");
2877 				r_core_cmd0 (core, "e asm.hint.lea=true");
2878 			} else if (r_config_get_i (core->config, "asm.hint.lea")) {
2879 				r_core_cmd0 (core, "e!asm.hint.lea");
2880 				r_core_cmd0 (core, "e asm.hint.call=true");
2881 			} else {
2882 				r_core_cmd0 (core, "e asm.hint.call=true");
2883 			}
2884 			visual_refresh (core);
2885 			break;
2886 		case ' ':
2887 		case 'V':
2888 			if (r_config_get_i (core->config, "graph.web")) {
2889 				r_core_cmd0 (core, "agv $$");
2890 			} else {
2891 				RAnalFunction *fun = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
2892 				int ocolor = r_config_get_i (core->config, "scr.color");
2893 				if (!fun) {
2894 					r_cons_message ("Not in a function. Type 'df' to define it here");
2895 					break;
2896 				} else if (r_list_empty (fun->bbs)) {
2897 					r_cons_message ("No basic blocks in this function. You may want to use 'afb+'.");
2898 					break;
2899 				}
2900 				reset_print_cur (core->print);
2901 				eprintf ("\rRendering graph...");
2902 				r_core_visual_graph (core, NULL, NULL, true);
2903 				r_config_set_i (core->config, "scr.color", ocolor);
2904 			}
2905 			break;
2906 		case 'v':
2907 			r_core_visual_anal (core, NULL);
2908 			break;
2909 		case 'h':
2910 		case 'l':
2911 			if (r_config_get_i (core->config, "scr.cursor")) {
2912 				core->cons->cpos.x += ch == 'h'? -1: 1;
2913 				if (core->cons->cpos.x < 1) {
2914 					core->cons->cpos.x = 0;
2915 				}
2916 				int h, w = r_cons_get_size (&h);
2917 				if (core->cons->cpos.x >= w) {
2918 					core->cons->cpos.x = w;
2919 				}
2920 			} else {
2921 				int distance = numbuf_pull ();
2922 				if (core->print->cur_enabled) {
2923 					if (ch == 'h') {
2924 						for (i = 0; i < distance; i++) {
2925 							cursor_left (core, false);
2926 						}
2927 					} else {
2928 						for (i = 0; i < distance; i++) {
2929 							cursor_right (core, false);
2930 						}
2931 					}
2932 				} else {
2933 					if (ch == 'h') {
2934 						distance = -distance;
2935 					}
2936 					r_core_seek_delta (core, distance);
2937 				}
2938 			}
2939 			break;
2940 		case 'L':
2941 		case 'H':
2942 			if (r_config_get_i (core->config, "scr.cursor")) {
2943 				int distance = 8; // numbuf_pull ();
2944 				core->cons->cpos.x += (ch == 'h' || ch == 'H')? -distance: distance;
2945 				if (core->cons->cpos.x < 1) {
2946 					core->cons->cpos.x = 0;
2947 				}
2948 				int h, w = r_cons_get_size (&h);
2949 				if (core->cons->cpos.x >= w) {
2950 					core->cons->cpos.x = w;
2951 				}
2952 			} else {
2953 				int distance = numbuf_pull ();
2954 				if (core->print->cur_enabled) {
2955 					if (ch == 'H') {
2956 						for (i = 0; i < distance; i++) {
2957 							cursor_left (core, true);
2958 						}
2959 					} else {
2960 						for (i = 0; i < distance; i++) {
2961 							cursor_right (core, true);
2962 						}
2963 					}
2964 				} else {
2965 					if (ch == 'H') {
2966 						distance = -distance;
2967 					}
2968 					r_core_seek_delta (core, distance * 2);
2969 				}
2970 			}
2971 			break;
2972 		case 'j':
2973 			if (r_config_get_i (core->config, "scr.cursor")) {
2974 				core->cons->cpos.y++;
2975 				int h;
2976 				(void)r_cons_get_size (&h);
2977 				if (core->cons->cpos.y >= h) {
2978 					core->cons->cpos.y = h;
2979 				}
2980 			} else if (core->print->cur_enabled) {
2981 				int distance = numbuf_pull ();
2982 				for (i = 0; i < distance; i++) {
2983 					cursor_nextrow (core, false);
2984 				}
2985 			} else {
2986 				if (r_config_get_i (core->config, "scr.wheel.nkey")) {
2987 					int i, distance = numbuf_pull ();
2988 					if (distance < 1)  {
2989 						distance =  1;
2990 					}
2991 					for (i = 0; i < distance; i++) {
2992 						r_core_cmd0 (core, "sn");
2993 					}
2994 				} else {
2995 					int times = R_MAX (1, wheelspeed);
2996 					// Check if we have a data annotation.
2997 					ut64 amisize;
2998 					RAnalMetaItem *ami = r_meta_get_at (core->anal, core->offset, R_META_TYPE_DATA, &amisize);
2999 					if (!ami) {
3000 						ami = r_meta_get_at (core->anal, core->offset, R_META_TYPE_STRING, &amisize);
3001 					}
3002 					if (ami) {
3003 						r_core_seek_delta (core, amisize);
3004 					} else {
3005 						int distance = numbuf_pull ();
3006 						if (distance > 1) {
3007 							times = distance;
3008 						}
3009 						while (times--) {
3010 							if (isDisasmPrint (core->printidx)) {
3011 								r_core_visual_disasm_down (core, &op, &cols);
3012 							} else if (!strcmp (__core_visual_print_command (core),
3013 							                    "prc")) {
3014 								cols = r_config_get_i (core->config, "hex.cols");
3015 							}
3016 							r_core_seek (core, core->offset + cols, true);
3017 						}
3018 					}
3019 				}
3020 			}
3021 			break;
3022 		case 'J':
3023 			if (r_config_get_i (core->config, "scr.cursor")) {
3024 				int distance = 4;// numbuf_pull ();
3025 				core->cons->cpos.y += distance;
3026 				int h;
3027 				(void)r_cons_get_size (&h);
3028 				if (core->cons->cpos.y >= h) {
3029 					core->cons->cpos.y = h;
3030 				}
3031 			} else if (core->print->cur_enabled) {
3032 				int distance = numbuf_pull ();
3033 				for (i = 0; i < distance; i++) {
3034 					cursor_nextrow (core, true);
3035 				}
3036 			} else {
3037 				if (core->print->screen_bounds > 1 && core->print->screen_bounds >= core->offset) {
3038 					ut64 addr = UT64_MAX;
3039 					if (isDisasmPrint (core->printidx)) {
3040 						if (core->print->screen_bounds == core->offset) {
3041 							r_asm_disassemble (core->rasm, &op, core->block, 32);
3042 						}
3043 						if (addr == core->offset || addr == UT64_MAX) {
3044 							addr = core->offset + 48;
3045 						}
3046 					} else {
3047 						int h;
3048 						int hexCols = r_config_get_i (core->config, "hex.cols");
3049 						if (hexCols < 1) {
3050 							hexCols = 16;
3051 						}
3052 						(void)r_cons_get_size (&h);
3053 						int delta = hexCols * (h / 4);
3054 						addr = core->offset + delta;
3055 					}
3056 					r_core_seek (core, addr, true);
3057 				} else {
3058 					r_core_seek (core, core->offset + obs, true);
3059 				}
3060 			}
3061 			break;
3062 		case 'k':
3063 			if (r_config_get_i (core->config, "scr.cursor")) {
3064 				core->cons->cpos.y--;
3065 				if (core->cons->cpos.y < 1) {
3066 					core->cons->cpos.y = 0;
3067 				}
3068 			} else if (core->print->cur_enabled) {
3069 				int distance = numbuf_pull ();
3070 				for (i = 0; i < distance; i++) {
3071 					cursor_prevrow (core, false);
3072 				}
3073 			} else {
3074 				if (r_config_get_i (core->config, "scr.wheel.nkey")) {
3075 					int i, distance = numbuf_pull ();
3076 					if (distance < 1)  {
3077 						distance =  1;
3078 					}
3079 					for (i = 0; i < distance; i++) {
3080 						r_core_cmd0 (core, "sp");
3081 					}
3082 				} else {
3083 					int times = wheelspeed;
3084 					if (times < 1) {
3085 						times = 1;
3086 					}
3087 					int distance = numbuf_pull ();
3088 					if (distance > 1) {
3089 						times = distance;
3090 					}
3091 					while (times--) {
3092 						if (isDisasmPrint (core->printidx)) {
3093 							r_core_visual_disasm_up (core, &cols);
3094 						} else if (!strcmp (__core_visual_print_command (core), "prc")) {
3095 							cols = r_config_get_i (core->config, "hex.cols");
3096 						}
3097 						r_core_seek_delta (core, -cols);
3098 					}
3099 				}
3100 			}
3101 			break;
3102 		case 'K':
3103 			if (r_config_get_i (core->config, "scr.cursor")) {
3104 				int distance = 4;// numbuf_pull ();
3105 				core->cons->cpos.y -= distance;
3106 				if (core->cons->cpos.y < 1) {
3107 					core->cons->cpos.y = 0;
3108 				}
3109 			} else if (core->print->cur_enabled) {
3110 				int distance = numbuf_pull ();
3111 				for (i = 0; i < distance; i++) {
3112 					cursor_prevrow (core, true);
3113 				}
3114 			} else {
3115 				if (core->print->screen_bounds > 1 && core->print->screen_bounds > core->offset) {
3116 					int delta = (core->print->screen_bounds - core->offset);
3117 					if (core->offset >= delta) {
3118 						r_core_seek (core, core->offset - delta, true);
3119 					} else {
3120 						r_core_seek (core, 0, true);
3121 					}
3122 				} else {
3123 					ut64 at = (core->offset > obs)? core->offset - obs: 0;
3124 					if (core->offset > obs) {
3125 						r_core_seek (core, at, true);
3126 					} else {
3127 						r_core_seek (core, 0, true);
3128 					}
3129 				}
3130 			}
3131 			break;
3132 		case '[':
3133 			// comments column
3134 			if (core->print->cur_enabled &&
3135 				(core->printidx == R_CORE_VISUAL_MODE_PD ||
3136 				(core->printidx == R_CORE_VISUAL_MODE_DB && core->seltab == 2))) {
3137 				int cmtcol = r_config_get_i (core->config, "asm.cmt.col");
3138 				if (cmtcol > 2) {
3139 					r_config_set_i (core->config, "asm.cmt.col", cmtcol - 2);
3140 				}
3141 			}
3142 			// hex column
3143 			if ((core->printidx != R_CORE_VISUAL_MODE_PD && core->printidx != R_CORE_VISUAL_MODE_DB) ||
3144 				(core->printidx == R_CORE_VISUAL_MODE_DB && core->seltab != 2)) {
3145 				int scrcols = r_config_get_i (core->config, "hex.cols");
3146 				if (scrcols > 1) {
3147 					r_config_set_i (core->config, "hex.cols", scrcols - 1);
3148 				}
3149 			}
3150 			break;
3151 		case ']':
3152 			// comments column
3153 			if (core->print->cur_enabled &&
3154 				(core->printidx == R_CORE_VISUAL_MODE_PD ||
3155 				(core->printidx == R_CORE_VISUAL_MODE_DB && core->seltab == 2))) {
3156 				int cmtcol = r_config_get_i (core->config, "asm.cmt.col");
3157 				r_config_set_i (core->config, "asm.cmt.col", cmtcol + 2);
3158 			}
3159 			// hex column
3160 			if ((core->printidx != R_CORE_VISUAL_MODE_PD && core->printidx != R_CORE_VISUAL_MODE_DB) ||
3161 				(core->printidx == R_CORE_VISUAL_MODE_DB && core->seltab != 2)) {
3162 				int scrcols = r_config_get_i (core->config, "hex.cols");
3163 				r_config_set_i (core->config, "hex.cols", scrcols + 1);
3164 			}
3165 			break;
3166 #if 0
3167 		case 'I':
3168 			r_core_cmd (core, "dsp", 0);
3169 			r_core_cmd (core, ".dr*", 0);
3170 			break;
3171 #endif
3172 		case 's':
3173 			key_s = r_config_get (core->config, "key.s");
3174 			if (key_s && *key_s) {
3175 				r_core_cmd0 (core, key_s);
3176 			} else {
3177 				visual_single_step_in (core);
3178 			}
3179 			break;
3180 		case 'S':
3181 			key_s = r_config_get (core->config, "key.S");
3182 			if (key_s && *key_s) {
3183 				r_core_cmd0 (core, key_s);
3184 			} else {
3185 				__core_visual_step_over (core);
3186 			}
3187 			break;
3188 		case '"':
3189 			r_config_toggle (core->config, "scr.dumpcols");
3190 			break;
3191 		case 'p':
3192 			r_core_visual_toggle_decompiler_disasm (core, false, true);
3193 			if (core->printidx == R_CORE_VISUAL_MODE_DB && core->print->cur_enabled) {
3194 				nextPrintCommand ();
3195 			} else {
3196 				setprintmode (core, 1);
3197 			}
3198 			break;
3199 		case 'P':
3200 			if (core->printidx == R_CORE_VISUAL_MODE_DB && core->print->cur_enabled) {
3201 				prevPrintCommand ();
3202 			} else {
3203 				setprintmode (core, -1);
3204 			}
3205 			break;
3206 		case '%':
3207 			if (core->print->cur_enabled) {
3208 				findPair (core);
3209 			} else {
3210 				/* do nothing? */
3211 				autoblocksize = !autoblocksize;
3212 				if (autoblocksize) {
3213 					obs = core->blocksize;
3214 				} else {
3215 					r_core_block_size (core, obs);
3216 				}
3217 				r_cons_clear ();
3218 			}
3219 			break;
3220 		case 'w':
3221 			findNextWord (core);
3222 			break;
3223 		case 'W':
3224 			findPrevWord (core);
3225 			//r_core_cmd0 (core, "=H");
3226 			break;
3227 		case 'm':
3228 			{
3229 				r_cons_gotoxy (0, 0);
3230 				r_cons_printf (R_CONS_CLEAR_LINE"Set shortcut key for 0x%"PFMT64x"\n", core->offset);
3231 				r_cons_flush ();
3232 				int ch = r_cons_readchar ();
3233 				r_core_visual_mark (core, ch);
3234 			}
3235 			break;
3236 		case 'M':
3237 			{
3238 				r_cons_gotoxy (0, 0);
3239 				if (r_core_visual_mark_dump (core)) {
3240 					r_cons_printf (R_CONS_CLEAR_LINE"Remove a shortcut key from the list\n");
3241 					r_cons_flush ();
3242 					int ch = r_cons_readchar ();
3243 					r_core_visual_mark_del (core, ch);
3244 				}
3245 			}
3246 			break;
3247 		case '\'':
3248 			{
3249 				r_cons_gotoxy (0, 0);
3250 				if (r_core_visual_mark_dump (core)) {
3251 					r_cons_flush ();
3252 					int ch = r_cons_readchar ();
3253 					r_core_visual_mark_seek (core, ch);
3254 				}
3255 			}
3256 			break;
3257 		case 'y':
3258 			if (core->print->ocur == -1) {
3259 				r_core_yank (core, core->offset + core->print->cur, 1);
3260 			} else {
3261 				r_core_yank (core, core->offset + ((core->print->ocur < core->print->cur) ?
3262 					core->print->ocur: core->print->cur), R_ABS (core->print->cur - core->print->ocur) + 1);
3263 			}
3264 			break;
3265 		case 'Y':
3266 			if (!core->yank_buf) {
3267 				r_cons_strcat ("Cannot paste, clipboard is empty.\n");
3268 				r_cons_flush ();
3269 				r_cons_any_key (NULL);
3270 				r_cons_clear00 ();
3271 			} else {
3272 				r_core_yank_paste (core, core->offset + core->print->cur, 0);
3273 			}
3274 			break;
3275 		case '0':
3276 		{
3277 			RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
3278 			if (fcn) {
3279 				r_core_seek (core, fcn->addr, true);
3280 			}
3281 		}
3282 		break;
3283 		case '-':
3284 			if (core->print->cur_enabled) {
3285 				if (core->seltab < 2 && core->printidx == R_CORE_VISUAL_MODE_DB) {
3286 					if (core->seltab) {
3287 						const char *creg = core->dbg->creg;
3288 						if (creg) {
3289 							r_core_cmdf (core, "dr %s = %s-1\n", creg, creg);
3290 						}
3291 					} else {
3292 						int w = r_config_get_i (core->config, "hex.cols");
3293 						r_config_set_i (core->config, "stack.size",
3294 							r_config_get_i (core->config, "stack.size") - w);
3295 					}
3296 				} else {
3297 					if (core->print->ocur == -1) {
3298 						sprintf (buf, "wos 01 @ $$+%i!1",core->print->cur);
3299 					} else {
3300 						sprintf (buf, "wos 01 @ $$+%i!%i", core->print->cur < core->print->ocur
3301 							? core->print->cur
3302 							: core->print->ocur,
3303 							R_ABS (core->print->ocur - core->print->cur) + 1);
3304 					}
3305 					r_core_cmd (core, buf, 0);
3306 				}
3307 			} else {
3308 				if (!autoblocksize) {
3309 					r_core_block_size (core, core->blocksize - 1);
3310 				}
3311 			}
3312 			break;
3313 		case '+':
3314 			if (core->print->cur_enabled) {
3315 				if (core->seltab < 2 && core->printidx == R_CORE_VISUAL_MODE_DB) {
3316 					if (core->seltab) {
3317 						const char *creg = core->dbg->creg;
3318 						if (creg) {
3319 							r_core_cmdf (core, "dr %s = %s+1\n", creg, creg);
3320 						}
3321 					} else {
3322 						int w = r_config_get_i (core->config, "hex.cols");
3323 						r_config_set_i (core->config, "stack.size",
3324 							r_config_get_i (core->config, "stack.size") + w);
3325 					}
3326 				} else {
3327 					if (core->print->ocur == -1) {
3328 						sprintf (buf, "woa 01 @ $$+%i!1", core->print->cur);
3329 					} else {
3330 						sprintf (buf, "woa 01 @ $$+%i!%i", core->print->cur < core->print->ocur
3331 							? core->print->cur
3332 							: core->print->ocur,
3333 							R_ABS (core->print->ocur - core->print->cur) + 1);
3334 					}
3335 					r_core_cmd (core, buf, 0);
3336 				}
3337 			} else {
3338 				if (!autoblocksize) {
3339 					r_core_block_size (core, core->blocksize + 1);
3340 				}
3341 			}
3342 			break;
3343 		case '/': {
3344 			bool mouse_state = __holdMouseState (core);
3345 			if (core->print->cur_enabled) {
3346 				if (core->seltab < 2 && core->printidx == R_CORE_VISUAL_MODE_DB) {
3347 					if (core->seltab) {
3348 						const char *creg = core->dbg->creg;
3349 						if (creg) {
3350 							int delta = core->rasm->bits / 8;
3351 							r_core_cmdf (core, "dr %s = %s-%d\n", creg, creg, delta);
3352 						}
3353 					} else {
3354 						int w = r_config_get_i (core->config, "hex.cols");
3355 						r_config_set_i (core->config, "stack.size",
3356 							r_config_get_i (core->config, "stack.size") - w);
3357 					}
3358 				} else {
3359 					visual_search (core);
3360 				}
3361 			} else {
3362 				if (autoblocksize) {
3363 					r_core_cmd0 (core, "?i highlight;e scr.highlight=`yp`");
3364 				} else {
3365 					r_core_block_size (core, core->blocksize - cols);
3366 				}
3367 			}
3368 			r_cons_enable_mouse (mouse_state && r_config_get_i (core->config, "scr.wheel"));
3369 		}	break;
3370 		case '(':
3371 			snowMode = !snowMode;
3372 			if (!snowMode) {
3373 				r_list_free (snows);
3374 				snows = NULL;
3375 			}
3376 			break;
3377 		case ')':
3378 			rotateAsmemu (core);
3379 			break;
3380 		case '#':
3381 			if (core->printidx == 1) {
3382 				r_core_visual_toggle_decompiler_disasm (core, false, false);
3383 			} else {
3384 				// do nothing for now :?, px vs pxa?
3385 			}
3386 			break;
3387 		case '*':
3388 			if (core->print->cur_enabled) {
3389 				if (core->seltab < 2 && core->printidx == R_CORE_VISUAL_MODE_DB) {
3390 					if (core->seltab) {
3391 						const char *creg = core->dbg->creg;
3392 						if (creg) {
3393 							int delta = core->rasm->bits / 8;
3394 							r_core_cmdf (core, "dr %s = %s+%d\n", creg, creg, delta);
3395 						}
3396 					} else {
3397 						int w = r_config_get_i (core->config, "hex.cols");
3398 						r_config_set_i (core->config, "stack.size",
3399 							r_config_get_i (core->config, "stack.size") + w);
3400 					}
3401 				} else {
3402 					r_core_cmdf (core, "dr PC=0x%08"PFMT64x, core->offset + core->print->cur);
3403 				}
3404 			} else if (!autoblocksize) {
3405 				r_core_block_size (core, core->blocksize + cols);
3406 			}
3407 			break;
3408 		case '>':
3409 			if (core->print->cur_enabled) {
3410 				if (core->print->ocur == -1) {
3411 					eprintf ("No range selected. Use HJKL.\n");
3412 					r_cons_any_key (NULL);
3413 					break;
3414 				}
3415 				char buf[128];
3416 				// TODO autocomplete filenames
3417 				prompt_read ("dump to file: ", buf, sizeof (buf));
3418 				if (buf[0]) {
3419 					ut64 from = core->offset + core->print->ocur;
3420 					ut64 size = R_ABS (core->print->cur - core->print->ocur) + 1;
3421 					r_core_dump (core, buf, from, size, false);
3422 				}
3423 			} else {
3424 				r_core_seek (core, core->offset + core->blocksize, false);
3425 				r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
3426 			}
3427 			break;
3428 		case '<': // "V<"
3429 			if (core->print->cur_enabled) {
3430 				char buf[128];
3431 				// TODO autocomplete filenames
3432 				prompt_read ("load from file: ", buf, sizeof (buf));
3433 				if (buf[0]) {
3434 					size_t sz;
3435 					char *data = r_file_slurp (buf, &sz);
3436 					if (data) {
3437 						int cur;
3438 						if (core->print->ocur != -1) {
3439 							cur = R_MIN (core->print->cur, core->print->ocur);
3440 						} else {
3441 							cur = core->print->cur;
3442 						}
3443 						ut64 from = core->offset + cur;
3444 						ut64 size = R_ABS (core->print->cur - core->print->ocur) + 1;
3445 						ut64 s = R_MIN (size, (ut64)sz);
3446 						r_io_write_at (core->io, from, (const ut8*)data, s);
3447 					}
3448 				}
3449 			} else {
3450 				r_core_seek (core, core->offset - core->blocksize, false);
3451 				r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
3452 			}
3453 			break;
3454 		case '.': // "V."
3455 			r_io_sundo_push (core->io, core->offset, r_print_get_cursor (core->print));
3456 			if (core->print->cur_enabled) {
3457 				r_config_set_i (core->config, "stack.delta", 0);
3458 				r_core_seek (core, core->offset + core->print->cur, true);
3459 				core->print->cur = 0;
3460 			} else {
3461 				ut64 addr = r_debug_reg_get (core->dbg, "PC");
3462 				if (addr && addr != UT64_MAX) {
3463 					r_core_seek (core, addr, true);
3464 					r_core_cmdf (core, "ar `arn PC`=0x%"PFMT64x, addr);
3465 				} else {
3466 					ut64 entry = r_num_get (core->num, "entry0");
3467 					if (!entry || entry == UT64_MAX) {
3468 						RBinObject *o = r_bin_cur_object (core->bin);
3469 						RBinSection *s = o?  r_bin_get_section_at (o, addr, core->io->va): NULL;
3470 						if (s) {
3471 							entry = s->vaddr;
3472 						} else {
3473 							RIOMap *map = r_pvector_pop (&core->io->maps);
3474 							if (map) {
3475 								entry = r_io_map_begin (map);
3476 							} else {
3477 								entry = r_config_get_i (core->config, "bin.baddr");
3478 							}
3479 							r_pvector_push_front (&core->io->maps, map);
3480 						}
3481 					}
3482 					if (entry != UT64_MAX) {
3483 						r_core_seek (core, entry, true);
3484 					}
3485 				}
3486 			}
3487 			break;
3488 #if 0
3489 		case 'n': r_core_seek_delta (core, core->blocksize); break;
3490 		case 'N': r_core_seek_delta (core, 0 - (int) core->blocksize); break;
3491 #endif
3492 		case ':':
3493 			r_core_visual_prompt_input (core);
3494 			break;
3495 		case '_':
3496 			r_core_visual_hudstuff (core);
3497 			break;
3498 		case ';':
3499 			r_cons_enable_mouse (false);
3500 			r_cons_gotoxy (0, 0);
3501 			r_cons_printf ("Enter a comment: ('-' to remove, '!' to use $EDITOR)\n");
3502 			r_core_visual_showcursor (core, true);
3503 			r_cons_flush ();
3504 			r_cons_set_raw (false);
3505 			r_line_set_prompt ("comment: ");
3506 			strcpy (buf, "\"CC ");
3507 			i = strlen (buf);
3508 			if (r_cons_fgets (buf + i, sizeof (buf) - i, 0, NULL) > 0) {
3509 				ut64 addr, orig;
3510 				addr = orig = core->offset;
3511 				if (core->print->cur_enabled) {
3512 					addr += core->print->cur;
3513 					r_core_seek (core, addr, false);
3514 					r_core_cmdf (core, "s 0x%"PFMT64x, addr);
3515 				}
3516 				if (!strcmp (buf + i, "-")) {
3517 					strcpy (buf, "CC-");
3518 				} else {
3519 					switch (buf[i]) {
3520 					case '-':
3521 						memcpy (buf, "\"CC-\x00", 5);
3522 						break;
3523 					case '!':
3524 						memcpy (buf, "\"CC!\x00", 5);
3525 						break;
3526 					default:
3527 						memcpy (buf, "\"CC ", 4);
3528 						break;
3529 					}
3530 					strcat (buf, "\"");
3531 				}
3532 				if (buf[3] == ' ') {
3533 					// have to escape any quotes.
3534 					int j, len = strlen (buf);
3535 					char *duped = strdup (buf);
3536 					for (i = 4, j = 4; i < len; i++, j++) {
3537 						char c = duped[i];
3538 						if (c == '"' && i != (len - 1)) {
3539 							buf[j] = '\\';
3540 							j++;
3541 							buf[j] = '"';
3542 						} else {
3543 							buf[j] = c;
3544 						}
3545 					}
3546 					buf[j] = 0;
3547 					free (duped);
3548 				}
3549 				r_core_cmd (core, buf, 1);
3550 				if (core->print->cur_enabled) {
3551 					r_core_seek (core, orig, true);
3552 				}
3553 			}
3554 			r_cons_set_raw (true);
3555 			r_core_visual_showcursor (core, false);
3556 			break;
3557 		case 'b':
3558 			r_core_visual_browse (core, arg + 1);
3559 			break;
3560 		case 'B':
3561 			{
3562 			ut64 addr = core->print->cur_enabled? core->offset + core->print->cur: core->offset;
3563 			r_core_cmdf (core, "dbs 0x%08"PFMT64x, addr);
3564 			}
3565 			break;
3566 		case 'u':
3567 		{
3568 			RIOUndos *undo = r_io_sundo (core->io, core->offset);
3569 			if (undo) {
3570 				r_core_visual_seek_animation (core, undo->off);
3571 				core->print->cur = undo->cursor;
3572 			} else {
3573 				eprintf ("Cannot undo\n");
3574 			}
3575 		}
3576 		break;
3577 		case 'U':
3578 		{
3579 			RIOUndos *undo = r_io_sundo_redo (core->io);
3580 			if (undo) {
3581 				r_core_visual_seek_animation (core, undo->off);
3582 				reset_print_cur (core->print);
3583 			}
3584 		}
3585 		break;
3586 		case 'z':
3587 		{
3588 			RAnalFunction *fcn;
3589 			if (core->print->cur_enabled) {
3590 				fcn = r_anal_get_fcn_in (core->anal,
3591 					core->offset + core->print->cur, R_ANAL_FCN_TYPE_NULL);
3592 			} else {
3593 				fcn = r_anal_get_fcn_in (core->anal,
3594 					core->offset, R_ANAL_FCN_TYPE_NULL);
3595 			}
3596 			if (fcn) {
3597 				fcn->folded = !fcn->folded;
3598 			} else {
3599 				r_config_toggle (core->config, "asm.cmt.fold");
3600 			}
3601 		}
3602 		break;
3603 		case 'Z': // shift-tab SHIFT-TAB
3604 			if (och == 27) { // shift-tab
3605 				if (core->print->cur_enabled && core->printidx == R_CORE_VISUAL_MODE_DB) {
3606 					core->print->cur = 0;
3607 					core->seltab--;
3608 					if (core->seltab < 0) {
3609 						core->seltab = 2;
3610 					}
3611 				} else {
3612 					prevPrintFormat (core);
3613 				}
3614 			} else { // "Z"
3615 				ut64 addr = core->print->cur_enabled? core->offset + core->print->cur: core->offset;
3616 				toggle_bb (core, addr);
3617 			}
3618 			break;
3619 		case '?':
3620 			if (visual_help (core) == '?') {
3621 				r_core_visual_hud (core);
3622 			}
3623 			break;
3624 		case 0x1b:
3625 		case 'q':
3626 		case 'Q':
3627 			setcursor (core, false);
3628 			return false;
3629 		}
3630 		numbuf_i = 0;
3631 	}
3632 	r_core_block_read (core);
3633 	return true;
3634 }
3635 
r_core_visual_title(RCore * core,int color)3636 R_API void r_core_visual_title(RCore *core, int color) {
3637 	bool showDelta = r_config_get_i (core->config, "scr.slow");
3638 	static ut64 oldpc = 0;
3639 	const char *BEGIN = core->cons->context->pal.prompt;
3640 	const char *filename;
3641 	char pos[512], bar[512], pcs[32];
3642 	if (!oldpc) {
3643 		oldpc = r_debug_reg_get (core->dbg, "PC");
3644 	}
3645 	/* automatic block size */
3646 	int pc, hexcols = r_config_get_i (core->config, "hex.cols");
3647 	if (autoblocksize) {
3648 		switch (core->printidx) {
3649 #if 0
3650 		case R_CORE_VISUAL_MODE_PXR: // prc
3651 		case R_CORE_VISUAL_MODE_PRC: // prc
3652 			r_core_block_size (core, (int)(core->cons->rows * hexcols * 3.5));
3653 			break;
3654 		case R_CORE_VISUAL_MODE_PXa: // pxa
3655 		case R_CORE_VISUAL_MODE_PW: // XXX pw
3656 			r_core_block_size (core, (int)(core->cons->rows * hexcols));
3657 			break;
3658 		case R_CORE_VISUAL_MODE_PC: // XXX pc
3659 			r_core_block_size (core, (int)(core->cons->rows * hexcols * 4));
3660 			break;
3661 		case R_CORE_VISUAL_MODE_PXA: // pxA
3662 			r_core_block_size (core, hexcols * core->cons->rows * 8);
3663 			break;
3664 #endif
3665 		case R_CORE_VISUAL_MODE_PX: // x
3666 			if (currentFormat == 3 || currentFormat == 9 || currentFormat == 5) { // prx
3667 				r_core_block_size (core, (int)(core->cons->rows * hexcols * 4));
3668 			} else if ((R_ABS (hexMode) % 3) == 0) { // prx
3669 				r_core_block_size (core, (int)(core->cons->rows * hexcols));
3670 			} else {
3671 				r_core_block_size (core, (int)(core->cons->rows * hexcols * 2));
3672 			}
3673 			break;
3674 		case R_CORE_VISUAL_MODE_OV:
3675 		case R_CORE_VISUAL_MODE_CD:
3676 			r_core_block_size (core, (int)(core->cons->rows * hexcols * 2));
3677 			break;
3678 		case R_CORE_VISUAL_MODE_PD: // pd
3679 		case R_CORE_VISUAL_MODE_DB: // pd+dbg
3680 		{
3681 			int bsize = core->cons->rows * 5;
3682 
3683 			if (core->print->screen_bounds > 1) {
3684 				// estimate new blocksize with the size of the last
3685 				// printed instructions
3686 				int new_sz = core->print->screen_bounds - core->offset + 32;
3687 				new_sz = R_MIN (new_sz, 16 * 1024);
3688 				if (new_sz > bsize) {
3689 					bsize = new_sz;
3690 				}
3691 			}
3692 			r_core_block_size (core, bsize);
3693 			break;
3694 		}
3695 		}
3696 	}
3697 	if (r_config_get_i (core->config, "scr.scrollbar") == 2) {
3698 		r_core_cmd (core, "fz:", 0);
3699 	}
3700 	if (r_config_get_i (core->config, "cfg.debug")) {
3701 		ut64 curpc = r_debug_reg_get (core->dbg, "PC");
3702 		if (curpc && curpc != UT64_MAX && curpc != oldpc) {
3703 			// check dbg.follow here
3704 			int follow = (int) (st64) r_config_get_i (core->config, "dbg.follow");
3705 			if (follow > 0) {
3706 				if ((curpc < core->offset) || (curpc > (core->offset + follow))) {
3707 					r_core_seek (core, curpc, true);
3708 				}
3709 			} else if (follow < 0) {
3710 				r_core_seek (core, curpc + follow, true);
3711 			}
3712 			oldpc = curpc;
3713 		}
3714 	}
3715 	RIOMap *map = r_io_map_get (core->io, core->offset);
3716 	RIODesc *desc = map ? r_io_desc_get (core->io, map->fd) : core->io->desc;
3717 	filename = desc? desc->name: "";
3718 
3719 	{ /* get flag with delta */
3720 		ut64 addr = core->offset + (core->print->cur_enabled? core->print->cur: 0);
3721 		/* TODO: we need a helper into r_flags to do that */
3722 		RFlagItem *f = NULL;
3723 		if (r_flag_space_push (core->flags, R_FLAGS_FS_SYMBOLS)) {
3724 			f = r_flag_get_at (core->flags, addr, showDelta);
3725 			r_flag_space_pop (core->flags);
3726 		}
3727 		if (!f) {
3728 			f = r_flag_get_at (core->flags, addr, showDelta);
3729 		}
3730 		if (f) {
3731 			if (f->offset == addr || !f->offset) {
3732 				snprintf (pos, sizeof (pos), "@ %s", f->name);
3733 			} else {
3734 				snprintf (pos, sizeof (pos), "@ %s+%d # 0x%"PFMT64x,
3735 					f->name, (int) (addr - f->offset), addr);
3736 			}
3737 		} else {
3738 			RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
3739 			if (fcn) {
3740 				int delta = addr - fcn->addr;
3741 				if (delta > 0) {
3742 					snprintf (pos, sizeof (pos), "@ %s+%d", fcn->name, delta);
3743 				} else if (delta < 0) {
3744 					snprintf (pos, sizeof (pos), "@ %s%d", fcn->name, delta);
3745 				} else {
3746 					snprintf (pos, sizeof (pos), "@ %s", fcn->name);
3747 				}
3748 			} else {
3749 				pos[0] = 0;
3750 			}
3751 		}
3752 	}
3753 
3754 	if (core->print->cur < 0) {
3755 		core->print->cur = 0;
3756 	}
3757 
3758 	if (color) {
3759 		r_cons_strcat (BEGIN);
3760 	}
3761 	const char *cmd_visual = r_config_get (core->config, "cmd.visual");
3762 	if (cmd_visual && *cmd_visual) {
3763 		r_str_ncpy (bar, cmd_visual, sizeof (bar) - 1);
3764 		bar[10] = '.'; // chop cmdfmt
3765 		bar[11] = '.'; // chop cmdfmt
3766 		bar[12] = 0; // chop cmdfmt
3767 	} else {
3768 		const char *cmd = __core_visual_print_command (core);
3769 		if (cmd) {
3770 			r_str_ncpy (bar, cmd, sizeof (bar) - 1);
3771 			bar[10] = '.'; // chop cmdfmt
3772 			bar[11] = '.'; // chop cmdfmt
3773 			bar[12] = 0; // chop cmdfmt
3774 		}
3775 	}
3776 	{
3777 		ut64 sz = r_io_size (core->io);
3778 		ut64 pa = core->offset;
3779 		{
3780 			RIOMap *map = r_io_map_get (core->io, core->offset);
3781 			if (map) {
3782 				pa = map->delta;
3783 			}
3784 		}
3785 		if (sz == UT64_MAX) {
3786 			pcs[0] = 0;
3787 		} else {
3788 			if (!sz || pa > sz) {
3789 				pc = 0;
3790 			} else {
3791 				pc = (pa * 100) / sz;
3792 			}
3793 			sprintf (pcs, "%d%% ", pc);
3794 		}
3795 	}
3796 	{
3797 		char *title;
3798 		char *address = (core->print->wide_offsets && core->dbg->bits & R_SYS_BITS_64)
3799 			? r_str_newf ("0x%016"PFMT64x, core->offset)
3800 			: r_str_newf ("0x%08"PFMT64x, core->offset);
3801 		if (__ime) {
3802 			title = r_str_newf ("[%s + %d> * INSERT MODE *\n",
3803 				address, core->print->cur);
3804 		} else {
3805 			char pm[32] = "[XADVC]";
3806 			int i;
3807 			for(i=0;i<6;i++) {
3808 				if (core->printidx == i) {
3809 					pm[i + 1] = toupper((unsigned char)pm[i + 1]);
3810 				} else {
3811 					pm[i + 1] = tolower((unsigned char)pm[i + 1]);
3812 				}
3813 			}
3814 			if (core->print->cur_enabled) {
3815 				if (core->print->ocur == -1) {
3816 					title = r_str_newf ("[%s *0x%08"PFMT64x" %s%d ($$+0x%x)]> %s %s\n",
3817 						address, core->offset + core->print->cur,
3818 						pm, currentFormat, core->print->cur,
3819 						bar, pos);
3820 				} else {
3821 					title = r_str_newf ("[%s 0x%08"PFMT64x" %s%d [0x%x..0x%x] %d]> %s %s\n",
3822 						address, core->offset + core->print->cur,
3823 						pm, currentFormat, core->print->ocur, core->print->cur,
3824 						R_ABS (core->print->cur - core->print->ocur) + 1,
3825 						bar, pos);
3826 				}
3827 			} else {
3828 				title = r_str_newf ("[%s %s%d %s%d %s]> %s %s\n",
3829 					address, pm, currentFormat, pcs, core->blocksize, filename, bar, pos);
3830 			}
3831 		}
3832 		const int tabsCount = __core_visual_tab_count (core);
3833 		if (tabsCount > 0) {
3834 			const char *kolor = core->cons->context->pal.prompt;
3835 			char *tabstring = __core_visual_tab_string (core, kolor);
3836 			if (tabstring) {
3837 				title = r_str_append (title, tabstring);
3838 				free (tabstring);
3839 			}
3840 #if 0
3841 			// TODO: add an option to show this tab mode instead?
3842 			const int curTab = core->visual.tab;
3843 			r_cons_printf ("[");
3844 			int i;
3845 			for (i = 0; i < tabsCount; i++) {
3846 				if (i == curTab) {
3847 					r_cons_printf ("%d", curTab + 1);
3848 				} else {
3849 					r_cons_printf (".");
3850 				}
3851 			}
3852 			r_cons_printf ("]");
3853 			r_cons_printf ("[tab:%d/%d]", core->visual.tab, tabsCount);
3854 #endif
3855 		}
3856 		r_cons_print (title);
3857 		free (title);
3858 		free (address);
3859 	}
3860 	if (color) {
3861 		r_cons_strcat (Color_RESET);
3862 	}
3863 }
3864 
visual_responsive(RCore * core)3865 static int visual_responsive(RCore *core) {
3866 	int h, w = r_cons_get_size (&h);
3867 	if (r_config_get_i (core->config, "scr.responsive")) {
3868 		if (w < 110) {
3869 			r_config_set_i (core->config, "asm.cmt.right", 0);
3870 		} else {
3871 			r_config_set_i (core->config, "asm.cmt.right", 1);
3872 		}
3873 		if (w < 68) {
3874 			r_config_set_i (core->config, "hex.cols", (int)(w / 5.2));
3875 		} else {
3876 			r_config_set_i (core->config, "hex.cols", 16);
3877 		}
3878 		if (w < 25) {
3879 			r_config_set_i (core->config, "asm.offset", 0);
3880 		} else {
3881 			r_config_set_i (core->config, "asm.offset", 1);
3882 		}
3883 		if (w > 80) {
3884 			r_config_set_i (core->config, "asm.lines.width", 14);
3885 			r_config_set_i (core->config, "asm.lines.width", w - (int)(w / 1.2));
3886 			r_config_set_i (core->config, "asm.cmt.col", w - (int)(w / 2.5));
3887 		} else {
3888 			r_config_set_i (core->config, "asm.lines.width", 7);
3889 		}
3890 		if (w < 70) {
3891 			r_config_set_i (core->config, "asm.lines.width", 1);
3892 			r_config_set_i (core->config, "asm.bytes", 0);
3893 		} else {
3894 			r_config_set_i (core->config, "asm.bytes", 1);
3895 		}
3896 	}
3897 	return w;
3898 }
3899 
3900 // TODO: use colors
3901 // TODO: find better name
r_core_print_scrollbar(RCore * core)3902 R_API void r_core_print_scrollbar(RCore *core) {
3903 	int i, h, w = r_cons_get_size (&h);
3904 
3905 	int scrollbar = r_config_get_i (core->config, "scr.scrollbar");
3906 	if (scrollbar == 2) {
3907 		// already handled by r_core_cmd("zf:") in visual.c
3908 		return;
3909 	}
3910 	if (scrollbar > 2) {
3911 		r_core_print_scrollbar_bottom (core);
3912 		return;
3913 	}
3914 
3915 	if (w < 10 || h < 3) {
3916 		return;
3917 	}
3918 	ut64 from = 0;
3919 	ut64 to = UT64_MAX;
3920 	if (r_config_get_i (core->config, "cfg.debug")) {
3921 		from = r_num_math (core->num, "$D");
3922 		to = r_num_math (core->num, "$D+$DD");
3923 	} else if (r_config_get_i (core->config, "io.va")) {
3924 		from = r_num_math (core->num, "$S");
3925 		to = r_num_math (core->num, "$S+$SS");
3926 	} else {
3927 		to = r_num_math (core->num, "$s");
3928 	}
3929 	char *s = r_str_newf ("[0x%08"PFMT64x"]", from);
3930 	r_cons_gotoxy (w - strlen (s) + 1, 2);
3931 	r_cons_strcat (s);
3932 	free (s);
3933 
3934 	ut64 block = (to - from) / h;
3935 
3936 	RList *words = r_flag_zone_barlist (core->flags, from, block, h);
3937 
3938 	bool hadMatch = false;
3939 	for (i = 0; i < h ; i++) {
3940 		const char *word = r_list_pop_head (words);
3941 		if (word && *word) {
3942 			r_cons_gotoxy (w - strlen (word) - 1, i + 3);
3943 			r_cons_printf ("%s>", word);
3944 		}
3945 		r_cons_gotoxy (w, i + 3);
3946 		if (hadMatch) {
3947 			r_cons_printf ("|");
3948 		} else {
3949 			ut64 cur = from + (block * i);
3950 			ut64 nex = from + (block * (i + 1));
3951 			if (R_BETWEEN (cur, core->offset, nex)) {
3952 				r_cons_printf (Color_INVERT"|"Color_RESET);
3953 				hadMatch = true;
3954 			} else {
3955 				r_cons_printf ("|");
3956 			}
3957 		}
3958 	}
3959 	s = r_str_newf ("[0x%08"PFMT64x"]", to);
3960 	if (s) {
3961 		r_cons_gotoxy (w - strlen (s) + 1, h + 1);
3962 		r_cons_strcat (s);
3963 		free (s);
3964 	}
3965 	r_list_free (words);
3966 	r_cons_flush ();
3967 }
3968 
r_core_print_scrollbar_bottom(RCore * core)3969 R_API void r_core_print_scrollbar_bottom(RCore *core) {
3970 	int i, h, w = r_cons_get_size (&h);
3971 
3972 	if (w < 10 || h < 4) {
3973 		return;
3974 	}
3975 	ut64 from = 0;
3976 	ut64 to = UT64_MAX;
3977 	if (r_config_get_i (core->config, "cfg.debug")) {
3978 		from = r_num_math (core->num, "$D");
3979 		to = r_num_math (core->num, "$D+$DD");
3980 	} else if (r_config_get_i (core->config, "io.va")) {
3981 		from = r_num_math (core->num, "$S");
3982 		to = r_num_math (core->num, "$S+$SS");
3983 	} else {
3984 		to = r_num_math (core->num, "$s");
3985 	}
3986 	char *s = r_str_newf ("[0x%08"PFMT64x"]", from);
3987 	int slen = strlen (s) + 1;
3988 	r_cons_gotoxy (0, h + 1);
3989 	r_cons_strcat (s);
3990 	free (s);
3991 
3992 	int linew = (w - (slen * 2)) + 1;
3993 	ut64 block = (to - from) / linew;
3994 
3995 	RList *words = r_flag_zone_barlist (core->flags, from, block, h);
3996 
3997 	bool hadMatch = false;
3998 	for (i = 0; i < linew + 1; i++) {
3999 		r_cons_gotoxy (i + slen, h + 1);
4000 		if (hadMatch) {
4001 			r_cons_strcat ("-");
4002 		} else {
4003 			ut64 cur = from + (block * i);
4004 			ut64 nex = from + (block * (i + 2));
4005 			if (R_BETWEEN (cur, core->offset, nex)) {
4006 				r_cons_strcat (Color_INVERT"-"Color_RESET);
4007 				hadMatch = true;
4008 			} else {
4009 				r_cons_strcat ("-");
4010 			}
4011 		}
4012 	}
4013 	for (i = 0; i < linew; i++) {
4014 		const char *word = r_list_pop_head (words);
4015 		if (word && *word) {
4016 			ut64 cur = from + (block * i);
4017 			ut64 nex = from + (block * (i + strlen (word) + 1));
4018 			r_cons_gotoxy (i + slen - 1, h);
4019 			if (R_BETWEEN (cur, core->offset, nex)) {
4020 				r_cons_printf (Color_INVERT"{%s}"Color_RESET, word);
4021 			} else {
4022 				r_cons_printf ("{%s}", word);
4023 			}
4024 		}
4025 	}
4026 	s = r_str_newf ("[0x%08"PFMT64x"]", to);
4027 	if (s) {
4028 		r_cons_gotoxy (linew + slen + 1, h + 1);
4029 		r_cons_strcat (s);
4030 		free (s);
4031 	}
4032 	r_list_free (words);
4033 	r_cons_flush ();
4034 }
4035 
show_cursor(RCore * core)4036 static void show_cursor(RCore *core) {
4037 	const bool keyCursor = r_config_get_i (core->config, "scr.cursor");
4038 	if (keyCursor) {
4039 		r_cons_gotoxy (core->cons->cpos.x, core->cons->cpos.y);
4040 		r_cons_show_cursor (1);
4041 		//r_cons_invert (1, 1);
4042 		//r_cons_print ("#");
4043 		r_cons_flush ();
4044 	}
4045 }
4046 
visual_refresh(RCore * core)4047 static void visual_refresh(RCore *core) {
4048 	static ut64 oseek = UT64_MAX;
4049 	const char *vi, *vcmd, *cmd_str;
4050 	if (!core) {
4051 		return;
4052 	}
4053 	r_print_set_cursor (core->print, core->print->cur_enabled, core->print->ocur, core->print->cur);
4054 	core->cons->blankline = true;
4055 
4056 	int w = visual_responsive (core);
4057 
4058 	if (autoblocksize) {
4059 		r_cons_gotoxy (0, 0);
4060 	} else {
4061 		r_cons_clear ();
4062 	}
4063 	r_cons_flush ();
4064 	r_cons_print_clear ();
4065 
4066 	int hex_cols = r_config_get_i (core->config, "hex.cols");
4067 	int split_w = 12 + 4 + hex_cols + (hex_cols * 3);
4068 	bool ce = core->print->cur_enabled;
4069 
4070 	vi = r_config_get (core->config, "cmd.cprompt");
4071 	bool vsplit = (vi && *vi);
4072 
4073 	if (vsplit) {
4074 		// XXX: slow
4075 		core->cons->blankline = false;
4076 		{
4077 			int hex_cols = r_config_get_i (core->config, "hex.cols");
4078 			int split_w = 12 + 4 + hex_cols + (hex_cols * 3);
4079 			if (split_w > w) {
4080 				// do not show column contents
4081 			} else {
4082 				r_cons_printf ("[cmd.cprompt=%s]\n", vi);
4083 				if (oseek != UT64_MAX) {
4084 					r_core_seek (core, oseek, true);
4085 				}
4086 				r_core_cmd0 (core, vi);
4087 				r_cons_column (split_w);
4088 				if (!strncmp (vi, "p=", 2) && core->print->cur_enabled) {
4089 					oseek = core->offset;
4090 					core->print->cur_enabled = false;
4091 					r_core_seek (core, core->num->value, true);
4092 				} else {
4093 					oseek = UT64_MAX;
4094 				}
4095 			}
4096 		}
4097 		r_cons_gotoxy (0, 0);
4098 	}
4099 	vi = r_config_get (core->config, "cmd.vprompt");
4100 	if (vi && *vi) {
4101 		r_core_cmd0 (core, vi);
4102 #if 0
4103 		char *output = r_core_cmd_str (core, vi);
4104 		r_cons_strcat_at (output, 10, 5, 20, 20);
4105 		free (output);
4106 #endif
4107 	}
4108 	r_core_visual_title (core, color);
4109 	vcmd = r_config_get (core->config, "cmd.visual");
4110 	if (vcmd && *vcmd) {
4111 		// disable screen bounds when it's a user-defined command
4112 		// because it can cause some issues
4113 		core->print->screen_bounds = 0;
4114 		cmd_str = vcmd;
4115 	} else {
4116 		if (splitView) {
4117 			static char debugstr[512];
4118 			const char *pxw = NULL;
4119 			int h = r_num_get (core->num, "$r");
4120 			int size = (h * 16) / 2;
4121 			switch (core->printidx) {
4122 			case 1:
4123 				size = (h - 2) / 2;
4124 				pxw = "pd";
4125 				break;
4126 			default:
4127 				pxw = stackPrintCommand (core);
4128 				break;
4129 			}
4130 			snprintf (debugstr, sizeof (debugstr),
4131 					"?t0;%s %d @ %"PFMT64d";cl;"
4132 					"?t1;%s %d @ %"PFMT64d";",
4133 					pxw, size, splitPtr,
4134 					pxw, size, core->offset);
4135 			core->print->screen_bounds = 1LL;
4136 			cmd_str = debugstr;
4137 		} else {
4138 			core->print->screen_bounds = 1LL;
4139 			cmd_str = (zoom ? "pz" : __core_visual_print_command (core));
4140 		}
4141 	}
4142 	if (cmd_str && *cmd_str) {
4143 		if (vsplit) {
4144 			char *cmd_result = r_core_cmd_str (core, cmd_str);
4145 			cmd_result = r_str_ansi_crop (cmd_result, 0, 0, split_w, -1);
4146 			r_cons_strcat (cmd_result);
4147 		} else {
4148 			r_core_cmd0 (core, cmd_str);
4149 		}
4150 	}
4151 	core->print->cur_enabled = ce;
4152 #if 0
4153 	if (core->print->screen_bounds != 1LL) {
4154 		r_cons_printf ("[0x%08"PFMT64x "..0x%08"PFMT64x "]\n",
4155 			core->offset, core->print->screen_bounds);
4156 	}
4157 #endif
4158 	blocksize = core->num->value? core->num->value: core->blocksize;
4159 
4160 	/* this is why there's flickering */
4161 	if (core->print->vflush) {
4162 		r_cons_visual_flush ();
4163 	} else {
4164 		r_cons_reset ();
4165 	}
4166 	if (core->scr_gadgets) {
4167 		r_core_cmd0 (core, "pg");
4168 		r_cons_flush ();
4169 	}
4170 	core->cons->blankline = false;
4171 	core->cons->blankline = true;
4172 	core->curtab = 0; // which command are we focusing
4173 	//core->seltab = 0; // user selected tab
4174 
4175 	if (snowMode) {
4176 		printSnow (core);
4177 	}
4178 	if (r_config_get_i (core->config, "scr.scrollbar")) {
4179 		r_core_print_scrollbar (core);
4180 	}
4181 	show_cursor (core);
4182 }
4183 
visual_refresh_oneshot(RCore * core)4184 static void visual_refresh_oneshot(RCore *core) {
4185 	r_core_task_enqueue_oneshot (&core->tasks, (RCoreTaskOneShot) visual_refresh, core);
4186 }
4187 
r_core_visual_disasm_up(RCore * core,int * cols)4188 R_API void r_core_visual_disasm_up(RCore *core, int *cols) {
4189 	RAnalFunction *f = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
4190 	if (f && f->folded) {
4191 		*cols = core->offset - f->addr; // + f->size;
4192 		if (*cols < 1) {
4193 			*cols = 4;
4194 		}
4195 	} else {
4196 		*cols = r_core_visual_prevopsz (core, core->offset);
4197 	}
4198 }
4199 
r_core_visual_disasm_down(RCore * core,RAsmOp * op,int * cols)4200 R_API void r_core_visual_disasm_down(RCore *core, RAsmOp *op, int *cols) {
4201 	int midflags = r_config_get_i (core->config, "asm.flags.middle");
4202 	const bool midbb = r_config_get_i (core->config, "asm.bb.middle");
4203 	RAnalFunction *f = NULL;
4204 	f = r_anal_get_fcn_in (core->anal, core->offset, 0);
4205 	op->size = 1;
4206 	if (f && f->folded) {
4207 		*cols = core->offset - r_anal_function_max_addr (f);
4208 	} else {
4209 		r_asm_set_pc (core->rasm, core->offset);
4210 		*cols = r_asm_disassemble (core->rasm,
4211 				op, core->block, 32);
4212 		if (midflags || midbb) {
4213 			int skip_bytes_flag = 0, skip_bytes_bb = 0;
4214 			if (midflags >= R_MIDFLAGS_REALIGN) {
4215 				skip_bytes_flag = r_core_flag_in_middle (core, core->offset, *cols, &midflags);
4216 			}
4217 			if (midbb) {
4218 				skip_bytes_bb = r_core_bb_starts_in_middle (core, core->offset, *cols);
4219 			}
4220 			if (skip_bytes_flag) {
4221 				*cols = skip_bytes_flag;
4222 			}
4223 			if (skip_bytes_bb && skip_bytes_bb < *cols) {
4224 				*cols = skip_bytes_bb;
4225 			}
4226 		}
4227 	}
4228 	if (*cols < 1) {
4229 		*cols = op->size > 1 ? op->size : 1;
4230 	}
4231 }
4232 
4233 #ifdef __WINDOWS__
4234 
is_mintty(RCons * cons)4235 static bool is_mintty(RCons *cons) {
4236 	return cons->term_xterm;
4237 }
4238 
flush_stdin(void)4239 static void flush_stdin(void) {
4240 	while (r_cons_readchar_timeout (1) != -1) ;
4241 }
4242 
4243 #else
4244 
is_mintty(RCons * cons)4245 static bool is_mintty(RCons *cons) {
4246 	return false;
4247 }
4248 
flush_stdin(void)4249 static void flush_stdin(void) {
4250 	tcflush (STDIN_FILENO, TCIFLUSH);
4251 }
4252 
4253 #endif
4254 
r_core_visual(RCore * core,const char * input)4255 R_API int r_core_visual(RCore *core, const char *input) {
4256 	const char *teefile;
4257 	ut64 scrseek;
4258 	int flags, ch;
4259 	bool skip;
4260 	char arg[2] = {
4261 		input[0], 0
4262 	};
4263 
4264 	splitPtr = UT64_MAX;
4265 
4266 	if (r_cons_get_size (&ch) < 1 || ch < 1) {
4267 		eprintf ("Cannot create Visual context. Use scr.fix_{columns|rows}\n");
4268 		return 0;
4269 	}
4270 
4271 	obs = core->blocksize;
4272 	//r_cons_set_cup (true);
4273 
4274 	core->vmode = false;
4275 	/* honor vim */
4276 	if (!strncmp (input, "im", 2)) {
4277 		char *cmd = r_str_newf ("!v%s", input);
4278 		int ret = r_core_cmd0 (core, cmd);
4279 		free (cmd);
4280 		return ret;
4281 	}
4282 	while (*input) {
4283 		int len = *input == 'd'? 2: 1;
4284 		if (!r_core_visual_cmd (core, input)) {
4285 			return 0;
4286 		}
4287 		input += len;
4288 	}
4289 	core->vmode = true;
4290 
4291 	// disable tee in cons
4292 	teefile = r_cons_singleton ()->teefile;
4293 	r_cons_singleton ()->teefile = "";
4294 
4295 	static char debugstr[512];
4296 	core->print->flags |= R_PRINT_FLAGS_ADDRMOD;
4297 	do {
4298 dodo:
4299 		r_core_visual_tab_update (core);
4300 		// update the cursor when it's not visible anymore
4301 		skip = fix_cursor (core);
4302 		r_cons_show_cursor (false);
4303 		r_cons_set_raw (1);
4304 		const int ref = r_config_get_i (core->config, "dbg.slow");
4305 #if 1
4306 		// This is why multiple debug views dont work
4307 		if (core->printidx == R_CORE_VISUAL_MODE_DB) {
4308 			const int pxa = r_config_get_i (core->config, "stack.anotated"); // stack.anotated
4309 			const char *reg = r_config_get (core->config, "stack.reg");
4310 			const int size = r_config_get_i (core->config, "stack.size");
4311 			const int delta = r_config_get_i (core->config, "stack.delta");
4312 			const char *cmdvhex = r_config_get (core->config, "cmd.stack");
4313 
4314 			if (cmdvhex && *cmdvhex) {
4315 				snprintf (debugstr, sizeof (debugstr),
4316 					"?t0;f tmp;ssr %s;%s;?1;%s;?1;"
4317 					"ss tmp;f-tmp;pd $r", reg, cmdvhex,
4318 					ref? "drr": "dr=");
4319 				debugstr[sizeof (debugstr) - 1] = 0;
4320 			} else {
4321 				const char *pxw = stackPrintCommand (core);
4322 				const char sign = (delta < 0)? '+': '-';
4323 				const int absdelta = R_ABS (delta);
4324 				snprintf (debugstr, sizeof (debugstr),
4325 					"diq;?0;f tmp;ssr %s;%s %d@$$%c%d;"
4326 					"?t1;%s;"
4327 					"?t1;ss tmp;f-tmp;afal;pd $r",
4328 					reg, pxa? "pxa": pxw, size, sign, absdelta,
4329 					ref? "drr": "dr=");
4330 			}
4331 			printfmtSingle[2] = debugstr;
4332 		}
4333 #endif
4334 		r_cons_show_cursor (false);
4335 		r_cons_enable_mouse (r_config_get_i (core->config, "scr.wheel"));
4336 		core->cons->event_resize = NULL; // avoid running old event with new data
4337 		core->cons->event_data = core;
4338 		core->cons->event_resize = (RConsEvent) visual_refresh_oneshot;
4339 		flags = core->print->flags;
4340 		color = r_config_get_i (core->config, "scr.color");
4341 		if (color) {
4342 			flags |= R_PRINT_FLAGS_COLOR;
4343 		}
4344 		debug = r_config_get_i (core->config, "cfg.debug");
4345 		flags |= R_PRINT_FLAGS_ADDRMOD | R_PRINT_FLAGS_HEADER;
4346 		r_print_set_flags (core->print, flags);
4347 		scrseek = r_num_math (core->num,
4348 			r_config_get (core->config, "scr.seek"));
4349 		if (scrseek != 0LL) {
4350 			r_core_seek (core, scrseek, true);
4351 		}
4352 		if (debug) {
4353 			r_core_cmd (core, ".dr*", 0);
4354 		}
4355 #if 0
4356 		cmdprompt = r_config_get (core->config, "cmd.vprompt");
4357 		if (cmdprompt && *cmdprompt) {
4358 			r_core_cmd (core, cmdprompt, 0);
4359 		}
4360 #endif
4361 		core->print->vflush = !skip;
4362 		visual_refresh (core);
4363 		if (insert_mode_enabled (core)) {
4364 			goto dodo;
4365 		}
4366 		if (!skip) {
4367 			if (snowMode) {
4368 				ch = r_cons_readchar_timeout (300);
4369 				if (ch == -1) {
4370 					skip = 1;
4371 					continue;
4372 				}
4373 			} else {
4374 				ch = r_cons_readchar ();
4375 			}
4376 			if (I->vtmode == 2 && !is_mintty (core->cons)) {
4377 				// Prevent runaway scrolling
4378 				if (IS_PRINTABLE (ch) || ch == '\t' || ch == '\n') {
4379 					flush_stdin ();
4380 				} else if (ch == 0x1b) {
4381 					char chrs[2];
4382 					int chrs_read = 1;
4383 					chrs[0] = r_cons_readchar ();
4384 					if (chrs[0] == '[') {
4385 						chrs[1] = r_cons_readchar ();
4386 						chrs_read++;
4387 						if (chrs[1] >= 'A' && chrs[1] <= 'D') { // arrow keys
4388 							flush_stdin ();
4389 #ifndef __WINDOWS__
4390 							// Following seems to fix an issue where scrolling slows
4391 							// down to a crawl for some terminals after some time
4392 							// mashing the up and down arrow keys
4393 							r_cons_set_raw (false);
4394 							r_cons_set_raw (true);
4395 #endif
4396 						}
4397 					}
4398 					(void)r_cons_readpush (chrs, chrs_read);
4399 				}
4400 			}
4401 			if (r_cons_is_breaked()) {
4402 				break;
4403 			}
4404 			r_core_visual_show_char (core, ch);
4405 			if (ch == -1 || ch == 4) {
4406 				break;                  // error or eof
4407 			}
4408 			arg[0] = ch;
4409 			arg[1] = 0;
4410 		}
4411 	} while (skip || (*arg && r_core_visual_cmd (core, arg)));
4412 
4413 	r_cons_enable_mouse (false);
4414 	if (color) {
4415 		r_cons_strcat (Color_RESET);
4416 	}
4417 	r_config_set_i (core->config, "scr.color", color);
4418 	core->print->cur_enabled = false;
4419 	if (autoblocksize) {
4420 		r_core_block_size (core, obs);
4421 	}
4422 	r_cons_singleton ()->teefile = teefile;
4423 	r_cons_set_cup (false);
4424 	r_cons_clear00 ();
4425 	core->vmode = false;
4426 	core->cons->event_resize = NULL;
4427 	core->cons->event_data = NULL;
4428 	r_cons_show_cursor (true);
4429 	return 0;
4430 }
4431 
r_listinfo_new(const char * name,RInterval pitv,RInterval vitv,int perm,const char * extra)4432 R_API RListInfo *r_listinfo_new(const char *name, RInterval pitv, RInterval vitv, int perm, const char *extra) {
4433 	RListInfo *info = R_NEW (RListInfo);
4434 	if (info) {
4435 		info->name = name ? strdup (name) : NULL;
4436 		info->pitv = pitv;
4437 		info->vitv = vitv;
4438 		info->perm = perm;
4439 		info->extra = extra ? strdup (extra) : NULL;
4440 	}
4441 	return info;
4442 }
4443 
r_listinfo_free(RListInfo * info)4444 R_API void r_listinfo_free (RListInfo *info) {
4445 	if (!info) {
4446 		return;
4447 	}
4448 	free (info->name);
4449 	free (info->extra);
4450 	free (info);
4451 }
4452