kgdb(1): Add missing "
[dragonfly.git] / contrib / diffutils / src / context.c
blobdd79f898234e5e3cd571ddd5d58fc3732de8baaa
1 /* Context-format output routines for GNU DIFF.
3 Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2006, 2009-2013
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/>. */
21 #include "diff.h"
22 #include "c-ctype.h"
23 #include <stat-time.h>
24 #include <strftime.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. */
40 static void
41 print_context_label (char const *mark,
42 struct file_data *inf,
43 char const *name,
44 char const *label)
46 if (label)
47 fprintf (outfile, "%s %s\n", mark, label);
48 else
50 char buf[MAX (INT_STRLEN_BOUND (int) + 32,
51 INT_STRLEN_BOUND (time_t) + 11)];
52 struct tm const *tm = localtime (&inf->stat.st_mtime);
53 int nsec = get_stat_mtime_ns (&inf->stat);
54 if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
56 verify (TYPE_IS_INTEGER (time_t));
57 if (LONG_MIN <= TYPE_MINIMUM (time_t)
58 && TYPE_MAXIMUM (time_t) <= LONG_MAX)
60 long int sec = inf->stat.st_mtime;
61 sprintf (buf, "%ld.%.9d", sec, nsec);
63 else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX)
65 intmax_t sec = inf->stat.st_mtime;
66 sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec);
68 else
70 uintmax_t sec = inf->stat.st_mtime;
71 sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
74 fprintf (outfile, "%s %s\t%s\n", mark, name, buf);
78 /* Print a header for a context diff, with the file names and dates. */
80 void
81 print_context_header (struct file_data inf[], char const *const *names, bool unidiff)
83 if (unidiff)
85 print_context_label ("---", &inf[0], names[0], file_label[0]);
86 print_context_label ("+++", &inf[1], names[1], file_label[1]);
88 else
90 print_context_label ("***", &inf[0], names[0], file_label[0]);
91 print_context_label ("---", &inf[1], names[1], file_label[1]);
95 /* Print an edit script in context format. */
97 void
98 print_context_script (struct change *script, bool unidiff)
100 if (ignore_blank_lines || ignore_regexp.fastmap)
101 mark_ignorable (script);
102 else
104 struct change *e;
105 for (e = script; e; e = e->link)
106 e->ignore = false;
109 find_function_last_search = - files[0].prefix_lines;
110 find_function_last_match = LIN_MAX;
112 if (unidiff)
113 print_script (script, find_hunk, pr_unidiff_hunk);
114 else
115 print_script (script, find_hunk, pr_context_hunk);
118 /* Print a pair of line numbers with a comma, translated for file FILE.
119 If the second number is not greater, use the first in place of it.
121 Args A and B are internal line numbers.
122 We print the translated (real) line numbers. */
124 static void
125 print_context_number_range (struct file_data const *file, lin a, lin b)
127 long int trans_a, trans_b;
128 translate_range (file, a, b, &trans_a, &trans_b);
130 /* We can have B <= A in the case of a range of no lines.
131 In this case, we should print the line number before the range,
132 which is B.
134 POSIX 1003.1-2001 requires two line numbers separated by a comma
135 even if the line numbers are the same. However, this does not
136 match existing practice and is surely an error in the
137 specification. */
139 if (trans_b <= trans_a)
140 fprintf (outfile, "%ld", trans_b);
141 else
142 fprintf (outfile, "%ld,%ld", trans_a, trans_b);
145 /* Print FUNCTION in a context header. */
146 static void
147 print_context_function (FILE *out, char const *function)
149 int i, j;
150 putc (' ', out);
151 for (i = 0; c_isspace ((unsigned char) function[i]) && function[i] != '\n'; i++)
152 continue;
153 for (j = i; j < i + 40 && function[j] != '\n'; j++)
154 continue;
155 while (i < j && c_isspace ((unsigned char) function[j - 1]))
156 j--;
157 fwrite (function + i, sizeof (char), j - i, out);
160 /* Print a portion of an edit script in context format.
161 HUNK is the beginning of the portion to be printed.
162 The end is marked by a 'link' that has been nulled out.
164 Prints out lines from both files, and precedes each
165 line with the appropriate flag-character. */
167 static void
168 pr_context_hunk (struct change *hunk)
170 lin first0, last0, first1, last1, i;
171 char const *prefix;
172 char const *function;
173 FILE *out;
175 /* Determine range of line numbers involved in each file. */
177 enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
178 if (! changes)
179 return;
181 /* Include a context's width before and after. */
183 i = - files[0].prefix_lines;
184 first0 = MAX (first0 - context, i);
185 first1 = MAX (first1 - context, i);
186 if (last0 < files[0].valid_lines - context)
187 last0 += context;
188 else
189 last0 = files[0].valid_lines - 1;
190 if (last1 < files[1].valid_lines - context)
191 last1 += context;
192 else
193 last1 = files[1].valid_lines - 1;
195 /* If desired, find the preceding function definition line in file 0. */
196 function = NULL;
197 if (function_regexp.fastmap)
198 function = find_function (files[0].linbuf, first0);
200 begin_output ();
201 out = outfile;
203 fputs ("***************", out);
205 if (function)
206 print_context_function (out, function);
208 fputs ("\n*** ", out);
209 print_context_number_range (&files[0], first0, last0);
210 fputs (" ****\n", out);
212 if (changes & OLD)
214 struct change *next = hunk;
216 for (i = first0; i <= last0; i++)
218 /* Skip past changes that apply (in file 0)
219 only to lines before line I. */
221 while (next && next->line0 + next->deleted <= i)
222 next = next->link;
224 /* Compute the marking for line I. */
226 prefix = " ";
227 if (next && next->line0 <= i)
228 /* The change NEXT covers this line.
229 If lines were inserted here in file 1, this is "changed".
230 Otherwise it is "deleted". */
231 prefix = (next->inserted > 0 ? "!" : "-");
233 print_1_line (prefix, &files[0].linbuf[i]);
237 fputs ("--- ", out);
238 print_context_number_range (&files[1], first1, last1);
239 fputs (" ----\n", out);
241 if (changes & NEW)
243 struct change *next = hunk;
245 for (i = first1; i <= last1; i++)
247 /* Skip past changes that apply (in file 1)
248 only to lines before line I. */
250 while (next && next->line1 + next->inserted <= i)
251 next = next->link;
253 /* Compute the marking for line I. */
255 prefix = " ";
256 if (next && next->line1 <= i)
257 /* The change NEXT covers this line.
258 If lines were deleted here in file 0, this is "changed".
259 Otherwise it is "inserted". */
260 prefix = (next->deleted > 0 ? "!" : "+");
262 print_1_line (prefix, &files[1].linbuf[i]);
267 /* Print a pair of line numbers with a comma, translated for file FILE.
268 If the second number is smaller, use the first in place of it.
269 If the numbers are equal, print just one number.
271 Args A and B are internal line numbers.
272 We print the translated (real) line numbers. */
274 static void
275 print_unidiff_number_range (struct file_data const *file, lin a, lin b)
277 long int trans_a, trans_b;
278 translate_range (file, a, b, &trans_a, &trans_b);
280 /* We can have B < A in the case of a range of no lines.
281 In this case, we print the line number before the range,
282 which is B. It would be more logical to print A, but
283 'patch' expects B in order to detect diffs against empty files. */
284 if (trans_b <= trans_a)
285 fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
286 else
287 fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1);
290 /* Print a portion of an edit script in unidiff format.
291 HUNK is the beginning of the portion to be printed.
292 The end is marked by a 'link' that has been nulled out.
294 Prints out lines from both files, and precedes each
295 line with the appropriate flag-character. */
297 static void
298 pr_unidiff_hunk (struct change *hunk)
300 lin first0, last0, first1, last1;
301 lin i, j, k;
302 struct change *next;
303 char const *function;
304 FILE *out;
306 /* Determine range of line numbers involved in each file. */
308 if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
309 return;
311 /* Include a context's width before and after. */
313 i = - files[0].prefix_lines;
314 first0 = MAX (first0 - context, i);
315 first1 = MAX (first1 - context, i);
316 if (last0 < files[0].valid_lines - context)
317 last0 += context;
318 else
319 last0 = files[0].valid_lines - 1;
320 if (last1 < files[1].valid_lines - context)
321 last1 += context;
322 else
323 last1 = files[1].valid_lines - 1;
325 /* If desired, find the preceding function definition line in file 0. */
326 function = NULL;
327 if (function_regexp.fastmap)
328 function = find_function (files[0].linbuf, first0);
330 begin_output ();
331 out = outfile;
333 fputs ("@@ -", out);
334 print_unidiff_number_range (&files[0], first0, last0);
335 fputs (" +", out);
336 print_unidiff_number_range (&files[1], first1, last1);
337 fputs (" @@", out);
339 if (function)
340 print_context_function (out, function);
342 putc ('\n', out);
344 next = hunk;
345 i = first0;
346 j = first1;
348 while (i <= last0 || j <= last1)
351 /* If the line isn't a difference, output the context from file 0. */
353 if (!next || i < next->line0)
355 char const *const *line = &files[0].linbuf[i++];
356 if (! (suppress_blank_empty && **line == '\n'))
357 putc (initial_tab ? '\t' : ' ', out);
358 print_1_line (NULL, line);
359 j++;
361 else
363 /* For each difference, first output the deleted part. */
365 k = next->deleted;
366 while (k--)
368 char const * const *line = &files[0].linbuf[i++];
369 putc ('-', out);
370 if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
371 putc ('\t', out);
372 print_1_line (NULL, line);
375 /* Then output the inserted part. */
377 k = next->inserted;
378 while (k--)
380 char const * const *line = &files[1].linbuf[j++];
381 putc ('+', out);
382 if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
383 putc ('\t', out);
384 print_1_line (NULL, line);
387 /* We're done with this hunk, so on to the next! */
389 next = next->link;
394 /* Scan a (forward-ordered) edit script for the first place that more than
395 2*CONTEXT unchanged lines appear, and return a pointer
396 to the 'struct change' for the last change before those lines. */
398 static struct change * _GL_ATTRIBUTE_PURE
399 find_hunk (struct change *start)
401 struct change *prev;
402 lin top0, top1;
403 lin thresh;
405 /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable
406 changes, but only CONTEXT if one is ignorable. Watch out for
407 integer overflow, though. */
408 lin non_ignorable_threshold =
409 (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
410 lin ignorable_threshold = context;
414 /* Compute number of first line in each file beyond this changed. */
415 top0 = start->line0 + start->deleted;
416 top1 = start->line1 + start->inserted;
417 prev = start;
418 start = start->link;
419 thresh = (prev->ignore || (start && start->ignore)
420 ? ignorable_threshold
421 : non_ignorable_threshold);
422 /* It is not supposed to matter which file we check in the end-test.
423 If it would matter, crash. */
424 if (start && start->line0 - top0 != start->line1 - top1)
425 abort ();
426 } while (start
427 /* Keep going if less than THRESH lines
428 elapse before the affected line. */
429 && start->line0 - top0 < thresh);
431 return prev;
434 /* Set the 'ignore' flag properly in each change in SCRIPT.
435 It should be 1 if all the lines inserted or deleted in that change
436 are ignorable lines. */
438 static void
439 mark_ignorable (struct change *script)
441 while (script)
443 struct change *next = script->link;
444 lin first0, last0, first1, last1;
446 /* Turn this change into a hunk: detach it from the others. */
447 script->link = NULL;
449 /* Determine whether this change is ignorable. */
450 script->ignore = ! analyze_hunk (script,
451 &first0, &last0, &first1, &last1);
453 /* Reconnect the chain as before. */
454 script->link = next;
456 /* Advance to the following change. */
457 script = next;
461 /* Find the last function-header line in LINBUF prior to line number LINENUM.
462 This is a line containing a match for the regexp in 'function_regexp'.
463 Return the address of the text, or NULL if no function-header is found. */
465 static char const *
466 find_function (char const * const *linbuf, lin linenum)
468 lin i = linenum;
469 lin last = find_function_last_search;
470 find_function_last_search = i;
472 while (last <= --i)
474 /* See if this line is what we want. */
475 char const *line = linbuf[i];
476 size_t linelen = linbuf[i + 1] - line - 1;
478 /* FIXME: re_search's size args should be size_t, not int. */
479 int len = MIN (linelen, INT_MAX);
481 if (0 <= re_search (&function_regexp, line, len, 0, len, NULL))
483 find_function_last_match = i;
484 return line;
487 /* If we search back to where we started searching the previous time,
488 find the line we found last time. */
489 if (find_function_last_match != LIN_MAX)
490 return linbuf[find_function_last_match];
492 return NULL;