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