xref: /freebsd/contrib/diff/src/ifdef.c (revision 1f469a9fc498c3d406ef7c4e347232678f49da0a)
1  /* #ifdef-format output routines for GNU DIFF.
2  
3     Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 Free
4     Software 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  #include "diff.h"
24  
25  #include <xalloc.h>
26  
27  struct group
28  {
29    struct file_data const *file;
30    lin from, upto; /* start and limit lines for this group of lines */
31  };
32  
33  static char const *format_group (FILE *, char const *, char,
34  				 struct group const *);
35  static char const *do_printf_spec (FILE *, char const *,
36  				   struct file_data const *, lin,
37  				   struct group const *);
38  static char const *scan_char_literal (char const *, char *);
39  static lin groups_letter_value (struct group const *, char);
40  static void format_ifdef (char const *, lin, lin, lin, lin);
41  static void print_ifdef_hunk (struct change *);
42  static void print_ifdef_lines (FILE *, char const *, struct group const *);
43  
44  static lin next_line0;
45  static lin next_line1;
46  
47  /* Print the edit-script SCRIPT as a merged #ifdef file.  */
48  
49  void
50  print_ifdef_script (struct change *script)
51  {
52    next_line0 = next_line1 = - files[0].prefix_lines;
53    print_script (script, find_change, print_ifdef_hunk);
54    if (next_line0 < files[0].valid_lines
55        || next_line1 < files[1].valid_lines)
56      {
57        begin_output ();
58        format_ifdef (group_format[UNCHANGED],
59  		    next_line0, files[0].valid_lines,
60  		    next_line1, files[1].valid_lines);
61      }
62  }
63  
64  /* Print a hunk of an ifdef diff.
65     This is a contiguous portion of a complete edit script,
66     describing changes in consecutive lines.  */
67  
68  static void
69  print_ifdef_hunk (struct change *hunk)
70  {
71    lin first0, last0, first1, last1;
72  
73    /* Determine range of line numbers involved in each file.  */
74    enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
75    if (!changes)
76      return;
77  
78    begin_output ();
79  
80    /* Print lines up to this change.  */
81    if (next_line0 < first0 || next_line1 < first1)
82      format_ifdef (group_format[UNCHANGED],
83  		  next_line0, first0,
84  		  next_line1, first1);
85  
86    /* Print this change.  */
87    next_line0 = last0 + 1;
88    next_line1 = last1 + 1;
89    format_ifdef (group_format[changes],
90  		first0, next_line0,
91  		first1, next_line1);
92  }
93  
94  /* Print a set of lines according to FORMAT.
95     Lines BEG0 up to END0 are from the first file;
96     lines BEG1 up to END1 are from the second file.  */
97  
98  static void
99  format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
100  {
101    struct group groups[2];
102  
103    groups[0].file = &files[0];
104    groups[0].from = beg0;
105    groups[0].upto = end0;
106    groups[1].file = &files[1];
107    groups[1].from = beg1;
108    groups[1].upto = end1;
109    format_group (outfile, format, 0, groups);
110  }
111  
112  /* Print to file OUT a set of lines according to FORMAT.
113     The format ends at the first free instance of ENDCHAR.
114     Yield the address of the terminating character.
115     GROUPS specifies which lines to print.
116     If OUT is zero, do not actually print anything; just scan the format.  */
117  
118  static char const *
119  format_group (register FILE *out, char const *format, char endchar,
120  	      struct group const *groups)
121  {
122    register char c;
123    register char const *f = format;
124  
125    while ((c = *f) != endchar && c != 0)
126      {
127        char const *f1 = ++f;
128        if (c == '%')
129  	switch ((c = *f++))
130  	  {
131  	  case '%':
132  	    break;
133  
134  	  case '(':
135  	    /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
136  	    {
137  	      int i;
138  	      uintmax_t value[2];
139  	      FILE *thenout, *elseout;
140  
141  	      for (i = 0; i < 2; i++)
142  		{
143  		  if (ISDIGIT (*f))
144  		    {
145  		      char *fend;
146  		      errno = 0;
147  		      value[i] = strtoumax (f, &fend, 10);
148  		      if (errno)
149  			goto bad_format;
150  		      f = fend;
151  		    }
152  		  else
153  		    {
154  		      value[i] = groups_letter_value (groups, *f);
155  		      if (value[i] == -1)
156  			goto bad_format;
157  		      f++;
158  		    }
159  		  if (*f++ != "=?"[i])
160  		    goto bad_format;
161  		}
162  	      if (value[0] == value[1])
163  		thenout = out, elseout = 0;
164  	      else
165  		thenout = 0, elseout = out;
166  	      f = format_group (thenout, f, ':', groups);
167  	      if (*f)
168  		{
169  		  f = format_group (elseout, f + 1, ')', groups);
170  		  if (*f)
171  		    f++;
172  		}
173  	    }
174  	    continue;
175  
176  	  case '<':
177  	    /* Print lines deleted from first file.  */
178  	    print_ifdef_lines (out, line_format[OLD], &groups[0]);
179  	    continue;
180  
181  	  case '=':
182  	    /* Print common lines.  */
183  	    print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
184  	    continue;
185  
186  	  case '>':
187  	    /* Print lines inserted from second file.  */
188  	    print_ifdef_lines (out, line_format[NEW], &groups[1]);
189  	    continue;
190  
191  	  default:
192  	    f = do_printf_spec (out, f - 2, 0, 0, groups);
193  	    if (f)
194  	      continue;
195  	    /* Fall through. */
196  	  bad_format:
197  	    c = '%';
198  	    f = f1;
199  	    break;
200  	  }
201  
202        if (out)
203  	putc (c, out);
204      }
205  
206    return f;
207  }
208  
209  /* For the line group pair G, return the number corresponding to LETTER.
210     Return -1 if LETTER is not a group format letter.  */
211  static lin
212  groups_letter_value (struct group const *g, char letter)
213  {
214    switch (letter)
215      {
216      case 'E': letter = 'e'; g++; break;
217      case 'F': letter = 'f'; g++; break;
218      case 'L': letter = 'l'; g++; break;
219      case 'M': letter = 'm'; g++; break;
220      case 'N': letter = 'n'; g++; break;
221      }
222  
223    switch (letter)
224      {
225        case 'e': return translate_line_number (g->file, g->from) - 1;
226        case 'f': return translate_line_number (g->file, g->from);
227        case 'l': return translate_line_number (g->file, g->upto) - 1;
228        case 'm': return translate_line_number (g->file, g->upto);
229        case 'n': return g->upto - g->from;
230        default: return -1;
231      }
232  }
233  
234  /* Print to file OUT, using FORMAT to print the line group GROUP.
235     But do nothing if OUT is zero.  */
236  static void
237  print_ifdef_lines (register FILE *out, char const *format,
238  		   struct group const *group)
239  {
240    struct file_data const *file = group->file;
241    char const * const *linbuf = file->linbuf;
242    lin from = group->from, upto = group->upto;
243  
244    if (!out)
245      return;
246  
247    /* If possible, use a single fwrite; it's faster.  */
248    if (!expand_tabs && format[0] == '%')
249      {
250        if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
251  	{
252  	  fwrite (linbuf[from], sizeof (char),
253  		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
254  		  out);
255  	  return;
256  	}
257        if (format[1] == 'L' && !format[2])
258  	{
259  	  fwrite (linbuf[from], sizeof (char),
260  		  linbuf[upto] -  linbuf[from], out);
261  	  return;
262  	}
263      }
264  
265    for (;  from < upto;  from++)
266      {
267        register char c;
268        register char const *f = format;
269  
270        while ((c = *f++) != 0)
271  	{
272  	  char const *f1 = f;
273  	  if (c == '%')
274  	    switch ((c = *f++))
275  	      {
276  	      case '%':
277  		break;
278  
279  	      case 'l':
280  		output_1_line (linbuf[from],
281  			       (linbuf[from + 1]
282  				- (linbuf[from + 1][-1] == '\n')),
283  			       0, 0);
284  		continue;
285  
286  	      case 'L':
287  		output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
288  		continue;
289  
290  	      default:
291  		f = do_printf_spec (out, f - 2, file, from, 0);
292  		if (f)
293  		  continue;
294  		c = '%';
295  		f = f1;
296  		break;
297  	      }
298  
299  	  putc (c, out);
300  	}
301      }
302  }
303  
304  static char const *
305  do_printf_spec (FILE *out, char const *spec,
306  		struct file_data const *file, lin n,
307  		struct group const *groups)
308  {
309    char const *f = spec;
310    char c;
311    char c1;
312  
313    /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX].  */
314    /* assert (*f == '%'); */
315    f++;
316    while ((c = *f++) == '-' || c == '\'' || c == '0')
317      continue;
318    while (ISDIGIT (c))
319      c = *f++;
320    if (c == '.')
321      while (ISDIGIT (c = *f++))
322        continue;
323    c1 = *f++;
324  
325    switch (c)
326      {
327      case 'c':
328        if (c1 != '\'')
329  	return 0;
330        else
331  	{
332  	  char value;
333  	  f = scan_char_literal (f, &value);
334  	  if (!f)
335  	    return 0;
336  	  if (out)
337  	    putc (value, out);
338  	}
339        break;
340  
341      case 'd': case 'o': case 'x': case 'X':
342        {
343  	lin value;
344  
345  	if (file)
346  	  {
347  	    if (c1 != 'n')
348  	      return 0;
349  	    value = translate_line_number (file, n);
350  	  }
351  	else
352  	  {
353  	    value = groups_letter_value (groups, c1);
354  	    if (value < 0)
355  	      return 0;
356  	  }
357  
358  	if (out)
359  	  {
360  	    /* For example, if the spec is "%3xn", use the printf
361  	       format spec "%3lx".  Here the spec prefix is "%3".  */
362  	    long int long_value = value;
363  	    size_t spec_prefix_len = f - spec - 2;
364  #if HAVE_C_VARARRAYS
365  	    char format[spec_prefix_len + 3];
366  #else
367  	    char *format = xmalloc (spec_prefix_len + 3);
368  #endif
369  	    char *p = format + spec_prefix_len;
370  	    memcpy (format, spec, spec_prefix_len);
371  	    *p++ = 'l';
372  	    *p++ = c;
373  	    *p = '\0';
374  	    fprintf (out, format, long_value);
375  #if ! HAVE_C_VARARRAYS
376  	    free (format);
377  #endif
378  	  }
379        }
380        break;
381  
382      default:
383        return 0;
384      }
385  
386    return f;
387  }
388  
389  /* Scan the character literal represented in the string LIT; LIT points just
390     after the initial apostrophe.  Put the literal's value into *VALPTR.
391     Yield the address of the first character after the closing apostrophe,
392     or zero if the literal is ill-formed.  */
393  static char const *
394  scan_char_literal (char const *lit, char *valptr)
395  {
396    register char const *p = lit;
397    char value;
398    ptrdiff_t digits;
399    char c = *p++;
400  
401    switch (c)
402      {
403        case 0:
404        case '\'':
405  	return 0;
406  
407        case '\\':
408  	value = 0;
409  	while ((c = *p++) != '\'')
410  	  {
411  	    unsigned int digit = c - '0';
412  	    if (8 <= digit)
413  	      return 0;
414  	    value = 8 * value + digit;
415  	  }
416  	digits = p - lit - 2;
417  	if (! (1 <= digits && digits <= 3))
418  	  return 0;
419  	break;
420  
421        default:
422  	value = c;
423  	if (*p++ != '\'')
424  	  return 0;
425  	break;
426      }
427  
428    *valptr = value;
429    return p;
430  }
431