Sync usage with man page.
[netbsd-mini2440.git] / gnu / dist / diffutils / src / context.c
bloba5bcf682e0c7f200732664375d1e8ae877feddf4
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)
13 any later version.
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. */
25 #include "diff.h"
26 #include <inttostr.h>
27 #include <regex.h>
29 #ifdef ST_MTIM_NSEC
30 # define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
31 #else
32 # define TIMESPEC_NS(timespec) 0
33 #endif
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. */
51 static void
52 print_context_label (char const *mark,
53 struct file_data *inf,
54 char const *label)
56 if (label)
57 fprintf (outfile, "%s %s\n", mark, label);
58 else
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. */
76 void
77 print_context_header (struct file_data inf[], bool unidiff)
79 if (unidiff)
81 print_context_label ("---", &inf[0], file_label[0]);
82 print_context_label ("+++", &inf[1], file_label[1]);
84 else
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. */
93 void
94 print_context_script (struct change *script, bool unidiff)
96 if (ignore_blank_lines || ignore_regexp.fastmap)
97 mark_ignorable (script);
98 else
100 struct change *e;
101 for (e = script; e; e = e->link)
102 e->ignore = 0;
105 find_function_last_search = - files[0].prefix_lines;
106 find_function_last_match = LIN_MAX;
108 if (unidiff)
109 print_script (script, find_hunk, pr_unidiff_hunk);
110 else
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. */
120 static void
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,
128 which is B.
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. */
135 if (trans_b <= trans_a)
136 fprintf (outfile, "%ld", trans_b);
137 else
138 fprintf (outfile, "%ld,%ld", trans_a, trans_b);
141 /* Print FUNCTION in a context header. */
142 static void
143 print_context_function (FILE *out, char const *function)
145 int i;
146 putc (' ', out);
147 for (i = 0; i < 40 && function[i] != '\n'; i++)
148 continue;
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. */
159 static void
160 pr_context_hunk (struct change *hunk)
162 lin first0, last0, first1, last1, i;
163 char const *prefix;
164 char const *function;
165 FILE *out;
167 /* Determine range of line numbers involved in each file. */
169 enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
170 if (! changes)
171 return;
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)
179 last0 += context;
180 else
181 last0 = files[0].valid_lines - 1;
182 if (last1 < files[1].valid_lines - context)
183 last1 += context;
184 else
185 last1 = files[1].valid_lines - 1;
187 /* If desired, find the preceding function definition line in file 0. */
188 function = 0;
189 if (function_regexp.fastmap)
190 function = find_function (files[0].linbuf, first0);
192 begin_output ();
193 out = outfile;
195 fprintf (out, "***************");
197 if (function)
198 print_context_function (out, function);
200 fprintf (out, "\n*** ");
201 print_context_number_range (&files[0], first0, last0);
202 fprintf (out, " ****\n");
204 if (changes & OLD)
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)
214 next = next->link;
216 /* Compute the marking for line I. */
218 prefix = " ";
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");
233 if (changes & NEW)
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)
243 next = next->link;
245 /* Compute the marking for line I. */
247 prefix = " ";
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. */
266 static void
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,
274 which is B. */
275 if (trans_b <= trans_a)
276 fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
277 else
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. */
288 static void
289 pr_unidiff_hunk (struct change *hunk)
291 lin first0, last0, first1, last1;
292 lin i, j, k;
293 struct change *next;
294 char const *function;
295 FILE *out;
297 /* Determine range of line numbers involved in each file. */
299 if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
300 return;
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)
308 last0 += context;
309 else
310 last0 = files[0].valid_lines - 1;
311 if (last1 < files[1].valid_lines - context)
312 last1 += context;
313 else
314 last1 = files[1].valid_lines - 1;
316 /* If desired, find the preceding function definition line in file 0. */
317 function = 0;
318 if (function_regexp.fastmap)
319 function = find_function (files[0].linbuf, first0);
321 begin_output ();
322 out = outfile;
324 fprintf (out, "@@ -");
325 print_unidiff_number_range (&files[0], first0, last0);
326 fprintf (out, " +");
327 print_unidiff_number_range (&files[1], first1, last1);
328 fprintf (out, " @@");
330 if (function)
331 print_context_function (out, function);
333 putc ('\n', out);
335 next = hunk;
336 i = first0;
337 j = first1;
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++]);
348 j++;
350 else
352 /* For each difference, first output the deleted part. */
354 k = next->deleted;
355 while (k--)
357 putc ('-', out);
358 if (initial_tab)
359 putc ('\t', out);
360 print_1_line (0, &files[0].linbuf[i++]);
363 /* Then output the inserted part. */
365 k = next->inserted;
366 while (k--)
368 putc ('+', out);
369 if (initial_tab)
370 putc ('\t', out);
371 print_1_line (0, &files[1].linbuf[j++]);
374 /* We're done with this hunk, so on to the next! */
376 next = next->link;
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)
388 struct change *prev;
389 lin top0, top1;
390 lin thresh;
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;
404 prev = start;
405 start = start->link;
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)
412 abort ();
413 } while (start
414 /* Keep going if less than THRESH lines
415 elapse before the affected line. */
416 && start->line0 - top0 < thresh);
418 return prev;
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. */
425 static void
426 mark_ignorable (struct change *script)
428 while (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. */
434 script->link = 0;
436 /* Determine whether this change is ignorable. */
437 script->ignore = ! analyze_hunk (script,
438 &first0, &last0, &first1, &last1);
440 /* Reconnect the chain as before. */
441 script->link = next;
443 /* Advance to the following change. */
444 script = next;
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. */
452 static char const *
453 find_function (char const * const *linbuf, lin linenum)
455 lin i = linenum;
456 lin last = find_function_last_search;
457 find_function_last_search = i;
459 while (last <= --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;
471 return line;
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];
479 return 0;