132785Speter/* sdiff-format output routines for GNU DIFF.
244852Speter   Copyright (C) 1991, 1992, 1993, 1998 Free Software Foundation, Inc.
332785Speter
432785SpeterThis file is part of GNU DIFF.
532785Speter
632785SpeterGNU DIFF is distributed in the hope that it will be useful,
732785Speterbut WITHOUT ANY WARRANTY.  No author or distributor
832785Speteraccepts responsibility to anyone for the consequences of using it
932785Speteror for whether it serves any particular purpose or works at all,
1032785Speterunless he says so in writing.  Refer to the GNU DIFF General Public
1132785SpeterLicense for full details.
1232785Speter
1332785SpeterEveryone is granted permission to copy, modify and redistribute
1432785SpeterGNU DIFF, but only under the conditions described in the
1532785SpeterGNU DIFF General Public License.   A copy of this license is
1632785Spetersupposed to have been given to you along with GNU DIFF so you
1732785Spetercan know your rights and responsibilities.  It should be in a
1832785Speterfile named COPYING.  Among other things, the copyright notice
1932785Speterand this notice must be preserved on all copies.  */
2032785Speter
2132785Speter
2232785Speter#include "diff.h"
2332785Speter
2432785Speterstatic unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
2532785Speterstatic unsigned tab_from_to PARAMS((unsigned, unsigned));
2632785Speterstatic void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
2732785Speterstatic void print_sdiff_common_lines PARAMS((int, int));
2832785Speterstatic void print_sdiff_hunk PARAMS((struct change *));
2932785Speter
3032785Speter/* Next line number to be printed in the two input files.  */
3132785Speterstatic int next0, next1;
3232785Speter
3332785Speter/* Print the edit-script SCRIPT as a sdiff style output.  */
3432785Speter
3532785Spetervoid
3632785Speterprint_sdiff_script (script)
3732785Speter     struct change *script;
3832785Speter{
3932785Speter  begin_output ();
4032785Speter
4132785Speter  next0 = next1 = - files[0].prefix_lines;
4232785Speter  print_script (script, find_change, print_sdiff_hunk);
4332785Speter
4432785Speter  print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
4532785Speter}
4632785Speter
4732785Speter/* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
4832785Speter
4932785Speterstatic unsigned
5032785Spetertab_from_to (from, to)
5132785Speter     unsigned from, to;
5232785Speter{
5332785Speter  unsigned tab;
5432785Speter
5532785Speter  if (! tab_expand_flag)
5632785Speter    for (tab = from + TAB_WIDTH - from % TAB_WIDTH;  tab <= to;  tab += TAB_WIDTH)
5732785Speter      {
5844852Speter	write_output ("\t", 1);
5932785Speter	from = tab;
6032785Speter      }
6132785Speter  while (from++ < to)
6244852Speter    write_output (" ", 1);
6332785Speter  return to;
6432785Speter}
6532785Speter
6632785Speter/*
6732785Speter * Print the text for half an sdiff line.  This means truncate to width
6832785Speter * observing tabs, and trim a trailing newline.  Returns the last column
6932785Speter * written (not the number of chars).
7032785Speter */
7132785Speterstatic unsigned
7232785Speterprint_half_line (line, indent, out_bound)
7332785Speter     char const * const *line;
7432785Speter     unsigned indent, out_bound;
7532785Speter{
7632785Speter  register unsigned in_position = 0, out_position = 0;
7732785Speter  register char const
7832785Speter	*text_pointer = line[0],
7932785Speter	*text_limit = line[1];
8032785Speter
8132785Speter  while (text_pointer < text_limit)
8232785Speter    {
8332785Speter      register unsigned char c = *text_pointer++;
8444852Speter      /* We use CC to avoid taking the address of the register
8544852Speter         variable C.  */
8644852Speter      char cc;
8732785Speter
8832785Speter      switch (c)
8932785Speter	{
9032785Speter	case '\t':
9132785Speter	  {
9232785Speter	    unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH;
9332785Speter	    if (in_position == out_position)
9432785Speter	      {
9532785Speter		unsigned tabstop = out_position + spaces;
9632785Speter		if (tab_expand_flag)
9732785Speter		  {
9832785Speter		    if (out_bound < tabstop)
9932785Speter		      tabstop = out_bound;
10032785Speter		    for (;  out_position < tabstop;  out_position++)
10144852Speter		      write_output (" ", 1);
10232785Speter		  }
10332785Speter		else
10432785Speter		  if (tabstop < out_bound)
10532785Speter		    {
10632785Speter		      out_position = tabstop;
10744852Speter		      cc = c;
10844852Speter		      write_output (&cc, 1);
10932785Speter		    }
11032785Speter	      }
11132785Speter	    in_position += spaces;
11232785Speter	  }
11332785Speter	  break;
11432785Speter
11532785Speter	case '\r':
11632785Speter	  {
11744852Speter	    cc = c;
11844852Speter	    write_output (&cc, 1);
11932785Speter	    tab_from_to (0, indent);
12032785Speter	    in_position = out_position = 0;
12132785Speter	  }
12232785Speter	  break;
12332785Speter
12432785Speter	case '\b':
12532785Speter	  if (in_position != 0 && --in_position < out_bound)
12632785Speter	    if (out_position <= in_position)
12732785Speter	      /* Add spaces to make up for suppressed tab past out_bound.  */
12832785Speter	      for (;  out_position < in_position;  out_position++)
12944852Speter		write_output (" ", 1);
13032785Speter	    else
13132785Speter	      {
13232785Speter		out_position = in_position;
13344852Speter		cc = c;
13444852Speter		write_output (&cc, 1);
13532785Speter	      }
13632785Speter	  break;
13732785Speter
13832785Speter	case '\f':
13932785Speter	case '\v':
14032785Speter	control_char:
14132785Speter	  if (in_position < out_bound)
14244852Speter	    {
14344852Speter	      cc = c;
14444852Speter	      write_output (&cc, 1);
14544852Speter	    }
14632785Speter	  break;
14732785Speter
14832785Speter	default:
14932785Speter	  if (! ISPRINT (c))
15032785Speter	    goto control_char;
15132785Speter	  /* falls through */
15232785Speter	case ' ':
15332785Speter	  if (in_position++ < out_bound)
15432785Speter	    {
15532785Speter	      out_position = in_position;
15644852Speter	      cc = c;
15744852Speter	      write_output (&cc, 1);
15832785Speter	    }
15932785Speter	  break;
16032785Speter
16132785Speter	case '\n':
16232785Speter	  return out_position;
16332785Speter	}
16432785Speter    }
16532785Speter
16632785Speter  return out_position;
16732785Speter}
16832785Speter
16932785Speter/*
17032785Speter * Print side by side lines with a separator in the middle.
17132785Speter * 0 parameters are taken to indicate white space text.
17232785Speter * Blank lines that can easily be caught are reduced to a single newline.
17332785Speter */
17432785Speter
17532785Speterstatic void
17632785Speterprint_1sdiff_line (left, sep, right)
17732785Speter     char const * const *left;
17832785Speter     int sep;
17932785Speter     char const * const *right;
18032785Speter{
18132785Speter  unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
18232785Speter  unsigned col = 0;
18332785Speter  int put_newline = 0;
18432785Speter
18532785Speter  if (left)
18632785Speter    {
18732785Speter      if (left[1][-1] == '\n')
18832785Speter	put_newline = 1;
18932785Speter      col = print_half_line (left, 0, hw);
19032785Speter    }
19132785Speter
19232785Speter  if (sep != ' ')
19332785Speter    {
19444852Speter      char cc;
19544852Speter
19632785Speter      col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
19732785Speter      if (sep == '|' && put_newline != (right[1][-1] == '\n'))
19832785Speter	sep = put_newline ? '/' : '\\';
19944852Speter      cc = sep;
20044852Speter      write_output (&cc, 1);
20132785Speter    }
20232785Speter
20332785Speter  if (right)
20432785Speter    {
20532785Speter      if (right[1][-1] == '\n')
20632785Speter	put_newline = 1;
20732785Speter      if (**right != '\n')
20832785Speter	{
20932785Speter	  col = tab_from_to (col, c2o);
21032785Speter	  print_half_line (right, col, hw);
21132785Speter	}
21232785Speter    }
21332785Speter
21432785Speter  if (put_newline)
21544852Speter    write_output ("\n", 1);
21632785Speter}
21732785Speter
21832785Speter/* Print lines common to both files in side-by-side format.  */
21932785Speterstatic void
22032785Speterprint_sdiff_common_lines (limit0, limit1)
22132785Speter     int limit0, limit1;
22232785Speter{
22332785Speter  int i0 = next0, i1 = next1;
22432785Speter
22532785Speter  if (! sdiff_skip_common_lines  &&  (i0 != limit0 || i1 != limit1))
22632785Speter    {
22732785Speter      if (sdiff_help_sdiff)
22844852Speter	printf_output ("i%d,%d\n", limit0 - i0, limit1 - i1);
22932785Speter
23032785Speter      if (! sdiff_left_only)
23132785Speter	{
23232785Speter	  while (i0 != limit0 && i1 != limit1)
23332785Speter	    print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
23432785Speter	  while (i1 != limit1)
23532785Speter	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
23632785Speter	}
23732785Speter      while (i0 != limit0)
23832785Speter	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
23932785Speter    }
24032785Speter
24132785Speter  next0 = limit0;
24232785Speter  next1 = limit1;
24332785Speter}
24432785Speter
24532785Speter/* Print a hunk of an sdiff diff.
24632785Speter   This is a contiguous portion of a complete edit script,
24732785Speter   describing changes in consecutive lines.  */
24832785Speter
24932785Speterstatic void
25032785Speterprint_sdiff_hunk (hunk)
25132785Speter     struct change *hunk;
25232785Speter{
25332785Speter  int first0, last0, first1, last1, deletes, inserts;
25432785Speter  register int i, j;
25532785Speter
25632785Speter  /* Determine range of line numbers involved in each file.  */
25732785Speter  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
25832785Speter  if (!deletes && !inserts)
25932785Speter    return;
26032785Speter
26132785Speter  /* Print out lines up to this change.  */
26232785Speter  print_sdiff_common_lines (first0, first1);
26332785Speter
26432785Speter  if (sdiff_help_sdiff)
26544852Speter    printf_output ("c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
26632785Speter
26732785Speter  /* Print ``xxx  |  xxx '' lines */
26832785Speter  if (inserts && deletes)
26932785Speter    {
27032785Speter      for (i = first0, j = first1;  i <= last0 && j <= last1; ++i, ++j)
27132785Speter	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
27232785Speter      deletes = i <= last0;
27332785Speter      inserts = j <= last1;
27432785Speter      next0 = first0 = i;
27532785Speter      next1 = first1 = j;
27632785Speter    }
27732785Speter
27832785Speter
27932785Speter  /* Print ``     >  xxx '' lines */
28032785Speter  if (inserts)
28132785Speter    {
28232785Speter      for (j = first1; j <= last1; ++j)
28332785Speter	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
28432785Speter      next1 = j;
28532785Speter    }
28632785Speter
28732785Speter  /* Print ``xxx  <     '' lines */
28832785Speter  if (deletes)
28932785Speter    {
29032785Speter      for (i = first0; i <= last0; ++i)
29132785Speter	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
29232785Speter      next0 = i;
29332785Speter    }
29432785Speter}
295