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