1/* sdiff-format output routines for GNU DIFF.
2   Copyright (C) 1991, 1992, 1993, 1998 Free Software Foundation, Inc.
3
4This file is part of GNU DIFF.
5
6GNU DIFF is distributed in the hope that it will be useful,
7but WITHOUT ANY WARRANTY.  No author or distributor
8accepts responsibility to anyone for the consequences of using it
9or for whether it serves any particular purpose or works at all,
10unless he says so in writing.  Refer to the GNU DIFF General Public
11License for full details.
12
13Everyone is granted permission to copy, modify and redistribute
14GNU DIFF, but only under the conditions described in the
15GNU DIFF General Public License.   A copy of this license is
16supposed to have been given to you along with GNU DIFF so you
17can know your rights and responsibilities.  It should be in a
18file named COPYING.  Among other things, the copyright notice
19and this notice must be preserved on all copies.  */
20
21
22#include "diff.h"
23
24static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
25static unsigned tab_from_to PARAMS((unsigned, unsigned));
26static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
27static void print_sdiff_common_lines PARAMS((int, int));
28static void print_sdiff_hunk PARAMS((struct change *));
29
30/* Next line number to be printed in the two input files.  */
31static int next0, next1;
32
33/* Print the edit-script SCRIPT as a sdiff style output.  */
34
35void
36print_sdiff_script (script)
37     struct change *script;
38{
39  begin_output ();
40
41  next0 = next1 = - files[0].prefix_lines;
42  print_script (script, find_change, print_sdiff_hunk);
43
44  print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
45}
46
47/* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
48
49static unsigned
50tab_from_to (from, to)
51     unsigned from, to;
52{
53  unsigned tab;
54
55  if (! tab_expand_flag)
56    for (tab = from + TAB_WIDTH - from % TAB_WIDTH;  tab <= to;  tab += TAB_WIDTH)
57      {
58	write_output ("\t", 1);
59	from = tab;
60      }
61  while (from++ < to)
62    write_output (" ", 1);
63  return to;
64}
65
66/*
67 * Print the text for half an sdiff line.  This means truncate to width
68 * observing tabs, and trim a trailing newline.  Returns the last column
69 * written (not the number of chars).
70 */
71static unsigned
72print_half_line (line, indent, out_bound)
73     char const * const *line;
74     unsigned indent, out_bound;
75{
76  register unsigned in_position = 0, out_position = 0;
77  register char const
78	*text_pointer = line[0],
79	*text_limit = line[1];
80
81  while (text_pointer < text_limit)
82    {
83      register unsigned char c = *text_pointer++;
84      /* We use CC to avoid taking the address of the register
85         variable C.  */
86      char cc;
87
88      switch (c)
89	{
90	case '\t':
91	  {
92	    unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH;
93	    if (in_position == out_position)
94	      {
95		unsigned tabstop = out_position + spaces;
96		if (tab_expand_flag)
97		  {
98		    if (out_bound < tabstop)
99		      tabstop = out_bound;
100		    for (;  out_position < tabstop;  out_position++)
101		      write_output (" ", 1);
102		  }
103		else
104		  if (tabstop < out_bound)
105		    {
106		      out_position = tabstop;
107		      cc = c;
108		      write_output (&cc, 1);
109		    }
110	      }
111	    in_position += spaces;
112	  }
113	  break;
114
115	case '\r':
116	  {
117	    cc = c;
118	    write_output (&cc, 1);
119	    tab_from_to (0, indent);
120	    in_position = out_position = 0;
121	  }
122	  break;
123
124	case '\b':
125	  if (in_position != 0 && --in_position < out_bound)
126	    if (out_position <= in_position)
127	      /* Add spaces to make up for suppressed tab past out_bound.  */
128	      for (;  out_position < in_position;  out_position++)
129		write_output (" ", 1);
130	    else
131	      {
132		out_position = in_position;
133		cc = c;
134		write_output (&cc, 1);
135	      }
136	  break;
137
138	case '\f':
139	case '\v':
140	control_char:
141	  if (in_position < out_bound)
142	    {
143	      cc = c;
144	      write_output (&cc, 1);
145	    }
146	  break;
147
148	default:
149	  if (! ISPRINT (c))
150	    goto control_char;
151	  /* falls through */
152	case ' ':
153	  if (in_position++ < out_bound)
154	    {
155	      out_position = in_position;
156	      cc = c;
157	      write_output (&cc, 1);
158	    }
159	  break;
160
161	case '\n':
162	  return out_position;
163	}
164    }
165
166  return out_position;
167}
168
169/*
170 * Print side by side lines with a separator in the middle.
171 * 0 parameters are taken to indicate white space text.
172 * Blank lines that can easily be caught are reduced to a single newline.
173 */
174
175static void
176print_1sdiff_line (left, sep, right)
177     char const * const *left;
178     int sep;
179     char const * const *right;
180{
181  unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
182  unsigned col = 0;
183  int put_newline = 0;
184
185  if (left)
186    {
187      if (left[1][-1] == '\n')
188	put_newline = 1;
189      col = print_half_line (left, 0, hw);
190    }
191
192  if (sep != ' ')
193    {
194      char cc;
195
196      col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
197      if (sep == '|' && put_newline != (right[1][-1] == '\n'))
198	sep = put_newline ? '/' : '\\';
199      cc = sep;
200      write_output (&cc, 1);
201    }
202
203  if (right)
204    {
205      if (right[1][-1] == '\n')
206	put_newline = 1;
207      if (**right != '\n')
208	{
209	  col = tab_from_to (col, c2o);
210	  print_half_line (right, col, hw);
211	}
212    }
213
214  if (put_newline)
215    write_output ("\n", 1);
216}
217
218/* Print lines common to both files in side-by-side format.  */
219static void
220print_sdiff_common_lines (limit0, limit1)
221     int limit0, limit1;
222{
223  int i0 = next0, i1 = next1;
224
225  if (! sdiff_skip_common_lines  &&  (i0 != limit0 || i1 != limit1))
226    {
227      if (sdiff_help_sdiff)
228	printf_output ("i%d,%d\n", limit0 - i0, limit1 - i1);
229
230      if (! sdiff_left_only)
231	{
232	  while (i0 != limit0 && i1 != limit1)
233	    print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
234	  while (i1 != limit1)
235	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
236	}
237      while (i0 != limit0)
238	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
239    }
240
241  next0 = limit0;
242  next1 = limit1;
243}
244
245/* Print a hunk of an sdiff diff.
246   This is a contiguous portion of a complete edit script,
247   describing changes in consecutive lines.  */
248
249static void
250print_sdiff_hunk (hunk)
251     struct change *hunk;
252{
253  int first0, last0, first1, last1, deletes, inserts;
254  register int i, j;
255
256  /* Determine range of line numbers involved in each file.  */
257  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
258  if (!deletes && !inserts)
259    return;
260
261  /* Print out lines up to this change.  */
262  print_sdiff_common_lines (first0, first1);
263
264  if (sdiff_help_sdiff)
265    printf_output ("c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
266
267  /* Print ``xxx  |  xxx '' lines */
268  if (inserts && deletes)
269    {
270      for (i = first0, j = first1;  i <= last0 && j <= last1; ++i, ++j)
271	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
272      deletes = i <= last0;
273      inserts = j <= last1;
274      next0 = first0 = i;
275      next1 = first1 = j;
276    }
277
278
279  /* Print ``     >  xxx '' lines */
280  if (inserts)
281    {
282      for (j = first1; j <= last1; ++j)
283	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
284      next1 = j;
285    }
286
287  /* Print ``xxx  <     '' lines */
288  if (deletes)
289    {
290      for (i = first0; i <= last0; ++i)
291	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
292      next0 = i;
293    }
294}
295