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