1 /* radare - LGPL - Copyright 2009-2019 - pancake */
2 
3 #include "r_core.h"
4 
5 static const char *help_msg_c[] = {
6 	"Usage:", "c[?dfx] [argument]", " # Compare",
7 	"c", " [string]", "Compare a plain with escaped chars string",
8 	"c*", " [string]", "Same as above, but printing r2 commands instead",
9 	"c1", " [addr]", "Compare 8 bits from current offset",
10 	"c2", " [value]", "Compare a word from a math expression",
11 	"c4", " [value]", "Compare a doubleword from a math expression",
12 	"c8", " [value]", "Compare a quadword from a math expression",
13 	"cat", " [file]", "Show contents of file (see pwd, ls)",
14 	"cc", " [at]", "Compares in two hexdump columns of block size",
15 	"ccc", " [at]", "Same as above, but only showing different lines",
16 	"ccd", " [at]", "Compares in two disasm columns of block size",
17 	"ccdd", " [at]", "Compares decompiler output (e cmd.pdc=pdg|pdd)",
18 	"cd", " [dir]", "chdir",
19 	// "cc", " [offset]", "code bindiff current block against offset"
20 	// "cD", " [file]", "like above, but using radiff -b",
21 	"cf", " [file]", "Compare contents of file at current seek",
22 	"cg", "[?] [o] [file]", "Graphdiff current file and [file]",
23 	"cl|cls|clear", "", "Clear screen, (clear0 to goto 0, 0 only)",
24 	"cu", "[?] [addr] @at", "Compare memory hexdumps of $$ and dst in unified diff",
25 	"cud", " [addr] @at", "Unified diff disasm from $$ and given address",
26 	"cv", "[1248] [hexpairs] @at", "Compare 1,2,4,8-byte (silent return in $?)",
27 	"cV", "[1248] [addr] @at", "Compare 1,2,4,8-byte address contents (silent, return in $?)",
28 	"cw", "[?] [us?] [...]", "Compare memory watchers",
29 	"cx", " [hexpair]", "Compare hexpair string (use '.' as nibble wildcard)",
30 	"cx*", " [hexpair]", "Compare hexpair string (output r2 commands)",
31 	"cX", " [addr]", "Like 'cc' but using hexdiff output",
32 	NULL
33 };
34 
cmd_cmp_init(RCore * core,RCmdDesc * parent)35 static void cmd_cmp_init(RCore *core, RCmdDesc *parent) {
36 	DEFINE_CMD_DESCRIPTOR (core, c);
37 }
38 
r_core_cmpwatch_free(RCoreCmpWatcher * w)39 R_API void r_core_cmpwatch_free(RCoreCmpWatcher *w) {
40 	free (w->ndata);
41 	free (w->odata);
42 	free (w);
43 }
44 
r_core_cmpwatch_get(RCore * core,ut64 addr)45 R_API RCoreCmpWatcher *r_core_cmpwatch_get(RCore *core, ut64 addr) {
46 	RListIter *iter;
47 	RCoreCmpWatcher *w;
48 	r_list_foreach (core->watchers, iter, w) {
49 		if (addr == w->addr) {
50 			return w;
51 		}
52 	}
53 	return NULL;
54 }
55 
r_core_cmpwatch_add(RCore * core,ut64 addr,int size,const char * cmd)56 R_API int r_core_cmpwatch_add(RCore *core, ut64 addr, int size, const char *cmd) {
57 	RCoreCmpWatcher *cmpw;
58 	if (size < 1) {
59 		return false;
60 	}
61 	cmpw = r_core_cmpwatch_get (core, addr);
62 	if (!cmpw) {
63 		cmpw = R_NEW (RCoreCmpWatcher);
64 		if (!cmpw) {
65 			return false;
66 		}
67 		cmpw->addr = addr;
68 	}
69 	cmpw->size = size;
70 	snprintf (cmpw->cmd, sizeof (cmpw->cmd), "%s", cmd);
71 	cmpw->odata = NULL;
72 	cmpw->ndata = malloc (size);
73 	if (!cmpw->ndata) {
74 		free (cmpw);
75 		return false;
76 	}
77 	r_io_read_at (core->io, addr, cmpw->ndata, size);
78 	r_list_append (core->watchers, cmpw);
79 	return true;
80 }
81 
r_core_cmpwatch_del(RCore * core,ut64 addr)82 R_API int r_core_cmpwatch_del(RCore *core, ut64 addr) {
83 	int ret = false;
84 	RCoreCmpWatcher *w;
85 	RListIter *iter, *iter2;
86 	r_list_foreach_safe (core->watchers, iter, iter2, w) {
87 		if (w->addr == addr || addr == UT64_MAX) {
88 			r_list_delete (core->watchers, iter);
89 			ret = true;
90 		}
91 	}
92 	return ret;
93 }
94 
r_core_cmpwatch_show(RCore * core,ut64 addr,int mode)95 R_API int r_core_cmpwatch_show(RCore *core, ut64 addr, int mode) {
96 	char cmd[128];
97 	RListIter *iter;
98 	RCoreCmpWatcher *w;
99 	r_list_foreach (core->watchers, iter, w) {
100 		int is_diff = w->odata? memcmp (w->odata, w->ndata, w->size): 0;
101 		switch (mode) {
102 		case '*':
103 			r_cons_printf ("cw 0x%08"PFMT64x " %d %s%s\n",
104 				w->addr, w->size, w->cmd, is_diff? " # differs": "");
105 			break;
106 		case 'd': // diff
107 			if (is_diff) {
108 				r_cons_printf ("0x%08"PFMT64x " has changed\n", w->addr);
109 			}
110 			break;
111 		case 'o': // old contents
112 		// use tmpblocksize
113 		default:
114 			r_cons_printf ("0x%08"PFMT64x "%s\n", w->addr, is_diff? " modified": "");
115 			snprintf (cmd, sizeof (cmd), "%s@%"PFMT64d "!%d",
116 				w->cmd, w->addr, w->size);
117 			r_core_cmd0 (core, cmd);
118 			break;
119 		}
120 	}
121 	return false;
122 }
123 
r_core_cmpwatch_update(RCore * core,ut64 addr)124 R_API int r_core_cmpwatch_update(RCore *core, ut64 addr) {
125 	RCoreCmpWatcher *w;
126 	RListIter *iter;
127 	r_list_foreach (core->watchers, iter, w) {
128 		free (w->odata);
129 		w->odata = w->ndata;
130 		w->ndata = malloc (w->size);
131 		if (!w->ndata) {
132 			return false;
133 		}
134 		r_io_read_at (core->io, w->addr, w->ndata, w->size);
135 	}
136 	return !r_list_empty (core->watchers);
137 }
138 
r_core_cmpwatch_revert(RCore * core,ut64 addr)139 R_API int r_core_cmpwatch_revert(RCore *core, ut64 addr) {
140 	RCoreCmpWatcher *w;
141 	int ret = false;
142 	RListIter *iter;
143 	r_list_foreach (core->watchers, iter, w) {
144 		if (w->addr == addr || addr == UT64_MAX) {
145 			if (w->odata) {
146 				free (w->ndata);
147 				w->ndata = w->odata;
148 				w->odata = NULL;
149 				ret = true;
150 			}
151 		}
152 	}
153 	return ret;
154 }
155 
radare_compare_words(RCore * core,ut64 of,ut64 od,int len,int ws)156 static int radare_compare_words(RCore *core, ut64 of, ut64 od, int len, int ws) {
157 	int i;
158 	bool useColor = r_config_get_i (core->config, "scr.color") != 0;
159 	utAny v0, v1;
160 	RConsPrintablePalette *pal = &r_cons_singleton ()->context->pal;
161 	for (i = 0; i < len; i+=ws) {
162 		memset (&v0, 0, sizeof (v0));
163 		memset (&v1, 0, sizeof (v1));
164 		r_io_read_at (core->io, of + i, (ut8*)&v0, ws);
165 		r_io_read_at (core->io, od + i, (ut8*)&v1, ws);
166 		char ch = (v0.v64 == v1.v64)? '=': '!';
167 		const char *color = useColor? ch == '='? "": pal->graph_false: "";
168 		const char *colorEnd = useColor? Color_RESET: "";
169 
170 		if (useColor) {
171 			r_cons_printf ("%s0x%08" PFMT64x"  "Color_RESET, pal->offset, of + i);
172 		} else {
173 			r_cons_printf ("0x%08" PFMT64x"  ", of + i);
174 		}
175 		switch (ws) {
176 		case 1:
177 			r_cons_printf ("%s0x%02x %c 0x%02x%s\n", color,
178 				(ut32)(v0.v8 & 0xff), ch, (ut32)(v1.v8 & 0xff), colorEnd);
179 			break;
180 		case 2:
181 			r_cons_printf ("%s0x%04hx %c 0x%04hx%s\n", color,
182 				v0.v16, ch, v1.v16, colorEnd);
183 			break;
184 		case 4:
185 			r_cons_printf ("%s0x%08"PFMT32x" %c 0x%08"PFMT32x"%s\n", color,
186 				v0.v32, ch, v1.v32, colorEnd);
187 			//r_core_cmdf (core, "fd@0x%"PFMT64x, v0.v32);
188 			if (v0.v32 != v1.v32) {
189 			//	r_core_cmdf (core, "fd@0x%"PFMT64x, v1.v32);
190 			}
191 			break;
192 		case 8:
193 			r_cons_printf ("%s0x%016"PFMT64x" %c 0x%016"PFMT64x"%s\n",
194 				color, v0.v64, ch, v1.v64, colorEnd);
195 			//r_core_cmdf (core, "fd@0x%"PFMT64x, v0.v64);
196 			if (v0.v64 != v1.v64) {
197 			//	r_core_cmdf (core, "fd@0x%"PFMT64x, v1.v64);
198 			}
199 			break;
200 		}
201 	}
202 	return 0;
203 }
204 
radare_compare_unified(RCore * core,ut64 of,ut64 od,int len)205 static int radare_compare_unified(RCore *core, ut64 of, ut64 od, int len) {
206 	int i, min, inc = 16;
207 	ut8 *f, *d;
208 	if (len < 1) {
209 		return false;
210 	}
211 	f = malloc (len);
212 	if (!f) {
213 		return false;
214 	}
215 	d = malloc (len);
216 	if (!d) {
217 		free (f);
218 		return false;
219 	}
220 	r_io_read_at (core->io, of, f, len);
221 	r_io_read_at (core->io, od, d, len);
222 	int headers = B_IS_SET (core->print->flags, R_PRINT_FLAGS_HEADER);
223 	if (headers) {
224 		B_UNSET (core->print->flags, R_PRINT_FLAGS_HEADER);
225 	}
226 	for (i = 0; i < len; i += inc) {
227 		min = R_MIN (16, (len - i));
228 		if (!memcmp (f + i, d + i, min)) {
229 			r_cons_printf ("  ");
230 			r_print_hexdiff (core->print, of + i, f + i, of + i, f + i, min, 0);
231 		} else {
232 			r_cons_printf ("- ");
233 			r_print_hexdiff (core->print, of + i, f + i, od + i, d + i, min, 0);
234 			r_cons_printf ("+ ");
235 			r_print_hexdiff (core->print, od + i, d + i, of + i, f + i, min, 0);
236 		}
237 	}
238 	if (headers) {
239 		B_SET (core->print->flags, R_PRINT_FLAGS_HEADER);
240 	}
241 	return true;
242 }
243 
radare_compare(RCore * core,const ut8 * f,const ut8 * d,int len,int mode)244 static int radare_compare(RCore *core, const ut8 *f, const ut8 *d, int len, int mode) {
245 	int i, eq = 0;
246 	PJ *pj = NULL;
247 	if (len < 1) {
248 		return 0;
249 	}
250 	if (mode == 'j') {
251 		pj = pj_new ();
252 		if (!pj) {
253 			return -1;
254 		}
255 		pj_o (pj);
256 		pj_k (pj, "diff_bytes");
257 		pj_a (pj);
258 	}
259 	for (i = 0; i < len; i++) {
260 		if (f[i] == d[i]) {
261 			eq++;
262 			continue;
263 		}
264 		switch (mode)
265 		{
266 		case 0:
267 			r_cons_printf ("0x%08"PFMT64x " (byte=%.2d)   %02x '%c'  ->  %02x '%c'\n",
268 				core->offset + i, i + 1,
269 				f[i], (IS_PRINTABLE (f[i]))? f[i]: ' ',
270 				d[i], (IS_PRINTABLE (d[i]))? d[i]: ' ');
271 			break;
272 		case '*':
273 			r_cons_printf ("wx %02x @ 0x%08"PFMT64x "\n",
274 				d[i],
275 				core->offset + i);
276 			break;
277 		case 'j':
278 			pj_o (pj);
279 			pj_kn (pj, "offset", core->offset + i);
280 			pj_ki (pj, "rel_offset", i);
281 			pj_ki (pj, "value", (int)f[i]);
282 			pj_ki (pj, "cmp_value", (int)d[i]);
283 			pj_end (pj);
284 			break;
285 
286 		}
287 	}
288 	if (mode == 0) {
289 		eprintf ("Compare %d/%d equal bytes (%d%%)\n", eq, len, (eq / len) * 100);
290 	} else if (mode == 'j') {
291 		pj_end (pj);
292 		pj_ki (pj, "equal_bytes", eq);
293 		pj_ki (pj, "total_bytes", len);
294 		pj_end (pj); // End array
295 		pj_end (pj); // End object
296 		r_cons_println (pj_string (pj));
297 	}
298 	return len - eq;
299 }
300 
cmd_cmp_watcher(RCore * core,const char * input)301 static void cmd_cmp_watcher(RCore *core, const char *input) {
302 	char *p, *q, *r = NULL;
303 	int size = 0;
304 	ut64 addr = 0;
305 	switch (*input) {
306 	case ' ':
307 		p = strdup (input + 1);
308 		q = strchr (p, ' ');
309 		if (q) {
310 			*q++ = 0;
311 			addr = r_num_math (core->num, p);
312 			r = strchr (q, ' ');
313 			if (r) {
314 				*r++ = 0;
315 				size = atoi (q);
316 			}
317 			r_core_cmpwatch_add (core, addr, size, r);
318 			// eprintf ("ADD (%llx) %d (%s)\n", addr, size, r);
319 		} else {
320 			eprintf ("Missing parameters\n");
321 		}
322 		free (p);
323 		break;
324 	case 'r':
325 		addr = input[1]? r_num_math (core->num, input + 1): UT64_MAX;
326 		r_core_cmpwatch_revert (core, addr);
327 		break;
328 	case 'u':
329 		addr = input[1]? r_num_math (core->num, input + 1): UT64_MAX;
330 		r_core_cmpwatch_update (core, addr);
331 		break;
332 	case '*':
333 		r_core_cmpwatch_show (core, UT64_MAX, '*');
334 		break;
335 	case '\0':
336 		r_core_cmpwatch_show (core, UT64_MAX, 0);
337 		break;
338 	case '?': {
339 		const char *help_message[] = {
340 			"Usage: cw", "", "Watcher commands",
341 			"cw", "", "List all compare watchers",
342 			"cw", " addr", "List all compare watchers",
343 			"cw", " addr sz cmd", "Add a memory watcher",
344 			// "cws", " [addr]", "Show watchers",
345 			"cw", "*", "List compare watchers in r2 cmds",
346 			"cwr", " [addr]", "Reset/revert watchers",
347 			"cwu", " [addr]", "Update watchers",
348 			NULL
349 		};
350 		r_core_cmd_help (core, help_message);
351 	}
352 		  break;
353 	}
354 }
355 
cmd_cmp_disasm(RCore * core,const char * input,int mode)356 static int cmd_cmp_disasm(RCore *core, const char *input, int mode) {
357 	RAsmOp op, op2;
358 	int i, j;
359 	char colpad[80];
360 	int hascolor = r_config_get_i (core->config, "scr.color");
361 	int cols = r_config_get_i (core->config, "hex.cols") * 2;
362 	ut64 off = r_num_math (core->num, input);
363 	ut8 *buf = calloc (core->blocksize + 32, 1);
364 	RConsPrintablePalette *pal = &r_cons_singleton ()->context->pal;
365 	if (!buf) {
366 		return false;
367 	}
368 	r_io_read_at (core->io, off, buf, core->blocksize + 32);
369 	switch (mode) {
370 	case 'd': // decompiler
371 		{
372 #if 0
373 		char *a = r_core_cmd_strf (core, "pdc @ 0x%"PFMT64x, off);
374 		char *b = r_core_cmd_strf (core, "pdc @ 0x%"PFMT64x, core->offset);
375 		RDiff *d = r_diff_new ();
376 		char *s = r_diff_buffers_unified (d, a, strlen(a), b, strlen(b));
377 		r_cons_printf ("%s\n", s);
378 		free (a);
379 		free (b);
380 		free (s);
381 		r_diff_free (d);
382 #else
383 		r_core_cmdf (core, "pdc @ 0x%"PFMT64x">$a", off);
384 		r_core_cmdf (core, "pdc @ 0x%"PFMT64x">$b", core->offset);
385 		r_core_cmd0 (core, "diff $a $b;rm $a;rm $b");
386 #endif
387 		}
388 		break;
389 	case 'c': // columns
390 		for (i = j = 0; i < core->blocksize && j < core->blocksize;) {
391 			// dis A
392 			r_asm_set_pc (core->rasm, core->offset + i);
393 			(void) r_asm_disassemble (core->rasm, &op,
394 				core->block + i, core->blocksize - i);
395 
396 			// dis B
397 			r_asm_set_pc (core->rasm, off + i);
398 			(void) r_asm_disassemble (core->rasm, &op2,
399 				buf + j, core->blocksize - j);
400 
401 			// show output
402 			bool iseq = r_strbuf_equals (&op.buf_asm, &op2.buf_asm);
403 			memset (colpad, ' ', sizeof (colpad));
404 			{
405 				int pos = strlen (r_strbuf_get (&op.buf_asm));
406 				pos = (pos > cols)? 0: cols - pos;
407 				colpad[pos] = 0;
408 			}
409 			if (hascolor) {
410 				r_cons_print (iseq? pal->graph_true: pal->graph_false);
411 			}
412 			r_cons_printf (" 0x%08"PFMT64x "  %s %s",
413 				core->offset + i, r_strbuf_get (&op.buf_asm), colpad);
414 			r_cons_printf ("%c 0x%08"PFMT64x "  %s\n",
415 				iseq? '=': '!', off + j, r_strbuf_get (&op2.buf_asm));
416 			if (hascolor) {
417 				r_cons_print (Color_RESET);
418 			}
419 			if (op.size < 1) {
420 				op.size = 1;
421 			}
422 			i += op.size;
423 			if (op2.size < 1) {
424 				op2.size = 1;
425 			}
426 			j += op2.size;
427 		}
428 		break;
429 	case 'u': // unified
430 		for (i = j = 0; i < core->blocksize && j < core->blocksize;) {
431 			// dis A
432 			r_asm_set_pc (core->rasm, core->offset + i);
433 			(void) r_asm_disassemble (core->rasm, &op,
434 				core->block + i, core->blocksize - i);
435 
436 			// dis B
437 			r_asm_set_pc (core->rasm, off + i);
438 			(void) r_asm_disassemble (core->rasm, &op2,
439 				buf + j, core->blocksize - j);
440 
441 			// show output
442 			bool iseq = r_strbuf_equals (&op.buf_asm, &op2.buf_asm); // (!strcmp (op.buf_asm, op2.buf_asm));
443 			if (iseq) {
444 				r_cons_printf (" 0x%08"PFMT64x "  %s\n",
445 					core->offset + i, r_strbuf_get (&op.buf_asm));
446 			} else {
447 				if (hascolor) {
448 					r_cons_print (pal->graph_false);
449 				}
450 				r_cons_printf ("-0x%08"PFMT64x "  %s\n",
451 					core->offset + i, r_strbuf_get (&op.buf_asm));
452 				if (hascolor) {
453 					r_cons_print (pal->graph_true);
454 				}
455 				r_cons_printf ("+0x%08"PFMT64x "  %s\n",
456 					off + j, r_strbuf_get (&op2.buf_asm));
457 				if (hascolor) {
458 					r_cons_print (Color_RESET);
459 				}
460 			}
461 			if (op.size < 1) {
462 				op.size = 1;
463 			}
464 			i += op.size;
465 			if (op2.size < 1) {
466 				op2.size = 1;
467 			}
468 			j += op2.size;
469 		}
470 		break;
471 	}
472 	return 0;
473 }
474 
cmd_cp(void * data,const char * input)475 static int cmd_cp(void *data, const char *input) {
476 	RCore *core = (RCore *)data;
477 	if (input[1] == '.') {
478 		char *file = r_core_cmd_strf (core, "ij~{core.file}");
479 		r_str_trim (file);
480 		char *newfile = r_str_newf ("%s.%s", file, input + 2);
481 		r_file_copy (file, newfile);
482 		free (file);
483 		free (newfile);
484 		return true;
485 	}
486 	if (strlen (input) < 3) {
487 		eprintf ("Usage: cp src dst\n");
488 		eprintf ("Usage: cp.orig  # cp $file $file.orig\n");
489 		return false;
490 	}
491 	char *cmd = strdup (input + 2);
492 	if (cmd) {
493 		char **files = r_str_argv (cmd, NULL);
494 		if (files[0] && files[1]) {
495 			bool rc = r_file_copy (files[0], files[1]);
496 			free (cmd);
497 			r_str_argv_free (files);
498 			return rc;
499 		}
500 		r_str_argv_free (files);
501 	}
502 	eprintf ("Usage: cp src dst\n");
503 	return false;
504 }
505 
__core_cmp_bits(RCore * core,ut64 addr)506 static void __core_cmp_bits (RCore *core, ut64 addr) {
507 	const bool scr_color = r_config_get_i (core->config, "scr.color");
508 	int i;
509 	ut8 a, b;
510 	r_io_read_at (core->io, core->offset, &a, 1);
511 	r_io_read_at (core->io, addr, &b, 1);
512 	RConsPrintablePalette *pal = &r_cons_singleton ()->context->pal;
513 	const char *color = scr_color? pal->offset: "";
514 	const char *color_end = scr_color? Color_RESET: "";
515 	if (r_config_get_i (core->config, "hex.header")) {
516 		char *n = r_str_newf ("0x%08"PFMT64x, core->offset);
517 		const char *extra = r_str_pad (' ', strlen (n) - 10);
518 		free (n);
519 		r_cons_printf ("%s- offset -%s  7 6 5 4 3 2 1 0%s\n", color, extra, color_end);
520 	}
521 	color = scr_color? pal->graph_false: "";
522 	color_end = scr_color? Color_RESET: "";
523 
524 	r_cons_printf ("%s0x%08"PFMT64x"%s  ", color, core->offset, color_end);
525 	for (i = 7; i >= 0; i--) {
526 		bool b0 = (a & 1<<i)? 1: 0;
527 		bool b1 = (b & 1<<i)? 1: 0;
528 		color = scr_color? (b0 == b1)? "": b0? pal->graph_true:pal->graph_false: "";
529 		color_end = scr_color ? Color_RESET: "";
530 		r_cons_printf ("%s%d%s ", color, b0, color_end);
531 	}
532 	color = scr_color? pal->graph_true: "";
533 	color_end = scr_color? Color_RESET: "";
534 	r_cons_printf ("\n%s0x%08"PFMT64x"%s  ", color, addr, color_end);
535 	for (i = 7; i >= 0; i--) {
536 		bool b0 = (a & 1<<i)? 1: 0;
537 		bool b1 = (b & 1<<i)? 1: 0;
538 		color = scr_color? (b0 == b1)? "": b1? pal->graph_true: pal->graph_false: "";
539 		color_end = scr_color ? Color_RESET: "";
540 		r_cons_printf ("%s%d%s ", color, b1, color_end);
541 	}
542 	r_cons_newline ();
543 }
544 
cmd_cmp(void * data,const char * input)545 static int cmd_cmp(void *data, const char *input) {
546 	static char *oldcwd = NULL;
547 	int ret = 0, i, mode = 0;
548 	RCore *core = (RCore *)data;
549 	ut64 val = UT64_MAX;
550 	char *filled;
551 	ut8 *buf;
552 	ut16 v16;
553 	ut32 v32;
554 	ut64 v64;
555 	FILE *fd;
556 	const ut8* block = core->block;
557 
558 	switch (*input) {
559 	case 'p':
560 		return cmd_cp (data, input);
561 		break;
562 	case 'a': // "cat"
563 		if (input[1] == 't') {
564 			const char *path = r_str_trim_head_ro (input + 2);
565 			if (*path == '$') {
566 				const char *oldText = r_cmd_alias_get (core->rcmd, path, 1);
567 				if (oldText) {
568 					r_cons_printf ("%s\n", oldText + 1);
569 				}
570 			} else {
571 				if (r_fs_check (core->fs, path)) {
572 					r_core_cmdf (core, "mg %s", path);
573 				} else {
574 					char *res = r_syscmd_cat (path);
575 					if (res) {
576 						r_cons_print (res);
577 						free (res);
578 					}
579 				}
580 			}
581 		}
582 		break;
583 	case 'w':
584 		cmd_cmp_watcher (core, input + 1);
585 		break;
586 	case '*':
587 		if (!input[2]) {
588 			eprintf ("Usage: cx* 00..22'\n");
589 			return 0;
590 		}
591 
592 		val = radare_compare (core, block, (ut8 *) input + 2,
593 			strlen (input + 2) + 1, '*');
594 		break;
595 	case ' ':
596 	{
597 		char *str = strdup (input + 1);
598 		int len = r_str_unescape (str);
599 		val = radare_compare (core, block, (ut8 *) str, len, 0);
600 		free (str);
601 	}
602 	break;
603 	case 'j':
604 	{
605 		if (input[1] != ' ') {
606 			eprintf ("Usage: cj [string]\n");
607 		} else {
608 			char *str = strdup (input + 2);
609 			int len = r_str_unescape (str);
610 			val = radare_compare (core, block, (ut8 *) str, len, 'j');
611 			free (str);
612 		}
613 	}
614 	break;
615 	case 'x':
616 		switch (input[1]) {
617 		case ' ':
618 			mode = 0;
619 			input += 2;
620 			break;
621 		case '*':
622 			if (input[2] != ' ') {
623 				eprintf ("Usage: cx* 00..22'\n");
624 				return 0;
625 			}
626 			mode = '*';
627 			input += 3;
628 			break;
629 		default:
630 			eprintf ("Usage: cx 00..22'\n");
631 			return 0;
632 		}
633 		if (!(filled = (char *) malloc (strlen (input) + 1))) {
634 			return false;
635 		}
636 		memcpy (filled, input, strlen (input) + 1);
637 		if (!(buf = (ut8 *) malloc (strlen (input) + 1))) {
638 			free (filled);
639 			return false;
640 		}
641 		ret = r_hex_bin2str (block, strlen (input) / 2, (char *) buf);
642 		for (i = 0; i < ret * 2; i++) {
643 			if (filled[i] == '.') {
644 				filled[i] = buf[i];
645 			}
646 		}
647 
648 		ret = r_hex_str2bin (filled, buf);
649 		if (ret < 1) {
650 			eprintf ("Cannot parse hexpair\n");
651 		} else {
652 			val = radare_compare (core, block, buf, ret, mode);
653 		}
654 		free (buf);
655 		free (filled);
656 		break;
657 	case 'X':
658 		buf = malloc (core->blocksize);
659 		if (buf) {
660 			if (!r_io_read_at (core->io, r_num_math (core->num,
661 					    input + 1), buf, core->blocksize)) {
662 				eprintf ("Cannot read hexdump\n");
663 			} else {
664 				val = radare_compare (core, block, buf, ret, mode);
665 			}
666 			free (buf);
667 		}
668 		return false;
669 		break;
670 	case 'f':
671 		if (input[1] != ' ') {
672 			eprintf ("Please. use 'cf [file]'\n");
673 			return false;
674 		}
675 		fd = r_sandbox_fopen (input + 2, "rb");
676 		if (!fd) {
677 			eprintf ("Cannot open file '%s'\n", input + 2);
678 			return false;
679 		}
680 		buf = (ut8 *) malloc (core->blocksize);
681 		if (buf) {
682 			if (fread (buf, 1, core->blocksize, fd) < 1) {
683 				eprintf ("Cannot read file %s\n", input + 2);
684 			} else {
685 				val = radare_compare (core, block, buf, core->blocksize, 0);
686 			}
687 			fclose (fd);
688 			free (buf);
689 		} else {
690 			fclose (fd);
691 			return false;
692 		}
693 		break;
694 	case 'd': // "cd"
695 		while (input[1] == ' ') input++;
696 		if (input[1]) {
697 			if (!strcmp (input + 1, "-")) {
698 				if (oldcwd) {
699 					char *newdir = oldcwd;
700 					oldcwd = r_sys_getdir ();
701 					if (r_sandbox_chdir (newdir) == -1) {
702 						eprintf ("Cannot chdir to %s\n", newdir);
703 						free (oldcwd);
704 						oldcwd = newdir;
705 					} else {
706 						free (newdir);
707 					}
708 				} else {
709 					// nothing to do here
710 				}
711 			} else if (input[1] == '~' && input[2] == '/') {
712 				char *homepath = r_str_home (input + 3);
713 				if (homepath) {
714 					if (*homepath) {
715 						free (oldcwd);
716 						oldcwd = r_sys_getdir ();
717 						if (r_sandbox_chdir (homepath) == -1) {
718 							eprintf ("Cannot chdir to %s\n", homepath);
719 						}
720 					}
721 					free (homepath);
722 				} else {
723 					eprintf ("Cannot find home\n");
724 				}
725 			} else {
726 				free (oldcwd);
727 				oldcwd = r_sys_getdir ();
728 				if (r_sandbox_chdir (input + 1) == -1) {
729 					eprintf ("Cannot chdir to %s\n", input + 1);
730 				}
731 			}
732 		} else {
733 			char *home = r_sys_getenv (R_SYS_HOME);
734 			if (!home || r_sandbox_chdir (home) == -1) {
735 				eprintf ("Cannot find home.\n");
736 			}
737 			free (home);
738 		}
739 		break;
740 	case '1': // "c1"
741 		__core_cmp_bits (core, r_num_math (core->num, input + 1));
742 		break;
743 	case '2': // "c2"
744 		v16 = (ut16) r_num_math (core->num, input + 1);
745 		val = radare_compare (core, block, (ut8 *) &v16, sizeof (v16), 0);
746 		break;
747 	case '4': // "c4"
748 		v32 = (ut32) r_num_math (core->num, input + 1);
749 		val = radare_compare (core, block, (ut8 *) &v32, sizeof (v32), 0);
750 		break;
751 	case '8': // "c8"
752 		v64 = (ut64) r_num_math (core->num, input + 1);
753 		val = radare_compare (core, block, (ut8 *) &v64, sizeof (v64), 0);
754 		break;
755 	case 'c': // "cc"
756 		if (input[1] == '?') { // "cc?"
757 			r_core_cmd0 (core, "c?~cc");
758 		} else if (input[1] == 'd') { // "ccd"
759 			if (input[2] == 'd') { // "ccdd"
760 				cmd_cmp_disasm (core, input + 3, 'd');
761 			} else {
762 				cmd_cmp_disasm (core, input + 2, 'c');
763 			}
764 		} else {
765 			ut32 oflags = core->print->flags;
766 			ut64 addr = 0; // TOTHINK: Not sure what default address should be
767 			if (input[1] == 'c') { // "ccc"
768 				core->print->flags |= R_PRINT_FLAGS_DIFFOUT;
769 				addr = r_num_math (core->num, input + 2);
770 			} else {
771 				if (*input && input[1]) {
772 					addr = r_num_math (core->num, input + 2);
773 				}
774 			}
775 			int col = core->cons->columns > 123;
776 			ut8 *b = malloc (core->blocksize);
777 			if (b != NULL) {
778 				memset (b, 0xff, core->blocksize);
779 				r_io_read_at (core->io, addr, b, core->blocksize);
780 				r_print_hexdiff (core->print, core->offset, block,
781 					addr, b, core->blocksize, col);
782 				free (b);
783 			}
784 			core->print->flags = oflags;
785 		}
786 		break;
787 	case 'g': // "cg"
788 	{          // XXX: this is broken
789 		int diffops = 0;
790 		RCore *core2;
791 		char *file2 = NULL;
792 		switch (input[1]) {
793 		case 'o':         // "cgo"
794 			file2 = (char *) r_str_trim_head_ro (input + 2);
795 			r_anal_diff_setup (core->anal, true, -1, -1);
796 			break;
797 		case 'f':         // "cgf"
798 			eprintf ("TODO: agf is experimental\n");
799 			r_anal_diff_setup (core->anal, true, -1, -1);
800 			r_core_gdiff_fcn (core, core->offset,
801 				r_num_math (core->num, input + 2));
802 			return false;
803 		case ' ':
804 			file2 = (char *) r_str_trim_head_ro (input + 2);
805 			r_anal_diff_setup (core->anal, false, -1, -1);
806 			break;
807 		default: {
808 			const char *help_message[] = {
809 				"Usage: cg", "", "Graph code commands",
810 				"cg", "", "diff ratio among functions (columns: off-A, match-ratio, off-B)",
811 				"cgf", "[fcn]", "Compare functions (curseek vs fcn)",
812 				"cgo", "", "Opcode-bytes code graph diff",
813 				NULL
814 			};
815 			r_core_cmd_help (core, help_message);
816 			return false;
817 		}
818 		}
819 
820 		if (r_file_size (file2) <= 0) {
821 			eprintf ("Cannot compare with file %s\n", file2);
822 			return false;
823 		}
824 
825 		if (!(core2 = r_core_new ())) {
826 			eprintf ("Cannot init diff core\n");
827 			return false;
828 		}
829 		r_core_loadlibs (core2, R_CORE_LOADLIBS_ALL, NULL);
830 		core2->io->va = core->io->va;
831 		if (!r_core_file_open (core2, file2, 0, 0LL)) {
832 			eprintf ("Cannot open diff file '%s'\n", file2);
833 			r_core_free (core2);
834 			r_core_bind_cons (core);
835 			return false;
836 		}
837 		// TODO: must replicate on core1 too
838 		r_config_set_i (core2->config, "io.va", true);
839 		r_anal_diff_setup (core->anal, diffops, -1, -1);
840 		r_anal_diff_setup (core2->anal, diffops, -1, -1);
841 
842 		r_core_bin_load (core2, file2,
843 			r_config_get_i (core->config, "bin.baddr"));
844 		r_core_gdiff (core, core2);
845 		r_core_diff_show (core, core2);
846 		/* exchange a segfault with a memleak */
847 		core2->config = NULL;
848 		r_core_free (core2);
849 		r_core_bind_cons (core);
850 	}
851 	break;
852 	case 'u': // "cu"
853 		switch (input[1]) {
854 		case '.':
855 		case ' ':
856 			radare_compare_unified (core, core->offset,
857 				r_num_math (core->num, input + 2),
858 				core->blocksize);
859 			break;
860 		case '1':
861 		case '2':
862 		case '4':
863 		case '8':
864 			radare_compare_words (core, core->offset,
865 				r_num_math (core->num, input + 2),
866 				core->blocksize, input[1] - '0');
867 			break;
868 		case 'd':
869 			cmd_cmp_disasm (core, input + 2, 'u');
870 			break;
871 		default: {
872 			const char *help_msg[] = {
873 				"Usage: cu", " [offset]", "# Prints unified comparison to make hexpatches",
874 				"cu", " $$+1 > p", "Compare hexpairs from  current seek and +1",
875 				"cu1", " $$+1 > p", "Compare bytes from current seek and +1",
876 				"cu2", " $$+1 > p", "Compare words (half, 16bit) from current seek and +1",
877 				"cu4", " $$+1 > p", "Compare dwords from current seek and +1",
878 				"cu8", " $$+1 > p", "Compare qwords from current seek and +1",
879 				"cud", " $$+1 > p", "Compare disasm current seek and +1",
880 				"wu", " p", "Apply unified hex patch (see output of cu)",
881 				NULL
882 			};
883 			r_core_cmd_help (core, help_msg);
884 		}
885 		}
886 		break;
887 	case '?':
888 		r_core_cmd_help (core, help_msg_c);
889 		break;
890 	case 'v': { // "cv"
891 		int sz = input[1];
892 		if (sz == ' ') {
893 			switch (r_config_get_i (core->config, "asm.bits")) {
894 			case 8: sz = '1'; break;
895 			case 16: sz = '2'; break;
896 			case 32: sz = '4'; break;
897 			case 64: sz = '8'; break;
898 			default: sz = '4'; break; // default
899 			}
900 		}
901 		// TODO: honor endian
902 		switch (sz) {
903 		case '1': { // "cv1"
904 			ut8 n = (ut8) r_num_math (core->num, input + 2);
905 			core->num->value = 1;
906 			if (block[0] == n) {
907 				r_cons_printf ("0x%08"PFMT64x "\n", core->offset);
908 				core->num->value = 0;
909 			}
910 			break;
911 		}
912 		case '2': { // "cv2"
913 			ut16 n = (ut16) r_num_math (core->num, input + 2);
914 			core->num->value = 1;
915 			if (core->blocksize >= 2 && *(ut16*)block == n) {
916 				r_cons_printf ("0x%08"PFMT64x "\n", core->offset);
917 				core->num->value = 0;
918 			}
919 			break;
920 		}
921 		case '4': { // "cv4"
922 			ut32 n = (ut32) r_num_math (core->num, input + 2);
923 			core->num->value = 1;
924 			if (core->blocksize >= 4 && *(ut32*)block == n) {
925 				r_cons_printf ("0x%08"PFMT64x "\n", core->offset);
926 				core->num->value = 0;
927 			}
928 			break;
929 		}
930 		case '8': { // "cv8"
931 			ut64 n = (ut64) r_num_math (core->num, input + 2);
932 			core->num->value = 1;
933 			if (core->blocksize >= 8 && *(ut64*)block == n) {
934 				r_cons_printf ("0x%08"PFMT64x "\n", core->offset);
935 				core->num->value = 0;
936 			}
937 			break;
938 		}
939 		default:
940 		case '?':
941 			eprintf ("Usage: cv[1248] [num]\n"
942 				"Show offset if current value equals to the one specified\n"
943 				" /v 18312   # serch for a known value\n"
944 				" dc\n"
945 				" cv4 18312 @@ hit*\n"
946 				" dc\n");
947 			break;
948 		}
949 	}
950 	break;
951 	case 'V': { // "cV"
952 		int sz = input[1];
953 		if (sz == ' ') {
954 			switch (r_config_get_i (core->config, "asm.bits")) {
955 			case 8: sz = '1'; break;
956 			case 16: sz = '2'; break;
957 			case 32: sz = '4'; break;
958 			case 64: sz = '8'; break;
959 			default: sz = '4'; break; // default
960 			}
961 		} else if (sz == '?') {
962 			eprintf ("Usage: cV[1248] [addr] @ addr2\n"
963 				"Compare n bytes from one address to current one and return in $? 0 or 1\n");
964 		}
965 		sz -= '0';
966 		if (sz > 0) {
967 			ut64 at = r_num_math (core->num, input + 2);
968 			ut8 buf[8] = {0};
969 			r_io_read_at (core->io, at, buf, sizeof (buf));
970 			core->num->value = memcmp (buf, core->block, sz)? 1: 0;
971 		}
972 		break;
973 	}
974 	case 'l': // "cl"
975 		if (strchr (input, 'f')) {
976 			r_cons_flush ();
977 		} else if (input[1] == 0) {
978 			r_cons_fill_line ();
979 			// r_cons_clear_line (0);
980 		} else if (!strchr (input, '0')) {
981 			r_cons_clear00 ();
982 		}
983 		break;
984 	default:
985 		r_core_cmd_help (core, help_msg_c);
986 	}
987 	if (val != UT64_MAX) {
988 		core->num->value = val;
989 	}
990 	return 0;
991 }
992