1 /* Context-format output routines for GNU DIFF.
3 Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2006, 2009-2011
4 Free Software Foundation, Inc.
6 This file is part of GNU DIFF.
8 This program 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 3 of the License, or
11 (at your option) any later version.
13 This program 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.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include <stat-time.h>
26 static char const *find_function (char const * const *, lin);
27 static struct change *find_hunk (struct change *);
28 static void mark_ignorable (struct change *);
29 static void pr_context_hunk (struct change *);
30 static void pr_unidiff_hunk (struct change *);
32 /* Last place find_function started searching from. */
33 static lin find_function_last_search;
35 /* The value find_function returned when it started searching there. */
36 static lin find_function_last_match;
38 /* Print a label for a context diff, with a file name and date or a label. */
41 print_context_label (char const *mark,
42 struct file_data *inf,
46 fprintf (outfile, "%s %s\n", mark, label);
49 char buf[MAX (INT_STRLEN_BOUND (int) + 32,
50 INT_STRLEN_BOUND (time_t) + 11)];
51 struct tm const *tm = localtime (&inf->stat.st_mtime);
52 int nsec = get_stat_mtime_ns (&inf->stat);
53 if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
55 verify (TYPE_IS_INTEGER (time_t));
56 if (LONG_MIN <= TYPE_MINIMUM (time_t)
57 && TYPE_MAXIMUM (time_t) <= LONG_MAX)
59 long int sec = inf->stat.st_mtime;
60 sprintf (buf, "%ld.%.9d", sec, nsec);
62 else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX)
64 intmax_t sec = inf->stat.st_mtime;
65 sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec);
69 uintmax_t sec = inf->stat.st_mtime;
70 sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
73 fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf);
77 /* Print a header for a context diff, with the file names and dates. */
80 print_context_header (struct file_data inf[], bool unidiff)
84 print_context_label ("---", &inf[0], file_label[0]);
85 print_context_label ("+++", &inf[1], file_label[1]);
89 print_context_label ("***", &inf[0], file_label[0]);
90 print_context_label ("---", &inf[1], file_label[1]);
94 /* Print an edit script in context format. */
97 print_context_script (struct change *script, bool unidiff)
99 if (ignore_blank_lines || ignore_regexp.fastmap)
100 mark_ignorable (script);
104 for (e = script; e; e = e->link)
108 find_function_last_search = - files[0].prefix_lines;
109 find_function_last_match = LIN_MAX;
112 print_script (script, find_hunk, pr_unidiff_hunk);
114 print_script (script, find_hunk, pr_context_hunk);
117 /* Print a pair of line numbers with a comma, translated for file FILE.
118 If the second number is not greater, use the first in place of it.
120 Args A and B are internal line numbers.
121 We print the translated (real) line numbers. */
124 print_context_number_range (struct file_data const *file, lin a, lin b)
126 long int trans_a, trans_b;
127 translate_range (file, a, b, &trans_a, &trans_b);
129 /* We can have B <= A in the case of a range of no lines.
130 In this case, we should print the line number before the range,
133 POSIX 1003.1-2001 requires two line numbers separated by a comma
134 even if the line numbers are the same. However, this does not
135 match existing practice and is surely an error in the
138 if (trans_b <= trans_a)
139 fprintf (outfile, "%ld", trans_b);
141 fprintf (outfile, "%ld,%ld", trans_a, trans_b);
144 /* Print FUNCTION in a context header. */
146 print_context_function (FILE *out, char const *function)
150 for (i = 0; c_isspace ((unsigned char) function[i]) && function[i] != '\n'; i++)
152 for (j = i; j < i + 40 && function[j] != '\n'; j++)
154 while (i < j && c_isspace ((unsigned char) function[j - 1]))
156 fwrite (function + i, sizeof (char), j - i, out);
159 /* Print a portion of an edit script in context format.
160 HUNK is the beginning of the portion to be printed.
161 The end is marked by a `link' that has been nulled out.
163 Prints out lines from both files, and precedes each
164 line with the appropriate flag-character. */
167 pr_context_hunk (struct change *hunk)
169 lin first0, last0, first1, last1, i;
171 char const *function;
174 /* Determine range of line numbers involved in each file. */
176 enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
180 /* Include a context's width before and after. */
182 i = - files[0].prefix_lines;
183 first0 = MAX (first0 - context, i);
184 first1 = MAX (first1 - context, i);
185 if (last0 < files[0].valid_lines - context)
188 last0 = files[0].valid_lines - 1;
189 if (last1 < files[1].valid_lines - context)
192 last1 = files[1].valid_lines - 1;
194 /* If desired, find the preceding function definition line in file 0. */
196 if (function_regexp.fastmap)
197 function = find_function (files[0].linbuf, first0);
202 fputs ("***************", out);
205 print_context_function (out, function);
207 fputs ("\n*** ", out);
208 print_context_number_range (&files[0], first0, last0);
209 fputs (" ****\n", out);
213 struct change *next = hunk;
215 for (i = first0; i <= last0; i++)
217 /* Skip past changes that apply (in file 0)
218 only to lines before line I. */
220 while (next && next->line0 + next->deleted <= i)
223 /* Compute the marking for line I. */
226 if (next && next->line0 <= i)
227 /* The change NEXT covers this line.
228 If lines were inserted here in file 1, this is "changed".
229 Otherwise it is "deleted". */
230 prefix = (next->inserted > 0 ? "!" : "-");
232 print_1_line (prefix, &files[0].linbuf[i]);
237 print_context_number_range (&files[1], first1, last1);
238 fputs (" ----\n", out);
242 struct change *next = hunk;
244 for (i = first1; i <= last1; i++)
246 /* Skip past changes that apply (in file 1)
247 only to lines before line I. */
249 while (next && next->line1 + next->inserted <= i)
252 /* Compute the marking for line I. */
255 if (next && next->line1 <= i)
256 /* The change NEXT covers this line.
257 If lines were deleted here in file 0, this is "changed".
258 Otherwise it is "inserted". */
259 prefix = (next->deleted > 0 ? "!" : "+");
261 print_1_line (prefix, &files[1].linbuf[i]);
266 /* Print a pair of line numbers with a comma, translated for file FILE.
267 If the second number is smaller, use the first in place of it.
268 If the numbers are equal, print just one number.
270 Args A and B are internal line numbers.
271 We print the translated (real) line numbers. */
274 print_unidiff_number_range (struct file_data const *file, lin a, lin b)
276 long int trans_a, trans_b;
277 translate_range (file, a, b, &trans_a, &trans_b);
279 /* We can have B < A in the case of a range of no lines.
280 In this case, we print the line number before the range,
281 which is B. It would be more logical to print A, but
282 'patch' expects B in order to detect diffs against empty files. */
283 if (trans_b <= trans_a)
284 fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
286 fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1);
289 /* Print a portion of an edit script in unidiff format.
290 HUNK is the beginning of the portion to be printed.
291 The end is marked by a `link' that has been nulled out.
293 Prints out lines from both files, and precedes each
294 line with the appropriate flag-character. */
297 pr_unidiff_hunk (struct change *hunk)
299 lin first0, last0, first1, last1;
302 char const *function;
305 /* Determine range of line numbers involved in each file. */
307 if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
310 /* Include a context's width before and after. */
312 i = - files[0].prefix_lines;
313 first0 = MAX (first0 - context, i);
314 first1 = MAX (first1 - context, i);
315 if (last0 < files[0].valid_lines - context)
318 last0 = files[0].valid_lines - 1;
319 if (last1 < files[1].valid_lines - context)
322 last1 = files[1].valid_lines - 1;
324 /* If desired, find the preceding function definition line in file 0. */
326 if (function_regexp.fastmap)
327 function = find_function (files[0].linbuf, first0);
333 print_unidiff_number_range (&files[0], first0, last0);
335 print_unidiff_number_range (&files[1], first1, last1);
339 print_context_function (out, function);
347 while (i <= last0 || j <= last1)
350 /* If the line isn't a difference, output the context from file 0. */
352 if (!next || i < next->line0)
354 char const *const *line = &files[0].linbuf[i++];
355 if (! (suppress_blank_empty && **line == '\n'))
356 putc (initial_tab ? '\t' : ' ', out);
357 print_1_line (NULL, line);
362 /* For each difference, first output the deleted part. */
367 char const * const *line = &files[0].linbuf[i++];
369 if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
371 print_1_line (NULL, line);
374 /* Then output the inserted part. */
379 char const * const *line = &files[1].linbuf[j++];
381 if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
383 print_1_line (NULL, line);
386 /* We're done with this hunk, so on to the next! */
393 /* Scan a (forward-ordered) edit script for the first place that more than
394 2*CONTEXT unchanged lines appear, and return a pointer
395 to the `struct change' for the last change before those lines. */
397 static struct change *
398 find_hunk (struct change *start)
404 /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable
405 changes, but only CONTEXT if one is ignorable. Watch out for
406 integer overflow, though. */
407 lin non_ignorable_threshold =
408 (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
409 lin ignorable_threshold = context;
413 /* Compute number of first line in each file beyond this changed. */
414 top0 = start->line0 + start->deleted;
415 top1 = start->line1 + start->inserted;
418 thresh = (prev->ignore || (start && start->ignore)
419 ? ignorable_threshold
420 : non_ignorable_threshold);
421 /* It is not supposed to matter which file we check in the end-test.
422 If it would matter, crash. */
423 if (start && start->line0 - top0 != start->line1 - top1)
426 /* Keep going if less than THRESH lines
427 elapse before the affected line. */
428 && start->line0 - top0 < thresh);
433 /* Set the `ignore' flag properly in each change in SCRIPT.
434 It should be 1 if all the lines inserted or deleted in that change
435 are ignorable lines. */
438 mark_ignorable (struct change *script)
442 struct change *next = script->link;
443 lin first0, last0, first1, last1;
445 /* Turn this change into a hunk: detach it from the others. */
448 /* Determine whether this change is ignorable. */
449 script->ignore = ! analyze_hunk (script,
450 &first0, &last0, &first1, &last1);
452 /* Reconnect the chain as before. */
455 /* Advance to the following change. */
460 /* Find the last function-header line in LINBUF prior to line number LINENUM.
461 This is a line containing a match for the regexp in `function_regexp'.
462 Return the address of the text, or NULL if no function-header is found. */
465 find_function (char const * const *linbuf, lin linenum)
468 lin last = find_function_last_search;
469 find_function_last_search = i;
473 /* See if this line is what we want. */
474 char const *line = linbuf[i];
475 size_t linelen = linbuf[i + 1] - line - 1;
477 /* FIXME: re_search's size args should be size_t, not int. */
478 int len = MIN (linelen, INT_MAX);
480 if (0 <= re_search (&function_regexp, line, len, 0, len, NULL))
482 find_function_last_match = i;
486 /* If we search back to where we started searching the previous time,
487 find the line we found last time. */
488 if (find_function_last_match != LIN_MAX)
489 return linbuf[find_function_last_match];