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 | } |
---|