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