1 /* sdiff-format output routines for GNU DIFF.
2 
3    Copyright (C) 1991, 1992, 1993, 1998, 2001, 2002 Free Software
4    Foundation, Inc.
5 
6    This file is part of GNU DIFF.
7 
8    GNU DIFF is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY.  No author or distributor
10    accepts responsibility to anyone for the consequences of using it
11    or for whether it serves any particular purpose or works at all,
12    unless he says so in writing.  Refer to the GNU DIFF General Public
13    License for full details.
14 
15    Everyone is granted permission to copy, modify and redistribute
16    GNU DIFF, but only under the conditions described in the
17    GNU DIFF General Public License.   A copy of this license is
18    supposed to have been given to you along with GNU DIFF so you
19    can know your rights and responsibilities.  It should be in a
20    file named COPYING.  Among other things, the copyright notice
21    and this notice must be preserved on all copies.  */
22 
23 /*
24     This file, coming from the source code of GNU DIFF,
25     has been modified by Ivano Primi  <ivprimi@libero.it>
26     so that it could be merged into the source code of Numdiff.
27 
28     Numdiff is free software; you can redistribute it and/or modify
29     it under the terms of the GNU General Public License as published by
30     the Free Software Foundation; either version 3 of the License, or
31     (at your option) any later version.
32 
33     Numdiff is distributed in the hope that it will be useful,
34     but WITHOUT ANY WARRANTY; without even the implied warranty of
35     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36     GNU General Public License for more details.
37 */
38 
39 #include "numdiff.h"
40 
41 static void print_sdiff_common_lines (lin, lin);
42 static void print_sdiff_hunk (struct change *);
43 
44 /* Next line number to be printed in the two input files.  */
45 static lin next0, next1;
46 
47 /* Print the edit-script SCRIPT as a sdiff style output.  */
48 
49 void
print_sdiff_script(struct change * script)50 print_sdiff_script (struct change *script)
51 {
52   next0 = next1 = - files[0].prefix_lines;
53   print_script (script, print_sdiff_hunk);
54 
55   print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines);
56 }
57 
58 /* Tab from column FROM to column TO, where FROM <= TO.  Yield TO.  */
59 
60 static unsigned int
tab_from_to(unsigned int from,unsigned int to)61 tab_from_to (unsigned int from, unsigned int to)
62 {
63   FILE *out = outfile;
64   unsigned int tab;
65 
66   if (!expand_tabs)
67     for (tab = from + TAB_WIDTH - from % TAB_WIDTH;  tab <= to;  tab += TAB_WIDTH)
68       {
69 	putc ('\t', out);
70 	from = tab;
71       }
72   while (from++ < to)
73     putc (' ', out);
74   return to;
75 }
76 
77 /*
78  * Print the text for half an sdiff line.  This means truncate to width
79  * observing tabs, and trim a trailing newline.  Returns the last column
80  * written (not the number of chars).
81  */
82 static unsigned int
print_half_line(char const * const * line,unsigned int indent,unsigned int out_bound)83 print_half_line (char const *const *line, unsigned int indent,
84 		 unsigned int out_bound)
85 {
86   FILE *out = outfile;
87   register unsigned int in_position = 0;
88   register unsigned int out_position = 0;
89   register char const *text_pointer = line[0];
90   register char const *text_limit = line[1];
91 
92   while (text_pointer < text_limit)
93     {
94       register unsigned char c = *text_pointer++;
95 
96       switch (c)
97 	{
98 	case '\t':
99 	  {
100 	    unsigned int spaces = TAB_WIDTH - in_position % TAB_WIDTH;
101 	    if (in_position == out_position)
102 	      {
103 		unsigned int tabstop = out_position + spaces;
104 		if ((expand_tabs))
105 		  {
106 		    if (out_bound < tabstop)
107 		      tabstop = out_bound;
108 		    for (;  out_position < tabstop;  out_position++)
109 		      putc (' ', out);
110 		  }
111 		else
112 		  if (tabstop < out_bound)
113 		    {
114 		      out_position = tabstop;
115 		      putc (c, out);
116 		    }
117 	      }
118 	    in_position += spaces;
119 	  }
120 	  break;
121 
122 	case '\r':
123 	  {
124 	    putc (c, out);
125 	    tab_from_to (0, indent);
126 	    in_position = out_position = 0;
127 	  }
128 	  break;
129 
130 	case '\b':
131 	  if (in_position != 0 && --in_position < out_bound)
132 	    {
133 	      if (out_position <= in_position)
134 		/* Add spaces to make up for suppressed tab past out_bound.  */
135 		for (;  out_position < in_position;  out_position++)
136 		  putc (' ', out);
137 	      else
138 		{
139 		  out_position = in_position;
140 		  putc (c, out);
141 		}
142 	    }
143 	  break;
144 
145 	case '\f':
146 	case '\v':
147 	control_char:
148 	  if (in_position < out_bound)
149 	    putc (c, out);
150 	  break;
151 
152 	default:
153 	  if (! ISPRINT (c))
154 	    goto control_char;
155 	  /* falls through */
156 	case ' ':
157 	  if (in_position++ < out_bound)
158 	    {
159 	      out_position = in_position;
160 	      putc (c, out);
161 	    }
162 	  break;
163 
164 	case '\n':
165 	  return out_position;
166 	}
167     }
168 
169   return out_position;
170 }
171 
172 /*
173  * Print side by side lines with a separator in the middle.
174  * 0 parameters are taken to indicate white space text.
175  * Blank lines that can easily be caught are reduced to a single newline.
176  */
177 
178 static void
print_1sdiff_line(char const * const * left,char sep,char const * const * right)179 print_1sdiff_line (char const *const *left, char sep,
180 		   char const *const *right)
181 {
182   FILE *out = outfile;
183   unsigned int hw = sdiff_half_width, c2o = sdiff_column2_offset;
184   unsigned int col = 0;
185   bool put_newline = 0;
186 
187   if (left)
188     {
189       put_newline |= left[1][-1] == '\n';
190       col = print_half_line (left, 0, hw);
191     }
192 
193   if (sep != ' ')
194     {
195       col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
196       if (sep == '|' && put_newline != (right[1][-1] == '\n'))
197 	sep = put_newline ? '/' : '\\';
198       putc (sep, out);
199     }
200 
201   if (right)
202     {
203       put_newline |= right[1][-1] == '\n';
204       if (**right != '\n')
205 	{
206 	  col = tab_from_to (col, c2o);
207 	  print_half_line (right, col, hw);
208 	}
209     }
210 
211   if (put_newline)
212     putc ('\n', out);
213 }
214 
215 /* Print lines common to both files in side-by-side format.  */
216 static void
print_sdiff_common_lines(lin limit0,lin limit1)217 print_sdiff_common_lines (lin limit0, lin limit1)
218 {
219   lin i0 = next0, i1 = next1;
220 /*   long len0, len1; */
221 
222   if (i0 != limit0 || i1 != limit1)
223     {
224       if (!suppress_common_lines)
225 	{
226 	  while (i0 != limit0 && i1 != limit1)
227 	    print_1sdiff_line (&files[0].linbuf[i0++], ' ',
228 			       &files[1].linbuf[i1++]);
229 	  while (i1 != limit1)
230 	    print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
231 
232 	  while (i0 != limit0)
233 	    print_1sdiff_line (&files[0].linbuf[i0++], '(', 0);
234 	}
235 /*       else */
236 /* 	{ */
237 /* 	  len0 = limit0 - i0; */
238 /* 	  len1 = limit1 - i1; */
239 /* 	  fprintf (outfile, "i%ld,%ld\n", len0, len1); */
240 /* 	} */
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(struct change * hunk)252 print_sdiff_hunk (struct change *hunk)
253 {
254   lin first0, last0, first1, last1;
255   register lin i, j;
256 
257   /* Determine range of line numbers involved in each file.  */
258   enum changes changes =
259     analyze_hunk (hunk, &first0, &last0, &first1, &last1);
260   if (!changes)
261     return;
262 
263   /* Print out lines up to this change.  */
264   print_sdiff_common_lines (first0, first1);
265 
266 /*   if ((suppress_common_lines)) */
267 /*     { */
268 /*       long len0 = last0 - first0 + 1; */
269 /*       long len1 = last1 - first1 + 1; */
270 /*       fprintf (outfile, "c%ld,%ld\n", len0, len1); */
271 /*     } */
272 
273   /* Print ``xxx  |  xxx '' lines */
274   if (changes == CHANGED)
275     {
276       for (i = first0, j = first1;  i <= last0 && j <= last1;  i++, j++)
277 	print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]);
278       changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0);
279       next0 = first0 = i;
280       next1 = first1 = j;
281     }
282 
283   /* Print ``     >  xxx '' lines */
284   if (changes & NEW)
285     {
286       for (j = first1; j <= last1; ++j)
287 	print_1sdiff_line (0, '>', &files[1].linbuf[j]);
288       next1 = j;
289     }
290 
291   /* Print ``xxx  <     '' lines */
292   if (changes & OLD)
293     {
294       for (i = first0; i <= last0; ++i)
295 	print_1sdiff_line (&files[0].linbuf[i], '<', 0);
296       next0 = i;
297     }
298 }
299 
300 /*
301  * This function does the same actions as print_half_line(),
302  * but takes as first argument a simple pointer to
303  * const char
304  */
305 static unsigned int
display_half_line(const char * line,unsigned int indent,unsigned int out_bound)306 display_half_line (const char* line, unsigned int indent,
307 		   unsigned int out_bound)
308 {
309   FILE *out = outfile;
310   register unsigned int in_position = 0;
311   register unsigned int out_position = 0;
312   register const char *text_pointer = line;
313   register const char *text_limit = line;
314 
315   for (; *text_limit != '\0'; text_limit++);
316   while (text_pointer < text_limit)
317     {
318       register unsigned char c = *text_pointer++;
319 
320       switch (c)
321 	{
322 	case '\t':
323 	  {
324 	    unsigned int spaces = TAB_WIDTH - in_position % TAB_WIDTH;
325 	    if (in_position == out_position)
326 	      {
327 		unsigned int tabstop = out_position + spaces;
328 		if ((expand_tabs))
329 		  {
330 		    if (out_bound < tabstop)
331 		      tabstop = out_bound;
332 		    for (;  out_position < tabstop;  out_position++)
333 		      putc (' ', out);
334 		  }
335 		else
336 		  if (tabstop < out_bound)
337 		    {
338 		      out_position = tabstop;
339 		      putc (c, out);
340 		    }
341 	      }
342 	    in_position += spaces;
343 	  }
344 	  break;
345 
346 	case '\r':
347 	  {
348 	    putc (c, out);
349 	    tab_from_to (0, indent);
350 	    in_position = out_position = 0;
351 	  }
352 	  break;
353 
354 	case '\b':
355 	  if (in_position != 0 && --in_position < out_bound)
356 	    {
357 	      if (out_position <= in_position)
358 		/* Add spaces to make up for suppressed tab past out_bound.  */
359 		for (;  out_position < in_position;  out_position++)
360 		  putc (' ', out);
361 	      else
362 		{
363 		  out_position = in_position;
364 		  putc (c, out);
365 		}
366 	    }
367 	  break;
368 
369 	case '\f':
370 	case '\v':
371 	control_character:
372 	  if (in_position < out_bound)
373 	    putc (c, out);
374 	  break;
375 
376 	default:
377 	  if (! ISPRINT (c))
378 	    goto control_character;
379 	  /* falls through */
380 	case ' ':
381 	  if (in_position++ < out_bound)
382 	    {
383 	      out_position = in_position;
384 	      putc (c, out);
385 	    }
386 	  break;
387 
388 	case '\n':
389 	  return out_position;
390 	}
391     }
392 
393   return out_position;
394 }
395 
396 /*
397  * Print side by side lines with a separator in the middle.
398  * 0 parameters are taken to indicate white space text.
399  * Blank lines that can easily be caught are reduced to a single newline.
400  * This function works almost like  print_1sdiff_line() (see above),
401  * but the parameter list is slightly different.
402  * In addition, different separators are used
403  */
404 
405 void
print_1overview_line(const char * left,int are_different,const char * right)406 print_1overview_line (const char *left, int are_different,
407 		      const char *right)
408 {
409   FILE *out = outfile;
410   unsigned int hw = sdiff_half_width, c2o = sdiff_column2_offset;
411   unsigned int col = 0;
412   bool put_newline = 0;
413   register const char *end_of_left = left;
414   register const char *end_of_right = right;
415   const char *sep = ":!:";
416 
417   if (left)
418     {
419       for (; *end_of_left != '\0'; end_of_left++);
420       if (end_of_left != left)
421 	end_of_left--;
422       put_newline |= *end_of_left == '\n';
423       col = display_half_line (left, 0, hw);
424     }
425   else
426     sep = ":>:";
427 
428   if (!right)
429     sep = ":<:";
430 
431   if ((are_different))
432     {
433       col = tab_from_to (col, (hw + c2o - 3) / 2) + 3;
434       fputs (sep, out);
435     }
436 
437   if (right)
438     {
439       for (; *end_of_right != '\0'; end_of_right++);
440       if (end_of_right != right)
441 	end_of_right--;
442       put_newline |= (*end_of_right == '\n');
443       if (*right != '\n')
444 	{
445 	  col = tab_from_to (col, c2o);
446 	  display_half_line (right, col, hw);
447 	}
448     }
449 
450   if (put_newline)
451     putc ('\n', out);
452 }
453