xref: /openbsd/gnu/usr.bin/cvs/diff/side.c (revision 2286d8ed)
1 /* sdiff-format output routines for GNU DIFF.
2    Copyright (C) 1991, 1992, 1993 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   FILE *out = outfile;
54   unsigned tab;
55 
56   if (! tab_expand_flag)
57     for (tab = from + TAB_WIDTH - from % TAB_WIDTH;  tab <= to;  tab += TAB_WIDTH)
58       {
59 	putc ('\t', out);
60 	from = tab;
61       }
62   while (from++ < to)
63     putc (' ', out);
64   return to;
65 }
66 
67 /*
68  * Print the text for half an sdiff line.  This means truncate to width
69  * observing tabs, and trim a trailing newline.  Returns the last column
70  * written (not the number of chars).
71  */
72 static unsigned
73 print_half_line (line, indent, out_bound)
74      char const * const *line;
75      unsigned indent, out_bound;
76 {
77   FILE *out = outfile;
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 
87       switch (c)
88 	{
89 	case '\t':
90 	  {
91 	    unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH;
92 	    if (in_position == out_position)
93 	      {
94 		unsigned tabstop = out_position + spaces;
95 		if (tab_expand_flag)
96 		  {
97 		    if (out_bound < tabstop)
98 		      tabstop = out_bound;
99 		    for (;  out_position < tabstop;  out_position++)
100 		      putc (' ', out);
101 		  }
102 		else
103 		  if (tabstop < out_bound)
104 		    {
105 		      out_position = tabstop;
106 		      putc (c, out);
107 		    }
108 	      }
109 	    in_position += spaces;
110 	  }
111 	  break;
112 
113 	case '\r':
114 	  {
115 	    putc (c, out);
116 	    tab_from_to (0, indent);
117 	    in_position = out_position = 0;
118 	  }
119 	  break;
120 
121 	case '\b':
122 	  if (in_position != 0 && --in_position < out_bound)
123 	    if (out_position <= in_position)
124 	      /* Add spaces to make up for suppressed tab past out_bound.  */
125 	      for (;  out_position < in_position;  out_position++)
126 		putc (' ', out);
127 	    else
128 	      {
129 		out_position = in_position;
130 		putc (c, out);
131 	      }
132 	  break;
133 
134 	case '\f':
135 	case '\v':
136 	control_char:
137 	  if (in_position < out_bound)
138 	    putc (c, out);
139 	  break;
140 
141 	default:
142 	  if (! ISPRINT (c))
143 	    goto control_char;
144 	  /* falls through */
145 	case ' ':
146 	  if (in_position++ < out_bound)
147 	    {
148 	      out_position = in_position;
149 	      putc (c, out);
150 	    }
151 	  break;
152 
153 	case '\n':
154 	  return out_position;
155 	}
156     }
157 
158   return out_position;
159 }
160 
161 /*
162  * Print side by side lines with a separator in the middle.
163  * 0 parameters are taken to indicate white space text.
164  * Blank lines that can easily be caught are reduced to a single newline.
165  */
166 
167 static void
168 print_1sdiff_line (left, sep, right)
169      char const * const *left;
170      int sep;
171      char const * const *right;
172 {
173   FILE *out = outfile;
174   unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
175   unsigned col = 0;
176   int put_newline = 0;
177 
178   if (left)
179     {
180       if (left[1][-1] == '\n')
181 	put_newline = 1;
182       col = print_half_line (left, 0, hw);
183     }
184 
185   if (sep != ' ')
186     {
187       col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
188       if (sep == '|' && put_newline != (right[1][-1] == '\n'))
189 	sep = put_newline ? '/' : '\\';
190       putc (sep, out);
191     }
192 
193   if (right)
194     {
195       if (right[1][-1] == '\n')
196 	put_newline = 1;
197       if (**right != '\n')
198 	{
199 	  col = tab_from_to (col, c2o);
200 	  print_half_line (right, col, hw);
201 	}
202     }
203 
204   if (put_newline)
205     putc ('\n', out);
206 }
207 
208 /* Print lines common to both files in side-by-side format.  */
209 static void
210 print_sdiff_common_lines (limit0, limit1)
211      int limit0, limit1;
212 {
213   int i0 = next0, i1 = next1;
214 
215   if (! sdiff_skip_common_lines  &&  (i0 != limit0 || i1 != limit1))
216     {
217       if (sdiff_help_sdiff)
218 	fprintf (outfile, "i%d,%d\n", limit0 - i0, limit1 - i1);
219 
220       if (! sdiff_left_only)
221 	{
222 	  while (i0 != limit0 && i1 != limit1)
223 	    print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
224 	  while (i1 != limit1)
225 	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
226 	}
227       while (i0 != limit0)
228 	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
229     }
230 
231   next0 = limit0;
232   next1 = limit1;
233 }
234 
235 /* Print a hunk of an sdiff diff.
236    This is a contiguous portion of a complete edit script,
237    describing changes in consecutive lines.  */
238 
239 static void
240 print_sdiff_hunk (hunk)
241      struct change *hunk;
242 {
243   int first0, last0, first1, last1, deletes, inserts;
244   register int i, j;
245 
246   /* Determine range of line numbers involved in each file.  */
247   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
248   if (!deletes && !inserts)
249     return;
250 
251   /* Print out lines up to this change.  */
252   print_sdiff_common_lines (first0, first1);
253 
254   if (sdiff_help_sdiff)
255     fprintf (outfile, "c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
256 
257   /* Print ``xxx  |  xxx '' lines */
258   if (inserts && deletes)
259     {
260       for (i = first0, j = first1;  i <= last0 && j <= last1; ++i, ++j)
261 	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
262       deletes = i <= last0;
263       inserts = j <= last1;
264       next0 = first0 = i;
265       next1 = first1 = j;
266     }
267 
268 
269   /* Print ``     >  xxx '' lines */
270   if (inserts)
271     {
272       for (j = first1; j <= last1; ++j)
273 	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
274       next1 = j;
275     }
276 
277   /* Print ``xxx  <     '' lines */
278   if (deletes)
279     {
280       for (i = first0; i <= last0; ++i)
281 	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
282       next0 = i;
283     }
284 }
285