xref: /dragonfly/contrib/diffutils/src/side.c (revision 6ea1f93e)
1855caec6SPeter Avalos /* sdiff-format output routines for GNU DIFF.
2855caec6SPeter Avalos 
3*6ea1f93eSDaniel Fojt    Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2018 Free
4*6ea1f93eSDaniel Fojt    Software Foundation, Inc.
5855caec6SPeter Avalos 
6855caec6SPeter Avalos    This file is part of GNU DIFF.
7855caec6SPeter Avalos 
8855caec6SPeter Avalos    GNU DIFF is distributed in the hope that it will be useful,
9855caec6SPeter Avalos    but WITHOUT ANY WARRANTY.  No author or distributor
10855caec6SPeter Avalos    accepts responsibility to anyone for the consequences of using it
11855caec6SPeter Avalos    or for whether it serves any particular purpose or works at all,
1244b87433SJohn Marino    unless he says so in writing.  Refer to the GNU General Public
13855caec6SPeter Avalos    License for full details.
14855caec6SPeter Avalos 
15855caec6SPeter Avalos    Everyone is granted permission to copy, modify and redistribute
16855caec6SPeter Avalos    GNU DIFF, but only under the conditions described in the
1744b87433SJohn Marino    GNU General Public License.   A copy of this license is
18855caec6SPeter Avalos    supposed to have been given to you along with GNU DIFF so you
19855caec6SPeter Avalos    can know your rights and responsibilities.  It should be in a
20855caec6SPeter Avalos    file named COPYING.  Among other things, the copyright notice
21855caec6SPeter Avalos    and this notice must be preserved on all copies.  */
22855caec6SPeter Avalos 
23855caec6SPeter Avalos #include "diff.h"
24855caec6SPeter Avalos 
2544b87433SJohn Marino #include <wchar.h>
2644b87433SJohn Marino 
27855caec6SPeter Avalos static void print_sdiff_common_lines (lin, lin);
28855caec6SPeter Avalos static void print_sdiff_hunk (struct change *);
29855caec6SPeter Avalos 
30855caec6SPeter Avalos /* Next line number to be printed in the two input files.  */
31855caec6SPeter Avalos static lin next0, next1;
32855caec6SPeter Avalos 
33855caec6SPeter Avalos /* Print the edit-script SCRIPT as a sdiff style output.  */
34855caec6SPeter Avalos 
35855caec6SPeter Avalos void
print_sdiff_script(struct change * script)36855caec6SPeter Avalos print_sdiff_script (struct change *script)
37855caec6SPeter Avalos {
38855caec6SPeter Avalos   begin_output ();
39855caec6SPeter Avalos 
40855caec6SPeter Avalos   next0 = next1 = - files[0].prefix_lines;
41855caec6SPeter Avalos   print_script (script, find_change, print_sdiff_hunk);
42855caec6SPeter Avalos 
43855caec6SPeter Avalos   print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
44855caec6SPeter Avalos }
45855caec6SPeter Avalos 
46855caec6SPeter Avalos /* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
47855caec6SPeter Avalos 
48855caec6SPeter Avalos static size_t
tab_from_to(size_t from,size_t to)49855caec6SPeter Avalos tab_from_to (size_t from, size_t to)
50855caec6SPeter Avalos {
51855caec6SPeter Avalos   FILE *out = outfile;
52855caec6SPeter Avalos   size_t tab;
53855caec6SPeter Avalos   size_t tab_size = tabsize;
54855caec6SPeter Avalos 
55855caec6SPeter Avalos   if (!expand_tabs)
56855caec6SPeter Avalos     for (tab = from + tab_size - from % tab_size;  tab <= to;  tab += tab_size)
57855caec6SPeter Avalos       {
58855caec6SPeter Avalos 	putc ('\t', out);
59855caec6SPeter Avalos 	from = tab;
60855caec6SPeter Avalos       }
61855caec6SPeter Avalos   while (from++ < to)
62855caec6SPeter Avalos     putc (' ', out);
63855caec6SPeter Avalos   return to;
64855caec6SPeter Avalos }
65855caec6SPeter Avalos 
66855caec6SPeter Avalos /* Print the text for half an sdiff line.  This means truncate to
67855caec6SPeter Avalos    width observing tabs, and trim a trailing newline.  Return the
68855caec6SPeter Avalos    last column written (not the number of chars).  */
69855caec6SPeter Avalos 
70855caec6SPeter Avalos static size_t
print_half_line(char const * const * line,size_t indent,size_t out_bound)71855caec6SPeter Avalos print_half_line (char const *const *line, size_t indent, size_t out_bound)
72855caec6SPeter Avalos {
73855caec6SPeter Avalos   FILE *out = outfile;
74855caec6SPeter Avalos   register size_t in_position = 0;
75855caec6SPeter Avalos   register size_t out_position = 0;
76855caec6SPeter Avalos   register char const *text_pointer = line[0];
77855caec6SPeter Avalos   register char const *text_limit = line[1];
7844b87433SJohn Marino   mbstate_t mbstate = { 0 };
79855caec6SPeter Avalos 
80855caec6SPeter Avalos   while (text_pointer < text_limit)
81855caec6SPeter Avalos     {
8244b87433SJohn Marino       char const *tp0 = text_pointer;
8344b87433SJohn Marino       register char c = *text_pointer++;
84855caec6SPeter Avalos 
85855caec6SPeter Avalos       switch (c)
86855caec6SPeter Avalos 	{
87855caec6SPeter Avalos 	case '\t':
88855caec6SPeter Avalos 	  {
89855caec6SPeter Avalos 	    size_t spaces = tabsize - in_position % tabsize;
90855caec6SPeter Avalos 	    if (in_position == out_position)
91855caec6SPeter Avalos 	      {
92855caec6SPeter Avalos 		size_t tabstop = out_position + spaces;
93855caec6SPeter Avalos 		if (expand_tabs)
94855caec6SPeter Avalos 		  {
95855caec6SPeter Avalos 		    if (out_bound < tabstop)
96855caec6SPeter Avalos 		      tabstop = out_bound;
97855caec6SPeter Avalos 		    for (;  out_position < tabstop;  out_position++)
98855caec6SPeter Avalos 		      putc (' ', out);
99855caec6SPeter Avalos 		  }
100855caec6SPeter Avalos 		else
101855caec6SPeter Avalos 		  if (tabstop < out_bound)
102855caec6SPeter Avalos 		    {
103855caec6SPeter Avalos 		      out_position = tabstop;
104855caec6SPeter Avalos 		      putc (c, out);
105855caec6SPeter Avalos 		    }
106855caec6SPeter Avalos 	      }
107855caec6SPeter Avalos 	    in_position += spaces;
108855caec6SPeter Avalos 	  }
109855caec6SPeter Avalos 	  break;
110855caec6SPeter Avalos 
111855caec6SPeter Avalos 	case '\r':
112855caec6SPeter Avalos 	  {
113855caec6SPeter Avalos 	    putc (c, out);
114855caec6SPeter Avalos 	    tab_from_to (0, indent);
115855caec6SPeter Avalos 	    in_position = out_position = 0;
116855caec6SPeter Avalos 	  }
117855caec6SPeter Avalos 	  break;
118855caec6SPeter Avalos 
119855caec6SPeter Avalos 	case '\b':
120855caec6SPeter Avalos 	  if (in_position != 0 && --in_position < out_bound)
121855caec6SPeter Avalos 	    {
122855caec6SPeter Avalos 	      if (out_position <= in_position)
123855caec6SPeter Avalos 		/* Add spaces to make up for suppressed tab past out_bound.  */
124855caec6SPeter Avalos 		for (;  out_position < in_position;  out_position++)
125855caec6SPeter Avalos 		  putc (' ', out);
126855caec6SPeter Avalos 	      else
127855caec6SPeter Avalos 		{
128855caec6SPeter Avalos 		  out_position = in_position;
129855caec6SPeter Avalos 		  putc (c, out);
130855caec6SPeter Avalos 		}
131855caec6SPeter Avalos 	    }
132855caec6SPeter Avalos 	  break;
133855caec6SPeter Avalos 
13444b87433SJohn Marino 	default:
13544b87433SJohn Marino 	  {
13644b87433SJohn Marino 	    wchar_t wc;
13744b87433SJohn Marino 	    size_t bytes = mbrtowc (&wc, tp0, text_limit - tp0, &mbstate);
13844b87433SJohn Marino 
13944b87433SJohn Marino 	    if (0 < bytes && bytes < (size_t) -2)
14044b87433SJohn Marino 	      {
14144b87433SJohn Marino 		int width = wcwidth (wc);
14244b87433SJohn Marino 		if (0 < width)
14344b87433SJohn Marino 		  in_position += width;
14444b87433SJohn Marino 		if (in_position <= out_bound)
14544b87433SJohn Marino 		  {
14644b87433SJohn Marino 		    out_position = in_position;
14744b87433SJohn Marino 		    fwrite (tp0, 1, bytes, stdout);
14844b87433SJohn Marino 		  }
14944b87433SJohn Marino 		text_pointer = tp0 + bytes;
15044b87433SJohn Marino 		break;
15144b87433SJohn Marino 	      }
15244b87433SJohn Marino 	  }
153*6ea1f93eSDaniel Fojt 	  FALLTHROUGH;
154855caec6SPeter Avalos 	case '\f':
155855caec6SPeter Avalos 	case '\v':
156855caec6SPeter Avalos 	  if (in_position < out_bound)
157855caec6SPeter Avalos 	    putc (c, out);
158855caec6SPeter Avalos 	  break;
159855caec6SPeter Avalos 
16044b87433SJohn Marino 	case ' ': case '!': case '"': case '#': case '%':
16144b87433SJohn Marino 	case '&': case '\'': case '(': case ')': case '*':
16244b87433SJohn Marino 	case '+': case ',': case '-': case '.': case '/':
16344b87433SJohn Marino 	case '0': case '1': case '2': case '3': case '4':
16444b87433SJohn Marino 	case '5': case '6': case '7': case '8': case '9':
16544b87433SJohn Marino 	case ':': case ';': case '<': case '=': case '>':
16644b87433SJohn Marino 	case '?':
16744b87433SJohn Marino 	case 'A': case 'B': case 'C': case 'D': case 'E':
16844b87433SJohn Marino 	case 'F': case 'G': case 'H': case 'I': case 'J':
16944b87433SJohn Marino 	case 'K': case 'L': case 'M': case 'N': case 'O':
17044b87433SJohn Marino 	case 'P': case 'Q': case 'R': case 'S': case 'T':
17144b87433SJohn Marino 	case 'U': case 'V': case 'W': case 'X': case 'Y':
17244b87433SJohn Marino 	case 'Z':
17344b87433SJohn Marino 	case '[': case '\\': case ']': case '^': case '_':
17444b87433SJohn Marino 	case 'a': case 'b': case 'c': case 'd': case 'e':
17544b87433SJohn Marino 	case 'f': case 'g': case 'h': case 'i': case 'j':
17644b87433SJohn Marino 	case 'k': case 'l': case 'm': case 'n': case 'o':
17744b87433SJohn Marino 	case 'p': case 'q': case 'r': case 's': case 't':
17844b87433SJohn Marino 	case 'u': case 'v': case 'w': case 'x': case 'y':
17944b87433SJohn Marino 	case 'z': case '{': case '|': case '}': case '~':
18044b87433SJohn Marino 	  /* These characters are printable ASCII characters.  */
181855caec6SPeter Avalos 	  if (in_position++ < out_bound)
182855caec6SPeter Avalos 	    {
183855caec6SPeter Avalos 	      out_position = in_position;
184855caec6SPeter Avalos 	      putc (c, out);
185855caec6SPeter Avalos 	    }
186855caec6SPeter Avalos 	  break;
187855caec6SPeter Avalos 
188855caec6SPeter Avalos 	case '\n':
189855caec6SPeter Avalos 	  return out_position;
190855caec6SPeter Avalos 	}
191855caec6SPeter Avalos     }
192855caec6SPeter Avalos 
193855caec6SPeter Avalos   return out_position;
194855caec6SPeter Avalos }
195855caec6SPeter Avalos 
196855caec6SPeter Avalos /* Print side by side lines with a separator in the middle.
197855caec6SPeter Avalos    0 parameters are taken to indicate white space text.
198855caec6SPeter Avalos    Blank lines that can easily be caught are reduced to a single newline.  */
199855caec6SPeter Avalos 
200855caec6SPeter Avalos static void
print_1sdiff_line(char const * const * left,char sep,char const * const * right)201855caec6SPeter Avalos print_1sdiff_line (char const *const *left, char sep,
202855caec6SPeter Avalos 		   char const *const *right)
203855caec6SPeter Avalos {
204855caec6SPeter Avalos   FILE *out = outfile;
205855caec6SPeter Avalos   size_t hw = sdiff_half_width;
206855caec6SPeter Avalos   size_t c2o = sdiff_column2_offset;
207855caec6SPeter Avalos   size_t col = 0;
208855caec6SPeter Avalos   bool put_newline = false;
209*6ea1f93eSDaniel Fojt   bool color_to_reset = false;
210*6ea1f93eSDaniel Fojt 
211*6ea1f93eSDaniel Fojt   if (sep == '<')
212*6ea1f93eSDaniel Fojt     {
213*6ea1f93eSDaniel Fojt       set_color_context (DELETE_CONTEXT);
214*6ea1f93eSDaniel Fojt       color_to_reset = true;
215*6ea1f93eSDaniel Fojt     }
216*6ea1f93eSDaniel Fojt   else if (sep == '>')
217*6ea1f93eSDaniel Fojt     {
218*6ea1f93eSDaniel Fojt       set_color_context (ADD_CONTEXT);
219*6ea1f93eSDaniel Fojt       color_to_reset = true;
220*6ea1f93eSDaniel Fojt     }
221855caec6SPeter Avalos 
222855caec6SPeter Avalos   if (left)
223855caec6SPeter Avalos     {
224855caec6SPeter Avalos       put_newline |= left[1][-1] == '\n';
225855caec6SPeter Avalos       col = print_half_line (left, 0, hw);
226855caec6SPeter Avalos     }
227855caec6SPeter Avalos 
228855caec6SPeter Avalos   if (sep != ' ')
229855caec6SPeter Avalos     {
230855caec6SPeter Avalos       col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
231855caec6SPeter Avalos       if (sep == '|' && put_newline != (right[1][-1] == '\n'))
232855caec6SPeter Avalos 	sep = put_newline ? '/' : '\\';
233855caec6SPeter Avalos       putc (sep, out);
234855caec6SPeter Avalos     }
235855caec6SPeter Avalos 
236855caec6SPeter Avalos   if (right)
237855caec6SPeter Avalos     {
238855caec6SPeter Avalos       put_newline |= right[1][-1] == '\n';
239855caec6SPeter Avalos       if (**right != '\n')
240855caec6SPeter Avalos 	{
241855caec6SPeter Avalos 	  col = tab_from_to (col, c2o);
242855caec6SPeter Avalos 	  print_half_line (right, col, hw);
243855caec6SPeter Avalos 	}
244855caec6SPeter Avalos     }
245855caec6SPeter Avalos 
246855caec6SPeter Avalos   if (put_newline)
247855caec6SPeter Avalos     putc ('\n', out);
248*6ea1f93eSDaniel Fojt 
249*6ea1f93eSDaniel Fojt   if (color_to_reset)
250*6ea1f93eSDaniel Fojt     set_color_context (RESET_CONTEXT);
251855caec6SPeter Avalos }
252855caec6SPeter Avalos 
253855caec6SPeter Avalos /* Print lines common to both files in side-by-side format.  */
254855caec6SPeter Avalos static void
print_sdiff_common_lines(lin limit0,lin limit1)255855caec6SPeter Avalos print_sdiff_common_lines (lin limit0, lin limit1)
256855caec6SPeter Avalos {
257855caec6SPeter Avalos   lin i0 = next0, i1 = next1;
258855caec6SPeter Avalos 
259855caec6SPeter Avalos   if (!suppress_common_lines && (i0 != limit0 || i1 != limit1))
260855caec6SPeter Avalos     {
261855caec6SPeter Avalos       if (sdiff_merge_assist)
262855caec6SPeter Avalos 	{
263*6ea1f93eSDaniel Fojt 	  printint len0 = limit0 - i0;
264*6ea1f93eSDaniel Fojt 	  printint len1 = limit1 - i1;
265*6ea1f93eSDaniel Fojt 	  fprintf (outfile, "i%"pI"d,%"pI"d\n", len0, len1);
266855caec6SPeter Avalos 	}
267855caec6SPeter Avalos 
268855caec6SPeter Avalos       if (!left_column)
269855caec6SPeter Avalos 	{
270855caec6SPeter Avalos 	  while (i0 != limit0 && i1 != limit1)
271855caec6SPeter Avalos 	    print_1sdiff_line (&files[0].linbuf[i0++], ' ',
272855caec6SPeter Avalos 			       &files[1].linbuf[i1++]);
273855caec6SPeter Avalos 	  while (i1 != limit1)
274855caec6SPeter Avalos 	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
275855caec6SPeter Avalos 	}
276855caec6SPeter Avalos       while (i0 != limit0)
277855caec6SPeter Avalos 	print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
278855caec6SPeter Avalos     }
279855caec6SPeter Avalos 
280855caec6SPeter Avalos   next0 = limit0;
281855caec6SPeter Avalos   next1 = limit1;
282855caec6SPeter Avalos }
283855caec6SPeter Avalos 
284855caec6SPeter Avalos /* Print a hunk of an sdiff diff.
285855caec6SPeter Avalos    This is a contiguous portion of a complete edit script,
286855caec6SPeter Avalos    describing changes in consecutive lines.  */
287855caec6SPeter Avalos 
288855caec6SPeter Avalos static void
print_sdiff_hunk(struct change * hunk)289855caec6SPeter Avalos print_sdiff_hunk (struct change *hunk)
290855caec6SPeter Avalos {
291855caec6SPeter Avalos   lin first0, last0, first1, last1;
292855caec6SPeter Avalos   register lin i, j;
293855caec6SPeter Avalos 
294855caec6SPeter Avalos   /* Determine range of line numbers involved in each file.  */
295855caec6SPeter Avalos   enum changes changes =
296855caec6SPeter Avalos     analyze_hunk (hunk, &first0, &last0, &first1, &last1);
297855caec6SPeter Avalos   if (!changes)
298855caec6SPeter Avalos     return;
299855caec6SPeter Avalos 
300855caec6SPeter Avalos   /* Print out lines up to this change.  */
301855caec6SPeter Avalos   print_sdiff_common_lines (first0, first1);
302855caec6SPeter Avalos 
303855caec6SPeter Avalos   if (sdiff_merge_assist)
304855caec6SPeter Avalos     {
305*6ea1f93eSDaniel Fojt       printint len0 = last0 - first0 + 1;
306*6ea1f93eSDaniel Fojt       printint len1 = last1 - first1 + 1;
307*6ea1f93eSDaniel Fojt       fprintf (outfile, "c%"pI"d,%"pI"d\n", len0, len1);
308855caec6SPeter Avalos     }
309855caec6SPeter Avalos 
3104536c563SJohn Marino   /* Print "xxx  |  xxx " lines.  */
311855caec6SPeter Avalos   if (changes == CHANGED)
312855caec6SPeter Avalos     {
313855caec6SPeter Avalos       for (i = first0, j = first1;  i <= last0 && j <= last1;  i++, j++)
314855caec6SPeter Avalos 	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
315855caec6SPeter Avalos       changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0);
316855caec6SPeter Avalos       next0 = first0 = i;
317855caec6SPeter Avalos       next1 = first1 = j;
318855caec6SPeter Avalos     }
319855caec6SPeter Avalos 
3204536c563SJohn Marino   /* Print "     >  xxx " lines.  */
321855caec6SPeter Avalos   if (changes & NEW)
322855caec6SPeter Avalos     {
323855caec6SPeter Avalos       for (j = first1; j <= last1; ++j)
324855caec6SPeter Avalos 	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
325855caec6SPeter Avalos       next1 = j;
326855caec6SPeter Avalos     }
327855caec6SPeter Avalos 
3284536c563SJohn Marino   /* Print "xxx  <     " lines.  */
329855caec6SPeter Avalos   if (changes & OLD)
330855caec6SPeter Avalos     {
331855caec6SPeter Avalos       for (i = first0; i <= last0; ++i)
332855caec6SPeter Avalos 	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
333855caec6SPeter Avalos       next0 = i;
334855caec6SPeter Avalos     }
335855caec6SPeter Avalos }
336