1 /* radare - LGPL - Copyright 2009-2020 - pancake */
2 
3 #include <r_core.h>
4 #include <r_main.h>
5 
6 enum {
7 	MODE_DIFF,
8 	MODE_DIFF_STRS,
9 	MODE_DIFF_IMPORTS,
10 	MODE_DIST_MYERS,
11 	MODE_DIST_LEVENSHTEIN,
12 	MODE_CODE,
13 	MODE_GRAPH,
14 	MODE_COLS,
15 	MODE_COLSII
16 };
17 
18 enum {
19 	GRAPH_DEFAULT_MODE,
20 	GRAPH_SDB_MODE,
21 	GRAPH_JSON_MODE,
22 	GRAPH_JSON_DIS_MODE,
23 	GRAPH_TINY_MODE,
24 	GRAPH_INTERACTIVE_MODE,
25 	GRAPH_DOT_MODE,
26 	GRAPH_STAR_MODE,
27 	GRAPH_GML_MODE
28 };
29 
30 typedef struct {
31 	ut64 gdiff_start;
32 	bool zignatures;
33 	const char *file;
34 	const char *file2;
35 	ut32 count;
36 	int showcount;
37 	int useva;
38 	int delta;
39 	int showbare;
40 	bool json_started;
41 	int diffmode;
42 	int diffops;
43 	int mode;
44 	int gmode;
45 	bool disasm;
46 	bool pdc;
47 	bool quiet;
48 	RCore *core;
49 	const char *arch;
50 	const char *runcmd;
51 	int bits;
52 	int anal_all;
53 	int threshold;
54 	bool verbose;
55 	RList *evals;
56 	PJ *pj;
57 } RadiffOptions;
58 
opencore(RadiffOptions * ro,const char * f)59 static RCore *opencore(RadiffOptions *ro, const char *f) {
60 	RListIter *iter;
61 	const ut64 baddr = UT64_MAX;
62 	const char *e;
63 	RCore *c = r_core_new ();
64 	if (!c) {
65 		return NULL;
66 	}
67 	r_core_loadlibs (c, R_CORE_LOADLIBS_ALL, NULL);
68 	r_config_set_i (c->config, "io.va", ro->useva);
69 	r_config_set_i (c->config, "scr.interactive", false);
70 	r_list_foreach (ro->evals, iter, e) {
71 		r_config_eval (c->config, e, false);
72 	}
73 	if (f) {
74 		RIODesc * rfile = NULL;
75 #if __WINDOWS__
76 		char *winf = r_acp_to_utf8 (f);
77 		rfile = r_core_file_open (c, winf, 0, 0);
78 		free (winf);
79 #else
80 		rfile = r_core_file_open (c, f, 0, 0);
81 #endif
82 
83 		if (!rfile) {
84 			r_core_free (c);
85 			return NULL;
86 		}
87 		(void) r_core_bin_load (c, NULL, baddr);
88 		(void) r_core_bin_update_arch_bits (c);
89 
90 		// force PA mode when working with raw bins
91 		if (r_list_empty (r_bin_get_sections (c->bin))) {
92 			r_config_set_i (c->config, "io.va", false);
93 		}
94 		if (ro->anal_all) {
95 			const char *cmd = "aac";
96 			switch (ro->anal_all) {
97 			case 1: cmd = "aaa"; break;
98 			case 2: cmd = "aaaa"; break;
99 			}
100 			r_core_cmd0 (c, cmd);
101 		}
102 		if (ro->runcmd) {
103 			r_core_cmd0 (c, ro->runcmd);
104 		}
105 		// generate zignaturez?
106 		if (ro->zignatures) {
107 			r_core_cmd0 (c, "zg");
108 		}
109 		r_cons_flush ();
110 	}
111 	// TODO: must enable io.va here if wanted .. r_config_set_i (c->config, "io.va", va);
112 	return c;
113 }
114 
readstr(char * s,int sz,const ut8 * buf,int len)115 static void readstr(char *s, int sz, const ut8 *buf, int len) {
116 	*s = 0;
117 	int last = R_MIN (len, sz);
118 	if (last < 1) {
119 		return;
120 	}
121 	s[sz - 1] = 0;
122 	while (*s && *s == '\n') {
123 		s++;
124 	}
125 	strncpy (s, (char *) buf, last);
126 }
127 
cb(RDiff * d,void * user,RDiffOp * op)128 static int cb(RDiff *d, void *user, RDiffOp *op) {
129 	int i;
130 	RadiffOptions *ro = user;
131 	char s[256] = {0};
132 	if (ro->showcount) {
133 		ro->count++;
134 		return 1;
135 	}
136 	switch (ro->diffmode) {
137 	case 'U': // 'U' in theory never handled here
138 	case 'u':
139 		if (op->a_len > 0) {
140 			readstr (s, sizeof (s), op->a_buf, op->a_len);
141 			if (*s) {
142 				if (!ro->quiet) {
143 					printf (Color_RED);
144 				}
145 				printf ("-0x%08"PFMT64x":", op->a_off);
146 				int len = op->a_len; // R_MIN (op->a_len, strlen (op->a_buf));
147 				for (i = 0; i < len; i++) {
148 					printf ("%02x ", op->a_buf[i]);
149 				}
150 				if (!ro->quiet) {
151 					char *p = r_str_escape ((const char*)op->a_buf);
152 					printf (" \"%s\"", p);
153 					free (p);
154 					printf (Color_RESET);
155 				}
156 				printf ("\n");
157 			}
158 		}
159 		if (op->b_len > 0) {
160 			readstr (s, sizeof (s), op->b_buf, op->b_len);
161 			if (*s) {
162 				if (!ro->quiet) {
163 					printf (Color_GREEN);
164 				}
165 				printf ("+0x%08"PFMT64x":", op->b_off);
166 				for (i = 0; i < op->b_len; i++) {
167 					printf ("%02x ", op->b_buf[i]);
168 				}
169 				if (!ro->quiet) {
170 					char *p = r_str_escape ((const char*)op->b_buf);
171 					printf (" \"%s\"", p);
172 					free (p);
173 					printf (Color_RESET);
174 				}
175 				printf ("\n");
176 			}
177 		}
178 		break;
179 	case 'r':
180 		if (ro->disasm) {
181 			eprintf ("r2cmds (-r) + disasm (-D) not yet implemented\n");
182 		}
183 		if (op->a_len == op->b_len) {
184 			printf ("wx ");
185 			for (i = 0; i < op->b_len; i++) {
186 				printf ("%02x", op->b_buf[i]);
187 			}
188 			printf (" @ 0x%08"PFMT64x "\n", op->b_off);
189 		} else {
190 			if (op->a_len > 0) {
191 				printf ("r-%d @ 0x%08"PFMT64x "\n",
192 					op->a_len, op->a_off + ro->delta);
193 			}
194 			if (op->b_len > 0) {
195 				printf ("r+%d @ 0x%08"PFMT64x "\n",
196 					op->b_len, op->b_off + ro->delta);
197 				printf ("wx ");
198 				for (i = 0; i < op->b_len; i++) {
199 					printf ("%02x", op->b_buf[i]);
200 				}
201 				printf (" @ 0x%08"PFMT64x "\n", op->b_off + ro->delta);
202 			}
203 			ro->delta += (op->b_off - op->a_off);
204 		}
205 		return 1;
206 	case 'j':
207 		// TODO PJ
208 		if (ro->disasm) {
209 			eprintf ("JSON (-j) + disasm (-D) not yet implemented\n");
210 		}
211 		{
212 			PJ *pj = ro->pj;
213 			pj_o (pj);
214 			pj_kn (pj, "addr", op->a_off);
215 			char *hex_from = r_hex_bin2strdup (op->a_buf, op->a_len);
216 			pj_ks (pj, "from", hex_from);
217 			char *hex_to = r_hex_bin2strdup (op->b_buf, op->b_len);
218 			pj_ks (pj, "to", hex_to);
219 			pj_end (pj);
220 		}
221 		return 1;
222 	case 0:
223 	default:
224 		if (ro->disasm) {
225 			int i;
226 			printf ("--- 0x%08"PFMT64x "  ", op->a_off);
227 			if (!ro->core) {
228 				ro->core = opencore (ro, ro->file);
229 				if (ro->arch) {
230 					r_config_set (ro->core->config, "asm.arch", ro->arch);
231 				}
232 				if (ro->bits) {
233 					r_config_set_i (ro->core->config, "asm.bits", ro->bits);
234 				}
235 			}
236 			for (i = 0; i < op->a_len; i++) {
237 				printf ("%02x", op->a_buf[i]);
238 			}
239 			printf ("\n");
240 			if (ro->core) {
241 				int len = R_MAX (4, op->a_len);
242 				RAsmCode *ac = r_asm_mdisassemble (ro->core->rasm, op->a_buf, len);
243 				char *acbufasm = strdup (ac->assembly);
244 				if (ro->quiet) {
245 					char *bufasm = r_str_prefix_all (acbufasm, "- ");
246 					printf ("%s\n", bufasm);
247 					free (bufasm);
248 				} else {
249 					char *bufasm = r_str_prefix_all (acbufasm, Color_RED"- ");
250 					printf ("%s"Color_RESET, bufasm);
251 					free (bufasm);
252 				}
253 				free (acbufasm);
254 				r_asm_code_free (ac);
255 			}
256 		} else {
257 			printf ("0x%08"PFMT64x " ", op->a_off);
258 			for (i = 0; i < op->a_len; i++) {
259 				printf ("%02x", op->a_buf[i]);
260 			}
261 		}
262 		if (ro->disasm) {
263 			int i;
264 			printf ("+++ 0x%08"PFMT64x "  ", op->b_off);
265 			if (!ro->core) {
266 				ro->core = opencore (ro, NULL);
267 			}
268 			for (i = 0; i < op->b_len; i++) {
269 				printf ("%02x", op->b_buf[i]);
270 			}
271 			printf ("\n");
272 			if (ro->core) {
273 				int len = R_MAX (4, op->b_len);
274 				RAsmCode *ac = r_asm_mdisassemble (ro->core->rasm, op->b_buf, len);
275 				char *acbufasm = strdup (ac->assembly);
276 				if (ro->quiet) {
277 					char *bufasm = r_str_prefix_all (acbufasm, "+ ");
278 					printf ("%s\n", bufasm);
279 					free (bufasm);
280 					free (acbufasm);
281 				} else {
282 					char *bufasm = r_str_prefix_all (acbufasm, Color_GREEN"+ ");
283 					printf ("%s\n" Color_RESET, bufasm);
284 					free (bufasm);
285 					free (acbufasm);
286 				}
287 				// r_asm_code_free (ac);
288 			}
289 		} else {
290 			printf (" => ");
291 			for (i = 0; i < op->b_len; i++) {
292 				printf ("%02x", op->b_buf[i]);
293 			}
294 			printf (" 0x%08"PFMT64x "\n", op->b_off);
295 		}
296 		return 1;
297 	}
298 	return 0;
299 }
300 
print_bytes(const void * p,size_t len,bool big_endian)301 void print_bytes(const void *p, size_t len, bool big_endian) {
302 	size_t i;
303 	for (i = 0; i < len; i++) {
304 		ut8 ch = ((ut8*) p)[big_endian ? (len - i - 1) : i];
305 		if (write (1, &ch, 1) != 1) {
306 			break;
307 		}
308 	}
309 }
310 
bcb(RDiff * d,void * user,RDiffOp * op)311 static int bcb(RDiff *d, void *user, RDiffOp *op) {
312 	RadiffOptions *ro = user;
313 	ut64 offset_diff = op->a_off - ro->gdiff_start;
314 	unsigned char opcode;
315 	unsigned short USAddr = 0;
316 	int IAddr = 0;
317 	unsigned char UCLen = 0;
318 	unsigned short USLen = 0;
319 	int ILen = 0;
320 
321 	// we copy from gdiff_start to a_off
322 	if (offset_diff > 0) {
323 
324 		// size for the position
325 		if (ro->gdiff_start <= USHRT_MAX) {
326 			opcode = 249;
327 			USAddr = (unsigned short) ro->gdiff_start;
328 		} else if (ro->gdiff_start <= INT_MAX) {
329 			opcode = 252;
330 			IAddr = (int) ro->gdiff_start;
331 		} else {
332 			opcode = 255;
333 		}
334 
335 		// size for the length
336 		if (opcode != 255 && offset_diff <= UCHAR_MAX) {
337 			UCLen = (unsigned char) offset_diff;
338 		} else if (opcode != 255 && offset_diff <= USHRT_MAX) {
339 			USLen = (unsigned short) offset_diff;
340 			opcode += 1;
341 		} else if (opcode != 255 && offset_diff <= INT_MAX) {
342 			ILen = (int) offset_diff;
343 			opcode += 2;
344 		} else if (offset_diff > INT_MAX) {
345 			int times = offset_diff / INT_MAX;
346 			int max = INT_MAX;
347 			size_t i;
348 			for (i = 0; i < times; i++) {
349 				print_bytes (&opcode, sizeof (opcode), true);
350 				// XXX this is overflowingly wrong
351 				// XXX print_bytes (&gdiff_start + i * max, sizeof (gdiff_start), true);
352 				print_bytes (&max, sizeof (max), true);
353 			}
354 		}
355 
356 		// print opcode for COPY
357 		print_bytes (&opcode, sizeof (opcode), true);
358 
359 		// print position for COPY
360 		if (opcode <= 251) {
361 			print_bytes (&USAddr, sizeof (USAddr), true);
362 		} else if (opcode < 255) {
363 			print_bytes (&IAddr, sizeof (IAddr), true);
364 		} else {
365 			print_bytes (&ro->gdiff_start, sizeof (ro->gdiff_start), true);
366 		}
367 
368 		// print length for COPY
369 		switch (opcode) {
370 		case 249:
371 		case 252:
372 			print_bytes (&UCLen, sizeof (UCLen), true);
373 			break;
374 		case 250:
375 		case 253:
376 			print_bytes (&USLen, sizeof (USLen), true);
377 			break;
378 		case 251:
379 		case 254:
380 		case 255:
381 			print_bytes (&ILen, sizeof (ILen), true);
382 			break;
383 		}
384 	}
385 
386 	// we append data
387 	if (op->b_len <= 246) {
388 		ut8 data = op->b_len;
389 		(void) write (1, &data, 1);
390 	} else if (op->b_len <= USHRT_MAX) {
391 		USLen = (ut16) op->b_len;
392 		ut8 data = 247;
393 		(void) write (1, &data, 1);
394 		print_bytes (&USLen, sizeof (USLen), true);
395 	} else if (op->b_len <= INT_MAX) {
396 		ut8 data = 248;
397 		(void) write (1, &data, 1);
398 		ILen = (int) op->b_len;
399 		print_bytes (&ILen, sizeof (ILen), true);
400 	} else {
401 		// split into multiple DATA, because op->b_len is greater than INT_MAX
402 		int times = op->b_len / INT_MAX;
403 		int max = INT_MAX;
404 		size_t i;
405 		for (i = 0; i < times; i++) {
406 			ut8 data = 248;
407 			if (write (1, &data, 1) != 1) {
408 				break;
409 			}
410 			print_bytes (&max, sizeof (max), true);
411 			print_bytes (op->b_buf, max, false);
412 			op->b_buf += max;
413 		}
414 		op->b_len = op->b_len % max;
415 
416 		// print the remaining size
417 		int remain_size = op->b_len;
418 		print_bytes (&remain_size, sizeof (remain_size), true);
419 	}
420 	print_bytes (op->b_buf, op->b_len, false);
421 	ro->gdiff_start = op->b_off + op->b_len;
422 	return 0;
423 }
424 
show_help(int v)425 static int show_help(int v) {
426 	printf ("Usage: radiff2 [-abBcCdeGhijnrOpqsSxuUvVzZ] [-A[A]] [-g sym] [-m graph_mode][-t %%] [file] [file]\n");
427 	if (v) {
428 		printf (
429 			"  -a [arch]  specify architecture plugin to use (x86, arm, ..)\n"
430 			"  -A [-A]    run aaa or aaaa after loading each binary (see -C)\n"
431 			"  -b [bits]  specify register size for arch (16 (thumb), 32, 64, ..)\n"
432 			"  -B         output in binary diff (GDIFF)\n"
433 			"  -c         count of changes\n"
434 			"  -C         graphdiff code (columns: off-A, match-ratio, off-B) (see -A)\n"
435 			"  -d         use delta diffing\n"
436 			"  -D         show disasm instead of hexpairs\n"
437 			"  -e [k=v]   set eval config var value for all RCore instances\n"
438 			"  -g [sym|off1,off2]   graph diff of given symbol, or between two offsets\n"
439 			"  -G [cmd]   run an r2 command on every RCore instance created\n"
440 			"  -i         diff imports of target files (see -u, -U and -z)\n"
441 			"  -j         output in json format\n"
442 			"  -n         print bare addresses only (diff.bare=1)\n"
443                         "  -m [aditsjJ]  choose the graph output mode\n"
444 			"  -O         code diffing with opcode bytes only\n"
445 			"  -p         use physical addressing (io.va=0)\n"
446 			"  -q         quiet mode (disable colors, reduce output)\n"
447 			"  -r         output in radare commands\n"
448 			"  -s         compute edit distance (no substitution, Eugene W. Myers' O(ND) diff algorithm)\n"
449 			"  -ss        compute Levenshtein edit distance (substitution is allowed, O(N^2))\n"
450 			"  -S [name]  sort code diff (name, namelen, addr, size, type, dist) (only for -C or -g)\n"
451 			"  -t [0-100] set threshold for code diff (default is 70%%)\n"
452 			"  -x         show two column hexdump diffing\n"
453 			"  -X         show two column hexII diffing\n"
454 			"  -u         unified output (---+++)\n"
455 			"  -U         unified output using system 'diff'\n"
456 			"  -v         show version information\n"
457 			"  -V         be verbose (current only for -s)\n"
458 			"  -z         diff on extracted strings\n"
459 			"  -Z         diff code comparing zignatures\n\n"
460                        "Graph Output formats: (-m [mode])\n"
461 		        "  <blank/a>  Ascii art\n"
462 	                "  s          r2 commands\n"
463 		        "  d          Graphviz dot\n"
464 	                "  g          Graph Modelling Language (gml)\n"
465 		        "  j          json\n"
466 	                "  J          json with disarm\n"
467 		        "  k          SDB key-value\n"
468 	                "  t          Tiny ascii art\n"
469 		        "  i          Interactive ascii art\n");
470 	}
471 	return 1;
472 }
473 
474 #define DUMP_CONTEXT 2
dump_cols(ut8 * a,int as,ut8 * b,int bs,int w)475 static void dump_cols(ut8 *a, int as, ut8 *b, int bs, int w) {
476 	ut32 sz = R_MIN (as, bs);
477 	ut32 i, j;
478 	int ctx = DUMP_CONTEXT;
479 	int pad = 0;
480 	if (!a || !b || as < 0 || bs < 0) {
481 		return;
482 	}
483 	switch (w) {
484 	case 8:
485 		r_cons_printf ("  offset     0 1 2 3 4 5 6 7 01234567    0 1 2 3 4 5 6 7 01234567\n");
486 		break;
487 	case 16:
488 		r_cons_printf ("  offset     "
489 			"0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF    "
490 			"0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n");
491 		break;
492 	default:
493 		eprintf ("Invalid column width\n");
494 		return;
495 	}
496 	r_cons_break_push (NULL, NULL);
497 	for (i = 0; i < sz; i += w) {
498 		if (r_cons_is_breaked()) {
499 			break;
500 		}
501 		if (i + w >= sz) {
502 			pad = w - sz + i;
503 			w = sz - i;
504 		}
505 		bool eq = !memcmp (a + i, b + i, w);
506 		if (eq) {
507 			ctx--;
508 			if (ctx == -1) {
509 				r_cons_printf ("...\n");
510 				continue;
511 			}
512 			if (ctx < 0) {
513 				ctx = -1;
514 				continue;
515 			}
516 		} else {
517 			ctx = DUMP_CONTEXT;
518 		}
519 		r_cons_printf (eq? Color_GREEN: Color_RED);
520 		r_cons_printf ("0x%08x%c ", i, eq? ' ': '!');
521 		r_cons_printf (Color_RESET);
522 		for (j = 0; j < w; j++) {
523 			bool eq2 = a[i + j] == b[i + j];
524 			if (!eq) {
525 				r_cons_printf (eq2? Color_GREEN: Color_RED);
526 			}
527 			r_cons_printf ("%02x", a[i + j]);
528 			if (!eq) {
529 				r_cons_printf (Color_RESET);
530 			}
531 		}
532 		for (j = 0; j < pad; j++) {
533 			r_cons_printf ("  ");
534 		}
535 		r_cons_printf (" ");
536 		for (j = 0; j < w; j++) {
537 			bool eq2 = a[i + j] == b[i + j];
538 			if (!eq) {
539 				r_cons_printf (eq2? Color_GREEN: Color_RED);
540 			}
541 			r_cons_printf ("%c", IS_PRINTABLE (a[i + j])? a[i + j]: '.');
542 			if (!eq) {
543 				r_cons_printf (Color_RESET);
544 			}
545 		}
546 		for (j = 0; j < pad; j++) {
547 			r_cons_printf (" ");
548 		}
549 		r_cons_printf ("   ");
550 		for (j = 0; j < w; j++) {
551 			bool eq2 = a[i + j] == b[i + j];
552 			if (!eq) {
553 				r_cons_printf (eq2? Color_GREEN: Color_RED);
554 			}
555 			r_cons_printf ("%02x", b[i + j]);
556 			if (!eq) {
557 				r_cons_printf (Color_RESET);
558 			}
559 		}
560 		for (j = 0; j < pad; j++) {
561 			r_cons_printf ("  ");
562 		}
563 		r_cons_printf (" ");
564 		for (j = 0; j < w; j++) {
565 			bool eq2 = a[i + j] == b[i + j];
566 			if (!eq) {
567 				r_cons_printf (eq2? Color_GREEN: Color_RED);
568 			}
569 			r_cons_printf ("%c", IS_PRINTABLE (b[i + j])? b[i + j]: '.');
570 			if (!eq) {
571 				r_cons_printf (Color_RESET);
572 			}
573 		}
574 		r_cons_printf ("\n");
575 		r_cons_flush ();
576 	}
577 	r_cons_break_end ();
578 	r_cons_printf ("\n"Color_RESET);
579 	r_cons_flush ();
580 	if (as != bs) {
581 		r_cons_printf ("...\n");
582 	}
583 }
584 
dump_cols_hexii(ut8 * a,int as,ut8 * b,int bs,int w)585 static void dump_cols_hexii(ut8 *a, int as, ut8 *b, int bs, int w) {
586 	bool spacy = false;
587 	ut32 sz = R_MIN (as, bs);
588 	ut32 i, j;
589 	int ctx = DUMP_CONTEXT;
590 	int pad = 0;
591 	if (!a || !b || as < 0 || bs < 0) {
592 		return;
593 	}
594 	PrintfCallback p = r_cons_printf;
595 	r_cons_break_push (NULL, NULL);
596 	for (i = 0; i < sz; i += w) {
597 		if (r_cons_is_breaked()) {
598 			break;
599 		}
600 		if (i + w >= sz) {
601 			pad = w - sz + i;
602 			w = sz - i;
603 		}
604 		bool eq = !memcmp (a + i, b + i, w);
605 		if (eq) {
606 			ctx--;
607 			if (ctx == -1) {
608 				r_cons_printf ("...\n");
609 				continue;
610 			}
611 			if (ctx < 0) {
612 				ctx = -1;
613 				continue;
614 			}
615 		} else {
616 			ctx = DUMP_CONTEXT;
617 		}
618 		r_cons_printf (eq? Color_GREEN: Color_RED);
619 		r_cons_printf ("0x%08x%c ", i, eq? ' ': '!');
620 		r_cons_printf (Color_RESET);
621 		for (j = 0; j < w; j++) {
622 			bool eq2 = a[i + j] == b[i + j];
623 			if (!eq) {
624 				r_cons_printf (eq2? Color_GREEN: Color_RED);
625 			}
626 			ut8 ch = a[i + j];
627 			if (spacy) {
628 				p (" ");
629 			}
630 			if (ch == 0x00) {
631 				p ("  ");
632 			} else if (ch == 0xff) {
633 				p ("##");
634 			} else if (IS_PRINTABLE (ch)) {
635 				p (".%c", ch);
636 			} else {
637 				p ("%02x", ch);
638 			}
639 			if (!eq) {
640 				r_cons_printf (Color_RESET);
641 			}
642 		}
643 		for (j = 0; j < pad; j++) {
644 			r_cons_printf ("  ");
645 		}
646 		for (j = 0; j < pad; j++) {
647 			r_cons_printf (" ");
648 		}
649 		r_cons_printf ("   ");
650 		for (j = 0; j < w; j++) {
651 			bool eq2 = a[i + j] == b[i + j];
652 			if (!eq) {
653 				r_cons_printf (eq2? Color_GREEN: Color_RED);
654 			}
655 			ut8 ch = b[i + j];
656 			if (spacy) {
657 				p (" ");
658 			}
659 			if (ch == 0x00) {
660 				p ("  ");
661 			} else if (ch == 0xff) {
662 				p ("##");
663 			} else if (IS_PRINTABLE (ch)) {
664 				p (".%c", ch);
665 			} else {
666 				p ("%02x", ch);
667 			}
668 			if (!eq) {
669 				r_cons_printf (Color_RESET);
670 			}
671 		}
672 		for (j = 0; j < pad; j++) {
673 			r_cons_printf ("  ");
674 		}
675 		r_cons_printf ("\n");
676 		r_cons_flush ();
677 	}
678 	r_cons_break_end ();
679 	r_cons_printf ("\n"Color_RESET);
680 	r_cons_flush ();
681 	if (as != bs) {
682 		r_cons_printf ("...\n");
683 	}
684 }
685 
handle_sha256(const ut8 * block,int len)686 static char *handle_sha256(const ut8 *block, int len) {
687 	int i = 0;
688 	char *p = malloc (128);
689 	RHash *ctx = r_hash_new (true, R_HASH_SHA256);
690 	const ut8 *c = r_hash_do_sha256 (ctx, block, len);
691 	if (!c) {
692 		r_hash_free (ctx);
693 		free (p);
694 		return NULL;
695 	}
696 	char *r = p;
697 	for (i = 0; i < R_HASH_SIZE_SHA256; i++) {
698 		snprintf (r + (i * 2), 3, "%02x", c[i]);
699 	}
700 	r_hash_free (ctx);
701 	return p;
702 }
703 
slurp(RadiffOptions * ro,RCore ** c,const char * file,size_t * sz)704 static ut8 *slurp(RadiffOptions *ro, RCore **c, const char *file, size_t *sz) {
705 	RIODesc *d;
706 	RIO *io;
707 	if (c && file && strstr (file, "://")) {
708 		ut8 *data = NULL;
709 		ut64 size;
710 		if (!*c) {
711 			*c = opencore (ro, NULL);
712 		}
713 		if (!*c) {
714 			eprintf ("opencore failed\n");
715 			return NULL;
716 		}
717 		io = (*c)->io;
718 		d = r_io_open (io, file, 0, 0);
719 		if (!d) {
720 			return NULL;
721 		}
722 		size = r_io_size (io);
723 		if (size > 0 && size < ST32_MAX) {
724 			data = calloc (1, size);
725 			if (r_io_read_at (io, 0, data, size)) {
726 				if (sz) {
727 					*sz = size;
728 				}
729 			} else {
730 				eprintf ("slurp: read error\n");
731 				R_FREE (data);
732 			}
733 		} else {
734 			eprintf ("slurp: Invalid file size\n");
735 		}
736 		r_io_desc_close (d);
737 		return data;
738 	}
739 	return (ut8 *) r_file_slurp (file, sz);
740 }
741 
import_cmp(const RBinImport * a,const RBinImport * b)742 static int import_cmp(const RBinImport *a, const RBinImport *b) {
743 	return strcmp (a->name, b->name);
744 }
745 
get_imports(RCore * c,int * len)746 static ut8 *get_imports(RCore *c, int *len) {
747 	RListIter *iter;
748 	RBinImport *str, *old = NULL;
749 	ut8 *buf, *ptr;
750 
751 	if (!c || !len) {
752 		return NULL;
753 	}
754 
755 	RList *list = r_bin_get_imports (c->bin);
756 	r_list_sort (list, (RListComparator) import_cmp);
757 
758 	*len = 0;
759 
760 	r_list_foreach (list, iter, str) {
761 		if (!old || (old && import_cmp (old, str) != 0)) {
762 			*len += strlen (str->name) + 1;
763 			old = str;
764 		}
765 	}
766 	ptr = buf = malloc (*len + 1);
767 	if (!ptr) {
768 		return NULL;
769 	}
770 
771 	old = NULL;
772 
773 	r_list_foreach (list, iter, str) {
774 		if (old && !import_cmp (old, str)) {
775 			continue;
776 		}
777 		int namelen = strlen (str->name);
778 		memcpy (ptr, str->name, namelen);
779 		ptr += namelen;
780 		*ptr++ = '\n';
781 		old = str;
782 	}
783 	*ptr = 0;
784 
785 	*len = strlen ((const char *) buf);
786 	return buf;
787 }
788 
bs_cmp(const RBinString * a,const RBinString * b)789 static int bs_cmp(const RBinString *a, const RBinString *b) {
790 	int diff = a->length - b->length;
791 	return diff == 0? strncmp (a->string, b->string, a->length): diff;
792 }
793 
get_strings(RCore * c,int * len)794 static ut8 *get_strings(RCore *c, int *len) {
795 	RList *list = r_bin_get_strings (c->bin);
796 	RListIter *iter;
797 	RBinString *str, *old = NULL;
798 	ut8 *buf, *ptr;
799 
800 	r_list_sort (list, (RListComparator) bs_cmp);
801 
802 	*len = 0;
803 
804 	r_list_foreach (list, iter, str) {
805 		if (!old || (old && bs_cmp (old, str) != 0)) {
806 			*len += str->length + 1;
807 			old = str;
808 		}
809 	}
810 
811 	ptr = buf = malloc (*len + 1);
812 	if (!ptr) {
813 		return NULL;
814 	}
815 
816 	old = NULL;
817 
818 	r_list_foreach (list, iter, str) {
819 		if (old && bs_cmp (old, str) == 0) {
820 			continue;
821 		}
822 		memcpy (ptr, str->string, str->length);
823 		ptr += str->length;
824 		*ptr++ = '\n';
825 		old = str;
826 	}
827 	*ptr = 0;
828 
829 	*len = strlen ((const char *) buf);
830 	return buf;
831 }
832 
get_graph_commands(RCore * c,ut64 off)833 static char *get_graph_commands(RCore *c, ut64 off) {
834         bool tmp_html = r_cons_singleton ()->is_html;
835         r_cons_singleton ()->is_html = false;
836         r_cons_push ();
837         r_core_anal_graph (c, off, R_CORE_ANAL_GRAPHBODY | R_CORE_ANAL_GRAPHDIFF |  R_CORE_ANAL_STAR);
838         const char *static_str = r_cons_get_buffer ();
839         char *retstr = strdup (r_str_get (static_str));
840         r_cons_pop ();
841         r_cons_echo (NULL);
842         r_cons_singleton ()->is_html = tmp_html;
843         return retstr;
844 }
845 
__generate_graph(RCore * c,ut64 off)846 static void __generate_graph(RCore *c, ut64 off) {
847         r_return_if_fail (c);
848         char *ptr = get_graph_commands (c, off);
849 	char *str = ptr;
850         r_cons_break_push (NULL, NULL);
851         if (str) {
852                 for (;;) {
853                         if (r_cons_is_breaked ()) {
854                                 break;
855                         }
856                         char *eol = strchr (ptr, '\n');
857                         if (eol) {
858                                 *eol = '\0';
859                         }
860                         if (*ptr) {
861                                 char *p = strdup (ptr);
862                                 if (!p) {
863                                         free (str);
864                                         return;
865                                 }
866                                 r_core_cmd0 (c, p);
867                                 free (p);
868                         }
869                         if (!eol) {
870                                 break;
871                         }
872                         ptr = eol + 1;
873                 }
874 		free (str);
875         }
876         r_cons_break_pop ();
877 }
878 
__print_diff_graph(RCore * c,ut64 off,int gmode)879 static void __print_diff_graph(RCore *c, ut64 off, int gmode) {
880         int opts = R_CORE_ANAL_GRAPHBODY | R_CORE_ANAL_GRAPHDIFF;
881         int use_utf8 = r_config_get_i (c->config, "scr.utf8");
882         r_agraph_reset(c->graph);
883         switch (gmode) {
884         case GRAPH_DOT_MODE:
885                 r_core_anal_graph (c, off, opts);
886                 break;
887         case GRAPH_STAR_MODE:
888                 r_core_anal_graph (c, off, opts |  R_CORE_ANAL_STAR);
889                 break;
890         case GRAPH_TINY_MODE:
891                 __generate_graph (c, off);
892                 r_core_agraph_print (c, use_utf8, "t");
893                 break;
894         case GRAPH_INTERACTIVE_MODE:
895                 __generate_graph (c, off);
896                 r_core_agraph_print (c, use_utf8, "v");
897                 r_cons_reset_colors ();
898                 break;
899         case GRAPH_SDB_MODE:
900                 __generate_graph (c, off);
901                 r_core_agraph_print (c, use_utf8, "k");
902                 break;
903         case GRAPH_GML_MODE:
904                 __generate_graph (c, off);
905                 r_core_agraph_print (c, use_utf8, "g");
906                 break;
907         case GRAPH_JSON_MODE:
908                 r_core_anal_graph (c, off, opts | R_CORE_ANAL_JSON);
909                 break;
910         case GRAPH_JSON_DIS_MODE:
911                 r_core_anal_graph (c, off, opts | R_CORE_ANAL_JSON | R_CORE_ANAL_JSON_FORMAT_DISASM);
912                 break;
913         case GRAPH_DEFAULT_MODE:
914         default:
915                 __generate_graph (c, off);
916                 r_core_agraph_print (c, use_utf8, "");
917                 r_cons_reset_colors ();
918         	break;
919         }
920 }
921 
radiff_options_init(RadiffOptions * ro)922 static void radiff_options_init(RadiffOptions *ro) {
923 	memset (ro, 0, sizeof (RadiffOptions));
924 	ro->threshold = -1;
925 	ro->useva = true;
926 	ro->evals = r_list_newf (NULL);
927 	ro->mode = MODE_DIFF;
928 	ro->gmode = GRAPH_DEFAULT_MODE;
929 }
930 
radiff_options_fini(RadiffOptions * ro)931 static void radiff_options_fini(RadiffOptions *ro) {
932 	r_list_free (ro->evals);
933 	r_core_free (ro->core);
934 	r_cons_free ();
935 }
936 
fileobj(RadiffOptions * ro,const char * ro_file,const ut8 * buf,size_t sz)937 static void fileobj(RadiffOptions *ro, const char *ro_file, const ut8 *buf, size_t sz) {
938 	PJ *pj = ro->pj;
939 	pj_o (pj);
940 	pj_ks (pj, "filename", ro_file);
941 	pj_kn (pj, "size", sz);
942 	char *hasha = handle_sha256 (buf, (int)sz);
943 	pj_ks (pj, "sha256", hasha);
944 	free (hasha);
945 	pj_end (pj);
946 }
947 
r_main_radiff2(int argc,const char ** argv)948 R_API int r_main_radiff2(int argc, const char **argv) {
949 	RadiffOptions ro;
950 	const char *columnSort = NULL;
951 	const char *addr = NULL;
952 	RCore *c = NULL, *c2 = NULL;
953 	ut8 *bufa = NULL, *bufb = NULL;
954 	int o, /*diffmode = 0,*/ delta = 0;
955 	ut64 sza = 0, szb = 0;
956 	double sim = 0.0;
957 	RDiff *d;
958 	RGetopt opt;
959 
960 	radiff_options_init (&ro);
961 
962 	r_getopt_init (&opt, argc, argv, "Aa:b:BCDe:npg:m:G:OijrhcdsS:uUvVxXt:zqZ");
963 	while ((o = r_getopt_next (&opt)) != -1) {
964 		switch (o) {
965 		case 'a':
966 			ro.arch = opt.arg;
967 			break;
968 		case 'A':
969 			ro.anal_all++;
970 			break;
971 		case 'b':
972 			ro.bits = atoi (opt.arg);
973 			break;
974 		case 'B':
975 			ro.diffmode = 'B';
976 			break;
977 		case 'e':
978 			r_list_append (ro.evals, (void*)opt.arg);
979 			break;
980 		case 'p':
981 			ro.useva = false;
982 			break;
983 		case 'r':
984 			ro.diffmode = 'r';
985 			break;
986 		case 'g':
987 			ro.mode = MODE_GRAPH;
988 			addr = opt.arg;
989 			break;
990 		case 'm':{
991 		        const char *tmp = opt.arg;
992 		        switch (tmp[0]) {
993 	                case 'i': ro.gmode = GRAPH_INTERACTIVE_MODE; break;
994 	                case 'k': ro.gmode = GRAPH_SDB_MODE; break;
995 	                case 'j': ro.gmode = GRAPH_JSON_MODE; break;
996 	                case 'J': ro.gmode = GRAPH_JSON_DIS_MODE; break;
997 	                case 't': ro.gmode = GRAPH_TINY_MODE; break;
998 	                case 'd': ro.gmode = GRAPH_DOT_MODE; break;
999 	                case 's': ro.gmode = GRAPH_STAR_MODE; break;
1000 	                case 'g': ro.gmode = GRAPH_GML_MODE; break;
1001 	                case 'a':
1002                         default: ro.gmode = GRAPH_DEFAULT_MODE; break;
1003 		        }
1004 		}       break;
1005 		case 'G':
1006 			ro.runcmd = opt.arg;
1007 			break;
1008 		case 'c':
1009 			ro.showcount = 1;
1010 			break;
1011 		case 'C':
1012 			ro.mode = MODE_CODE;
1013 			break;
1014 		case 'i':
1015 			ro.mode = MODE_DIFF_IMPORTS;
1016 			break;
1017 		case 'n':
1018 			ro.showbare = true;
1019 			break;
1020 		case 'O':
1021 			ro.diffops = 1;
1022 			break;
1023 		case 't':
1024 			ro.threshold = atoi (opt.arg);
1025 			printf ("%s\n", opt.arg);
1026 			break;
1027 		case 'd':
1028 			delta = 1;
1029 			break;
1030 		case 'D':
1031 			if (ro.disasm) {
1032 				ro.pdc = true;
1033 				ro.disasm = false;
1034 				ro.mode = MODE_CODE;
1035 			} else {
1036 				ro.disasm = true;
1037 			}
1038 			break;
1039 		case 'h':
1040 			return show_help (1);
1041 		case 's':
1042 			if (ro.mode == MODE_DIST_MYERS) {
1043 				ro.mode = MODE_DIST_LEVENSHTEIN;
1044 			} else {
1045 				ro.mode = MODE_DIST_MYERS;
1046 			}
1047 			break;
1048 		case 'S':
1049 			columnSort = opt.arg;
1050 			break;
1051 		case 'x':
1052 			ro.mode = MODE_COLS;
1053 			break;
1054 		case 'X':
1055 			ro.mode = MODE_COLSII;
1056 			break;
1057 		case 'u':
1058 			ro.diffmode = 'u';
1059 			break;
1060 		case 'U':
1061 			ro.diffmode = 'U';
1062 			break;
1063 		case 'v':
1064 			return r_main_version_print ("radiff2");
1065 		case 'q':
1066 			ro.quiet = true;
1067 			break;
1068 		case 'V':
1069 			ro.verbose = true;
1070 			break;
1071 		case 'j':
1072 			ro.diffmode = 'j';
1073 			ro.pj = pj_new ();
1074 			break;
1075 		case 'z':
1076 			ro.mode = MODE_DIFF_STRS;
1077 			break;
1078 		case 'Z':
1079 			ro.zignatures = true;
1080 			break;
1081 		default:
1082 			return show_help (0);
1083 		}
1084 	}
1085 
1086 	if (argc < 3 || opt.ind + 2 > argc) {
1087 		return show_help (0);
1088 	}
1089 	ro.file = (opt.ind < argc)? argv[opt.ind]: NULL;
1090 	ro.file2 = (opt.ind + 1 < argc)? argv[opt.ind + 1]: NULL;
1091 
1092 	if (R_STR_ISEMPTY (ro.file) || R_STR_ISEMPTY (ro.file2)) {
1093 		eprintf ("Cannot open empty path\n");
1094 		return 1;
1095 	}
1096 
1097 	switch (ro.mode) {
1098 	case MODE_GRAPH:
1099 	case MODE_CODE:
1100 	case MODE_DIFF_STRS:
1101 	case MODE_DIFF_IMPORTS:
1102 		c = opencore (&ro, ro.file);
1103 		if (!c) {
1104 			eprintf ("Cannot open '%s'\n", r_str_getf (ro.file));
1105 		}
1106 		c2 = opencore (&ro, ro.file2);
1107 		if (!c2) {
1108 			eprintf ("Cannot open '%s'\n", r_str_getf (ro.file2));
1109 		}
1110 		if (!c || !c2) {
1111 			return 1;
1112 		}
1113 		c->c2 = c2;
1114 		c2->c2 = c;
1115 		r_core_parse_radare2rc (c);
1116 		if (ro.arch) {
1117 			r_config_set (c->config, "asm.arch", ro.arch);
1118 			r_config_set (c2->config, "asm.arch", ro.arch);
1119 		}
1120 		if (ro.bits) {
1121 			r_config_set_i (c->config, "asm.bits", ro.bits);
1122 			r_config_set_i (c2->config, "asm.bits", ro.bits);
1123 		}
1124 		if (columnSort) {
1125 			r_config_set (c->config, "diff.sort", columnSort);
1126 			r_config_set (c2->config, "diff.sort", columnSort);
1127 		}
1128 		r_config_set_i (c->config, "diff.bare", ro.showbare);
1129 		r_config_set_i (c2->config, "diff.bare", ro.showbare);
1130 		r_anal_diff_setup_i (c->anal, ro.diffops, ro.threshold, ro.threshold);
1131 		r_anal_diff_setup_i (c2->anal, ro.diffops, ro.threshold, ro.threshold);
1132 		if (ro.pdc) {
1133 			if (!addr) {
1134 				//addr = "entry0";
1135 				addr = "main";
1136 			}
1137 			/* should be in mode not in bool pdc */
1138 			r_config_set_i (c->config, "scr.color", COLOR_MODE_DISABLED);
1139 			r_config_set_i (c2->config, "scr.color", COLOR_MODE_DISABLED);
1140 
1141 			ut64 addra = r_num_math (c->num, addr);
1142 			bufa = (ut8 *) r_core_cmd_strf (c, "af;pdc @ 0x%08"PFMT64x, addra);
1143 			sza = (ut64)strlen ((const char *) bufa);
1144 
1145 			ut64 addrb = r_num_math (c2->num, addr);
1146 			bufb = (ut8 *) r_core_cmd_strf (c2, "af;pdc @ 0x%08"PFMT64x, addrb);
1147 			szb = (ut64)strlen ((const char *) bufb);
1148 			ro.mode = MODE_DIFF;
1149 		} else if (ro.mode == MODE_GRAPH) {
1150 			int depth = r_config_get_i (c->config, "anal.depth");
1151 			if (depth < 1) {
1152 				depth = 64;
1153 			}
1154 			char *words = strdup (r_str_get_fail (addr, "0"));
1155 			char *second = strchr (words, ',');
1156 			if (second) {
1157 				*second++ = 0;
1158 				ut64 off = r_num_math (c->num, words);
1159 				// define the same function at each offset
1160 				r_core_anal_fcn (c, off, UT64_MAX, R_ANAL_REF_TYPE_NULL, depth);
1161 				r_core_anal_fcn (c2, r_num_math (c2->num, second),
1162 					UT64_MAX, R_ANAL_REF_TYPE_NULL, depth);
1163 				r_core_gdiff (c, c2);
1164 				__print_diff_graph (c, off, ro.gmode);
1165 			} else {
1166 				r_core_anal_fcn (c, r_num_math (c->num, words),
1167 					UT64_MAX, R_ANAL_REF_TYPE_NULL, depth);
1168 				r_core_anal_fcn (c2, r_num_math (c2->num, words),
1169 					UT64_MAX, R_ANAL_REF_TYPE_NULL, depth);
1170 				r_core_gdiff (c, c2);
1171 				__print_diff_graph (c, r_num_math (c->num, addr), ro.gmode);
1172 			}
1173 			free (words);
1174 		} else if (ro.mode == MODE_CODE) {
1175 			if (ro.zignatures) {
1176 				r_core_cmd0 (c, "z~?");
1177 				r_core_cmd0 (c2, "z~?");
1178 				r_core_zdiff (c, c2);
1179 			} else {
1180 				r_core_gdiff (c, c2);
1181 				r_core_diff_show (c, c2);
1182 			}
1183 		} else if (ro.mode == MODE_DIFF_IMPORTS) {
1184 			int sz;
1185 			bufa = get_imports (c, &sz);
1186 			sza = sz;
1187 			bufb = get_imports (c2, &sz);
1188 			szb = sz;
1189 		} else if (ro.mode == MODE_DIFF_STRS) {
1190 			int sz;
1191 			bufa = get_strings (c, &sz);
1192 			sza = sz;
1193 			bufb = get_strings (c2, &sz);
1194 			szb = sz;
1195 		}
1196 		if (ro.mode == MODE_CODE || ro.mode == MODE_GRAPH) {
1197 			r_cons_flush ();
1198 		}
1199 		r_core_free (c);
1200 		r_core_free (c2);
1201 
1202 		if (ro.mode == MODE_CODE || ro.mode == MODE_GRAPH) {
1203 			return 0;
1204 		}
1205 		break;
1206 	default: {
1207 		size_t fsz;
1208 		bufa = slurp (&ro, &c, ro.file, &fsz);
1209 		sza = fsz;
1210 		if (!bufa) {
1211 			eprintf ("radiff2: Cannot open %s\n", r_str_getf (ro.file));
1212 			return 1;
1213 		}
1214 		bufb = slurp (&ro, &c, ro.file2, &fsz);
1215 		szb = fsz;
1216 		if (!bufb) {
1217 			eprintf ("radiff2: Cannot open: %s\n", r_str_getf (ro.file2));
1218 			free (bufa);
1219 			return 1;
1220 		}
1221 		if (sza != szb) {
1222 			eprintf ("File size differs %"PFMT64u" vs %"PFMT64u"\n", (ut64)sza, (ut64)szb);
1223 		}
1224 		break;
1225 	}
1226 	}
1227 
1228 	// initialize RCons
1229 	(void)r_cons_new ();
1230 
1231 	switch (ro.mode) {
1232 	case MODE_COLSII:
1233 		if (!c && !r_list_empty (ro.evals)) {
1234 			c = opencore (&ro, NULL);
1235 		}
1236 		dump_cols_hexii (bufa, (int)sza, bufb, (int)szb, (r_cons_get_size (NULL) > 112)? 16: 8);
1237 		break;
1238 	case MODE_COLS:
1239 		if (!c && !r_list_empty (ro.evals)) {
1240 			c = opencore (&ro, NULL);
1241 		}
1242 		dump_cols (bufa, (int)sza, bufb, (int)szb, (r_cons_get_size (NULL) > 112)? 16: 8);
1243 		break;
1244 	case MODE_DIFF:
1245 	case MODE_DIFF_STRS:
1246 	case MODE_DIFF_IMPORTS:
1247 		d = r_diff_new ();
1248 		r_diff_set_delta (d, delta);
1249 		if (ro.diffmode == 'j') {
1250 			pj_o (ro.pj);
1251 			pj_ka (ro.pj, "files");
1252 			fileobj (&ro, ro.file, bufa, sza);
1253 			fileobj (&ro, ro.file2, bufb, szb);
1254 			pj_end (ro.pj);
1255 			pj_ka (ro.pj, "changes");
1256 		}
1257 		if (ro.diffmode == 'B') {
1258 			(void) write (1, "\xd1\xff\xd1\xff\x04", 5);
1259 		}
1260 		if (ro.diffmode == 'U') {
1261 			char *res = r_diff_buffers_unified (d, bufa, (int)sza, bufb, (int)szb);
1262 			if (res) {
1263 				printf ("%s", res);
1264 				free (res);
1265 			}
1266 		} else if (ro.diffmode == 'B') {
1267 			r_diff_set_callback (d, &bcb, &ro);
1268 			r_diff_buffers (d, bufa, (ut32)sza, bufb, (ut32)szb);
1269 			(void) write (1, "\x00", 1);
1270 		} else {
1271 			r_diff_set_callback (d, &cb, &ro);
1272 			r_diff_buffers (d, bufa, (ut32)sza, bufb, (ut32)szb);
1273 		}
1274 		if (ro.diffmode == 'j') {
1275 			pj_end (ro.pj);
1276 		}
1277 		r_diff_free (d);
1278 		break;
1279 	case MODE_DIST_MYERS:
1280 	case MODE_DIST_LEVENSHTEIN:
1281 		{
1282 			RDiff *d = r_diff_new ();
1283 			if (d) {
1284 				d->verbose = ro.verbose;
1285 				if (ro.mode == MODE_DIST_MYERS) {
1286 					d->type = 'm';
1287 				} else {
1288 					d->type = 'l';
1289 				}
1290 				r_diff_buffers_distance (d, bufa, (ut32)sza, bufb, (ut32)szb, &ro.count, &sim);
1291 				r_diff_free (d);
1292 			}
1293 		}
1294 		printf ("similarity: %.3f\n", sim);
1295 		printf ("distance: %d\n", ro.count);
1296 		break;
1297 	}
1298 
1299 	if (ro.diffmode == 'j' && ro.showcount) {
1300 		pj_kd (ro.pj, "count", ro.count);
1301 	} else if (ro.showcount && ro.diffmode != 'j') {
1302 		printf ("%d\n", ro.count);
1303 	}
1304 	if (ro.pj) {
1305 		pj_end (ro.pj);
1306 		char *s = pj_drain (ro.pj);
1307 		printf ("%s\n", s);
1308 		free (s);
1309 		ro.pj = NULL;
1310 	}
1311 	free (bufa);
1312 	free (bufb);
1313 	radiff_options_fini (&ro);
1314 
1315 	return 0;
1316 }
1317