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