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