xref: /netbsd-src/external/gpl2/xcvs/dist/diff/side.c (revision 292a31115cd425403e51da039f4022d7c8f00032)
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 static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
25 static unsigned tab_from_to PARAMS((unsigned, unsigned));
26 static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
27 static void print_sdiff_common_lines PARAMS((int, int));
28 static void print_sdiff_hunk PARAMS((struct change *));
29 
30 /* Next line number to be printed in the two input files.  */
31 static int next0, next1;
32 
33 /* Print the edit-script SCRIPT as a sdiff style output.  */
34 
35 void
36 print_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 
49 static unsigned
50 tab_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  */
71 static unsigned
72 print_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 	  {
127 	    if (out_position <= in_position)
128 	      /* Add spaces to make up for suppressed tab past out_bound.  */
129 	      for (;  out_position < in_position;  out_position++)
130 		write_output (" ", 1);
131 	    else
132 	      {
133 		out_position = in_position;
134 		cc = c;
135 		write_output (&cc, 1);
136 	      }
137 	  }
138 	  break;
139 
140 	case '\f':
141 	case '\v':
142 	control_char:
143 	  if (in_position < out_bound)
144 	    {
145 	      cc = c;
146 	      write_output (&cc, 1);
147 	    }
148 	  break;
149 
150 	default:
151 	  if (! ISPRINT (c))
152 	    goto control_char;
153 	  /* falls through */
154 	case ' ':
155 	  if (in_position++ < out_bound)
156 	    {
157 	      out_position = in_position;
158 	      cc = c;
159 	      write_output (&cc, 1);
160 	    }
161 	  break;
162 
163 	case '\n':
164 	  return out_position;
165 	}
166     }
167 
168   return out_position;
169 }
170 
171 /*
172  * Print side by side lines with a separator in the middle.
173  * 0 parameters are taken to indicate white space text.
174  * Blank lines that can easily be caught are reduced to a single newline.
175  */
176 
177 static void
178 print_1sdiff_line (left, sep, right)
179      char const * const *left;
180      int sep;
181      char const * const *right;
182 {
183   unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
184   unsigned col = 0;
185   int put_newline = 0;
186 
187   if (left)
188     {
189       if (left[1][-1] == '\n')
190 	put_newline = 1;
191       col = print_half_line (left, 0, hw);
192     }
193 
194   if (sep != ' ')
195     {
196       char cc;
197 
198       col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
199       if (sep == '|' && put_newline != (right[1][-1] == '\n'))
200 	sep = put_newline ? '/' : '\\';
201       cc = sep;
202       write_output (&cc, 1);
203     }
204 
205   if (right)
206     {
207       if (right[1][-1] == '\n')
208 	put_newline = 1;
209       if (**right != '\n')
210 	{
211 	  col = tab_from_to (col, c2o);
212 	  print_half_line (right, col, hw);
213 	}
214     }
215 
216   if (put_newline)
217     write_output ("\n", 1);
218 }
219 
220 /* Print lines common to both files in side-by-side format.  */
221 static void
222 print_sdiff_common_lines (limit0, limit1)
223      int limit0, limit1;
224 {
225   int i0 = next0, i1 = next1;
226 
227   if (! sdiff_skip_common_lines  &&  (i0 != limit0 || i1 != limit1))
228     {
229       if (sdiff_help_sdiff)
230 	printf_output ("i%d,%d\n", limit0 - i0, limit1 - i1);
231 
232       if (! sdiff_left_only)
233 	{
234 	  while (i0 != limit0 && i1 != limit1)
235 	    print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
236 	  while (i1 != limit1)
237 	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
238 	}
239       while (i0 != limit0)
240 	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
241     }
242 
243   next0 = limit0;
244   next1 = limit1;
245 }
246 
247 /* Print a hunk of an sdiff diff.
248    This is a contiguous portion of a complete edit script,
249    describing changes in consecutive lines.  */
250 
251 static void
252 print_sdiff_hunk (hunk)
253      struct change *hunk;
254 {
255   int first0, last0, first1, last1, deletes, inserts;
256   register int i, j;
257 
258   /* Determine range of line numbers involved in each file.  */
259   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
260   if (!deletes && !inserts)
261     return;
262 
263   /* Print out lines up to this change.  */
264   print_sdiff_common_lines (first0, first1);
265 
266   if (sdiff_help_sdiff)
267     printf_output ("c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
268 
269   /* Print ``xxx  |  xxx '' lines */
270   if (inserts && deletes)
271     {
272       for (i = first0, j = first1;  i <= last0 && j <= last1; ++i, ++j)
273 	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
274       deletes = i <= last0;
275       inserts = j <= last1;
276       next0 = first0 = i;
277       next1 = first1 = j;
278     }
279 
280 
281   /* Print ``     >  xxx '' lines */
282   if (inserts)
283     {
284       for (j = first1; j <= last1; ++j)
285 	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
286       next1 = j;
287     }
288 
289   /* Print ``xxx  <     '' lines */
290   if (deletes)
291     {
292       for (i = first0; i <= last0; ++i)
293 	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
294       next0 = i;
295     }
296 }
297