1*0b459c2cSDavid du Colombier /* sdiff-format output routines for GNU DIFF.
2*0b459c2cSDavid du Colombier Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
3*0b459c2cSDavid du Colombier
4*0b459c2cSDavid du Colombier This file is part of GNU DIFF.
5*0b459c2cSDavid du Colombier
6*0b459c2cSDavid du Colombier GNU DIFF is distributed in the hope that it will be useful,
7*0b459c2cSDavid du Colombier but WITHOUT ANY WARRANTY. No author or distributor
8*0b459c2cSDavid du Colombier accepts responsibility to anyone for the consequences of using it
9*0b459c2cSDavid du Colombier or for whether it serves any particular purpose or works at all,
10*0b459c2cSDavid du Colombier unless he says so in writing. Refer to the GNU DIFF General Public
11*0b459c2cSDavid du Colombier License for full details.
12*0b459c2cSDavid du Colombier
13*0b459c2cSDavid du Colombier Everyone is granted permission to copy, modify and redistribute
14*0b459c2cSDavid du Colombier GNU DIFF, but only under the conditions described in the
15*0b459c2cSDavid du Colombier GNU DIFF General Public License. A copy of this license is
16*0b459c2cSDavid du Colombier supposed to have been given to you along with GNU DIFF so you
17*0b459c2cSDavid du Colombier can know your rights and responsibilities. It should be in a
18*0b459c2cSDavid du Colombier file named COPYING. Among other things, the copyright notice
19*0b459c2cSDavid du Colombier and this notice must be preserved on all copies. */
20*0b459c2cSDavid du Colombier
21*0b459c2cSDavid du Colombier
22*0b459c2cSDavid du Colombier #include "diff.h"
23*0b459c2cSDavid du Colombier
24*0b459c2cSDavid du Colombier static unsigned print_half_line PARAMS((char const * const *, unsigned, unsigned));
25*0b459c2cSDavid du Colombier static unsigned tab_from_to PARAMS((unsigned, unsigned));
26*0b459c2cSDavid du Colombier static void print_1sdiff_line PARAMS((char const * const *, int, char const * const *));
27*0b459c2cSDavid du Colombier static void print_sdiff_common_lines PARAMS((int, int));
28*0b459c2cSDavid du Colombier static void print_sdiff_hunk PARAMS((struct change *));
29*0b459c2cSDavid du Colombier
30*0b459c2cSDavid du Colombier /* Next line number to be printed in the two input files. */
31*0b459c2cSDavid du Colombier static int next0, next1;
32*0b459c2cSDavid du Colombier
33*0b459c2cSDavid du Colombier /* Print the edit-script SCRIPT as a sdiff style output. */
34*0b459c2cSDavid du Colombier
35*0b459c2cSDavid du Colombier void
print_sdiff_script(script)36*0b459c2cSDavid du Colombier print_sdiff_script (script)
37*0b459c2cSDavid du Colombier struct change *script;
38*0b459c2cSDavid du Colombier {
39*0b459c2cSDavid du Colombier begin_output ();
40*0b459c2cSDavid du Colombier
41*0b459c2cSDavid du Colombier next0 = next1 = - files[0].prefix_lines;
42*0b459c2cSDavid du Colombier print_script (script, find_change, print_sdiff_hunk);
43*0b459c2cSDavid du Colombier
44*0b459c2cSDavid du Colombier print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
45*0b459c2cSDavid du Colombier }
46*0b459c2cSDavid du Colombier
47*0b459c2cSDavid du Colombier /* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */
48*0b459c2cSDavid du Colombier
49*0b459c2cSDavid du Colombier static unsigned
tab_from_to(from,to)50*0b459c2cSDavid du Colombier tab_from_to (from, to)
51*0b459c2cSDavid du Colombier unsigned from, to;
52*0b459c2cSDavid du Colombier {
53*0b459c2cSDavid du Colombier FILE *out = outfile;
54*0b459c2cSDavid du Colombier unsigned tab;
55*0b459c2cSDavid du Colombier
56*0b459c2cSDavid du Colombier if (! tab_expand_flag)
57*0b459c2cSDavid du Colombier for (tab = from + TAB_WIDTH - from % TAB_WIDTH; tab <= to; tab += TAB_WIDTH)
58*0b459c2cSDavid du Colombier {
59*0b459c2cSDavid du Colombier putc ('\t', out);
60*0b459c2cSDavid du Colombier from = tab;
61*0b459c2cSDavid du Colombier }
62*0b459c2cSDavid du Colombier while (from++ < to)
63*0b459c2cSDavid du Colombier putc (' ', out);
64*0b459c2cSDavid du Colombier return to;
65*0b459c2cSDavid du Colombier }
66*0b459c2cSDavid du Colombier
67*0b459c2cSDavid du Colombier /*
68*0b459c2cSDavid du Colombier * Print the text for half an sdiff line. This means truncate to width
69*0b459c2cSDavid du Colombier * observing tabs, and trim a trailing newline. Returns the last column
70*0b459c2cSDavid du Colombier * written (not the number of chars).
71*0b459c2cSDavid du Colombier */
72*0b459c2cSDavid du Colombier static unsigned
print_half_line(line,indent,out_bound)73*0b459c2cSDavid du Colombier print_half_line (line, indent, out_bound)
74*0b459c2cSDavid du Colombier char const * const *line;
75*0b459c2cSDavid du Colombier unsigned indent, out_bound;
76*0b459c2cSDavid du Colombier {
77*0b459c2cSDavid du Colombier FILE *out = outfile;
78*0b459c2cSDavid du Colombier register unsigned in_position = 0, out_position = 0;
79*0b459c2cSDavid du Colombier register char const
80*0b459c2cSDavid du Colombier *text_pointer = line[0],
81*0b459c2cSDavid du Colombier *text_limit = line[1];
82*0b459c2cSDavid du Colombier
83*0b459c2cSDavid du Colombier while (text_pointer < text_limit)
84*0b459c2cSDavid du Colombier {
85*0b459c2cSDavid du Colombier register unsigned char c = *text_pointer++;
86*0b459c2cSDavid du Colombier
87*0b459c2cSDavid du Colombier switch (c)
88*0b459c2cSDavid du Colombier {
89*0b459c2cSDavid du Colombier case '\t':
90*0b459c2cSDavid du Colombier {
91*0b459c2cSDavid du Colombier unsigned spaces = TAB_WIDTH - in_position % TAB_WIDTH;
92*0b459c2cSDavid du Colombier if (in_position == out_position)
93*0b459c2cSDavid du Colombier {
94*0b459c2cSDavid du Colombier unsigned tabstop = out_position + spaces;
95*0b459c2cSDavid du Colombier if (tab_expand_flag)
96*0b459c2cSDavid du Colombier {
97*0b459c2cSDavid du Colombier if (out_bound < tabstop)
98*0b459c2cSDavid du Colombier tabstop = out_bound;
99*0b459c2cSDavid du Colombier for (; out_position < tabstop; out_position++)
100*0b459c2cSDavid du Colombier putc (' ', out);
101*0b459c2cSDavid du Colombier }
102*0b459c2cSDavid du Colombier else
103*0b459c2cSDavid du Colombier if (tabstop < out_bound)
104*0b459c2cSDavid du Colombier {
105*0b459c2cSDavid du Colombier out_position = tabstop;
106*0b459c2cSDavid du Colombier putc (c, out);
107*0b459c2cSDavid du Colombier }
108*0b459c2cSDavid du Colombier }
109*0b459c2cSDavid du Colombier in_position += spaces;
110*0b459c2cSDavid du Colombier }
111*0b459c2cSDavid du Colombier break;
112*0b459c2cSDavid du Colombier
113*0b459c2cSDavid du Colombier case '\r':
114*0b459c2cSDavid du Colombier {
115*0b459c2cSDavid du Colombier putc (c, out);
116*0b459c2cSDavid du Colombier tab_from_to (0, indent);
117*0b459c2cSDavid du Colombier in_position = out_position = 0;
118*0b459c2cSDavid du Colombier }
119*0b459c2cSDavid du Colombier break;
120*0b459c2cSDavid du Colombier
121*0b459c2cSDavid du Colombier case '\b':
122*0b459c2cSDavid du Colombier if (in_position != 0 && --in_position < out_bound)
123*0b459c2cSDavid du Colombier if (out_position <= in_position)
124*0b459c2cSDavid du Colombier /* Add spaces to make up for suppressed tab past out_bound. */
125*0b459c2cSDavid du Colombier for (; out_position < in_position; out_position++)
126*0b459c2cSDavid du Colombier putc (' ', out);
127*0b459c2cSDavid du Colombier else
128*0b459c2cSDavid du Colombier {
129*0b459c2cSDavid du Colombier out_position = in_position;
130*0b459c2cSDavid du Colombier putc (c, out);
131*0b459c2cSDavid du Colombier }
132*0b459c2cSDavid du Colombier break;
133*0b459c2cSDavid du Colombier
134*0b459c2cSDavid du Colombier case '\f':
135*0b459c2cSDavid du Colombier case '\v':
136*0b459c2cSDavid du Colombier control_char:
137*0b459c2cSDavid du Colombier if (in_position < out_bound)
138*0b459c2cSDavid du Colombier putc (c, out);
139*0b459c2cSDavid du Colombier break;
140*0b459c2cSDavid du Colombier
141*0b459c2cSDavid du Colombier default:
142*0b459c2cSDavid du Colombier if (! ISPRINT (c))
143*0b459c2cSDavid du Colombier goto control_char;
144*0b459c2cSDavid du Colombier /* falls through */
145*0b459c2cSDavid du Colombier case ' ':
146*0b459c2cSDavid du Colombier if (in_position++ < out_bound)
147*0b459c2cSDavid du Colombier {
148*0b459c2cSDavid du Colombier out_position = in_position;
149*0b459c2cSDavid du Colombier putc (c, out);
150*0b459c2cSDavid du Colombier }
151*0b459c2cSDavid du Colombier break;
152*0b459c2cSDavid du Colombier
153*0b459c2cSDavid du Colombier case '\n':
154*0b459c2cSDavid du Colombier return out_position;
155*0b459c2cSDavid du Colombier }
156*0b459c2cSDavid du Colombier }
157*0b459c2cSDavid du Colombier
158*0b459c2cSDavid du Colombier return out_position;
159*0b459c2cSDavid du Colombier }
160*0b459c2cSDavid du Colombier
161*0b459c2cSDavid du Colombier /*
162*0b459c2cSDavid du Colombier * Print side by side lines with a separator in the middle.
163*0b459c2cSDavid du Colombier * 0 parameters are taken to indicate white space text.
164*0b459c2cSDavid du Colombier * Blank lines that can easily be caught are reduced to a single newline.
165*0b459c2cSDavid du Colombier */
166*0b459c2cSDavid du Colombier
167*0b459c2cSDavid du Colombier static void
print_1sdiff_line(left,sep,right)168*0b459c2cSDavid du Colombier print_1sdiff_line (left, sep, right)
169*0b459c2cSDavid du Colombier char const * const *left;
170*0b459c2cSDavid du Colombier int sep;
171*0b459c2cSDavid du Colombier char const * const *right;
172*0b459c2cSDavid du Colombier {
173*0b459c2cSDavid du Colombier FILE *out = outfile;
174*0b459c2cSDavid du Colombier unsigned hw = sdiff_half_width, c2o = sdiff_column2_offset;
175*0b459c2cSDavid du Colombier unsigned col = 0;
176*0b459c2cSDavid du Colombier int put_newline = 0;
177*0b459c2cSDavid du Colombier
178*0b459c2cSDavid du Colombier if (left)
179*0b459c2cSDavid du Colombier {
180*0b459c2cSDavid du Colombier if (left[1][-1] == '\n')
181*0b459c2cSDavid du Colombier put_newline = 1;
182*0b459c2cSDavid du Colombier col = print_half_line (left, 0, hw);
183*0b459c2cSDavid du Colombier }
184*0b459c2cSDavid du Colombier
185*0b459c2cSDavid du Colombier if (sep != ' ')
186*0b459c2cSDavid du Colombier {
187*0b459c2cSDavid du Colombier col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
188*0b459c2cSDavid du Colombier if (sep == '|' && put_newline != (right[1][-1] == '\n'))
189*0b459c2cSDavid du Colombier sep = put_newline ? '/' : '\\';
190*0b459c2cSDavid du Colombier putc (sep, out);
191*0b459c2cSDavid du Colombier }
192*0b459c2cSDavid du Colombier
193*0b459c2cSDavid du Colombier if (right)
194*0b459c2cSDavid du Colombier {
195*0b459c2cSDavid du Colombier if (right[1][-1] == '\n')
196*0b459c2cSDavid du Colombier put_newline = 1;
197*0b459c2cSDavid du Colombier if (**right != '\n')
198*0b459c2cSDavid du Colombier {
199*0b459c2cSDavid du Colombier col = tab_from_to (col, c2o);
200*0b459c2cSDavid du Colombier print_half_line (right, col, hw);
201*0b459c2cSDavid du Colombier }
202*0b459c2cSDavid du Colombier }
203*0b459c2cSDavid du Colombier
204*0b459c2cSDavid du Colombier if (put_newline)
205*0b459c2cSDavid du Colombier putc ('\n', out);
206*0b459c2cSDavid du Colombier }
207*0b459c2cSDavid du Colombier
208*0b459c2cSDavid du Colombier /* Print lines common to both files in side-by-side format. */
209*0b459c2cSDavid du Colombier static void
print_sdiff_common_lines(limit0,limit1)210*0b459c2cSDavid du Colombier print_sdiff_common_lines (limit0, limit1)
211*0b459c2cSDavid du Colombier int limit0, limit1;
212*0b459c2cSDavid du Colombier {
213*0b459c2cSDavid du Colombier int i0 = next0, i1 = next1;
214*0b459c2cSDavid du Colombier
215*0b459c2cSDavid du Colombier if (! sdiff_skip_common_lines && (i0 != limit0 || i1 != limit1))
216*0b459c2cSDavid du Colombier {
217*0b459c2cSDavid du Colombier if (sdiff_help_sdiff)
218*0b459c2cSDavid du Colombier fprintf (outfile, "i%d,%d\n", limit0 - i0, limit1 - i1);
219*0b459c2cSDavid du Colombier
220*0b459c2cSDavid du Colombier if (! sdiff_left_only)
221*0b459c2cSDavid du Colombier {
222*0b459c2cSDavid du Colombier while (i0 != limit0 && i1 != limit1)
223*0b459c2cSDavid du Colombier print_1sdiff_line (&files[0].linbuf[i0++], ' ', &files[1].linbuf[i1++]);
224*0b459c2cSDavid du Colombier while (i1 != limit1)
225*0b459c2cSDavid du Colombier print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
226*0b459c2cSDavid du Colombier }
227*0b459c2cSDavid du Colombier while (i0 != limit0)
228*0b459c2cSDavid du Colombier print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
229*0b459c2cSDavid du Colombier }
230*0b459c2cSDavid du Colombier
231*0b459c2cSDavid du Colombier next0 = limit0;
232*0b459c2cSDavid du Colombier next1 = limit1;
233*0b459c2cSDavid du Colombier }
234*0b459c2cSDavid du Colombier
235*0b459c2cSDavid du Colombier /* Print a hunk of an sdiff diff.
236*0b459c2cSDavid du Colombier This is a contiguous portion of a complete edit script,
237*0b459c2cSDavid du Colombier describing changes in consecutive lines. */
238*0b459c2cSDavid du Colombier
239*0b459c2cSDavid du Colombier static void
print_sdiff_hunk(hunk)240*0b459c2cSDavid du Colombier print_sdiff_hunk (hunk)
241*0b459c2cSDavid du Colombier struct change *hunk;
242*0b459c2cSDavid du Colombier {
243*0b459c2cSDavid du Colombier int first0, last0, first1, last1, deletes, inserts;
244*0b459c2cSDavid du Colombier register int i, j;
245*0b459c2cSDavid du Colombier
246*0b459c2cSDavid du Colombier /* Determine range of line numbers involved in each file. */
247*0b459c2cSDavid du Colombier analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
248*0b459c2cSDavid du Colombier if (!deletes && !inserts)
249*0b459c2cSDavid du Colombier return;
250*0b459c2cSDavid du Colombier
251*0b459c2cSDavid du Colombier /* Print out lines up to this change. */
252*0b459c2cSDavid du Colombier print_sdiff_common_lines (first0, first1);
253*0b459c2cSDavid du Colombier
254*0b459c2cSDavid du Colombier if (sdiff_help_sdiff)
255*0b459c2cSDavid du Colombier fprintf (outfile, "c%d,%d\n", last0 - first0 + 1, last1 - first1 + 1);
256*0b459c2cSDavid du Colombier
257*0b459c2cSDavid du Colombier /* Print ``xxx | xxx '' lines */
258*0b459c2cSDavid du Colombier if (inserts && deletes)
259*0b459c2cSDavid du Colombier {
260*0b459c2cSDavid du Colombier for (i = first0, j = first1; i <= last0 && j <= last1; ++i, ++j)
261*0b459c2cSDavid du Colombier print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
262*0b459c2cSDavid du Colombier deletes = i <= last0;
263*0b459c2cSDavid du Colombier inserts = j <= last1;
264*0b459c2cSDavid du Colombier next0 = first0 = i;
265*0b459c2cSDavid du Colombier next1 = first1 = j;
266*0b459c2cSDavid du Colombier }
267*0b459c2cSDavid du Colombier
268*0b459c2cSDavid du Colombier
269*0b459c2cSDavid du Colombier /* Print `` > xxx '' lines */
270*0b459c2cSDavid du Colombier if (inserts)
271*0b459c2cSDavid du Colombier {
272*0b459c2cSDavid du Colombier for (j = first1; j <= last1; ++j)
273*0b459c2cSDavid du Colombier print_1sdiff_line (0, '>', &files[1].linbuf[j]);
274*0b459c2cSDavid du Colombier next1 = j;
275*0b459c2cSDavid du Colombier }
276*0b459c2cSDavid du Colombier
277*0b459c2cSDavid du Colombier /* Print ``xxx < '' lines */
278*0b459c2cSDavid du Colombier if (deletes)
279*0b459c2cSDavid du Colombier {
280*0b459c2cSDavid du Colombier for (i = first0; i <= last0; ++i)
281*0b459c2cSDavid du Colombier print_1sdiff_line (&files[0].linbuf[i], '<', 0);
282*0b459c2cSDavid du Colombier next0 = i;
283*0b459c2cSDavid du Colombier }
284*0b459c2cSDavid du Colombier }
285