1 /* $NetBSD: context.c,v 1.1.1.1 2003/01/26 00:43:16 wiz Exp $ */
3 /* Context-format output routines for GNU DIFF.
5 Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1998, 2001,
6 2002 Free Software Foundation, Inc.
8 This file is part of GNU DIFF.
10 GNU DIFF is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
15 GNU DIFF is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; see the file COPYING.
22 If not, write to the Free Software Foundation,
23 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
30 # define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
32 # define TIMESPEC_NS(timespec) 0
35 size_t nstrftime (char *, size_t, char const *, struct tm
const *, int, int);
37 static char const *find_function (char const * const *, lin
);
38 static struct change
*find_hunk (struct change
*);
39 static void mark_ignorable (struct change
*);
40 static void pr_context_hunk (struct change
*);
41 static void pr_unidiff_hunk (struct change
*);
43 /* Last place find_function started searching from. */
44 static lin find_function_last_search
;
46 /* The value find_function returned when it started searching there. */
47 static lin find_function_last_match
;
49 /* Print a label for a context diff, with a file name and date or a label. */
52 print_context_label (char const *mark
,
53 struct file_data
*inf
,
57 fprintf (outfile
, "%s %s\n", mark
, label
);
60 char buf
[MAX (INT_STRLEN_BOUND (int) + 32,
61 INT_STRLEN_BOUND (time_t) + 11)];
62 struct tm
const *tm
= localtime (&inf
->stat
.st_mtime
);
63 int nsec
= TIMESPEC_NS (inf
->stat
.st_mtim
);
64 if (! (tm
&& nstrftime (buf
, sizeof buf
, time_format
, tm
, 0, nsec
)))
66 long long sec
= inf
->stat
.st_mtime
;
67 verify (info_preserved
, sizeof inf
->stat
.st_mtime
<= sizeof sec
);
68 sprintf (buf
, "%lld.%.9d", sec
, nsec
);
70 fprintf (outfile
, "%s %s\t%s\n", mark
, inf
->name
, buf
);
74 /* Print a header for a context diff, with the file names and dates. */
77 print_context_header (struct file_data inf
[], bool unidiff
)
81 print_context_label ("---", &inf
[0], file_label
[0]);
82 print_context_label ("+++", &inf
[1], file_label
[1]);
86 print_context_label ("***", &inf
[0], file_label
[0]);
87 print_context_label ("---", &inf
[1], file_label
[1]);
91 /* Print an edit script in context format. */
94 print_context_script (struct change
*script
, bool unidiff
)
96 if (ignore_blank_lines
|| ignore_regexp
.fastmap
)
97 mark_ignorable (script
);
101 for (e
= script
; e
; e
= e
->link
)
105 find_function_last_search
= - files
[0].prefix_lines
;
106 find_function_last_match
= LIN_MAX
;
109 print_script (script
, find_hunk
, pr_unidiff_hunk
);
111 print_script (script
, find_hunk
, pr_context_hunk
);
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.
117 Args A and B are internal line numbers.
118 We print the translated (real) line numbers. */
121 print_context_number_range (struct file_data
const *file
, lin a
, lin b
)
123 long trans_a
, trans_b
;
124 translate_range (file
, a
, b
, &trans_a
, &trans_b
);
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,
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
135 if (trans_b
<= trans_a
)
136 fprintf (outfile
, "%ld", trans_b
);
138 fprintf (outfile
, "%ld,%ld", trans_a
, trans_b
);
141 /* Print FUNCTION in a context header. */
143 print_context_function (FILE *out
, char const *function
)
147 for (i
= 0; i
< 40 && function
[i
] != '\n'; i
++)
149 fwrite (function
, 1, i
, out
);
152 /* Print a portion of an edit script in context format.
153 HUNK is the beginning of the portion to be printed.
154 The end is marked by a `link' that has been nulled out.
156 Prints out lines from both files, and precedes each
157 line with the appropriate flag-character. */
160 pr_context_hunk (struct change
*hunk
)
162 lin first0
, last0
, first1
, last1
, i
;
164 char const *function
;
167 /* Determine range of line numbers involved in each file. */
169 enum changes changes
= analyze_hunk (hunk
, &first0
, &last0
, &first1
, &last1
);
173 /* Include a context's width before and after. */
175 i
= - files
[0].prefix_lines
;
176 first0
= MAX (first0
- context
, i
);
177 first1
= MAX (first1
- context
, i
);
178 if (last0
< files
[0].valid_lines
- context
)
181 last0
= files
[0].valid_lines
- 1;
182 if (last1
< files
[1].valid_lines
- context
)
185 last1
= files
[1].valid_lines
- 1;
187 /* If desired, find the preceding function definition line in file 0. */
189 if (function_regexp
.fastmap
)
190 function
= find_function (files
[0].linbuf
, first0
);
195 fprintf (out
, "***************");
198 print_context_function (out
, function
);
200 fprintf (out
, "\n*** ");
201 print_context_number_range (&files
[0], first0
, last0
);
202 fprintf (out
, " ****\n");
206 struct change
*next
= hunk
;
208 for (i
= first0
; i
<= last0
; i
++)
210 /* Skip past changes that apply (in file 0)
211 only to lines before line I. */
213 while (next
&& next
->line0
+ next
->deleted
<= i
)
216 /* Compute the marking for line I. */
219 if (next
&& next
->line0
<= i
)
220 /* The change NEXT covers this line.
221 If lines were inserted here in file 1, this is "changed".
222 Otherwise it is "deleted". */
223 prefix
= (next
->inserted
> 0 ? "!" : "-");
225 print_1_line (prefix
, &files
[0].linbuf
[i
]);
229 fprintf (out
, "--- ");
230 print_context_number_range (&files
[1], first1
, last1
);
231 fprintf (out
, " ----\n");
235 struct change
*next
= hunk
;
237 for (i
= first1
; i
<= last1
; i
++)
239 /* Skip past changes that apply (in file 1)
240 only to lines before line I. */
242 while (next
&& next
->line1
+ next
->inserted
<= i
)
245 /* Compute the marking for line I. */
248 if (next
&& next
->line1
<= i
)
249 /* The change NEXT covers this line.
250 If lines were deleted here in file 0, this is "changed".
251 Otherwise it is "inserted". */
252 prefix
= (next
->deleted
> 0 ? "!" : "+");
254 print_1_line (prefix
, &files
[1].linbuf
[i
]);
259 /* Print a pair of line numbers with a comma, translated for file FILE.
260 If the second number is smaller, use the first in place of it.
261 If the numbers are equal, print just one number.
263 Args A and B are internal line numbers.
264 We print the translated (real) line numbers. */
267 print_unidiff_number_range (struct file_data
const *file
, lin a
, lin b
)
269 long trans_a
, trans_b
;
270 translate_range (file
, a
, b
, &trans_a
, &trans_b
);
272 /* We can have B < A in the case of a range of no lines.
273 In this case, we should print the line number before the range,
275 if (trans_b
<= trans_a
)
276 fprintf (outfile
, trans_b
< trans_a
? "%ld,0" : "%ld", trans_b
);
278 fprintf (outfile
, "%ld,%ld", trans_a
, trans_b
- trans_a
+ 1);
281 /* Print a portion of an edit script in unidiff format.
282 HUNK is the beginning of the portion to be printed.
283 The end is marked by a `link' that has been nulled out.
285 Prints out lines from both files, and precedes each
286 line with the appropriate flag-character. */
289 pr_unidiff_hunk (struct change
*hunk
)
291 lin first0
, last0
, first1
, last1
;
294 char const *function
;
297 /* Determine range of line numbers involved in each file. */
299 if (! analyze_hunk (hunk
, &first0
, &last0
, &first1
, &last1
))
302 /* Include a context's width before and after. */
304 i
= - files
[0].prefix_lines
;
305 first0
= MAX (first0
- context
, i
);
306 first1
= MAX (first1
- context
, i
);
307 if (last0
< files
[0].valid_lines
- context
)
310 last0
= files
[0].valid_lines
- 1;
311 if (last1
< files
[1].valid_lines
- context
)
314 last1
= files
[1].valid_lines
- 1;
316 /* If desired, find the preceding function definition line in file 0. */
318 if (function_regexp
.fastmap
)
319 function
= find_function (files
[0].linbuf
, first0
);
324 fprintf (out
, "@@ -");
325 print_unidiff_number_range (&files
[0], first0
, last0
);
327 print_unidiff_number_range (&files
[1], first1
, last1
);
328 fprintf (out
, " @@");
331 print_context_function (out
, function
);
339 while (i
<= last0
|| j
<= last1
)
342 /* If the line isn't a difference, output the context from file 0. */
344 if (!next
|| i
< next
->line0
)
346 putc (initial_tab
? '\t' : ' ', out
);
347 print_1_line (0, &files
[0].linbuf
[i
++]);
352 /* For each difference, first output the deleted part. */
360 print_1_line (0, &files
[0].linbuf
[i
++]);
363 /* Then output the inserted part. */
371 print_1_line (0, &files
[1].linbuf
[j
++]);
374 /* We're done with this hunk, so on to the next! */
381 /* Scan a (forward-ordered) edit script for the first place that more than
382 2*CONTEXT unchanged lines appear, and return a pointer
383 to the `struct change' for the last change before those lines. */
385 static struct change
*
386 find_hunk (struct change
*start
)
392 /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable
393 changes, but only CONTEXT if one is ignorable. Watch out for
394 integer overflow, though. */
395 lin non_ignorable_threshold
=
396 (LIN_MAX
- 1) / 2 < context
? LIN_MAX
: 2 * context
+ 1;
397 lin ignorable_threshold
= context
;
401 /* Compute number of first line in each file beyond this changed. */
402 top0
= start
->line0
+ start
->deleted
;
403 top1
= start
->line1
+ start
->inserted
;
406 thresh
= (prev
->ignore
|| (start
&& start
->ignore
)
407 ? ignorable_threshold
408 : non_ignorable_threshold
);
409 /* It is not supposed to matter which file we check in the end-test.
410 If it would matter, crash. */
411 if (start
&& start
->line0
- top0
!= start
->line1
- top1
)
414 /* Keep going if less than THRESH lines
415 elapse before the affected line. */
416 && start
->line0
- top0
< thresh
);
421 /* Set the `ignore' flag properly in each change in SCRIPT.
422 It should be 1 if all the lines inserted or deleted in that change
423 are ignorable lines. */
426 mark_ignorable (struct change
*script
)
430 struct change
*next
= script
->link
;
431 lin first0
, last0
, first1
, last1
;
433 /* Turn this change into a hunk: detach it from the others. */
436 /* Determine whether this change is ignorable. */
437 script
->ignore
= ! analyze_hunk (script
,
438 &first0
, &last0
, &first1
, &last1
);
440 /* Reconnect the chain as before. */
443 /* Advance to the following change. */
448 /* Find the last function-header line in LINBUF prior to line number LINENUM.
449 This is a line containing a match for the regexp in `function_regexp'.
450 Return the address of the text, or 0 if no function-header is found. */
453 find_function (char const * const *linbuf
, lin linenum
)
456 lin last
= find_function_last_search
;
457 find_function_last_search
= i
;
461 /* See if this line is what we want. */
462 char const *line
= linbuf
[i
];
463 size_t linelen
= linbuf
[i
+ 1] - line
- 1;
465 /* FIXME: re_search's size args should be size_t, not int. */
466 int len
= MIN (linelen
, INT_MAX
);
468 if (0 <= re_search (&function_regexp
, line
, len
, 0, len
, 0))
470 find_function_last_match
= i
;
474 /* If we search back to where we started searching the previous time,
475 find the line we found last time. */
476 if (find_function_last_match
!= LIN_MAX
)
477 return linbuf
[find_function_last_match
];