xref: /dragonfly/contrib/cvs-1.12/diff/side.c (revision 6ca88057)
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 	    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 
175 static void
176 print_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.  */
219 static void
220 print_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 
249 static void
250 print_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