Blame src/ifdef.c

Packit 33f14e
/* #ifdef-format output routines for GNU DIFF.
Packit 33f14e
Packit 33f14e
   Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2017
Packit 33f14e
   Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This file is part of GNU DIFF.
Packit 33f14e
Packit 33f14e
   GNU DIFF is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY.  No author or distributor
Packit 33f14e
   accepts responsibility to anyone for the consequences of using it
Packit 33f14e
   or for whether it serves any particular purpose or works at all,
Packit 33f14e
   unless he says so in writing.  Refer to the GNU General Public
Packit 33f14e
   License for full details.
Packit 33f14e
Packit 33f14e
   Everyone is granted permission to copy, modify and redistribute
Packit 33f14e
   GNU DIFF, but only under the conditions described in the
Packit 33f14e
   GNU General Public License.   A copy of this license is
Packit 33f14e
   supposed to have been given to you along with GNU DIFF so you
Packit 33f14e
   can know your rights and responsibilities.  It should be in a
Packit 33f14e
   file named COPYING.  Among other things, the copyright notice
Packit 33f14e
   and this notice must be preserved on all copies.  */
Packit 33f14e
Packit 33f14e
#include "diff.h"
Packit 33f14e
Packit 33f14e
#include <xalloc.h>
Packit 33f14e
Packit 33f14e
struct group
Packit 33f14e
{
Packit 33f14e
  struct file_data const *file;
Packit 33f14e
  lin from, upto; /* start and limit lines for this group of lines */
Packit 33f14e
};
Packit 33f14e
Packit 33f14e
static char const *format_group (FILE *, char const *, char,
Packit 33f14e
				 struct group const *);
Packit 33f14e
static char const *do_printf_spec (FILE *, char const *,
Packit 33f14e
				   struct file_data const *, lin,
Packit 33f14e
				   struct group const *);
