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             {
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
print_1sdiff_line(left,sep,right)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
print_sdiff_common_lines(limit0,limit1)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
print_sdiff_hunk(hunk)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