source: trunk/third/diffutils/context.c @ 16149

Revision 16149, 12.9 KB checked in by rbasch, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16148, which included commits to RCS files with non-trunk default branches.
Line 
1/* Context-format output routines for GNU DIFF.
2   Copyright (C) 1988,1989,1991,1992,1993,1994 Free Software Foundation, Inc.
3
4This file is part of GNU DIFF.
5
6GNU DIFF is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU DIFF is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU DIFF; see the file COPYING.  If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20#include "diff.h"
21
22static struct change *find_hunk PARAMS((struct change *));
23static void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
24static void mark_ignorable PARAMS((struct change *));
25static void pr_context_hunk PARAMS((struct change *));
26static void pr_unidiff_hunk PARAMS((struct change *));
27static void print_context_label PARAMS ((char const *, struct file_data *, char const *));
28static void print_context_number_range PARAMS((struct file_data const *, int, int));
29static void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
30
31/* Last place find_function started searching from.  */
32static int find_function_last_search;
33
34/* The value find_function returned when it started searching there.  */
35static int find_function_last_match;
36
37/* Print a label for a context diff, with a file name and date or a label.  */
38
39static void
40print_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
59void
60print_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
78void
79print_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
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
107static void
108print_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
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
131static void
132pr_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
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
241static void
242print_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
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
265static void
266pr_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
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
364static struct change *
365find_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
400static void
401mark_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
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
431static void
432find_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}
Note: See TracBrowser for help on using the repository browser.