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