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