Packit 33f14e
static char const *scan_char_literal (char const *, char *);
Packit 33f14e
static lin groups_letter_value (struct group const *, char);
Packit 33f14e
static void format_ifdef (char const *, lin, lin, lin, lin);
Packit 33f14e
static void print_ifdef_hunk (struct change *);
Packit 33f14e
static void print_ifdef_lines (FILE *, char const *, struct group const *);
Packit 33f14e
Packit 33f14e
static lin next_line0;
Packit 33f14e
static lin next_line1;
Packit 33f14e
Packit 33f14e
/* Print the edit-script SCRIPT as a merged #ifdef file.  */
Packit 33f14e
Packit 33f14e
void
Packit 33f14e
print_ifdef_script (struct change *script)
Packit 33f14e
{
Packit 33f14e
  next_line0 = next_line1 = - files[0].prefix_lines;
Packit 33f14e
  print_script (script, find_change, print_ifdef_hunk);
Packit 33f14e
  if (next_line0 < files[0].valid_lines
Packit 33f14e
      || next_line1 < files[1].valid_lines)
Packit 33f14e
    {
Packit 33f14e
      begin_output ();
Packit 33f14e
      format_ifdef (group_format[UNCHANGED],
Packit 33f14e
		    next_line0, files[0].valid_lines,
Packit 33f14e
		    next_line1, files[1].valid_lines);
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Print a hunk of an ifdef diff.
Packit 33f14e
   This is a contiguous portion of a complete edit script,
Packit 33f14e
   describing changes in consecutive lines.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
print_ifdef_hunk (struct change *hunk)
Packit 33f14e
{
Packit 33f14e
  lin first0, last0, first1, last1;
Packit 33f14e
Packit 33f14e
  /* Determine range of line numbers involved in each file.  */
Packit 33f14e
  enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
Packit 33f14e
  if (!changes)
Packit 33f14e
    return;
Packit 33f14e
Packit 33f14e
  begin_output ();
Packit 33f14e
Packit 33f14e
  /* Print lines up to this change.  */
Packit 33f14e
  if (next_line0 < first0 || next_line1 < first1)
Packit 33f14e
    format_ifdef (group_format[UNCHANGED],
Packit 33f14e
		  next_line0, first0,
Packit 33f14e
		  next_line1, first1);
Packit 33f14e
Packit 33f14e
  /* Print this change.  */
Packit 33f14e
  next_line0 = last0 + 1;
Packit 33f14e
  next_line1 = last1 + 1;
Packit 33f14e
  format_ifdef (group_format[changes],
Packit 33f14e
		first0, next_line0,
Packit 33f14e
		first1, next_line1);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Print a set of lines according to FORMAT.
Packit 33f14e
   Lines BEG0 up to END0 are from the first file;
Packit 33f14e
   lines BEG1 up to END1 are from the second file.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
Packit 33f14e
{
Packit 33f14e
  struct group groups[2];
Packit 33f14e
Packit 33f14e
  groups[0].file = &files[0];
Packit 33f14e
  groups[0].from = beg0;
Packit 33f14e
  groups[0].upto = end0;
Packit 33f14e
  groups[1].file = &files[1];
Packit 33f14e
  groups[1].from = beg1;
Packit 33f14e
  groups[1].upto = end1;
Packit 33f14e
  format_group (outfile, format, 0, groups);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Print to file OUT a set of lines according to FORMAT.
Packit 33f14e
   The format ends at the first free instance of ENDCHAR.
Packit 33f14e
   Yield the address of the terminating character.
Packit 33f14e
   GROUPS specifies which lines to print.
Packit 33f14e
   If OUT is zero, do not actually print anything; just scan the format.  */
Packit 33f14e
Packit 33f14e
static char const *
Packit 33f14e
format_group (register FILE *out, char const *format, char endchar,
Packit 33f14e
	      struct group const *groups)
Packit 33f14e
{
Packit 33f14e
  register char c;
Packit 33f14e
  register char const *f = format;
Packit 33f14e
Packit 33f14e
  while ((c = *f) != endchar && c != 0)
Packit 33f14e
    {
Packit 33f14e
      char const *f1 = ++f;
Packit 33f14e
      if (c == '%')
Packit 33f14e
	switch ((c = *f++))
Packit 33f14e
	  {
Packit 33f14e
	  case '%':
Packit 33f14e
	    break;
Packit 33f14e
Packit 33f14e
	  case '(':
Packit 33f14e
	    /* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'.  */
Packit 33f14e
	    {
Packit 33f14e
	      int i;
Packit 33f14e
	      uintmax_t value[2];
Packit 33f14e
	      FILE *thenout, *elseout;
Packit 33f14e
Packit 33f14e
	      for (i = 0; i < 2; i++)
Packit 33f14e
		{
Packit 33f14e
		  if (ISDIGIT (*f))
Packit 33f14e
		    {
Packit 33f14e
		      char *fend;
Packit 33f14e
		      errno = 0;
Packit 33f14e
		      value[i] = strtoumax (f, &fend, 10);
Packit 33f14e
		      if (errno)
Packit 33f14e
			goto bad_format;
Packit 33f14e
		      f = fend;
Packit 33f14e
		    }
Packit 33f14e
		  else
Packit 33f14e
		    {
Packit 33f14e
		      value[i] = groups_letter_value (groups, *f);
Packit 33f14e
		      if (value[i] == -1)
Packit 33f14e
			goto bad_format;
Packit 33f14e
		      f++;
Packit 33f14e
		    }
Packit 33f14e
		  if (*f++ != "=?"[i])
Packit 33f14e
		    goto bad_format;
Packit 33f14e
		}
Packit 33f14e
	      if (value[0] == value[1])
Packit 33f14e
		thenout = out, elseout = 0;
Packit 33f14e
	      else
Packit 33f14e
		thenout = 0, elseout = out;
Packit 33f14e
	      f = format_group (thenout, f, ':', groups);
Packit 33f14e
	      if (*f)
Packit 33f14e
		{
Packit 33f14e
		  f = format_group (elseout, f + 1, ')', groups);
Packit 33f14e
		  if (*f)
Packit 33f14e
		    f++;
Packit 33f14e
		}
Packit 33f14e
	    }
Packit 33f14e
	    continue;
Packit 33f14e
Packit 33f14e
	  case '<':
Packit 33f14e
	    /* Print lines deleted from first file.  */
Packit 33f14e
	    print_ifdef_lines (out, line_format[OLD], &groups[0]);
Packit 33f14e
	    continue;
Packit 33f14e
Packit 33f14e
	  case '=':
Packit 33f14e
	    /* Print common lines.  */
Packit 33f14e
	    print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
Packit 33f14e
	    continue;
Packit 33f14e
Packit 33f14e
	  case '>':
Packit 33f14e
	    /* Print lines inserted from second file.  */
Packit 33f14e
	    print_ifdef_lines (out, line_format[NEW], &groups[1]);
Packit 33f14e
	    continue;
Packit 33f14e
Packit 33f14e
	  default:
Packit 33f14e
	    f = do_printf_spec (out, f - 2, 0, 0, groups);
Packit 33f14e
	    if (f)
Packit 33f14e
	      continue;
Packit 33f14e
	    /* Fall through. */
Packit 33f14e
	  bad_format:
Packit 33f14e
	    c = '%';
Packit 33f14e
	    f = f1;
Packit 33f14e
	    break;
Packit 33f14e
	  }
Packit 33f14e
Packit 33f14e
      if (out)
Packit 33f14e
	putc (c, out);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  return f;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* For the line group pair G, return the number corresponding to LETTER.
Packit 33f14e
   Return -1 if LETTER is not a group format letter.  */
Packit 33f14e
static lin
Packit 33f14e
groups_letter_value (struct group const *g, char letter)
Packit 33f14e
{
Packit 33f14e
  switch (letter)
Packit 33f14e
    {
Packit 33f14e
    case 'E': letter = 'e'; g++; break;
Packit 33f14e
    case 'F': letter = 'f'; g++; break;
Packit 33f14e
    case 'L': letter = 'l'; g++; break;
Packit 33f14e
    case 'M': letter = 'm'; g++; break;
Packit 33f14e
    case 'N': letter = 'n'; g++; break;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  switch (letter)
Packit 33f14e
    {
Packit 33f14e
      case 'e': return translate_line_number (g->file, g->from) - 1;
Packit 33f14e
      case 'f': return translate_line_number (g->file, g->from);
Packit 33f14e
      case 'l': return translate_line_number (g->file, g->upto) - 1;
Packit 33f14e
      case 'm': return translate_line_number (g->file, g->upto);
Packit 33f14e
      case 'n': return g->upto - g->from;
Packit 33f14e
      default: return -1;
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Print to file OUT, using FORMAT to print the line group GROUP.
Packit 33f14e
   But do nothing if OUT is zero.  */
Packit 33f14e
static void
Packit 33f14e
print_ifdef_lines (register FILE *out, char const *format,
Packit 33f14e
		   struct group const *group)
Packit 33f14e
{
Packit 33f14e
  struct file_data const *file = group->file;
Packit 33f14e
  char const * const *linbuf = file->linbuf;
Packit 33f14e
  lin from = group->from, upto = group->upto;
Packit 33f14e
Packit 33f14e
  if (!out)
Packit 33f14e
    return;
Packit 33f14e
Packit 33f14e
  /* If possible, use a single fwrite; it's faster.  */
Packit 33f14e
  if (!expand_tabs && format[0] == '%')
Packit 33f14e
    {
Packit 33f14e
      if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
Packit 33f14e
	{
Packit 33f14e
	  fwrite (linbuf[from], sizeof (char),
Packit 33f14e
		  linbuf[upto] + (linbuf[upto][-1] != '\n') -  linbuf[from],
Packit 33f14e
		  out);
Packit 33f14e
	  return;
Packit 33f14e
	}
Packit 33f14e
      if (format[1] == 'L' && !format[2])
Packit 33f14e
	{
Packit 33f14e
	  fwrite (linbuf[from], sizeof (char),
Packit 33f14e
		  linbuf[upto] -  linbuf[from], out);
Packit 33f14e
	  return;
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  for (;  from < upto;  from++)
Packit 33f14e
    {
Packit 33f14e
      register char c;
Packit 33f14e
      register char const *f = format;
Packit 33f14e
Packit 33f14e
      while ((c = *f++) != 0)
Packit 33f14e
	{
Packit 33f14e
	  char const *f1 = f;
Packit 33f14e
	  if (c == '%')
Packit 33f14e
	    switch ((c = *f++))
Packit 33f14e
	      {
Packit 33f14e
	      case '%':
Packit 33f14e
		break;
Packit 33f14e
Packit 33f14e
	      case 'l':
Packit 33f14e
		output_1_line (linbuf[from],
Packit 33f14e
			       (linbuf[from + 1]
Packit 33f14e
				- (linbuf[from + 1][-1] == '\n')),
Packit 33f14e
			       0, 0);
Packit 33f14e
		continue;
Packit 33f14e
Packit 33f14e
	      case 'L':
Packit 33f14e
		output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
Packit 33f14e
		continue;
Packit 33f14e
Packit 33f14e
	      default:
Packit 33f14e
		f = do_printf_spec (out, f - 2, file, from, 0);
Packit 33f14e
		if (f)
Packit 33f14e
		  continue;
Packit 33f14e
		c = '%';
Packit 33f14e
		f = f1;
Packit 33f14e
		break;
Packit 33f14e
	      }
Packit 33f14e
Packit 33f14e
	  putc (c, out);
Packit 33f14e
	}
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static char const *
Packit 33f14e
do_printf_spec (FILE *out, char const *spec,
Packit 33f14e
		struct file_data const *file, lin n,
Packit 33f14e
		struct group const *groups)
Packit 33f14e
{
Packit 33f14e
  char const *f = spec;
Packit 33f14e
  char c;
Packit 33f14e
  char c1;
Packit 33f14e
Packit 33f14e
  /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX].  */
Packit 33f14e
  /* assert (*f == '%'); */
Packit 33f14e
  f++;
Packit 33f14e
  while ((c = *f++) == '-' || c == '\'' || c == '0')
Packit 33f14e
    continue;
Packit 33f14e
  while (ISDIGIT (c))
Packit 33f14e
    c = *f++;
Packit 33f14e
  if (c == '.')
Packit 33f14e
    while (ISDIGIT (c = *f++))
Packit 33f14e
      continue;
Packit 33f14e
  c1 = *f++;
Packit 33f14e
Packit 33f14e
  switch (c)
Packit 33f14e
    {
Packit 33f14e
    case 'c':
Packit 33f14e
      if (c1 != '\'')
Packit 33f14e
	return 0;
Packit 33f14e
      else
Packit 33f14e
	{
Packit 33f14e
	  char value IF_LINT (= 0);
Packit 33f14e
	  f = scan_char_literal (f, &value);
Packit 33f14e
	  if (!f)
Packit 33f14e
	    return 0;
Packit 33f14e
	  if (out)
Packit 33f14e
	    putc (value, out);
Packit 33f14e
	}
Packit 33f14e
      break;
Packit 33f14e
Packit 33f14e
    case 'd': case 'o': case 'x': case 'X':
Packit 33f14e
      {
Packit 33f14e
	lin value;
Packit 33f14e
Packit 33f14e
	if (file)
Packit 33f14e
	  {
Packit 33f14e
	    if (c1 != 'n')
Packit 33f14e
	      return 0;
Packit 33f14e
	    value = translate_line_number (file, n);
Packit 33f14e
	  }
Packit 33f14e
	else
Packit 33f14e
	  {
Packit 33f14e
	    value = groups_letter_value (groups, c1);
Packit 33f14e
	    if (value < 0)
Packit 33f14e
	      return 0;
Packit 33f14e
	  }
Packit 33f14e
Packit 33f14e
	if (out)
Packit 33f14e
	  {
Packit 33f14e
	    /* For example, if the spec is "%3xn" and pI is "l", use the printf
Packit 33f14e
	       format spec "%3lx".  Here the spec prefix is "%3".  */
Packit 33f14e
	    printint print_value = value;
Packit 33f14e
	    size_t spec_prefix_len = f - spec - 2;
Packit 33f14e
	    size_t pI_len = sizeof pI - 1;
Packit Service 3a92f2
#if 0
Packit Service 3a92f2
	    char format[spec_prefix_len + pI_len + 2];
Packit Service 3a92f2
#else
Packit 33f14e
	    char *format = xmalloc (spec_prefix_len + pI_len + 2);
Packit Service 3a92f2
#endif
Packit 33f14e
	    char *p = format + spec_prefix_len + pI_len;
Packit 33f14e
	    memcpy (format, spec, spec_prefix_len);
Packit 33f14e
	    memcpy (format + spec_prefix_len, pI, pI_len);
Packit 33f14e
	    *p++ = c;
Packit 33f14e
	    *p = '\0';
Packit 33f14e
	    fprintf (out, format, print_value);
Packit Service 3a92f2
#if ! HAVE_C_VARARRAYS
Packit 33f14e
	    free (format);
Packit Service 3a92f2
#endif
Packit 33f14e
	  }
Packit 33f14e
      }
Packit 33f14e
      break;
Packit 33f14e
Packit 33f14e
    default:
Packit 33f14e
      return 0;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  return f;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Scan the character literal represented in the string LIT; LIT points just
Packit 33f14e
   after the initial apostrophe.  Put the literal's value into *VALPTR.
Packit 33f14e
   Yield the address of the first character after the closing apostrophe,
Packit 33f14e
   or a null pointer if the literal is ill-formed.  */
Packit 33f14e
static char const *
Packit 33f14e
scan_char_literal (char const *lit, char *valptr)
Packit 33f14e
{
Packit 33f14e
  register char const *p = lit;
Packit 33f14e
  char value;
Packit 33f14e
  ptrdiff_t digits;
Packit 33f14e
  char c = *p++;
Packit 33f14e
Packit 33f14e
  switch (c)
Packit 33f14e
    {
Packit 33f14e
      case 0:
Packit 33f14e
      case '\'':
Packit 33f14e
	return NULL;
Packit 33f14e
Packit 33f14e
      case '\\':
Packit 33f14e
	value = 0;
Packit 33f14e
	while ((c = *p++) != '\'')
Packit 33f14e
	  {
Packit 33f14e
	    unsigned int digit = c - '0';
Packit 33f14e
	    if (8 <= digit)
Packit 33f14e
	      return NULL;
Packit 33f14e
	    value = 8 * value + digit;
Packit 33f14e
	  }
Packit 33f14e
	digits = p - lit - 2;
Packit 33f14e
	if (! (1 <= digits && digits <= 3))
Packit 33f14e
	  return NULL;
Packit 33f14e
	break;
Packit 33f14e
Packit 33f14e
      default:
Packit 33f14e
	value = c;
Packit 33f14e
	if (*p++ != '\'')
Packit 33f14e
	  return NULL;
Packit 33f14e
	break;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  *valptr = value;
Packit 33f14e
  return p;
Packit 33f14e
}