1170754Sdelphij/* sdiff-format output routines for GNU DIFF.
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1991, 1992, 1993, 1998, 2001, 2002, 2004 Free
4170754Sdelphij   Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This file is part of GNU DIFF.
7170754Sdelphij
8170754Sdelphij   GNU DIFF is distributed in the hope that it will be useful,
9170754Sdelphij   but WITHOUT ANY WARRANTY.  No author or distributor
10170754Sdelphij   accepts responsibility to anyone for the consequences of using it
11170754Sdelphij   or for whether it serves any particular purpose or works at all,
12170754Sdelphij   unless he says so in writing.  Refer to the GNU DIFF General Public
13170754Sdelphij   License for full details.
14170754Sdelphij
15170754Sdelphij   Everyone is granted permission to copy, modify and redistribute
16170754Sdelphij   GNU DIFF, but only under the conditions described in the
17170754Sdelphij   GNU DIFF General Public License.   A copy of this license is
18170754Sdelphij   supposed to have been given to you along with GNU DIFF so you
19170754Sdelphij   can know your rights and responsibilities.  It should be in a
20170754Sdelphij   file named COPYING.  Among other things, the copyright notice
21170754Sdelphij   and this notice must be preserved on all copies.  */
22170754Sdelphij
23170754Sdelphij#include "diff.h"
24170754Sdelphij
25170754Sdelphijstatic void print_sdiff_common_lines (lin, lin);
26170754Sdelphijstatic void print_sdiff_hunk (struct change *);
27170754Sdelphij
28170754Sdelphij/* Next line number to be printed in the two input files.  */
29170754Sdelphijstatic lin next0, next1;
30170754Sdelphij
31170754Sdelphij/* Print the edit-script SCRIPT as a sdiff style output.  */
32170754Sdelphij
33170754Sdelphijvoid
34170754Sdelphijprint_sdiff_script (struct change *script)
35170754Sdelphij{
36170754Sdelphij  begin_output ();
37170754Sdelphij
38170754Sdelphij  next0 = next1 = - files[0].prefix_lines;
39170754Sdelphij  print_script (script, find_change, print_sdiff_hunk);
40170754Sdelphij
41170754Sdelphij  print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
42170754Sdelphij}
43170754Sdelphij
44170754Sdelphij/* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
45170754Sdelphij
46170754Sdelphijstatic size_t
47170754Sdelphijtab_from_to (size_t from, size_t to)
48170754Sdelphij{
49170754Sdelphij  FILE *out = outfile;
50170754Sdelphij  size_t tab;
51170754Sdelphij  size_t tab_size = tabsize;
52170754Sdelphij
53170754Sdelphij  if (!expand_tabs)
54170754Sdelphij    for (tab = from + tab_size - from % tab_size;  tab <= to;  tab += tab_size)
55170754Sdelphij      {
56170754Sdelphij	putc ('\t', out);
57170754Sdelphij	from = tab;
58170754Sdelphij      }
59170754Sdelphij  while (from++ < to)
60170754Sdelphij    putc (' ', out);
61170754Sdelphij  return to;
62170754Sdelphij}
63170754Sdelphij
64170754Sdelphij/* Print the text for half an sdiff line.  This means truncate to
65170754Sdelphij   width observing tabs, and trim a trailing newline.  Return the
66170754Sdelphij   last column written (not the number of chars).  */
67170754Sdelphij
68170754Sdelphijstatic size_t
69170754Sdelphijprint_half_line (char const *const *line, size_t indent, size_t out_bound)
70170754Sdelphij{
71170754Sdelphij  FILE *out = outfile;
72170754Sdelphij  register size_t in_position = 0;
73170754Sdelphij  register size_t out_position = 0;
74170754Sdelphij  register char const *text_pointer = line[0];
75170754Sdelphij  register char const *text_limit = line[1];
76170754Sdelphij
77170754Sdelphij  while (text_pointer < text_limit)
78170754Sdelphij    {
79170754Sdelphij      register unsigned char c = *text_pointer++;
80170754Sdelphij
81170754Sdelphij      switch (c)
82170754Sdelphij	{
83170754Sdelphij	case '\t':
84170754Sdelphij	  {
85170754Sdelphij	    size_t spaces = tabsize - in_position % tabsize;
86170754Sdelphij	    if (in_position == out_position)
87170754Sdelphij	      {
88170754Sdelphij		size_t tabstop = out_position + spaces;
89170754Sdelphij		if (expand_tabs)
90170754Sdelphij		  {
91170754Sdelphij		    if (out_bound < tabstop)
92170754Sdelphij		      tabstop = out_bound;
93170754Sdelphij		    for (;  out_position < tabstop;  out_position++)
94170754Sdelphij		      putc (' ', out);
95170754Sdelphij		  }
96170754Sdelphij		else
97170754Sdelphij		  if (tabstop < out_bound)
98170754Sdelphij		    {
99170754Sdelphij		      out_position = tabstop;
100170754Sdelphij		      putc (c, out);
101170754Sdelphij		    }
102170754Sdelphij	      }
103170754Sdelphij	    in_position += spaces;
104170754Sdelphij	  }
105170754Sdelphij	  break;
106170754Sdelphij
107170754Sdelphij	case '\r':
108170754Sdelphij	  {
109170754Sdelphij	    putc (c, out);
110170754Sdelphij	    tab_from_to (0, indent);
111170754Sdelphij	    in_position = out_position = 0;
112170754Sdelphij	  }
113170754Sdelphij	  break;
114170754Sdelphij
115170754Sdelphij	case '\b':
116170754Sdelphij	  if (in_position != 0 && --in_position < out_bound)
117170754Sdelphij	    {
118170754Sdelphij	      if (out_position <= in_position)
119170754Sdelphij		/* Add spaces to make up for suppressed tab past out_bound.  */
120170754Sdelphij		for (;  out_position < in_position;  out_position++)
121170754Sdelphij		  putc (' ', out);
122170754Sdelphij	      else
123170754Sdelphij		{
124170754Sdelphij		  out_position = in_position;
125170754Sdelphij		  putc (c, out);
126170754Sdelphij		}
127170754Sdelphij	    }
128170754Sdelphij	  break;
129170754Sdelphij
130170754Sdelphij	case '\f':
131170754Sdelphij	case '\v':
132170754Sdelphij	control_char:
133170754Sdelphij	  if (in_position < out_bound)
134170754Sdelphij	    putc (c, out);
135170754Sdelphij	  break;
136170754Sdelphij
137170754Sdelphij	default:
138170754Sdelphij	  if (! isprint (c))
139170754Sdelphij	    goto control_char;
140170754Sdelphij	  /* falls through */
141170754Sdelphij	case ' ':
142170754Sdelphij	  if (in_position++ < out_bound)
143170754Sdelphij	    {
144170754Sdelphij	      out_position = in_position;
145170754Sdelphij	      putc (c, out);
146170754Sdelphij	    }
147170754Sdelphij	  break;
148170754Sdelphij
149170754Sdelphij	case '\n':
150170754Sdelphij	  return out_position;
151170754Sdelphij	}
152170754Sdelphij    }
153170754Sdelphij
154170754Sdelphij  return out_position;
155170754Sdelphij}
156170754Sdelphij
157170754Sdelphij/* Print side by side lines with a separator in the middle.
158170754Sdelphij   0 parameters are taken to indicate white space text.
159170754Sdelphij   Blank lines that can easily be caught are reduced to a single newline.  */
160170754Sdelphij
161170754Sdelphijstatic void
162170754Sdelphijprint_1sdiff_line (char const *const *left, char sep,
163170754Sdelphij		   char const *const *right)
164170754Sdelphij{
165170754Sdelphij  FILE *out = outfile;
166170754Sdelphij  size_t hw = sdiff_half_width;
167170754Sdelphij  size_t c2o = sdiff_column2_offset;
168170754Sdelphij  size_t col = 0;
169170754Sdelphij  bool put_newline = false;
170170754Sdelphij
171170754Sdelphij  if (left)
172170754Sdelphij    {
173170754Sdelphij      put_newline |= left[1][-1] == '\n';
174170754Sdelphij      col = print_half_line (left, 0, hw);
175170754Sdelphij    }
176170754Sdelphij
177170754Sdelphij  if (sep != ' ')
178170754Sdelphij    {
179170754Sdelphij      col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
180170754Sdelphij      if (sep == '|' && put_newline != (right[1][-1] == '\n'))
181170754Sdelphij	sep = put_newline ? '/' : '\\';
182170754Sdelphij      putc (sep, out);
183170754Sdelphij    }
184170754Sdelphij
185170754Sdelphij  if (right)
186170754Sdelphij    {
187170754Sdelphij      put_newline |= right[1][-1] == '\n';
188170754Sdelphij      if (**right != '\n')
189170754Sdelphij	{
190170754Sdelphij	  col = tab_from_to (col, c2o);
191170754Sdelphij	  print_half_line (right, col, hw);
192170754Sdelphij	}
193170754Sdelphij    }
194170754Sdelphij
195170754Sdelphij  if (put_newline)
196170754Sdelphij    putc ('\n', out);
197170754Sdelphij}
198170754Sdelphij
199170754Sdelphij/* Print lines common to both files in side-by-side format.  */
200170754Sdelphijstatic void
201170754Sdelphijprint_sdiff_common_lines (lin limit0, lin limit1)
202170754Sdelphij{
203170754Sdelphij  lin i0 = next0, i1 = next1;
204170754Sdelphij
205170754Sdelphij  if (!suppress_common_lines && (i0 != limit0 || i1 != limit1))
206170754Sdelphij    {
207170754Sdelphij      if (sdiff_merge_assist)
208170754Sdelphij	{
209170754Sdelphij	  long int len0 = limit0 - i0;
210170754Sdelphij	  long int len1 = limit1 - i1;
211170754Sdelphij	  fprintf (outfile, "i%ld,%ld\n", len0, len1);
212170754Sdelphij	}
213170754Sdelphij
214170754Sdelphij      if (!left_column)
215170754Sdelphij	{
216170754Sdelphij	  while (i0 != limit0 && i1 != limit1)
217170754Sdelphij	    print_1sdiff_line (&files[0].linbuf[i0++], ' ',
218170754Sdelphij			       &files[1].linbuf[i1++]);
219170754Sdelphij	  while (i1 != limit1)
220170754Sdelphij	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
221170754Sdelphij	}
222170754Sdelphij      while (i0 != limit0)
223170754Sdelphij	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
224170754Sdelphij    }
225170754Sdelphij
226170754Sdelphij  next0 = limit0;
227170754Sdelphij  next1 = limit1;
228170754Sdelphij}
229170754Sdelphij
230170754Sdelphij/* Print a hunk of an sdiff diff.
231170754Sdelphij   This is a contiguous portion of a complete edit script,
232170754Sdelphij   describing changes in consecutive lines.  */
233170754Sdelphij
234170754Sdelphijstatic void
235170754Sdelphijprint_sdiff_hunk (struct change *hunk)
236170754Sdelphij{
237170754Sdelphij  lin first0, last0, first1, last1;
238170754Sdelphij  register lin i, j;
239170754Sdelphij
240170754Sdelphij  /* Determine range of line numbers involved in each file.  */
241170754Sdelphij  enum changes changes =
242170754Sdelphij    analyze_hunk (hunk, &first0, &last0, &first1, &last1);
243170754Sdelphij  if (!changes)
244170754Sdelphij    return;
245170754Sdelphij
246170754Sdelphij  /* Print out lines up to this change.  */
247170754Sdelphij  print_sdiff_common_lines (first0, first1);
248170754Sdelphij
249170754Sdelphij  if (sdiff_merge_assist)
250170754Sdelphij    {
251170754Sdelphij      long int len0 = last0 - first0 + 1;
252170754Sdelphij      long int len1 = last1 - first1 + 1;
253170754Sdelphij      fprintf (outfile, "c%ld,%ld\n", len0, len1);
254170754Sdelphij    }
255170754Sdelphij
256170754Sdelphij  /* Print ``xxx  |  xxx '' lines */
257170754Sdelphij  if (changes == CHANGED)
258170754Sdelphij    {
259170754Sdelphij      for (i = first0, j = first1;  i <= last0 && j <= last1;  i++, j++)
260170754Sdelphij	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
261170754Sdelphij      changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0);
262170754Sdelphij      next0 = first0 = i;
263170754Sdelphij      next1 = first1 = j;
264170754Sdelphij    }
265170754Sdelphij
266170754Sdelphij  /* Print ``     >  xxx '' lines */
267170754Sdelphij  if (changes & NEW)
268170754Sdelphij    {
269170754Sdelphij      for (j = first1; j <= last1; ++j)
270170754Sdelphij	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
271170754Sdelphij      next1 = j;
272170754Sdelphij    }
273170754Sdelphij
274170754Sdelphij  /* Print ``xxx  <     '' lines */
275170754Sdelphij  if (changes & OLD)
276170754Sdelphij    {
277170754Sdelphij      for (i = first0; i <= last0; ++i)
278170754Sdelphij	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
279170754Sdelphij      next0 = i;
280170754Sdelphij    }
281170754Sdelphij}
282