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