Rizin
unix-like reverse engineering framework and cli tools
unified_diff.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
2 // SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 #include <rz_cons.h>
6 
7 #define Color_RANGE Color_BBLUE
8 #define Color_INSERT Color_BGREEN
9 #define Color_DELETE Color_BRED
10 #define Color_BGINSERT "\x1b[48;5;22m"
11 #define Color_BGDELETE "\x1b[48;5;52m"
12 
13 #define FAST_MOD2(x, y) ((x) & (y - 1))
14 #define FAST_MOD64(x) FAST_MOD2(x, 64)
15 #define DIFF_COLOR(prefix) (prefix == '+' ? Color_INSERT : (prefix == '-' ? Color_DELETE : ""))
16 #define DIFF_BGCOLOR(prefix) (prefix == '+' ? Color_BGINSERT : (prefix == '-' ? Color_BGDELETE : ""))
17 
18 static inline ut32 count_newlines(RzDiff *diff, const void *array, st32 beg, st32 end) {
19  RzDiffMethodElemAt elem_at = diff->methods.elem_at;
20  RzDiffMethodStringify stringify = diff->methods.stringify;
21  int len = 0;
22  ut32 count = 0;
23  const char *p;
24  const void *elem;
25  RzStrBuf tmp;
26 
27  for (st32 i = beg; i < end; ++i) {
29  elem = elem_at(array, i);
30  stringify(elem, &tmp);
32  p = rz_strbuf_get(&tmp);
33  if (len > 0 && p[len - 1] == '\n') {
34  count++;
35  }
37  }
38  return count;
39 }
40 
41 static inline void diff_unified_append_ranges(RzList *opcodes, RzStrBuf *sb, bool color) {
42  const char *color_beg = color ? Color_RANGE : "";
43  const char *color_end = color ? Color_RESET : "";
44 
45  RzDiffOp *first = rz_list_first(opcodes);
46  RzDiffOp *last = rz_list_last(opcodes);
47  st32 a_len = last->a_end - first->a_beg;
48  st32 b_len = last->b_end - first->b_beg;
49 
50  rz_strbuf_appendf(sb, "%s@@ -%d,%d +%d,%d @@%s\n", color_beg, first->a_beg + 1, a_len, first->b_beg + 1, b_len, color_end);
51 }
52 
53 static inline void diff_unified_json_ranges(RzList *opcodes, PJ *pj) {
54  RzDiffOp *first = rz_list_first(opcodes);
55  RzDiffOp *last = rz_list_last(opcodes);
56  st32 a_len = last->a_end - first->a_beg;
57  st32 b_len = last->b_end - first->b_beg;
58 
59  pj_ka(pj, "from");
60  pj_N(pj, first->a_beg + 1);
61  pj_N(pj, a_len);
62  pj_end(pj);
63 
64  pj_ka(pj, "to");
65  pj_N(pj, first->b_beg + 1);
66  pj_N(pj, b_len);
67  pj_end(pj);
68 }
69 
70 static inline void diff_unified_append_data(RzDiff *diff, const void *array, st32 beg, st32 end, RzStrBuf *sb, char prefix, bool color) {
71  RzDiffMethodElemAt elem_at = diff->methods.elem_at;
72  RzDiffMethodStringify stringify = diff->methods.stringify;
73  int len = 0;
74  ut32 count = 0;
75  const char *p;
76  const void *elem;
77  RzStrBuf tmp;
78  bool newline = false;
79  bool is_bytes = DIFF_IS_BYTES_METHOD(diff->methods);
80 
81  if (beg < 0) {
82  beg = 0;
83  }
84 
85  const char *bcol = color ? DIFF_COLOR(prefix) : "";
86  const char *ecol = color ? (Color_RESET) : "";
87 
88  rz_strbuf_appendf(sb, "%s%c", bcol, prefix);
89  for (st32 i = beg; i < end; ++i) {
90  if (newline || (is_bytes && count > 0 && !FAST_MOD64(count))) {
91  rz_strbuf_appendf(sb, "%s\n%s%c", ecol, bcol, prefix);
92  newline = false;
93  }
95  elem = elem_at(array, i);
96  stringify(elem, &tmp);
98  p = rz_strbuf_get(&tmp);
99  count += len;
100  if (len > 0 && p[len - 1] == '\n') {
101  len--;
102  newline = true;
103  }
106  }
107  rz_strbuf_appendf(sb, "%s\n", ecol);
108 }
109 
110 // Assumes that color is true, diffing lines, op->type is RZ_DIFF_OP_REPLACE
111 // and that the number of inserted lines is equal to the number of deleted
112 // lines.
113 static inline void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *sb, char del_prefix, char ins_prefix) {
114  RzDiffMethodElemAt elem_at = diff->methods.elem_at;
115  RzDiffMethodStringify stringify = diff->methods.stringify;
116  int len = 0;
117  const char *p;
118  const void *elem;
119  RzStrBuf tmp, tmp2;
120  const char *ecol = Color_RESET;
121  const char *ebgcol = Color_RESET_BG;
122 
123  const void *a_array = diff->a;
124  st32 a_beg = op->a_beg;
125  if (a_beg < 0) {
126  a_beg = 0;
127  }
128  st32 a_end = op->a_end;
129 
130  const void *b_array = diff->b;
131  st32 b_beg = op->b_beg;
132  if (b_beg < 0) {
133  b_beg = 0;
134  }
135  st32 b_end = op->b_end;
136 
137  ut32 num_nl = count_newlines(diff, a_array, a_beg, a_end);
138  // + 1 just in case there's no nl at end
139  ut32 num_bounds = num_nl + 1;
140  st32 *char_bounds = malloc(sizeof(st32) * 2 * num_bounds);
141  if (!char_bounds) {
142  return;
143  }
144  for (ut32 i = 0; i < num_bounds; i++) {
145  char_bounds[i * 2] = char_bounds[i * 2 + 1] = -1;
146  }
147 
148  // Fill char_bounds array
149  ut32 bounds_idx = 0;
150  bool newline = false;
151  st32 i = a_beg;
152  st32 j = b_beg;
153  for (; i < a_end; ++i) {
154  if (newline) {
155  bounds_idx++;
156  newline = false;
157  }
159  elem = elem_at(a_array, i);
160  stringify(elem, &tmp);
162  p = rz_strbuf_get(&tmp);
163  if (len > 0 && p[len - 1] == '\n') {
164  len--;
165  newline = true;
166  }
167 
168  int len_b = 0;
169  const void *elem_b;
170  const char *p_b;
171 
172  for (; j < b_end; ++j) {
173  rz_strbuf_init(&tmp2);
174  elem_b = elem_at(b_array, j);
175  stringify(elem_b, &tmp2);
176  len_b = rz_strbuf_length(&tmp2);
177  p_b = rz_strbuf_get(&tmp2);
178  if (len_b > 0 && p_b[len_b - 1] == '\n') {
179  len_b--;
180  }
181 
182  if (len && len_b && (p[0] == p_b[0] || p[len - 1] == p_b[len_b - 1])) {
183  // Get left bound.
184  st32 left = 0;
185  for (; left < RZ_MIN(len, len_b) && p[left] == p_b[left]; left++)
186  ;
187  char_bounds[bounds_idx * 2] = left;
188  // Get right bound (offset). "- left" chops off
189  // the left portion that has already matched.
190  st32 right = 0;
191  for (; right < RZ_MIN(len, len_b) - left && p[len - 1 - right] == p_b[len_b - 1 - right];
192  right++)
193  ;
194  char_bounds[bounds_idx * 2 + 1] = right;
195  }
196 
197  rz_strbuf_fini(&tmp2);
198  ++j;
199  break;
200  }
202  }
203 
204  // Show deleted lines
205  char prefix = del_prefix;
206  const char *bcol = DIFF_COLOR(prefix);
207  const char *bbgcol = DIFF_BGCOLOR(prefix);
208  newline = false;
209  bounds_idx = 0;
210 
211  rz_strbuf_appendf(sb, "%s%c", bcol, prefix);
212  for (st32 i = a_beg; i < a_end; ++i) {
213  if (newline) {
214  rz_strbuf_appendf(sb, "%s\n%s%c", ecol, bcol, prefix);
215  newline = false;
216  bounds_idx++;
217  }
219  elem = elem_at(a_array, i);
220  stringify(elem, &tmp);
222  p = rz_strbuf_get(&tmp);
223  if (len > 0 && p[len - 1] == '\n') {
224  len--;
225  newline = true;
226  }
227  st32 left = char_bounds[bounds_idx * 2];
228  st32 right = char_bounds[bounds_idx * 2 + 1];
229  if (left < 0 || right < 0 || len - right < left) {
231  } else {
232  rz_strbuf_append_n(sb, p, left);
233  rz_strbuf_append(sb, bbgcol);
234  rz_strbuf_append_n(sb, p + left, len - right - left);
235  rz_strbuf_append(sb, ebgcol);
236  rz_strbuf_append_n(sb, p + len - right, right);
237  }
239  }
240  rz_strbuf_appendf(sb, "%s\n", ecol);
241 
242  // Show inserted lines
243  prefix = ins_prefix;
244  bcol = DIFF_COLOR(prefix);
245  bbgcol = DIFF_BGCOLOR(prefix);
246  newline = false;
247  bounds_idx = 0;
248 
249  rz_strbuf_appendf(sb, "%s%c", bcol, prefix);
250  for (st32 i = b_beg; i < b_end; ++i) {
251  if (newline) {
252  rz_strbuf_appendf(sb, "%s\n%s%c", ecol, bcol, prefix);
253  newline = false;
254  bounds_idx++;
255  }
257  elem = elem_at(b_array, i);
258  stringify(elem, &tmp);
260  p = rz_strbuf_get(&tmp);
261  if (len > 0 && p[len - 1] == '\n') {
262  len--;
263  newline = true;
264  }
265  st32 left = char_bounds[bounds_idx * 2];
266  st32 right = char_bounds[bounds_idx * 2 + 1];
267  if (left < 0 || right < 0 || len - right < left) {
269  } else {
270  rz_strbuf_append_n(sb, p, left);
271  rz_strbuf_append(sb, bbgcol);
272  rz_strbuf_append_n(sb, p + left, len - right - left);
273  rz_strbuf_append(sb, ebgcol);
274  rz_strbuf_append_n(sb, p + len - right, right);
275  }
277  }
278  rz_strbuf_appendf(sb, "%s\n", ecol);
279  free(char_bounds);
280 }
281 
282 static inline void diff_unified_json_data(RzDiff *diff, const void *array, st32 beg, st32 end, PJ *pj, const char *op) {
283  RzDiffMethodElemAt elem_at = diff->methods.elem_at;
284  RzDiffMethodStringify stringify = diff->methods.stringify;
285  int len = 0;
286  ut32 count = 0;
287  const char *p;
288  const void *elem;
289  RzStrBuf tmp;
290  bool newline = false;
291  bool is_bytes = DIFF_IS_BYTES_METHOD(diff->methods);
292 
293  if (beg < 0) {
294  beg = 0;
295  }
296 
297  pj_o(pj);
298  pj_ks(pj, "op", op);
300  for (st32 i = beg; i < end; ++i) {
301  if (newline || (is_bytes && count > 0 && !FAST_MOD64(count))) {
302  pj_ks(pj, "value", rz_strbuf_get(&tmp));
303  pj_end(pj);
304 
307 
308  pj_o(pj);
309  pj_ks(pj, "op", op);
310  newline = false;
311  }
312  elem = elem_at(array, i);
313  stringify(elem, &tmp);
315  p = rz_strbuf_get(&tmp);
316  count += len;
317  if (len > 0 && p[len - 1] == '\n') {
318  newline = true;
319  }
320  }
321  pj_ks(pj, "value", rz_strbuf_get(&tmp));
322  pj_end(pj);
324 }
325 
333 RZ_API RZ_OWN char *rz_diff_unified_text(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time, bool color) {
334  rz_return_val_if_fail(diff && diff->methods.elem_at && diff->methods.stringify, NULL);
335  RzStrBuf *sb = NULL;
336  RzList *groups = NULL;
337  RzList *opcodes = NULL;
338  RzDiffOp *op = NULL;
339  RzListIter *itg = NULL;
340  RzListIter *ito = NULL;
341 
342  if (!from) {
343  from = "/original";
344  }
345  if (!to) {
346  to = "/modified";
347  }
348  sb = rz_strbuf_new("");
349  if (!sb) {
350  RZ_LOG_ERROR("rz_diff_unified: cannot allocate sb\n");
351  goto rz_diff_unified_text_fail;
352  }
353 
354  if (show_time) {
356  rz_strbuf_appendf(sb, "--- %s %s\n+++ %s %s\n", from, (time ? time : ""), to, (time ? time : ""));
357  free(time);
358  } else {
359  rz_strbuf_appendf(sb, "--- %s\n+++ %s\n", from, to);
360  }
361 
363  if (!groups) {
364  goto rz_diff_unified_text_fail;
365  }
366 
367  rz_list_foreach (groups, itg, opcodes) {
368  if (rz_list_length(opcodes) < 1) {
369  continue;
370  }
372  rz_list_foreach (opcodes, ito, op) {
373  if (op->type == RZ_DIFF_OP_EQUAL) {
374  diff_unified_append_data(diff, diff->a, op->a_beg, op->a_end, sb, ' ', color);
375  continue;
376  }
377  if (op->type == RZ_DIFF_OP_DELETE) {
378  diff_unified_append_data(diff, diff->a, op->a_beg, op->a_end, sb, '-', color);
379  } else if (op->type == RZ_DIFF_OP_INSERT) {
380  diff_unified_append_data(diff, diff->b, op->b_beg, op->b_end, sb, '+', color);
381  } else if (op->type == RZ_DIFF_OP_REPLACE) {
382  if (!color || !DIFF_IS_LINES_METHOD(diff->methods) ||
383  count_newlines(diff, diff->a, op->a_beg, op->a_end) !=
384  count_newlines(diff, diff->b, op->b_beg, op->b_end)) {
385  diff_unified_append_data(diff, diff->a, op->a_beg, op->a_end, sb, '-', color);
386  diff_unified_append_data(diff, diff->b, op->b_beg, op->b_end, sb, '+', color);
387  } else {
388  diff_unified_lines_hl(diff, op, sb, '-', '+');
389  }
390  }
391  }
392  }
393 
395  return rz_strbuf_drain(sb);
396 
397 rz_diff_unified_text_fail:
400  return NULL;
401 }
402 
409 RZ_API RZ_OWN PJ *rz_diff_unified_json(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time) {
410  rz_return_val_if_fail(diff && diff->methods.elem_at && diff->methods.stringify, NULL);
411  PJ *pj = NULL;
412  RzList *groups = NULL;
413  RzList *opcodes = NULL;
414  RzDiffOp *op = NULL;
415  RzListIter *itg = NULL;
416  RzListIter *ito = NULL;
417 
418  if (!from) {
419  from = "/original";
420  }
421  if (!to) {
422  to = "/modified";
423  }
424 
425  pj = pj_new();
426  if (!pj) {
427  RZ_LOG_ERROR("rz_diff_unified: failed to allocate json\n");
428  goto rz_diff_unified_json_fail;
429  }
430  pj_o(pj);
431 
432  if (show_time) {
434  if (!time) {
435  RZ_LOG_ERROR("rz_diff_unified: failed to allocate timestamp\n");
436  goto rz_diff_unified_json_fail;
437  }
438  pj_ks(pj, "timestamp", time);
439  free(time);
440  }
441 
442  pj_ks(pj, "from", from);
443  pj_ks(pj, "to", to);
444 
446  if (!groups) {
447  goto rz_diff_unified_json_fail;
448  }
449 
450  pj_ka(pj, "diff");
451  rz_list_foreach (groups, itg, opcodes) {
452  if (rz_list_length(opcodes) < 1) {
453  continue;
454  }
455  pj_o(pj);
457  pj_ka(pj, "ops");
458  rz_list_foreach (opcodes, ito, op) {
459  if (op->type == RZ_DIFF_OP_EQUAL) {
460  diff_unified_json_data(diff, diff->a, op->a_beg, op->a_end, pj, "equal");
461  continue;
462  }
463  if (op->type == RZ_DIFF_OP_DELETE || op->type == RZ_DIFF_OP_REPLACE) {
464  diff_unified_json_data(diff, diff->a, op->a_beg, op->a_end, pj, "delete");
465  }
466  if (op->type == RZ_DIFF_OP_INSERT || op->type == RZ_DIFF_OP_REPLACE) {
467  diff_unified_json_data(diff, diff->b, op->b_beg, op->b_end, pj, "insert");
468  }
469  }
470  pj_end(pj);
471  pj_end(pj);
472  }
473  pj_end(pj);
474  pj_end(pj);
475 
477  return pj;
478 
479 rz_diff_unified_json_fail:
480  pj_free(pj);
482  return NULL;
483 }
size_t len
Definition: 6502dis.c:15
lzma_index ** i
Definition: index.h:629
OPCODE_DESC opcodes[]
Definition: avr_esil.c:1270
static SblHeader sb
Definition: bin_mbn.c:26
#define DIFF_IS_BYTES_METHOD(x)
Definition: bytes_diff.c:6
#define RZ_API
#define NULL
Definition: cris-opc.c:27
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
uint32_t ut32
RZ_API RZ_OWN RzList * rz_diff_opcodes_grouped_new(RZ_NONNULL RzDiff *diff, ut32 n_groups)
Generates groups of opcodes needed to go from A to B.
Definition: diff.c:712
unsigned short prefix[65536]
Definition: gun.c:163
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
void * p
Definition: libc.cpp:67
RZ_API RZ_BORROW void * rz_list_first(RZ_NONNULL const RzList *list)
Returns the first element of the list.
Definition: list.c:77
RZ_API RZ_BORROW void * rz_list_last(RZ_NONNULL const RzList *list)
Returns the last element of the list.
Definition: list.c:86
RZ_API ut32 rz_list_length(RZ_NONNULL const RzList *list)
Returns the length of the list.
Definition: list.c:109
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
void * malloc(size_t size)
Definition: malloc.c:123
#define DIFF_IS_LINES_METHOD(x)
Definition: lines_diff.c:6
static static fork const void static count static fd const char const char static newpath char char char static envp time
Definition: sflib.h:42
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
#define Color_RESET
Definition: rz_cons.h:617
#define Color_RESET_BG
Definition: rz_cons.h:619
const void *(* RzDiffMethodElemAt)(RZ_BORROW const void *array, ut32 index)
Definition: rz_diff.h:36
@ RZ_DIFF_OP_REPLACE
Definition: rz_diff.h:24
@ RZ_DIFF_OP_DELETE
Definition: rz_diff.h:21
@ RZ_DIFF_OP_EQUAL
Definition: rz_diff.h:22
@ RZ_DIFF_OP_INSERT
Definition: rz_diff.h:23
#define RZ_DIFF_DEFAULT_N_GROUPS
Definition: rz_diff.h:59
void(* RzDiffMethodStringify)(RZ_BORROW const void *elem, RZ_BORROW RzStrBuf *sb)
Definition: rz_diff.h:40
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API PJ * pj_ka(PJ *j, const char *k)
Definition: pj.c:163
RZ_API PJ * pj_new(void)
Definition: pj.c:25
RZ_API PJ * pj_end(PJ *j)
Definition: pj.c:87
RZ_API void pj_free(PJ *j)
Definition: pj.c:34
RZ_API PJ * pj_o(PJ *j)
Definition: pj.c:75
RZ_API PJ * pj_ks(PJ *j, const char *k, const char *v)
Definition: pj.c:170
RZ_API PJ * pj_N(PJ *j, st64 n)
Definition: pj.c:260
RZ_API RZ_OWN char * rz_strbuf_drain(RzStrBuf *sb)
Definition: strbuf.c:342
RZ_API char * rz_strbuf_get(RzStrBuf *sb)
Definition: strbuf.c:321
RZ_API bool rz_strbuf_append(RzStrBuf *sb, const char *s)
Definition: strbuf.c:222
RZ_API void rz_strbuf_fini(RzStrBuf *sb)
Definition: strbuf.c:365
RZ_API RzStrBuf * rz_strbuf_new(const char *s)
Definition: strbuf.c:8
RZ_API void rz_strbuf_free(RzStrBuf *sb)
Definition: strbuf.c:358
RZ_API bool rz_strbuf_appendf(RzStrBuf *sb, const char *fmt,...) RZ_PRINTF_CHECK(2
RZ_API void rz_strbuf_init(RzStrBuf *sb)
Definition: strbuf.c:33
RZ_API int rz_strbuf_length(RzStrBuf *sb)
Definition: strbuf.c:28
RZ_API bool rz_strbuf_append_n(RzStrBuf *sb, const char *s, size_t l)
Definition: strbuf.c:229
RZ_API RZ_OWN char * rz_time_date_now_to_string(void)
Returns the timestamp in string format of the current time (now)
Definition: time.c:278
#define RZ_NULLABLE
Definition: rz_types.h:65
#define RZ_OWN
Definition: rz_types.h:62
#define RZ_NONNULL
Definition: rz_types.h:64
#define RZ_MIN(x, y)
#define st32
Definition: rz_types_base.h:12
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr from
Definition: sfsocketcall.h:123
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr socklen_t static fromlen const void const struct sockaddr to
Definition: sfsocketcall.h:125
RzDiffMethodElemAt elem_at
Definition: diff.c:81
RzDiffMethodStringify stringify
Definition: diff.c:85
Definition: rz_pj.h:12
st32 a_end
Definition: rz_diff.h:52
st32 b_end
Definition: rz_diff.h:54
st32 a_beg
Definition: rz_diff.h:51
st32 b_beg
Definition: rz_diff.h:53
Definition: diff.c:89
MethodsInternal methods
Definition: diff.c:95
const void * a
Definition: diff.c:90
const void * b
Definition: diff.c:91
RZ_API RZ_OWN char * rz_diff_unified_text(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time, bool color)
Produces a diff output with A and B inputs presented immediately adjacent to each other.
Definition: unified_diff.c:333
RZ_API RZ_OWN PJ * rz_diff_unified_json(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time)
Produces a diff output to convert A in B in a JSON format.
Definition: unified_diff.c:409
#define FAST_MOD64(x)
Definition: unified_diff.c:14
#define DIFF_COLOR(prefix)
Definition: unified_diff.c:15
#define Color_RANGE
Definition: unified_diff.c:7
static void diff_unified_append_data(RzDiff *diff, const void *array, st32 beg, st32 end, RzStrBuf *sb, char prefix, bool color)
Definition: unified_diff.c:70
static void diff_unified_json_data(RzDiff *diff, const void *array, st32 beg, st32 end, PJ *pj, const char *op)
Definition: unified_diff.c:282
static ut32 count_newlines(RzDiff *diff, const void *array, st32 beg, st32 end)
Definition: unified_diff.c:18
static void diff_unified_append_ranges(RzList *opcodes, RzStrBuf *sb, bool color)
Definition: unified_diff.c:41
#define DIFF_BGCOLOR(prefix)
Definition: unified_diff.c:16
static void diff_unified_json_ranges(RzList *opcodes, PJ *pj)
Definition: unified_diff.c:53
static void diff_unified_lines_hl(RzDiff *diff, RzDiffOp *op, RzStrBuf *sb, char del_prefix, char ins_prefix)
Definition: unified_diff.c:113
Definition: dis.c:32
static int color
Definition: visual.c:20