]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/diff/context.c
Import cvs-1.9.23 as at 19980123. There are a number of really nice
[FreeBSD/FreeBSD.git] / contrib / cvs / diff / context.c
1 /* Context-format output routines for GNU DIFF.
2    Copyright (C) 1988,1989,1991,1992,1993,1994 Free Software Foundation, Inc.
3
4 This file is part of GNU DIFF.
5
6 GNU DIFF is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU DIFF is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU DIFF; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include "diff.h"
21
22 static struct change *find_hunk PARAMS((struct change *));
23 static void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
24 static void mark_ignorable PARAMS((struct change *));
25 static void pr_context_hunk PARAMS((struct change *));
26 static void pr_unidiff_hunk PARAMS((struct change *));
27 static void print_context_label PARAMS ((char const *, struct file_data *, char const *));
28 static void print_context_number_range PARAMS((struct file_data const *, int, int));
29 static void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
30
31 /* Last place find_function started searching from.  */
32 static int find_function_last_search;
33
34 /* The value find_function returned when it started searching there.  */
35 static int find_function_last_match;
36 \f
37 /* Print a label for a context diff, with a file name and date or a label.  */
38
39 static void
40 print_context_label (mark, inf, label)
41      char const *mark;
42      struct file_data *inf;
43      char const *label;
44 {
45   if (label)
46     fprintf (outfile, "%s %s\n", mark, label);
47   else
48     {
49       char const *ct = ctime (&inf->stat.st_mtime);
50       if (!ct)
51         ct = "?\n";
52       /* See Posix.2 section 4.17.6.1.4 for this format.  */
53       fprintf (outfile, "%s %s\t%s", mark, inf->name, ct);
54     }
55 }
56
57 /* Print a header for a context diff, with the file names and dates.  */
58
59 void
60 print_context_header (inf, unidiff_flag)
61      struct file_data inf[];
62      int unidiff_flag;
63 {
64   if (unidiff_flag)
65     {
66       print_context_label ("---", &inf[0], file_label[0]);
67       print_context_label ("+++", &inf[1], file_label[1]);
68     }
69   else
70     {
71       print_context_label ("***", &inf[0], file_label[0]);
72       print_context_label ("---", &inf[1], file_label[1]);
73     }
74 }
75
76 /* Print an edit script in context format.  */
77
78 void
79 print_context_script (script, unidiff_flag)
80      struct change *script;
81      int unidiff_flag;
82 {
83   if (ignore_blank_lines_flag || ignore_regexp_list)
84     mark_ignorable (script);
85   else
86     {
87       struct change *e;
88       for (e = script; e; e = e->link)
89         e->ignore = 0;
90     }
91
92   find_function_last_search = - files[0].prefix_lines;
93   find_function_last_match = find_function_last_search - 1;
94
95   if (unidiff_flag)
96     print_script (script, find_hunk, pr_unidiff_hunk);
97   else
98     print_script (script, find_hunk, pr_context_hunk);
99 }
100 \f
101 /* Print a pair of line numbers with a comma, translated for file FILE.
102    If the second number is not greater, use the first in place of it.
103
104    Args A and B are internal line numbers.
105    We print the translated (real) line numbers.  */
106
107 static void
108 print_context_number_range (file, a, b)
109      struct file_data const *file;
110      int a, b;
111 {
112   int trans_a, trans_b;
113   translate_range (file, a, b, &trans_a, &trans_b);
114
115   /* Note: we can have B < A in the case of a range of no lines.
116      In this case, we should print the line number before the range,
117      which is B.  */
118   if (trans_b > trans_a)
119     fprintf (outfile, "%d,%d", trans_a, trans_b);
120   else
121     fprintf (outfile, "%d", trans_b);
122 }
123 \f
124 /* Print a portion of an edit script in context format.
125    HUNK is the beginning of the portion to be printed.
126    The end is marked by a `link' that has been nulled out.
127
128    Prints out lines from both files, and precedes each
129    line with the appropriate flag-character.  */
130
131 static void
132 pr_context_hunk (hunk)
133      struct change *hunk;
134 {
135   int first0, last0, first1, last1, show_from, show_to, i;
136   struct change *next;
137   char const *prefix;
138   char const *function;
139   size_t function_length;
140   FILE *out;
141
142   /* Determine range of line numbers involved in each file.  */
143
144   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
145
146   if (!show_from && !show_to)
147     return;
148
149   /* Include a context's width before and after.  */
150
151   i = - files[0].prefix_lines;
152   first0 = max (first0 - context, i);
153   first1 = max (first1 - context, i);
154   last0 = min (last0 + context, files[0].valid_lines - 1);
155   last1 = min (last1 + context, files[1].valid_lines - 1);
156
157   /* If desired, find the preceding function definition line in file 0.  */
158   function = 0;
159   if (function_regexp_list)
160     find_function (&files[0], first0, &function, &function_length);
161
162   begin_output ();
163   out = outfile;
164
165   /* If we looked for and found a function this is part of,
166      include its name in the header of the diff section.  */
167   fprintf (out, "***************");
168
169   if (function)
170     {
171       fprintf (out, " ");
172       fwrite (function, 1, min (function_length - 1, 40), out);
173     }
174
175   fprintf (out, "\n*** ");
176   print_context_number_range (&files[0], first0, last0);
177   fprintf (out, " ****\n");
178
179   if (show_from)
180     {
181       next = hunk;
182
183       for (i = first0; i <= last0; i++)
184         {
185           /* Skip past changes that apply (in file 0)
186              only to lines before line I.  */
187
188           while (next && next->line0 + next->deleted <= i)
189             next = next->link;
190
191           /* Compute the marking for line I.  */
192
193           prefix = " ";
194           if (next && next->line0 <= i)
195             /* The change NEXT covers this line.
196                If lines were inserted here in file 1, this is "changed".
197                Otherwise it is "deleted".  */
198             prefix = (next->inserted > 0 ? "!" : "-");
199
200           print_1_line (prefix, &files[0].linbuf[i]);
201         }
202     }
203
204   fprintf (out, "--- ");
205   print_context_number_range (&files[1], first1, last1);
206   fprintf (out, " ----\n");
207
208   if (show_to)
209     {
210       next = hunk;
211
212       for (i = first1; i <= last1; i++)
213         {
214           /* Skip past changes that apply (in file 1)
215              only to lines before line I.  */
216
217           while (next && next->line1 + next->inserted <= i)
218             next = next->link;
219
220           /* Compute the marking for line I.  */
221
222           prefix = " ";
223           if (next && next->line1 <= i)
224             /* The change NEXT covers this line.
225                If lines were deleted here in file 0, this is "changed".
226                Otherwise it is "inserted".  */
227             prefix = (next->deleted > 0 ? "!" : "+");
228
229           print_1_line (prefix, &files[1].linbuf[i]);
230         }
231     }
232 }
233 \f
234 /* Print a pair of line numbers with a comma, translated for file FILE.
235    If the second number is smaller, use the first in place of it.
236    If the numbers are equal, print just one number.
237
238    Args A and B are internal line numbers.
239    We print the translated (real) line numbers.  */
240
241 static void
242 print_unidiff_number_range (file, a, b)
243      struct file_data const *file;
244      int a, b;
245 {
246   int trans_a, trans_b;
247   translate_range (file, a, b, &trans_a, &trans_b);
248
249   /* Note: we can have B < A in the case of a range of no lines.
250      In this case, we should print the line number before the range,
251      which is B.  */
252   if (trans_b <= trans_a)
253     fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b);
254   else
255     fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1);
256 }
257 \f
258 /* Print a portion of an edit script in unidiff format.
259    HUNK is the beginning of the portion to be printed.
260    The end is marked by a `link' that has been nulled out.
261
262    Prints out lines from both files, and precedes each
263    line with the appropriate flag-character.  */
264
265 static void
266 pr_unidiff_hunk (hunk)
267      struct change *hunk;
268 {
269   int first0, last0, first1, last1, show_from, show_to, i, j, k;
270   struct change *next;
271   char const *function;
272   size_t function_length;
273   FILE *out;
274
275   /* Determine range of line numbers involved in each file.  */
276
277   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
278
279   if (!show_from && !show_to)
280     return;
281
282   /* Include a context's width before and after.  */
283
284   i = - files[0].prefix_lines;
285   first0 = max (first0 - context, i);
286   first1 = max (first1 - context, i);
287   last0 = min (last0 + context, files[0].valid_lines - 1);
288   last1 = min (last1 + context, files[1].valid_lines - 1);
289
290   /* If desired, find the preceding function definition line in file 0.  */
291   function = 0;
292   if (function_regexp_list)
293     find_function (&files[0], first0, &function, &function_length);
294
295   begin_output ();
296   out = outfile;
297
298   fprintf (out, "@@ -");
299   print_unidiff_number_range (&files[0], first0, last0);
300   fprintf (out, " +");
301   print_unidiff_number_range (&files[1], first1, last1);
302   fprintf (out, " @@");
303
304   /* If we looked for and found a function this is part of,
305      include its name in the header of the diff section.  */
306
307   if (function)
308     {
309       putc (' ', out);
310       fwrite (function, 1, min (function_length - 1, 40), out);
311     }
312   putc ('\n', out);
313
314   next = hunk;
315   i = first0;
316   j = first1;
317
318   while (i <= last0 || j <= last1)
319     {
320
321       /* If the line isn't a difference, output the context from file 0. */
322
323       if (!next || i < next->line0)
324         {
325           putc (tab_align_flag ? '\t' : ' ', out);
326           print_1_line (0, &files[0].linbuf[i++]);
327           j++;
328         }
329       else
330         {
331           /* For each difference, first output the deleted part. */
332
333           k = next->deleted;
334           while (k--)
335             {
336               putc ('-', out);
337               if (tab_align_flag)
338                 putc ('\t', out);
339               print_1_line (0, &files[0].linbuf[i++]);
340             }
341
342           /* Then output the inserted part. */
343
344           k = next->inserted;
345           while (k--)
346             {
347               putc ('+', out);
348               if (tab_align_flag)
349                 putc ('\t', out);
350               print_1_line (0, &files[1].linbuf[j++]);
351             }
352
353           /* We're done with this hunk, so on to the next! */
354
355           next = next->link;
356         }
357     }
358 }
359 \f
360 /* Scan a (forward-ordered) edit script for the first place that more than
361    2*CONTEXT unchanged lines appear, and return a pointer
362    to the `struct change' for the last change before those lines.  */
363
364 static struct change *
365 find_hunk (start)
366      struct change *start;
367 {
368   struct change *prev;
369   int top0, top1;
370   int thresh;
371
372   do
373     {
374       /* Compute number of first line in each file beyond this changed.  */
375       top0 = start->line0 + start->deleted;
376       top1 = start->line1 + start->inserted;
377       prev = start;
378       start = start->link;
379       /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
380          but only CONTEXT if one is ignorable.  */
381       thresh = ((prev->ignore || (start && start->ignore))
382                 ? context
383                 : 2 * context + 1);
384       /* It is not supposed to matter which file we check in the end-test.
385          If it would matter, crash.  */
386       if (start && start->line0 - top0 != start->line1 - top1)
387         abort ();
388     } while (start
389              /* Keep going if less than THRESH lines
390                 elapse before the affected line.  */
391              && start->line0 < top0 + thresh);
392
393   return prev;
394 }
395
396 /* Set the `ignore' flag properly in each change in SCRIPT.
397    It should be 1 if all the lines inserted or deleted in that change
398    are ignorable lines.  */
399
400 static void
401 mark_ignorable (script)
402      struct change *script;
403 {
404   while (script)
405     {
406       struct change *next = script->link;
407       int first0, last0, first1, last1, deletes, inserts;
408
409       /* Turn this change into a hunk: detach it from the others.  */
410       script->link = 0;
411
412       /* Determine whether this change is ignorable.  */
413       analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
414       /* Reconnect the chain as before.  */
415       script->link = next;
416
417       /* If the change is ignorable, mark it.  */
418       script->ignore = (!deletes && !inserts);
419
420       /* Advance to the following change.  */
421       script = next;
422     }
423 }
424 \f
425 /* Find the last function-header line in FILE prior to line number LINENUM.
426    This is a line containing a match for the regexp in `function_regexp'.
427    Store the address of the line text into LINEP and the length of the
428    line into LENP.
429    Do not store anything if no function-header is found.  */
430
431 static void
432 find_function (file, linenum, linep, lenp)
433      struct file_data const *file;
434      int linenum;
435      char const **linep;
436      size_t *lenp;
437 {
438   int i = linenum;
439   int last = find_function_last_search;
440   find_function_last_search = i;
441
442   while (--i >= last)
443     {
444       /* See if this line is what we want.  */
445       struct regexp_list *r;
446       char const *line = file->linbuf[i];
447       size_t len = file->linbuf[i + 1] - line;
448
449       for (r = function_regexp_list; r; r = r->next)
450         if (0 <= re_search (&r->buf, line, len, 0, len, 0))
451           {
452             *linep = line;
453             *lenp = len;
454             find_function_last_match = i;
455             return;
456           }
457     }
458   /* If we search back to where we started searching the previous time,
459      find the line we found last time.  */
460   if (find_function_last_match >= - file->prefix_lines)
461     {
462       i = find_function_last_match;
463       *linep = file->linbuf[i];
464       *lenp = file->linbuf[i + 1] - *linep;
465       return;
466     }
467   return;
468 }