1 |
/* Context-format output routines for GNU DIFF. |
2 |
Copyright (C) 1988,1989,1991,1992,1993,1994 Free Software Foundation, Inc. |
3 |
|
4 |
This file is part of GNU DIFF. |
5 |
|
6 |
GNU DIFF is free software; you can redistribute it and/or modify |
7 |
it under the terms of the GNU General Public License as published by |
8 |
the Free Software Foundation; either version 2, or (at your option) |
9 |
any later version. |
10 |
|
11 |
GNU DIFF is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
GNU General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU General Public License |
17 |
along with GNU DIFF; see the file COPYING. If not, write to |
18 |
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ |
19 |
|
20 |
#include "diff.h" |
21 |
|
22 |
static struct change *find_hunk PARAMS((struct change *)); |
23 |
static void find_function PARAMS((struct file_data const *, int, char const **, size_t *)); |
24 |
static void mark_ignorable PARAMS((struct change *)); |
25 |
static void pr_context_hunk PARAMS((struct change *)); |
26 |
static void pr_unidiff_hunk PARAMS((struct change *)); |
27 |
static void print_context_label PARAMS ((char const *, struct file_data *, char const *)); |
28 |
static void print_context_number_range PARAMS((struct file_data const *, int, int)); |
29 |
static void print_unidiff_number_range PARAMS((struct file_data const *, int, int)); |
30 |
|
31 |
/* Last place find_function started searching from. */ |
32 |
static int find_function_last_search; |
33 |
|
34 |
/* The value find_function returned when it started searching there. */ |
35 |
static int find_function_last_match; |
36 |
|
37 |
/* Print a label for a context diff, with a file name and date or a label. */ |
38 |
|
39 |
static void |
40 |
print_context_label (mark, inf, label) |
41 |
char const *mark; |
42 |
struct file_data *inf; |
43 |
char const *label; |
44 |
{ |
45 |
if (label) |
46 |
fprintf (outfile, "%s %s\n", mark, label); |
47 |
else |
48 |
{ |
49 |
char const *ct = ctime (&inf->stat.st_mtime); |
50 |
if (!ct) |
51 |
ct = "?\n"; |
52 |
/* See Posix.2 section 4.17.6.1.4 for this format. */ |
53 |
fprintf (outfile, "%s %s\t%s", mark, inf->name, ct); |
54 |
} |
55 |
} |
56 |
|
57 |
/* Print a header for a context diff, with the file names and dates. */ |
58 |
|
59 |
void |
60 |
print_context_header (inf, unidiff_flag) |
61 |
struct file_data inf[]; |
62 |
int unidiff_flag; |
63 |
{ |
64 |
if (unidiff_flag) |
65 |
{ |
66 |
print_context_label ("---", &inf[0], file_label[0]); |
67 |
print_context_label ("+++", &inf[1], file_label[1]); |
68 |
} |
69 |
else |
70 |
{ |
71 |
print_context_label ("***", &inf[0], file_label[0]); |
72 |
print_context_label ("---", &inf[1], file_label[1]); |
73 |
} |
74 |
} |
75 |
|
76 |
/* Print an edit script in context format. */ |
77 |
|
78 |
void |
79 |
print_context_script (script, unidiff_flag) |
80 |
struct change *script; |
81 |
int unidiff_flag; |
82 |
{ |
83 |
if (ignore_blank_lines_flag || ignore_regexp_list) |
84 |
mark_ignorable (script); |
85 |
else |
86 |
{ |
87 |
struct change *e; |
88 |
for (e = script; e; e = e->link) |
89 |
e->ignore = 0; |
90 |
} |
91 |
|
92 |
find_function_last_search = - files[0].prefix_lines; |
93 |
find_function_last_match = find_function_last_search - 1; |
94 |
|
95 |
if (unidiff_flag) |
96 |
print_script (script, find_hunk, pr_unidiff_hunk); |
97 |
else |
98 |
print_script (script, find_hunk, pr_context_hunk); |
99 |
} |
100 |
|
101 |
/* Print a pair of line numbers with a comma, translated for file FILE. |
102 |
If the second number is not greater, use the first in place of it. |
103 |
|
104 |
Args A and B are internal line numbers. |
105 |
We print the translated (real) line numbers. */ |
106 |
|
107 |
static void |
108 |
print_context_number_range (file, a, b) |
109 |
struct file_data const *file; |
110 |
int a, b; |
111 |
{ |
112 |
int trans_a, trans_b; |
113 |
translate_range (file, a, b, &trans_a, &trans_b); |
114 |
|
115 |
/* Note: we can have B < A in the case of a range of no lines. |
116 |
In this case, we should print the line number before the range, |
117 |
which is B. */ |
118 |
if (trans_b > trans_a) |
119 |
fprintf (outfile, "%d,%d", trans_a, trans_b); |
120 |
else |
121 |
fprintf (outfile, "%d", trans_b); |
122 |
} |
123 |
|
124 |
/* Print a portion of an edit script in context format. |
125 |
HUNK is the beginning of the portion to be printed. |
126 |
The end is marked by a `link' that has been nulled out. |
127 |
|
128 |
Prints out lines from both files, and precedes each |
129 |
line with the appropriate flag-character. */ |
130 |
|
131 |
static void |
132 |
pr_context_hunk (hunk) |
133 |
struct change *hunk; |
134 |
{ |
135 |
int first0, last0, first1, last1, show_from, show_to, i; |
136 |
struct change *next; |
137 |
char const *prefix; |
138 |
char const *function; |
139 |
size_t function_length; |
140 |
FILE *out; |
141 |
|
142 |
/* Determine range of line numbers involved in each file. */ |
143 |
|
144 |
analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); |
145 |
|
146 |
if (!show_from && !show_to) |
147 |
return; |
148 |
|
149 |
/* Include a context's width before and after. */ |
150 |
|
151 |
i = - files[0].prefix_lines; |
152 |
first0 = max (first0 - context, i); |
153 |
first1 = max (first1 - context, i); |
154 |
last0 = min (last0 + context, files[0].valid_lines - 1); |
155 |
last1 = min (last1 + context, files[1].valid_lines - 1); |
156 |
|
157 |
/* If desired, find the preceding function definition line in file 0. */ |
158 |
function = 0; |
159 |
if (function_regexp_list) |
160 |
find_function (&files[0], first0, &function, &function_length); |
161 |
|
162 |
begin_output (); |
163 |
out = outfile; |
164 |
|
165 |
/* If we looked for and found a function this is part of, |
166 |
include its name in the header of the diff section. */ |
167 |
fprintf (out, "***************"); |
168 |
|
169 |
if (function) |
170 |
{ |
171 |
fprintf (out, " "); |
172 |
fwrite (function, 1, min (function_length - 1, 40), out); |
173 |
} |
174 |
|
175 |
fprintf (out, "\n*** "); |
176 |
print_context_number_range (&files[0], first0, last0); |
177 |
fprintf (out, " ****\n"); |
178 |
|
179 |
if (show_from) |
180 |
{ |
181 |
next = hunk; |
182 |
|
183 |
for (i = first0; i <= last0; i++) |
184 |
{ |
185 |
/* Skip past changes that apply (in file 0) |
186 |
only to lines before line I. */ |
187 |
|
188 |
while (next && next->line0 + next->deleted <= i) |
189 |
next = next->link; |
190 |
|
191 |
/* Compute the marking for line I. */ |
192 |
|
193 |
prefix = " "; |
194 |
if (next && next->line0 <= i) |
195 |
/* The change NEXT covers this line. |
196 |
If lines were inserted here in file 1, this is "changed". |
197 |
Otherwise it is "deleted". */ |
198 |
prefix = (next->inserted > 0 ? "!" : "-"); |
199 |
|
200 |
print_1_line (prefix, &files[0].linbuf[i]); |
201 |
} |
202 |
} |
203 |
|
204 |
fprintf (out, "--- "); |
205 |
print_context_number_range (&files[1], first1, last1); |
206 |
fprintf (out, " ----\n"); |
207 |
|
208 |
if (show_to) |
209 |
{ |
210 |
next = hunk; |
211 |
|
212 |
for (i = first1; i <= last1; i++) |
213 |
{ |
214 |
/* Skip past changes that apply (in file 1) |
215 |
only to lines before line I. */ |
216 |
|
217 |
while (next && next->line1 + next->inserted <= i) |
218 |
next = next->link; |
219 |
|
220 |
/* Compute the marking for line I. */ |
221 |
|
222 |
prefix = " "; |
223 |
if (next && next->line1 <= i) |
224 |
/* The change NEXT covers this line. |
225 |
If lines were deleted here in file 0, this is "changed". |
226 |
Otherwise it is "inserted". */ |
227 |
prefix = (next->deleted > 0 ? "!" : "+"); |
228 |
|
229 |
print_1_line (prefix, &files[1].linbuf[i]); |
230 |
} |
231 |
} |
232 |
} |
233 |
|
234 |
/* Print a pair of line numbers with a comma, translated for file FILE. |
235 |
If the second number is smaller, use the first in place of it. |
236 |
If the numbers are equal, print just one number. |
237 |
|
238 |
Args A and B are internal line numbers. |
239 |
We print the translated (real) line numbers. */ |
240 |
|
241 |
static void |
242 |
print_unidiff_number_range (file, a, b) |
243 |
struct file_data const *file; |
244 |
int a, b; |
245 |
{ |
246 |
int trans_a, trans_b; |
247 |
translate_range (file, a, b, &trans_a, &trans_b); |
248 |
|
249 |
/* Note: we can have B < A in the case of a range of no lines. |
250 |
In this case, we should print the line number before the range, |
251 |
which is B. */ |
252 |
if (trans_b <= trans_a) |
253 |
fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b); |
254 |
else |
255 |
fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1); |
256 |
} |
257 |
|
258 |
/* Print a portion of an edit script in unidiff format. |
259 |
HUNK is the beginning of the portion to be printed. |
260 |
The end is marked by a `link' that has been nulled out. |
261 |
|
262 |
Prints out lines from both files, and precedes each |
263 |
line with the appropriate flag-character. */ |
264 |
|
265 |
static void |
266 |
pr_unidiff_hunk (hunk) |
267 |
struct change *hunk; |
268 |
{ |
269 |
int first0, last0, first1, last1, show_from, show_to, i, j, k; |
270 |
struct change *next; |
271 |
char const *function; |
272 |
size_t function_length; |
273 |
FILE *out; |
274 |
|
275 |
/* Determine range of line numbers involved in each file. */ |
276 |
|
277 |
analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); |
278 |
|
279 |
if (!show_from && !show_to) |
280 |
return; |
281 |
|
282 |
/* Include a context's width before and after. */ |
283 |
|
284 |
i = - files[0].prefix_lines; |
285 |
first0 = max (first0 - context, i); |
286 |
first1 = max (first1 - context, i); |
287 |
last0 = min (last0 + context, files[0].valid_lines - 1); |
288 |
last1 = min (last1 + context, files[1].valid_lines - 1); |
289 |
|
290 |
/* If desired, find the preceding function definition line in file 0. */ |
291 |
function = 0; |
292 |
if (function_regexp_list) |
293 |
find_function (&files[0], first0, &function, &function_length); |
294 |
|
295 |
begin_output (); |
296 |
out = outfile; |
297 |
|
298 |
fprintf (out, "@@ -"); |
299 |
print_unidiff_number_range (&files[0], first0, last0); |
300 |
fprintf (out, " +"); |
301 |
print_unidiff_number_range (&files[1], first1, last1); |
302 |
fprintf (out, " @@"); |
303 |
|
304 |
/* If we looked for and found a function this is part of, |
305 |
include its name in the header of the diff section. */ |
306 |
|
307 |
if (function) |
308 |
{ |
309 |
putc (' ', out); |
310 |
fwrite (function, 1, min (function_length - 1, 40), out); |
311 |
} |
312 |
putc ('\n', out); |
313 |
|
314 |
next = hunk; |
315 |
i = first0; |
316 |
j = first1; |
317 |
|
318 |
while (i <= last0 || j <= last1) |
319 |
{ |
320 |
|
321 |
/* If the line isn't a difference, output the context from file 0. */ |
322 |
|
323 |
if (!next || i < next->line0) |
324 |
{ |
325 |
putc (tab_align_flag ? '\t' : ' ', out); |
326 |
print_1_line (0, &files[0].linbuf[i++]); |
327 |
j++; |
328 |
} |
329 |
else |
330 |
{ |
331 |
/* For each difference, first output the deleted part. */ |
332 |
|
333 |
k = next->deleted; |
334 |
while (k--) |
335 |
{ |
336 |
putc ('-', out); |
337 |
if (tab_align_flag) |
338 |
putc ('\t', out); |
339 |
print_1_line (0, &files[0].linbuf[i++]); |
340 |
} |
341 |
|
342 |
/* Then output the inserted part. */ |
343 |
|
344 |
k = next->inserted; |
345 |
while (k--) |
346 |
{ |
347 |
putc ('+', out); |
348 |
if (tab_align_flag) |
349 |
putc ('\t', out); |
350 |
print_1_line (0, &files[1].linbuf[j++]); |
351 |
} |
352 |
|
353 |
/* We're done with this hunk, so on to the next! */ |
354 |
|
355 |
next = next->link; |
356 |
} |
357 |
} |
358 |
} |
359 |
|
360 |
/* Scan a (forward-ordered) edit script for the first place that more than |
361 |
2*CONTEXT unchanged lines appear, and return a pointer |
362 |
to the `struct change' for the last change before those lines. */ |
363 |
|
364 |
static struct change * |
365 |
find_hunk (start) |
366 |
struct change *start; |
367 |
{ |
368 |
struct change *prev; |
369 |
int top0, top1; |
370 |
int thresh; |
371 |
|
372 |
do |
373 |
{ |
374 |
/* Compute number of first line in each file beyond this changed. */ |
375 |
top0 = start->line0 + start->deleted; |
376 |
top1 = start->line1 + start->inserted; |
377 |
prev = start; |
378 |
start = start->link; |
379 |
/* Threshold distance is 2*CONTEXT between two non-ignorable changes, |
380 |
but only CONTEXT if one is ignorable. */ |
381 |
thresh = ((prev->ignore || (start && start->ignore)) |
382 |
? context |
383 |
: 2 * context + 1); |
384 |
/* It is not supposed to matter which file we check in the end-test. |
385 |
If it would matter, crash. */ |
386 |
if (start && start->line0 - top0 != start->line1 - top1) |
387 |
abort (); |
388 |
} while (start |
389 |
/* Keep going if less than THRESH lines |
390 |
elapse before the affected line. */ |
391 |
&& start->line0 < top0 + thresh); |
392 |
|
393 |
return prev; |
394 |
} |
395 |
|
396 |
/* Set the `ignore' flag properly in each change in SCRIPT. |
397 |
It should be 1 if all the lines inserted or deleted in that change |
398 |
are ignorable lines. */ |
399 |
|
400 |
static void |
401 |
mark_ignorable (script) |
402 |
struct change *script; |
403 |
{ |
404 |
while (script) |
405 |
{ |
406 |
struct change *next = script->link; |
407 |
int first0, last0, first1, last1, deletes, inserts; |
408 |
|
409 |
/* Turn this change into a hunk: detach it from the others. */ |
410 |
script->link = 0; |
411 |
|
412 |
/* Determine whether this change is ignorable. */ |
413 |
analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts); |
414 |
/* Reconnect the chain as before. */ |
415 |
script->link = next; |
416 |
|
417 |
/* If the change is ignorable, mark it. */ |
418 |
script->ignore = (!deletes && !inserts); |
419 |
|
420 |
/* Advance to the following change. */ |
421 |
script = next; |
422 |
} |
423 |
} |
424 |
|
425 |
/* Find the last function-header line in FILE prior to line number LINENUM. |
426 |
This is a line containing a match for the regexp in `function_regexp'. |
427 |
Store the address of the line text into LINEP and the length of the |
428 |
line into LENP. |
429 |
Do not store anything if no function-header is found. */ |
430 |
|
431 |
static void |
432 |
find_function (file, linenum, linep, lenp) |
433 |
struct file_data const *file; |
434 |
int linenum; |
435 |
char const **linep; |
436 |
size_t *lenp; |
437 |
{ |
438 |
int i = linenum; |
439 |
int last = find_function_last_search; |
440 |
find_function_last_search = i; |
441 |
|
442 |
while (--i >= last) |
443 |
{ |
444 |
/* See if this line is what we want. */ |
445 |
struct regexp_list *r; |
446 |
char const *line = file->linbuf[i]; |
447 |
size_t len = file->linbuf[i + 1] - line; |
448 |
|
449 |
for (r = function_regexp_list; r; r = r->next) |
450 |
if (0 <= re_search (&r->buf, line, len, 0, len, 0)) |
451 |
{ |
452 |
*linep = line; |
453 |
*lenp = len; |
454 |
find_function_last_match = i; |
455 |
return; |
456 |
} |
457 |
} |
458 |
/* If we search back to where we started searching the previous time, |
459 |
find the line we found last time. */ |
460 |
if (find_function_last_match >= - file->prefix_lines) |
461 |
{ |
462 |
i = find_function_last_match; |
463 |
*linep = file->linbuf[i]; |
464 |
*lenp = file->linbuf[i + 1] - *linep; |
465 |
return; |
466 |
} |
467 |
return; |
468 |
} |