132785Speter/* Context-format output routines for GNU DIFF.
244852Speter   Copyright (C) 1988,1989,1991,1992,1993,1994,1998 Free Software Foundation, Inc.
332785Speter
432785SpeterThis file is part of GNU DIFF.
532785Speter
632785SpeterGNU DIFF is free software; you can redistribute it and/or modify
732785Speterit under the terms of the GNU General Public License as published by
832785Speterthe Free Software Foundation; either version 2, or (at your option)
932785Speterany later version.
1032785Speter
1132785SpeterGNU DIFF is distributed in the hope that it will be useful,
1232785Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of
1332785SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1432785SpeterGNU General Public License for more details.
1532785Speter
1654427Speter*/
1732785Speter
1832785Speter#include "diff.h"
1932785Speter
2032785Speterstatic struct change *find_hunk PARAMS((struct change *));
2132785Speterstatic void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
2232785Speterstatic void mark_ignorable PARAMS((struct change *));
2332785Speterstatic void pr_context_hunk PARAMS((struct change *));
2432785Speterstatic void pr_unidiff_hunk PARAMS((struct change *));
2532785Speterstatic void print_context_label PARAMS ((char const *, struct file_data *, char const *));
2632785Speterstatic void print_context_number_range PARAMS((struct file_data const *, int, int));
2732785Speterstatic void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
2832785Speter
2932785Speter/* Last place find_function started searching from.  */
3032785Speterstatic int find_function_last_search;
3132785Speter
3232785Speter/* The value find_function returned when it started searching there.  */
3332785Speterstatic int find_function_last_match;
3432785Speter
3532785Speter/* Print a label for a context diff, with a file name and date or a label.  */
3632785Speter
3732785Speterstatic void
3832785Speterprint_context_label (mark, inf, label)
3932785Speter     char const *mark;
4032785Speter     struct file_data *inf;
4132785Speter     char const *label;
4232785Speter{
4332785Speter  if (label)
4444852Speter    printf_output ("%s %s\n", mark, label);
4532785Speter  else
4632785Speter    {
4732785Speter      char const *ct = ctime (&inf->stat.st_mtime);
4832785Speter      if (!ct)
4932785Speter	ct = "?\n";
5032785Speter      /* See Posix.2 section 4.17.6.1.4 for this format.  */
5144852Speter      printf_output ("%s %s\t%s", mark, inf->name, ct);
5232785Speter    }
5332785Speter}
5432785Speter
5532785Speter/* Print a header for a context diff, with the file names and dates.  */
5632785Speter
5732785Spetervoid
5832785Speterprint_context_header (inf, unidiff_flag)
5932785Speter     struct file_data inf[];
6032785Speter     int unidiff_flag;
6132785Speter{
6232785Speter  if (unidiff_flag)
6332785Speter    {
6432785Speter      print_context_label ("---", &inf[0], file_label[0]);
6532785Speter      print_context_label ("+++", &inf[1], file_label[1]);
6632785Speter    }
6732785Speter  else
6832785Speter    {
6932785Speter      print_context_label ("***", &inf[0], file_label[0]);
7032785Speter      print_context_label ("---", &inf[1], file_label[1]);
7132785Speter    }
7232785Speter}
7332785Speter
7432785Speter/* Print an edit script in context format.  */
7532785Speter
7632785Spetervoid
7732785Speterprint_context_script (script, unidiff_flag)
7832785Speter     struct change *script;
7932785Speter     int unidiff_flag;
8032785Speter{
8132785Speter  if (ignore_blank_lines_flag || ignore_regexp_list)
8232785Speter    mark_ignorable (script);
8332785Speter  else
8432785Speter    {
8532785Speter      struct change *e;
8632785Speter      for (e = script; e; e = e->link)
8732785Speter	e->ignore = 0;
8832785Speter    }
8932785Speter
9032785Speter  find_function_last_search = - files[0].prefix_lines;
9132785Speter  find_function_last_match = find_function_last_search - 1;
9232785Speter
9332785Speter  if (unidiff_flag)
9432785Speter    print_script (script, find_hunk, pr_unidiff_hunk);
9532785Speter  else
9632785Speter    print_script (script, find_hunk, pr_context_hunk);
9732785Speter}
9832785Speter
9932785Speter/* Print a pair of line numbers with a comma, translated for file FILE.
10032785Speter   If the second number is not greater, use the first in place of it.
10132785Speter
10232785Speter   Args A and B are internal line numbers.
10332785Speter   We print the translated (real) line numbers.  */
10432785Speter
10532785Speterstatic void
10632785Speterprint_context_number_range (file, a, b)
10732785Speter     struct file_data const *file;
10832785Speter     int a, b;
10932785Speter{
11032785Speter  int trans_a, trans_b;
11132785Speter  translate_range (file, a, b, &trans_a, &trans_b);
11232785Speter
11332785Speter  /* Note: we can have B < A in the case of a range of no lines.
11432785Speter     In this case, we should print the line number before the range,
11532785Speter     which is B.  */
11632785Speter  if (trans_b > trans_a)
11744852Speter    printf_output ("%d,%d", trans_a, trans_b);
11832785Speter  else
11944852Speter    printf_output ("%d", trans_b);
12032785Speter}
12132785Speter
12232785Speter/* Print a portion of an edit script in context format.
12332785Speter   HUNK is the beginning of the portion to be printed.
12432785Speter   The end is marked by a `link' that has been nulled out.
12532785Speter
12632785Speter   Prints out lines from both files, and precedes each
12732785Speter   line with the appropriate flag-character.  */
12832785Speter
12932785Speterstatic void
13032785Speterpr_context_hunk (hunk)
13132785Speter     struct change *hunk;
13232785Speter{
13332785Speter  int first0, last0, first1, last1, show_from, show_to, i;
13432785Speter  struct change *next;
13532785Speter  char const *prefix;
13632785Speter  char const *function;
13732785Speter  size_t function_length;
13832785Speter
13932785Speter  /* Determine range of line numbers involved in each file.  */
14032785Speter
14132785Speter  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
14232785Speter
14332785Speter  if (!show_from && !show_to)
14432785Speter    return;
14532785Speter
14632785Speter  /* Include a context's width before and after.  */
14732785Speter
14832785Speter  i = - files[0].prefix_lines;
14932785Speter  first0 = max (first0 - context, i);
15032785Speter  first1 = max (first1 - context, i);
15132785Speter  last0 = min (last0 + context, files[0].valid_lines - 1);
15232785Speter  last1 = min (last1 + context, files[1].valid_lines - 1);
15332785Speter
15432785Speter  /* If desired, find the preceding function definition line in file 0.  */
15532785Speter  function = 0;
15632785Speter  if (function_regexp_list)
15732785Speter    find_function (&files[0], first0, &function, &function_length);
15832785Speter
15932785Speter  begin_output ();
16032785Speter
16132785Speter  /* If we looked for and found a function this is part of,
16232785Speter     include its name in the header of the diff section.  */
16344852Speter  printf_output ("***************");
16432785Speter
16532785Speter  if (function)
16632785Speter    {
16744852Speter      printf_output (" ");
16844852Speter      write_output (function, min (function_length - 1, 40));
16932785Speter    }
17032785Speter
17144852Speter  printf_output ("\n*** ");
17232785Speter  print_context_number_range (&files[0], first0, last0);
17344852Speter  printf_output (" ****\n");
17432785Speter
17532785Speter  if (show_from)
17632785Speter    {
17732785Speter      next = hunk;
17832785Speter
17932785Speter      for (i = first0; i <= last0; i++)
18032785Speter	{
18132785Speter	  /* Skip past changes that apply (in file 0)
18232785Speter	     only to lines before line I.  */
18332785Speter
18432785Speter	  while (next && next->line0 + next->deleted <= i)
18532785Speter	    next = next->link;
18632785Speter
18732785Speter	  /* Compute the marking for line I.  */
18832785Speter
18932785Speter	  prefix = " ";
19032785Speter	  if (next && next->line0 <= i)
19132785Speter	    /* The change NEXT covers this line.
19232785Speter	       If lines were inserted here in file 1, this is "changed".
19332785Speter	       Otherwise it is "deleted".  */
19432785Speter	    prefix = (next->inserted > 0 ? "!" : "-");
19532785Speter
19632785Speter	  print_1_line (prefix, &files[0].linbuf[i]);
19732785Speter	}
19832785Speter    }
19932785Speter
20044852Speter  printf_output ("--- ");
20132785Speter  print_context_number_range (&files[1], first1, last1);
20244852Speter  printf_output (" ----\n");
20332785Speter
20432785Speter  if (show_to)
20532785Speter    {
20632785Speter      next = hunk;
20732785Speter
20832785Speter      for (i = first1; i <= last1; i++)
20932785Speter	{
21032785Speter	  /* Skip past changes that apply (in file 1)
21132785Speter	     only to lines before line I.  */
21232785Speter
21332785Speter	  while (next && next->line1 + next->inserted <= i)
21432785Speter	    next = next->link;
21532785Speter
21632785Speter	  /* Compute the marking for line I.  */
21732785Speter
21832785Speter	  prefix = " ";
21932785Speter	  if (next && next->line1 <= i)
22032785Speter	    /* The change NEXT covers this line.
22132785Speter	       If lines were deleted here in file 0, this is "changed".
22232785Speter	       Otherwise it is "inserted".  */
22332785Speter	    prefix = (next->deleted > 0 ? "!" : "+");
22432785Speter
22532785Speter	  print_1_line (prefix, &files[1].linbuf[i]);
22632785Speter	}
22732785Speter    }
22832785Speter}
22932785Speter
23032785Speter/* Print a pair of line numbers with a comma, translated for file FILE.
23132785Speter   If the second number is smaller, use the first in place of it.
23232785Speter   If the numbers are equal, print just one number.
23332785Speter
23432785Speter   Args A and B are internal line numbers.
23532785Speter   We print the translated (real) line numbers.  */
23632785Speter
23732785Speterstatic void
23832785Speterprint_unidiff_number_range (file, a, b)
23932785Speter     struct file_data const *file;
24032785Speter     int a, b;
24132785Speter{
24232785Speter  int trans_a, trans_b;
24332785Speter  translate_range (file, a, b, &trans_a, &trans_b);
24432785Speter
24532785Speter  /* Note: we can have B < A in the case of a range of no lines.
24632785Speter     In this case, we should print the line number before the range,
24732785Speter     which is B.  */
24832785Speter  if (trans_b <= trans_a)
24944852Speter    printf_output (trans_b == trans_a ? "%d" : "%d,0", trans_b);
25032785Speter  else
25144852Speter    printf_output ("%d,%d", trans_a, trans_b - trans_a + 1);
25232785Speter}
25332785Speter
25432785Speter/* Print a portion of an edit script in unidiff format.
25532785Speter   HUNK is the beginning of the portion to be printed.
25632785Speter   The end is marked by a `link' that has been nulled out.
25732785Speter
25832785Speter   Prints out lines from both files, and precedes each
25932785Speter   line with the appropriate flag-character.  */
26032785Speter
26132785Speterstatic void
26232785Speterpr_unidiff_hunk (hunk)
26332785Speter     struct change *hunk;
26432785Speter{
26532785Speter  int first0, last0, first1, last1, show_from, show_to, i, j, k;
26632785Speter  struct change *next;
26732785Speter  char const *function;
26832785Speter  size_t function_length;
26932785Speter
27032785Speter  /* Determine range of line numbers involved in each file.  */
27132785Speter
27232785Speter  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
27332785Speter
27432785Speter  if (!show_from && !show_to)
27532785Speter    return;
27632785Speter
27732785Speter  /* Include a context's width before and after.  */
27832785Speter
27932785Speter  i = - files[0].prefix_lines;
28032785Speter  first0 = max (first0 - context, i);
28132785Speter  first1 = max (first1 - context, i);
28232785Speter  last0 = min (last0 + context, files[0].valid_lines - 1);
28332785Speter  last1 = min (last1 + context, files[1].valid_lines - 1);
28432785Speter
28532785Speter  /* If desired, find the preceding function definition line in file 0.  */
28632785Speter  function = 0;
28732785Speter  if (function_regexp_list)
28832785Speter    find_function (&files[0], first0, &function, &function_length);
28932785Speter
29032785Speter  begin_output ();
29132785Speter
29244852Speter  printf_output ("@@ -");
29332785Speter  print_unidiff_number_range (&files[0], first0, last0);
29444852Speter  printf_output (" +");
29532785Speter  print_unidiff_number_range (&files[1], first1, last1);
29644852Speter  printf_output (" @@");
29732785Speter
29832785Speter  /* If we looked for and found a function this is part of,
29932785Speter     include its name in the header of the diff section.  */
30032785Speter
30132785Speter  if (function)
30232785Speter    {
30344852Speter      write_output (" ", 1);
30444852Speter      write_output (function, min (function_length - 1, 40));
30532785Speter    }
30644852Speter  write_output ("\n", 1);
30732785Speter
30832785Speter  next = hunk;
30932785Speter  i = first0;
31032785Speter  j = first1;
31132785Speter
31232785Speter  while (i <= last0 || j <= last1)
31332785Speter    {
31432785Speter
31532785Speter      /* If the line isn't a difference, output the context from file 0. */
31632785Speter
31732785Speter      if (!next || i < next->line0)
31832785Speter	{
31944852Speter	  write_output (tab_align_flag ? "\t" : " ", 1);
32032785Speter	  print_1_line (0, &files[0].linbuf[i++]);
32132785Speter	  j++;
32232785Speter	}
32332785Speter      else
32432785Speter	{
32532785Speter	  /* For each difference, first output the deleted part. */
32632785Speter
32732785Speter	  k = next->deleted;
32832785Speter	  while (k--)
32932785Speter	    {
33044852Speter	      write_output ("-", 1);
33132785Speter	      if (tab_align_flag)
33244852Speter		write_output ("\t", 1);
33332785Speter	      print_1_line (0, &files[0].linbuf[i++]);
33432785Speter	    }
33532785Speter
33632785Speter	  /* Then output the inserted part. */
33732785Speter
33832785Speter	  k = next->inserted;
33932785Speter	  while (k--)
34032785Speter	    {
34144852Speter	      write_output ("+", 1);
34232785Speter	      if (tab_align_flag)
34344852Speter		write_output ("\t", 1);
34432785Speter	      print_1_line (0, &files[1].linbuf[j++]);
34532785Speter	    }
34632785Speter
34732785Speter	  /* We're done with this hunk, so on to the next! */
34832785Speter
34932785Speter	  next = next->link;
35032785Speter	}
35132785Speter    }
35232785Speter}
35332785Speter
35432785Speter/* Scan a (forward-ordered) edit script for the first place that more than
35532785Speter   2*CONTEXT unchanged lines appear, and return a pointer
35632785Speter   to the `struct change' for the last change before those lines.  */
35732785Speter
35832785Speterstatic struct change *
35932785Speterfind_hunk (start)
36032785Speter     struct change *start;
36132785Speter{
36232785Speter  struct change *prev;
36332785Speter  int top0, top1;
36432785Speter  int thresh;
36532785Speter
36632785Speter  do
36732785Speter    {
36832785Speter      /* Compute number of first line in each file beyond this changed.  */
36932785Speter      top0 = start->line0 + start->deleted;
37032785Speter      top1 = start->line1 + start->inserted;
37132785Speter      prev = start;
37232785Speter      start = start->link;
37332785Speter      /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
37432785Speter	 but only CONTEXT if one is ignorable.  */
37532785Speter      thresh = ((prev->ignore || (start && start->ignore))
37632785Speter		? context
37732785Speter		: 2 * context + 1);
37832785Speter      /* It is not supposed to matter which file we check in the end-test.
37932785Speter	 If it would matter, crash.  */
38032785Speter      if (start && start->line0 - top0 != start->line1 - top1)
38132785Speter	abort ();
38232785Speter    } while (start
38332785Speter	     /* Keep going if less than THRESH lines
38432785Speter		elapse before the affected line.  */
38532785Speter	     && start->line0 < top0 + thresh);
38632785Speter
38732785Speter  return prev;
38832785Speter}
38932785Speter
39032785Speter/* Set the `ignore' flag properly in each change in SCRIPT.
39132785Speter   It should be 1 if all the lines inserted or deleted in that change
39232785Speter   are ignorable lines.  */
39332785Speter
39432785Speterstatic void
39532785Spetermark_ignorable (script)
39632785Speter     struct change *script;
39732785Speter{
39832785Speter  while (script)
39932785Speter    {
40032785Speter      struct change *next = script->link;
40132785Speter      int first0, last0, first1, last1, deletes, inserts;
40232785Speter
40332785Speter      /* Turn this change into a hunk: detach it from the others.  */
40432785Speter      script->link = 0;
40532785Speter
40632785Speter      /* Determine whether this change is ignorable.  */
40732785Speter      analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
40832785Speter      /* Reconnect the chain as before.  */
40932785Speter      script->link = next;
41032785Speter
41132785Speter      /* If the change is ignorable, mark it.  */
41232785Speter      script->ignore = (!deletes && !inserts);
41332785Speter
41432785Speter      /* Advance to the following change.  */
41532785Speter      script = next;
41632785Speter    }
41732785Speter}
41832785Speter
41932785Speter/* Find the last function-header line in FILE prior to line number LINENUM.
42032785Speter   This is a line containing a match for the regexp in `function_regexp'.
42132785Speter   Store the address of the line text into LINEP and the length of the
42232785Speter   line into LENP.
42332785Speter   Do not store anything if no function-header is found.  */
42432785Speter
42532785Speterstatic void
42632785Speterfind_function (file, linenum, linep, lenp)
42732785Speter     struct file_data const *file;
42832785Speter     int linenum;
42932785Speter     char const **linep;
43032785Speter     size_t *lenp;
43132785Speter{
43232785Speter  int i = linenum;
43332785Speter  int last = find_function_last_search;
43432785Speter  find_function_last_search = i;
43532785Speter
43632785Speter  while (--i >= last)
43732785Speter    {
43832785Speter      /* See if this line is what we want.  */
43932785Speter      struct regexp_list *r;
44032785Speter      char const *line = file->linbuf[i];
44132785Speter      size_t len = file->linbuf[i + 1] - line;
44232785Speter
44332785Speter      for (r = function_regexp_list; r; r = r->next)
44432785Speter	if (0 <= re_search (&r->buf, line, len, 0, len, 0))
44532785Speter	  {
44632785Speter	    *linep = line;
44732785Speter	    *lenp = len;
44832785Speter	    find_function_last_match = i;
44932785Speter	    return;
45032785Speter	  }
45132785Speter    }
45232785Speter  /* If we search back to where we started searching the previous time,
45332785Speter     find the line we found last time.  */
45432785Speter  if (find_function_last_match >= - file->prefix_lines)
45532785Speter    {
45632785Speter      i = find_function_last_match;
45732785Speter      *linep = file->linbuf[i];
45832785Speter      *lenp = file->linbuf[i + 1] - *linep;
45932785Speter      return;
46032785Speter    }
46132785Speter  return;
46232785Speter}
463