1170754Sdelphij/* #ifdef-format output routines for GNU DIFF.
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 Free
4170754Sdelphij   Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This file is part of GNU DIFF.
7170754Sdelphij
8170754Sdelphij   GNU DIFF is distributed in the hope that it will be useful,
9170754Sdelphij   but WITHOUT ANY WARRANTY.  No author or distributor
10170754Sdelphij   accepts responsibility to anyone for the consequences of using it
11170754Sdelphij   or for whether it serves any particular purpose or works at all,
12170754Sdelphij   unless he says so in writing.  Refer to the GNU DIFF General Public
13170754Sdelphij   License for full details.
14170754Sdelphij
15170754Sdelphij   Everyone is granted permission to copy, modify and redistribute
16170754Sdelphij   GNU DIFF, but only under the conditions described in the
17170754Sdelphij   GNU DIFF General Public License.   A copy of this license is
18170754Sdelphij   supposed to have been given to you along with GNU DIFF so you
19170754Sdelphij   can know your rights and responsibilities.  It should be in a
20170754Sdelphij   file named COPYING.  Among other things, the copyright notice
21170754Sdelphij   and this notice must be preserved on all copies.  */
22170754Sdelphij
23170754Sdelphij#include "diff.h"
24170754Sdelphij
25170754Sdelphij#include <xalloc.h>
26170754Sdelphij
27170754Sdelphijstruct group
28170754Sdelphij{
29170754Sdelphij  struct file_data const *file;
30170754Sdelphij  lin from, upto; /* start and limit lines for this group of lines */
31170754Sdelphij};
32170754Sdelphij
33170754Sdelphijstatic char const *format_group (FILE *, char const *, char,
34170754Sdelphij				 struct group const *);
35170754Sdelphijstatic char const *do_printf_spec (FILE *, char const *,
36170754Sdelphij				   struct file_data const *, lin,
37170754Sdelphij				   struct group const *);
38170754Sdelphijstatic char const *scan_char_literal (char const *, char *);
39170754Sdelphijstatic lin groups_letter_value (struct group const *, char);
40170754Sdelphijstatic void format_ifdef (char const *, lin, lin, lin, lin);
41170754Sdelphijstatic void print_ifdef_hunk (struct change *);
42170754Sdelphijstatic void print_ifdef_lines (FILE *, char const *, struct group const *);
43170754Sdelphij
44170754Sdelphijstatic lin next_line0;
45170754Sdelphijstatic lin next_line1;
46170754Sdelphij
47170754Sdelphij/* Print the edit-script SCRIPT as a merged #ifdef file.  */
48170754Sdelphij
49170754Sdelphijvoid
50170754Sdelphijprint_ifdef_script (struct change *script)
51170754Sdelphij{
52170754Sdelphij  next_line0 = next_line1 = - files[0].prefix_lines;
53170754Sdelphij  print_script (script, find_change, print_ifdef_hunk);
54170754Sdelphij  if (next_line0 < files[0].valid_lines
55170754Sdelphij      || next_line1 < files[1].valid_lines)
56170754Sdelphij    {
57170754Sdelphij      begin_output ();
58170754Sdelphij      format_ifdef (group_format[UNCHANGED],
59170754Sdelphij		    next_line0, files[0].valid_lines,
60170754Sdelphij		    next_line1, files[1].valid_lines);
61170754Sdelphij    }
62170754Sdelphij}
63170754Sdelphij
64170754Sdelphij/* Print a hunk of an ifdef diff.
65170754Sdelphij   This is a contiguous portion of a complete edit script,
66170754Sdelphij   describing changes in consecutive lines.  */
67170754Sdelphij
68170754Sdelphijstatic void
69170754Sdelphijprint_ifdef_hunk (struct change *hunk)
70170754Sdelphij{
71170754Sdelphij  lin first0, last0, first1, last1;
72170754Sdelphij
73170754Sdelphij  /* Determine range of line numbers involved in each file.  */
74170754Sdelphij  enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
75170754Sdelphij  if (!changes)
76170754Sdelphij    return;
77170754Sdelphij
78170754Sdelphij  begin_output ();
79170754Sdelphij
80170754Sdelphij  /* Print lines up to this change.  */
81170754Sdelphij  if (next_line0 < first0 || next_line1 < first1)
82170754Sdelphij    format_ifdef (group_format[UNCHANGED],
83170754Sdelphij		  next_line0, first0,
84170754Sdelphij		  next_line1, first1);
85170754Sdelphij
86170754Sdelphij  /* Print this change.  */
87170754Sdelphij  next_line0 = last0 + 1;
88170754Sdelphij  next_line1 = last1 + 1;
89170754Sdelphij  format_ifdef (group_format[changes],
90170754Sdelphij		first0, next_line0,
91170754Sdelphij		first1, next_line1);
92170754Sdelphij}
93170754Sdelphij
94170754Sdelphij/* Print a set of lines according to FORMAT.
95170754Sdelphij   Lines BEG0 up to END0 are from the first file;
96170754Sdelphij   lines BEG1 up to END1 are from the second file.  */
97170754Sdelphij
98170754Sdelphijstatic void
99170754Sdelphijformat_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
100170754Sdelphij{
101170754Sdelphij  struct group groups[2];
102170754Sdelphij
103170754Sdelphij  groups[0].file = &files[0];
104170754Sdelphij  groups[0].from = beg0;
105170754Sdelphij  groups[0].upto = end0;
106170754Sdelphij  groups[1].file = &files[1];
107170754Sdelphij  groups[1].from = beg1;
108170754Sdelphij  groups[1].upto = end1;
109170754Sdelphij  format_group (outfile, format, 0, groups);
110170754Sdelphij}
111170754Sdelphij
112170754Sdelphij/* Print to file OUT a set of lines according to FORMAT.
113170754Sdelphij   The format ends at the first free instance of ENDCHAR.
114170754Sdelphij   Yield the address of the terminating character.
115170754Sdelphij   GROUPS specifies which lines to print.
116170754Sdelphij   If OUT is zero, do not actually print anything; just scan the format.  */
117170754Sdelphij
118170754Sdelphijstatic char const *
119170754Sdelphijformat_group (register FILE *out, char const *format, char endchar,
120170754Sdelphij	      struct group const *groups)
121170754Sdelphij{
122170754Sdelphij  register char c;
123170754Sdelphij  register char const *f = format;
124170754Sdelphij
125170754Sdelphij  while ((c = *f) != endchar && c != 0)
126170754Sdelphij    {
127170754Sdelphij      char const *f1 = ++f;
128170754Sdelphij      if (c == '%')
129170754Sdelphij	switch ((c = *f++))
130170754Sdelphij	  {
131170754Sdelphij	  case '%':
132170754Sdelphij	    break;
133170754Sdelphij
134170754Sdelphij	  case '(':
135170754Sdelphij	    /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
136170754Sdelphij	    {
137170754Sdelphij	      int i;
138170754Sdelphij	      uintmax_t value[2];
139170754Sdelphij	      FILE *thenout, *elseout;
140170754Sdelphij
141170754Sdelphij	      for (i = 0; i < 2; i++)
142170754Sdelphij		{
143170754Sdelphij		  if (ISDIGIT (*f))
144170754Sdelphij		    {
145170754Sdelphij		      char *fend;
146170754Sdelphij		      errno = 0;
147170754Sdelphij		      value[i] = strtoumax (f, &fend, 10);
148170754Sdelphij		      if (errno)
149170754Sdelphij			goto bad_format;
150170754Sdelphij		      f = fend;
151170754Sdelphij		    }
152170754Sdelphij		  else
153170754Sdelphij		    {
154170754Sdelphij		      value[i] = groups_letter_value (groups, *f);
155170754Sdelphij		      if (value[i] == -1)
156170754Sdelphij			goto bad_format;
157170754Sdelphij		      f++;
158170754Sdelphij		    }
159170754Sdelphij		  if (*f++ != "=?"[i])
160170754Sdelphij		    goto bad_format;
161170754Sdelphij		}
162170754Sdelphij	      if (value[0] == value[1])
163170754Sdelphij		thenout = out, elseout = 0;
164170754Sdelphij	      else
165170754Sdelphij		thenout = 0, elseout = out;
166170754Sdelphij	      f = format_group (thenout, f, ':', groups);
167170754Sdelphij	      if (*f)
168170754Sdelphij		{
169170754Sdelphij		  f = format_group (elseout, f + 1, ')', groups);
170170754Sdelphij		  if (*f)
171170754Sdelphij		    f++;
172170754Sdelphij		}
173170754Sdelphij	    }
174170754Sdelphij	    continue;
175170754Sdelphij
176170754Sdelphij	  case '<':
177170754Sdelphij	    /* Print lines deleted from first file.  */
178170754Sdelphij	    print_ifdef_lines (out, line_format[OLD], &groups[0]);
179170754Sdelphij	    continue;
180170754Sdelphij
181170754Sdelphij	  case '=':
182170754Sdelphij	    /* Print common lines.  */
183170754Sdelphij	    print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
184170754Sdelphij	    continue;
185170754Sdelphij
186170754Sdelphij	  case '>':
187170754Sdelphij	    /* Print lines inserted from second file.  */
188170754Sdelphij	    print_ifdef_lines (out, line_format[NEW], &groups[1]);
189170754Sdelphij	    continue;
190170754Sdelphij
191170754Sdelphij	  default:
192170754Sdelphij	    f = do_printf_spec (out, f - 2, 0, 0, groups);
193170754Sdelphij	    if (f)
194170754Sdelphij	      continue;
195170754Sdelphij	    /* Fall through. */
196170754Sdelphij	  bad_format:
197170754Sdelphij	    c = '%';
198170754Sdelphij	    f = f1;
199170754Sdelphij	    break;
200170754Sdelphij	  }
201170754Sdelphij
202170754Sdelphij      if (out)
203170754Sdelphij	putc (c, out);
204170754Sdelphij    }
205170754Sdelphij
206170754Sdelphij  return f;
207170754Sdelphij}
208170754Sdelphij
209170754Sdelphij/* For the line group pair G, return the number corresponding to LETTER.
210170754Sdelphij   Return -1 if LETTER is not a group format letter.  */
211170754Sdelphijstatic lin
212170754Sdelphijgroups_letter_value (struct group const *g, char letter)
213170754Sdelphij{
214170754Sdelphij  switch (letter)
215170754Sdelphij    {
216170754Sdelphij    case 'E': letter = 'e'; g++; break;
217170754Sdelphij    case 'F': letter = 'f'; g++; break;
218170754Sdelphij    case 'L': letter = 'l'; g++; break;
219170754Sdelphij    case 'M': letter = 'm'; g++; break;
220170754Sdelphij    case 'N': letter = 'n'; g++; break;
221170754Sdelphij    }
222170754Sdelphij
223170754Sdelphij  switch (letter)
224170754Sdelphij    {
225170754Sdelphij      case 'e': return translate_line_number (g->file, g->from) - 1;
226170754Sdelphij      case 'f': return translate_line_number (g->file, g->from);
227170754Sdelphij      case 'l': return translate_line_number (g->file, g->upto) - 1;
228170754Sdelphij      case 'm': return translate_line_number (g->file, g->upto);
229170754Sdelphij      case 'n': return g->upto - g->from;
230170754Sdelphij      default: return -1;
231170754Sdelphij    }
232170754Sdelphij}
233170754Sdelphij
234170754Sdelphij/* Print to file OUT, using FORMAT to print the line group GROUP.
235170754Sdelphij   But do nothing if OUT is zero.  */
236170754Sdelphijstatic void
237170754Sdelphijprint_ifdef_lines (register FILE *out, char const *format,
238170754Sdelphij		   struct group const *group)
239170754Sdelphij{
240170754Sdelphij  struct file_data const *file = group->file;
241170754Sdelphij  char const * const *linbuf = file->linbuf;
242170754Sdelphij  lin from = group->from, upto = group->upto;
243170754Sdelphij
244170754Sdelphij  if (!out)
245170754Sdelphij    return;
246170754Sdelphij
247170754Sdelphij  /* If possible, use a single fwrite; it's faster.  */
248170754Sdelphij  if (!expand_tabs && format[0] == '%')
249170754Sdelphij    {
250170754Sdelphij      if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
251170754Sdelphij	{
252170754Sdelphij	  fwrite (linbuf[from], sizeof (char),
253170754Sdelphij		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
254170754Sdelphij		  out);
255170754Sdelphij	  return;
256170754Sdelphij	}
257170754Sdelphij      if (format[1] == 'L' && !format[2])
258170754Sdelphij	{
259170754Sdelphij	  fwrite (linbuf[from], sizeof (char),
260170754Sdelphij		  linbuf[upto] -  linbuf[from], out);
261170754Sdelphij	  return;
262170754Sdelphij	}
263170754Sdelphij    }
264170754Sdelphij
265170754Sdelphij  for (;  from < upto;  from++)
266170754Sdelphij    {
267170754Sdelphij      register char c;
268170754Sdelphij      register char const *f = format;
269170754Sdelphij
270170754Sdelphij      while ((c = *f++) != 0)
271170754Sdelphij	{
272170754Sdelphij	  char const *f1 = f;
273170754Sdelphij	  if (c == '%')
274170754Sdelphij	    switch ((c = *f++))
275170754Sdelphij	      {
276170754Sdelphij	      case '%':
277170754Sdelphij		break;
278170754Sdelphij
279170754Sdelphij	      case 'l':
280170754Sdelphij		output_1_line (linbuf[from],
281170754Sdelphij			       (linbuf[from + 1]
282170754Sdelphij				- (linbuf[from + 1][-1] == '\n')),
283170754Sdelphij			       0, 0);
284170754Sdelphij		continue;
285170754Sdelphij
286170754Sdelphij	      case 'L':
287170754Sdelphij		output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
288170754Sdelphij		continue;
289170754Sdelphij
290170754Sdelphij	      default:
291170754Sdelphij		f = do_printf_spec (out, f - 2, file, from, 0);
292170754Sdelphij		if (f)
293170754Sdelphij		  continue;
294170754Sdelphij		c = '%';
295170754Sdelphij		f = f1;
296170754Sdelphij		break;
297170754Sdelphij	      }
298170754Sdelphij
299170754Sdelphij	  putc (c, out);
300170754Sdelphij	}
301170754Sdelphij    }
302170754Sdelphij}
303170754Sdelphij
304170754Sdelphijstatic char const *
305170754Sdelphijdo_printf_spec (FILE *out, char const *spec,
306170754Sdelphij		struct file_data const *file, lin n,
307170754Sdelphij		struct group const *groups)
308170754Sdelphij{
309170754Sdelphij  char const *f = spec;
310170754Sdelphij  char c;
311170754Sdelphij  char c1;
312170754Sdelphij
313170754Sdelphij  /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX].  */
314170754Sdelphij  /* assert (*f == '%'); */
315170754Sdelphij  f++;
316170754Sdelphij  while ((c = *f++) == '-' || c == '\'' || c == '0')
317170754Sdelphij    continue;
318170754Sdelphij  while (ISDIGIT (c))
319170754Sdelphij    c = *f++;
320170754Sdelphij  if (c == '.')
321170754Sdelphij    while (ISDIGIT (c = *f++))
322170754Sdelphij      continue;
323170754Sdelphij  c1 = *f++;
324170754Sdelphij
325170754Sdelphij  switch (c)
326170754Sdelphij    {
327170754Sdelphij    case 'c':
328170754Sdelphij      if (c1 != '\'')
329170754Sdelphij	return 0;
330170754Sdelphij      else
331170754Sdelphij	{
332170754Sdelphij	  char value;
333170754Sdelphij	  f = scan_char_literal (f, &value);
334170754Sdelphij	  if (!f)
335170754Sdelphij	    return 0;
336170754Sdelphij	  if (out)
337170754Sdelphij	    putc (value, out);
338170754Sdelphij	}
339170754Sdelphij      break;
340170754Sdelphij
341170754Sdelphij    case 'd': case 'o': case 'x': case 'X':
342170754Sdelphij      {
343170754Sdelphij	lin value;
344170754Sdelphij
345170754Sdelphij	if (file)
346170754Sdelphij	  {
347170754Sdelphij	    if (c1 != 'n')
348170754Sdelphij	      return 0;
349170754Sdelphij	    value = translate_line_number (file, n);
350170754Sdelphij	  }
351170754Sdelphij	else
352170754Sdelphij	  {
353170754Sdelphij	    value = groups_letter_value (groups, c1);
354170754Sdelphij	    if (value < 0)
355170754Sdelphij	      return 0;
356170754Sdelphij	  }
357170754Sdelphij
358170754Sdelphij	if (out)
359170754Sdelphij	  {
360170754Sdelphij	    /* For example, if the spec is "%3xn", use the printf
361170754Sdelphij	       format spec "%3lx".  Here the spec prefix is "%3".  */
362170754Sdelphij	    long int long_value = value;
363170754Sdelphij	    size_t spec_prefix_len = f - spec - 2;
364170754Sdelphij#if HAVE_C_VARARRAYS
365170754Sdelphij	    char format[spec_prefix_len + 3];
366170754Sdelphij#else
367170754Sdelphij	    char *format = xmalloc (spec_prefix_len + 3);
368170754Sdelphij#endif
369170754Sdelphij	    char *p = format + spec_prefix_len;
370170754Sdelphij	    memcpy (format, spec, spec_prefix_len);
371170754Sdelphij	    *p++ = 'l';
372170754Sdelphij	    *p++ = c;
373170754Sdelphij	    *p = '\0';
374170754Sdelphij	    fprintf (out, format, long_value);
375170754Sdelphij#if ! HAVE_C_VARARRAYS
376170754Sdelphij	    free (format);
377170754Sdelphij#endif
378170754Sdelphij	  }
379170754Sdelphij      }
380170754Sdelphij      break;
381170754Sdelphij
382170754Sdelphij    default:
383170754Sdelphij      return 0;
384170754Sdelphij    }
385170754Sdelphij
386170754Sdelphij  return f;
387170754Sdelphij}
388170754Sdelphij
389170754Sdelphij/* Scan the character literal represented in the string LIT; LIT points just
390170754Sdelphij   after the initial apostrophe.  Put the literal's value into *VALPTR.
391170754Sdelphij   Yield the address of the first character after the closing apostrophe,
392170754Sdelphij   or zero if the literal is ill-formed.  */
393170754Sdelphijstatic char const *
394170754Sdelphijscan_char_literal (char const *lit, char *valptr)
395170754Sdelphij{
396170754Sdelphij  register char const *p = lit;
397170754Sdelphij  char value;
398170754Sdelphij  ptrdiff_t digits;
399170754Sdelphij  char c = *p++;
400170754Sdelphij
401170754Sdelphij  switch (c)
402170754Sdelphij    {
403170754Sdelphij      case 0:
404170754Sdelphij      case '\'':
405170754Sdelphij	return 0;
406170754Sdelphij
407170754Sdelphij      case '\\':
408170754Sdelphij	value = 0;
409170754Sdelphij	while ((c = *p++) != '\'')
410170754Sdelphij	  {
411170754Sdelphij	    unsigned int digit = c - '0';
412170754Sdelphij	    if (8 <= digit)
413170754Sdelphij	      return 0;
414170754Sdelphij	    value = 8 * value + digit;
415170754Sdelphij	  }
416170754Sdelphij	digits = p - lit - 2;
417170754Sdelphij	if (! (1 <= digits && digits <= 3))
418170754Sdelphij	  return 0;
419170754Sdelphij	break;
420170754Sdelphij
421170754Sdelphij      default:
422170754Sdelphij	value = c;
423170754Sdelphij	if (*p++ != '\'')
424170754Sdelphij	  return 0;
425170754Sdelphij	break;
426170754Sdelphij    }
427170754Sdelphij
428170754Sdelphij  *valptr = value;
429170754Sdelphij  return p;
430170754Sdelphij}
431