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