xref: /plan9/sys/src/ape/cmd/diff/side.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
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