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