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