Blame src/analyze.c

Packit Service fdd496
/* Analyze file differences for GNU DIFF.
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
Packit Service fdd496
   2009-2013, 2015-2017 Free Software Foundation, Inc.
Packit Service fdd496
Packit Service fdd496
   This file is part of GNU DIFF.
Packit Service fdd496
Packit Service fdd496
   This program is free software: you can redistribute it and/or modify
Packit Service fdd496
   it under the terms of the GNU General Public License as published by
Packit Service fdd496
   the Free Software Foundation, either version 3 of the License, or
Packit Service fdd496
   (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   This program is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service fdd496
   GNU General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public License
Packit Service fdd496
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service fdd496
Packit Service fdd496
#include "diff.h"
Packit Service fdd496
#include <cmpbuf.h>
Packit Service fdd496
#include <error.h>
Packit Service fdd496
#include <file-type.h>
Packit Service fdd496
#include <xalloc.h>
Packit Service fdd496
Packit Service fdd496
/* The core of the Diff algorithm.  */
Packit Service fdd496
#define ELEMENT lin
Packit Service fdd496
#define EQUAL(x,y) ((x) == (y))
Packit Service fdd496
#define OFFSET lin
Packit Service fdd496
#define EXTRA_CONTEXT_FIELDS /* none */
Packit Service fdd496
#define NOTE_DELETE(c, xoff) (files[0].changed[files[0].realindexes[xoff]] = 1)
Packit Service fdd496
#define NOTE_INSERT(c, yoff) (files[1].changed[files[1].realindexes[yoff]] = 1)
Packit Service fdd496
#define USE_HEURISTIC 1
Packit Service fdd496
#include <diffseq.h>
Packit Service fdd496
Packit Service fdd496
/* Discard lines from one file that have no matches in the other file.
Packit Service fdd496
Packit Service fdd496
   A line which is discarded will not be considered by the actual
Packit Service fdd496
   comparison algorithm; it will be as if that line were not in the file.
Packit Service fdd496
   The file's 'realindexes' table maps virtual line numbers
Packit Service fdd496
   (which don't count the discarded lines) into real line numbers;
Packit Service fdd496
   this is how the actual comparison algorithm produces results
Packit Service fdd496
   that are comprehensible when the discarded lines are counted.
Packit Service fdd496
Packit Service fdd496
   When we discard a line, we also mark it as a deletion or insertion
Packit Service fdd496
   so that it will be printed in the output.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
discard_confusing_lines (struct file_data filevec[])
Packit Service fdd496
{
Packit Service fdd496
  int f;
Packit Service fdd496
  lin i;
Packit Service fdd496
  char *discarded[2];
Packit Service fdd496
  lin *equiv_count[2];
Packit Service fdd496
  lin *p;
Packit Service fdd496
Packit Service fdd496
  /* Allocate our results.  */
Packit Service fdd496
  p = xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines)
Packit Service fdd496
	       * (2 * sizeof *p));
Packit Service fdd496
  for (f = 0; f < 2; f++)
Packit Service fdd496
    {
Packit Service fdd496
      filevec[f].undiscarded = p;  p += filevec[f].buffered_lines;
Packit Service fdd496
      filevec[f].realindexes = p;  p += filevec[f].buffered_lines;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Set up equiv_count[F][I] as the number of lines in file F
Packit Service fdd496
     that fall in equivalence class I.  */
Packit Service fdd496
Packit Service fdd496
  p = zalloc (filevec[0].equiv_max * (2 * sizeof *p));
Packit Service fdd496
  equiv_count[0] = p;
Packit Service fdd496
  equiv_count[1] = p + filevec[0].equiv_max;
Packit Service fdd496
Packit Service fdd496
  for (i = 0; i < filevec[0].buffered_lines; ++i)
Packit Service fdd496
    ++equiv_count[0][filevec[0].equivs[i]];
Packit Service fdd496
  for (i = 0; i < filevec[1].buffered_lines; ++i)
Packit Service fdd496
    ++equiv_count[1][filevec[1].equivs[i]];
Packit Service fdd496
Packit Service fdd496
  /* Set up tables of which lines are going to be discarded.  */
Packit Service fdd496
Packit Service fdd496
  discarded[0] = zalloc (filevec[0].buffered_lines
Packit Service fdd496
			 + filevec[1].buffered_lines);
Packit Service fdd496
  discarded[1] = discarded[0] + filevec[0].buffered_lines;
Packit Service fdd496
Packit Service fdd496
  /* Mark to be discarded each line that matches no line of the other file.
Packit Service fdd496
     If a line matches many lines, mark it as provisionally discardable.  */
Packit Service fdd496
Packit Service fdd496
  for (f = 0; f < 2; f++)
Packit Service fdd496
    {
Packit Service fdd496
      size_t end = filevec[f].buffered_lines;
Packit Service fdd496
      char *discards = discarded[f];
Packit Service fdd496
      lin *counts = equiv_count[1 - f];
Packit Service fdd496
      lin *equivs = filevec[f].equivs;
Packit Service fdd496
      size_t many = 5;
Packit Service fdd496
      size_t tem = end / 64;
Packit Service fdd496
Packit Service fdd496
      /* Multiply MANY by approximate square root of number of lines.
Packit Service fdd496
	 That is the threshold for provisionally discardable lines.  */
Packit Service fdd496
      while ((tem = tem >> 2) > 0)
Packit Service fdd496
	many *= 2;
Packit Service fdd496
Packit Service fdd496
      for (i = 0; i < end; i++)
Packit Service fdd496
	{
Packit Service fdd496
	  lin nmatch;
Packit Service fdd496
	  if (equivs[i] == 0)
Packit Service fdd496
	    continue;
Packit Service fdd496
	  nmatch = counts[equivs[i]];
Packit Service fdd496
	  if (nmatch == 0)
Packit Service fdd496
	    discards[i] = 1;
Packit Service fdd496
	  else if (nmatch > many)
Packit Service fdd496
	    discards[i] = 2;
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Don't really discard the provisional lines except when they occur
Packit Service fdd496
     in a run of discardables, with nonprovisionals at the beginning
Packit Service fdd496
     and end.  */
Packit Service fdd496
Packit Service fdd496
  for (f = 0; f < 2; f++)
Packit Service fdd496
    {
Packit Service fdd496
      lin end = filevec[f].buffered_lines;
Packit Service fdd496
      register char *discards = discarded[f];
Packit Service fdd496
Packit Service fdd496
      for (i = 0; i < end; i++)
Packit Service fdd496
	{
Packit Service fdd496
	  /* Cancel provisional discards not in middle of run of discards.  */
Packit Service fdd496
	  if (discards[i] == 2)
Packit Service fdd496
	    discards[i] = 0;
Packit Service fdd496
	  else if (discards[i] != 0)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* We have found a nonprovisional discard.  */
Packit Service fdd496
	      register lin j;
Packit Service fdd496
	      lin length;
Packit Service fdd496
	      lin provisional = 0;
Packit Service fdd496
Packit Service fdd496
	      /* Find end of this run of discardable lines.
Packit Service fdd496
		 Count how many are provisionally discardable.  */
Packit Service fdd496
	      for (j = i; j < end; j++)
Packit Service fdd496
		{
Packit Service fdd496
		  if (discards[j] == 0)
Packit Service fdd496
		    break;
Packit Service fdd496
		  if (discards[j] == 2)
Packit Service fdd496
		    ++provisional;
Packit Service fdd496
		}
Packit Service fdd496
Packit Service fdd496
	      /* Cancel provisional discards at end, and shrink the run.  */
Packit Service fdd496
	      while (j > i && discards[j - 1] == 2)
Packit Service fdd496
		discards[--j] = 0, --provisional;
Packit Service fdd496
Packit Service fdd496
	      /* Now we have the length of a run of discardable lines
Packit Service fdd496
		 whose first and last are not provisional.  */
Packit Service fdd496
	      length = j - i;
Packit Service fdd496
Packit Service fdd496
	      /* If 1/4 of the lines in the run are provisional,
Packit Service fdd496
		 cancel discarding of all provisional lines in the run.  */
Packit Service fdd496
	      if (provisional * 4 > length)
Packit Service fdd496
		{
Packit Service fdd496
		  while (j > i)
Packit Service fdd496
		    if (discards[--j] == 2)
Packit Service fdd496
		      discards[j] = 0;
Packit Service fdd496
		}
Packit Service fdd496
	      else
Packit Service fdd496
		{
Packit Service fdd496
		  register lin consec;
Packit Service fdd496
		  lin minimum = 1;
Packit Service fdd496
		  lin tem = length >> 2;
Packit Service fdd496
Packit Service fdd496
		  /* MINIMUM is approximate square root of LENGTH/4.
Packit Service fdd496
		     A subrun of two or more provisionals can stand
Packit Service fdd496
		     when LENGTH is at least 16.
Packit Service fdd496
		     A subrun of 4 or more can stand when LENGTH >= 64.  */
Packit Service fdd496
		  while (0 < (tem >>= 2))
Packit Service fdd496
		    minimum <<= 1;
Packit Service fdd496
		  minimum++;
Packit Service fdd496
Packit Service fdd496
		  /* Cancel any subrun of MINIMUM or more provisionals
Packit Service fdd496
		     within the larger run.  */
Packit Service fdd496
		  for (j = 0, consec = 0; j < length; j++)
Packit Service fdd496
		    if (discards[i + j] != 2)
Packit Service fdd496
		      consec = 0;
Packit Service fdd496
		    else if (minimum == ++consec)
Packit Service fdd496
		      /* Back up to start of subrun, to cancel it all.  */
Packit Service fdd496
		      j -= consec;
Packit Service fdd496
		    else if (minimum < consec)
Packit Service fdd496
		      discards[i + j] = 0;
Packit Service fdd496
Packit Service fdd496
		  /* Scan from beginning of run
Packit Service fdd496
		     until we find 3 or more nonprovisionals in a row
Packit Service fdd496
		     or until the first nonprovisional at least 8 lines in.
Packit Service fdd496
		     Until that point, cancel any provisionals.  */
Packit Service fdd496
		  for (j = 0, consec = 0; j < length; j++)
Packit Service fdd496
		    {
Packit Service fdd496
		      if (j >= 8 && discards[i + j] == 1)
Packit Service fdd496
			break;
Packit Service fdd496
		      if (discards[i + j] == 2)
Packit Service fdd496
			consec = 0, discards[i + j] = 0;
Packit Service fdd496
		      else if (discards[i + j] == 0)
Packit Service fdd496
			consec = 0;
Packit Service fdd496
		      else
Packit Service fdd496
			consec++;
Packit Service fdd496
		      if (consec == 3)
Packit Service fdd496
			break;
Packit Service fdd496
		    }
Packit Service fdd496
Packit Service fdd496
		  /* I advances to the last line of the run.  */
Packit Service fdd496
		  i += length - 1;
Packit Service fdd496
Packit Service fdd496
		  /* Same thing, from end.  */
Packit Service fdd496
		  for (j = 0, consec = 0; j < length; j++)
Packit Service fdd496
		    {
Packit Service fdd496
		      if (j >= 8 && discards[i - j] == 1)
Packit Service fdd496
			break;
Packit Service fdd496
		      if (discards[i - j] == 2)
Packit Service fdd496
			consec = 0, discards[i - j] = 0;
Packit Service fdd496
		      else if (discards[i - j] == 0)
Packit Service fdd496
			consec = 0;
Packit Service fdd496
		      else
Packit Service fdd496
			consec++;
Packit Service fdd496
		      if (consec == 3)
Packit Service fdd496
			break;
Packit Service fdd496
		    }
Packit Service fdd496
		}
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Actually discard the lines. */
Packit Service fdd496
  for (f = 0; f < 2; f++)
Packit Service fdd496
    {
Packit Service fdd496
      char *discards = discarded[f];
Packit Service fdd496
      lin end = filevec[f].buffered_lines;
Packit Service fdd496
      lin j = 0;
Packit Service fdd496
      for (i = 0; i < end; ++i)
Packit Service fdd496
	if (minimal || discards[i] == 0)
Packit Service fdd496
	  {
Packit Service fdd496
	    filevec[f].undiscarded[j] = filevec[f].equivs[i];
Packit Service fdd496
	    filevec[f].realindexes[j++] = i;
Packit Service fdd496
	  }
Packit Service fdd496
	else
Packit Service fdd496
	  filevec[f].changed[i] = 1;
Packit Service fdd496
      filevec[f].nondiscarded_lines = j;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  free (discarded[0]);
Packit Service fdd496
  free (equiv_count[0]);
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Adjust inserts/deletes of identical lines to join changes
Packit Service fdd496
   as much as possible.
Packit Service fdd496
Packit Service fdd496
   We do something when a run of changed lines include a
Packit Service fdd496
   line at one end and have an excluded, identical line at the other.
Packit Service fdd496
   We are free to choose which identical line is included.
Packit Service fdd496
   'compareseq' usually chooses the one at the beginning,
Packit Service fdd496
   but usually it is cleaner to consider the following identical line
Packit Service fdd496
   to be the "change".  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
shift_boundaries (struct file_data filevec[])
Packit Service fdd496
{
Packit Service fdd496
  int f;
Packit Service fdd496
Packit Service fdd496
  for (f = 0; f < 2; f++)
Packit Service fdd496
    {
Packit Service fdd496
      char *changed = filevec[f].changed;
Packit Service fdd496
      char *other_changed = filevec[1 - f].changed;
Packit Service fdd496
      lin const *equivs = filevec[f].equivs;
Packit Service fdd496
      lin i = 0;
Packit Service fdd496
      lin j = 0;
Packit Service fdd496
      lin i_end = filevec[f].buffered_lines;
Packit Service fdd496
Packit Service fdd496
      while (1)
Packit Service fdd496
	{
Packit Service fdd496
	  lin runlength, start, corresponding;
Packit Service fdd496
Packit Service fdd496
	  /* Scan forwards to find beginning of another run of changes.
Packit Service fdd496
	     Also keep track of the corresponding point in the other file.  */
Packit Service fdd496
Packit Service fdd496
	  while (i < i_end && !changed[i])
Packit Service fdd496
	    {
Packit Service fdd496
	      while (other_changed[j++])
Packit Service fdd496
		continue;
Packit Service fdd496
	      i++;
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  if (i == i_end)
Packit Service fdd496
	    break;
Packit Service fdd496
Packit Service fdd496
	  start = i;
Packit Service fdd496
Packit Service fdd496
	  /* Find the end of this run of changes.  */
Packit Service fdd496
Packit Service fdd496
	  while (changed[++i])
Packit Service fdd496
	    continue;
Packit Service fdd496
	  while (other_changed[j])
Packit Service fdd496
	    j++;
Packit Service fdd496
Packit Service fdd496
	  do
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Record the length of this run of changes, so that
Packit Service fdd496
		 we can later determine whether the run has grown.  */
Packit Service fdd496
	      runlength = i - start;
Packit Service fdd496
Packit Service fdd496
	      /* Move the changed region back, so long as the
Packit Service fdd496
		 previous unchanged line matches the last changed one.
Packit Service fdd496
		 This merges with previous changed regions.  */
Packit Service fdd496
Packit Service fdd496
	      while (start && equivs[start - 1] == equivs[i - 1])
Packit Service fdd496
		{
Packit Service fdd496
		  changed[--start] = 1;
Packit Service fdd496
		  changed[--i] = 0;
Packit Service fdd496
		  while (changed[start - 1])
Packit Service fdd496
		    start--;
Packit Service fdd496
		  while (other_changed[--j])
Packit Service fdd496
		    continue;
Packit Service fdd496
		}
Packit Service fdd496
Packit Service fdd496
	      /* Set CORRESPONDING to the end of the changed run, at the last
Packit Service fdd496
		 point where it corresponds to a changed run in the other file.
Packit Service fdd496
		 CORRESPONDING == I_END means no such point has been found.  */
Packit Service fdd496
	      corresponding = other_changed[j - 1] ? i : i_end;
Packit Service fdd496
Packit Service fdd496
	      /* Move the changed region forward, so long as the
Packit Service fdd496
		 first changed line matches the following unchanged one.
Packit Service fdd496
		 This merges with following changed regions.
Packit Service fdd496
		 Do this second, so that if there are no merges,
Packit Service fdd496
		 the changed region is moved forward as far as possible.  */
Packit Service fdd496
Packit Service fdd496
	      while (i != i_end && equivs[start] == equivs[i])
Packit Service fdd496
		{
Packit Service fdd496
		  changed[start++] = 0;
Packit Service fdd496
		  changed[i++] = 1;
Packit Service fdd496
		  while (changed[i])
Packit Service fdd496
		    i++;
Packit Service fdd496
		  while (other_changed[++j])
Packit Service fdd496
		    corresponding = i;
Packit Service fdd496
		}
Packit Service fdd496
	    }
Packit Service fdd496
	  while (runlength != i - start);
Packit Service fdd496
Packit Service fdd496
	  /* If possible, move the fully-merged run of changes
Packit Service fdd496
	     back to a corresponding run in the other file.  */
Packit Service fdd496
Packit Service fdd496
	  while (corresponding < i)
Packit Service fdd496
	    {
Packit Service fdd496
	      changed[--start] = 1;
Packit Service fdd496
	      changed[--i] = 0;
Packit Service fdd496
	      while (other_changed[--j])
Packit Service fdd496
		continue;
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Cons an additional entry onto the front of an edit script OLD.
Packit Service fdd496
   LINE0 and LINE1 are the first affected lines in the two files (origin 0).
Packit Service fdd496
   DELETED is the number of lines deleted here from file 0.
Packit Service fdd496
   INSERTED is the number of lines inserted here in file 1.
Packit Service fdd496
Packit Service fdd496
   If DELETED is 0 then LINE0 is the number of the line before
Packit Service fdd496
   which the insertion was done; vice versa for INSERTED and LINE1.  */
Packit Service fdd496
Packit Service fdd496
static struct change *
Packit Service fdd496
add_change (lin line0, lin line1, lin deleted, lin inserted,
Packit Service fdd496
	    struct change *old)
Packit Service fdd496
{
Packit Service fdd496
  struct change *new = xmalloc (sizeof *new);
Packit Service fdd496
Packit Service fdd496
  new->line0 = line0;
Packit Service fdd496
  new->line1 = line1;
Packit Service fdd496
  new->inserted = inserted;
Packit Service fdd496
  new->deleted = deleted;
Packit Service fdd496
  new->link = old;
Packit Service fdd496
  return new;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Scan the tables of which lines are inserted and deleted,
Packit Service fdd496
   producing an edit script in reverse order.  */
Packit Service fdd496
Packit Service fdd496
static struct change *
Packit Service fdd496
build_reverse_script (struct file_data const filevec[])
Packit Service fdd496
{
Packit Service fdd496
  struct change *script = 0;
Packit Service fdd496
  char *changed0 = filevec[0].changed;
Packit Service fdd496
  char *changed1 = filevec[1].changed;
Packit Service fdd496
  lin len0 = filevec[0].buffered_lines;
Packit Service fdd496
  lin len1 = filevec[1].buffered_lines;
Packit Service fdd496
Packit Service fdd496
  /* Note that changedN[lenN] does exist, and is 0.  */
Packit Service fdd496
Packit Service fdd496
  lin i0 = 0, i1 = 0;
Packit Service fdd496
Packit Service fdd496
  while (i0 < len0 || i1 < len1)
Packit Service fdd496
    {
Packit Service fdd496
      if (changed0[i0] | changed1[i1])
Packit Service fdd496
	{
Packit Service fdd496
	  lin line0 = i0, line1 = i1;
Packit Service fdd496
Packit Service fdd496
	  /* Find # lines changed here in each file.  */
Packit Service fdd496
	  while (changed0[i0]) ++i0;
Packit Service fdd496
	  while (changed1[i1]) ++i1;
Packit Service fdd496
Packit Service fdd496
	  /* Record this change.  */
Packit Service fdd496
	  script = add_change (line0, line1, i0 - line0, i1 - line1, script);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* We have reached lines in the two files that match each other.  */
Packit Service fdd496
      i0++, i1++;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return script;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Scan the tables of which lines are inserted and deleted,
Packit Service fdd496
   producing an edit script in forward order.  */
Packit Service fdd496
Packit Service fdd496
static struct change *
Packit Service fdd496
build_script (struct file_data const filevec[])
Packit Service fdd496
{
Packit Service fdd496
  struct change *script = 0;
Packit Service fdd496
  char *changed0 = filevec[0].changed;
Packit Service fdd496
  char *changed1 = filevec[1].changed;
Packit Service fdd496
  lin i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines;
Packit Service fdd496
Packit Service fdd496
  /* Note that changedN[-1] does exist, and is 0.  */
Packit Service fdd496
Packit Service fdd496
  while (i0 >= 0 || i1 >= 0)
Packit Service fdd496
    {
Packit Service fdd496
      if (changed0[i0 - 1] | changed1[i1 - 1])
Packit Service fdd496
	{
Packit Service fdd496
	  lin line0 = i0, line1 = i1;
Packit Service fdd496
Packit Service fdd496
	  /* Find # lines changed here in each file.  */
Packit Service fdd496
	  while (changed0[i0 - 1]) --i0;
Packit Service fdd496
	  while (changed1[i1 - 1]) --i1;
Packit Service fdd496
Packit Service fdd496
	  /* Record this change.  */
Packit Service fdd496
	  script = add_change (i0, i1, line0 - i0, line1 - i1, script);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* We have reached lines in the two files that match each other.  */
Packit Service fdd496
      i0--, i1--;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return script;
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* If CHANGES, briefly report that two files differed.  */
Packit Service fdd496
static void
Packit Service fdd496
briefly_report (int changes, struct file_data const filevec[])
Packit Service fdd496
{
Packit Service fdd496
  if (changes)
Packit Service fdd496
    message ((brief
Packit Service fdd496
	      ? _("Files %s and %s differ\n")
Packit Service fdd496
	      : _("Binary files %s and %s differ\n")),
Packit Service fdd496
	     file_label[0] ? file_label[0] : filevec[0].name,
Packit Service fdd496
	     file_label[1] ? file_label[1] : filevec[1].name);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Report the differences of two files.  */
Packit Service fdd496
int
Packit Service fdd496
diff_2_files (struct comparison *cmp)
Packit Service fdd496
{
Packit Service fdd496
  int f;
Packit Service fdd496
  struct change *e, *p;
Packit Service fdd496
  struct change *script;
Packit Service fdd496
  int changes;
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
  /* If we have detected that either file is binary,
Packit Service fdd496
     compare the two files as binary.  This can happen
Packit Service fdd496
     only when the first chunk is read.
Packit Service fdd496
     Also, --brief without any --ignore-* options means
Packit Service fdd496
     we can speed things up by treating the files as binary.  */
Packit Service fdd496
Packit Service fdd496
  if (read_files (cmp->file, files_can_be_treated_as_binary))
Packit Service fdd496
    {
Packit Service fdd496
      /* Files with different lengths must be different.  */
Packit Service fdd496
      if (cmp->file[0].stat.st_size != cmp->file[1].stat.st_size
Packit Service fdd496
	  && 0 < cmp->file[0].stat.st_size
Packit Service fdd496
	  && 0 < cmp->file[1].stat.st_size
Packit Service fdd496
	  && (cmp->file[0].desc < 0 || S_ISREG (cmp->file[0].stat.st_mode))
Packit Service fdd496
	  && (cmp->file[1].desc < 0 || S_ISREG (cmp->file[1].stat.st_mode)))
Packit Service fdd496
	changes = 1;
Packit Service fdd496
Packit Service fdd496
      /* Standard input equals itself.  */
Packit Service fdd496
      else if (cmp->file[0].desc == cmp->file[1].desc)
Packit Service fdd496
	changes = 0;
Packit Service fdd496
Packit Service fdd496
      else
Packit Service fdd496
	/* Scan both files, a buffer at a time, looking for a difference.  */
Packit Service fdd496
	{
Packit Service fdd496
	  /* Allocate same-sized buffers for both files.  */
Packit Service fdd496
	  size_t lcm_max = PTRDIFF_MAX - 1;
Packit Service fdd496
	  size_t buffer_size =
Packit Service fdd496
	    buffer_lcm (sizeof (word),
Packit Service fdd496
			buffer_lcm (STAT_BLOCKSIZE (cmp->file[0].stat),
Packit Service fdd496
				    STAT_BLOCKSIZE (cmp->file[1].stat),
Packit Service fdd496
				    lcm_max),
Packit Service fdd496
			lcm_max);
Packit Service fdd496
	  for (f = 0; f < 2; f++)
Packit Service fdd496
	    cmp->file[f].buffer = xrealloc (cmp->file[f].buffer, buffer_size);
Packit Service fdd496
Packit Service fdd496
	  for (;; cmp->file[0].buffered = cmp->file[1].buffered = 0)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Read a buffer's worth from both files.  */
Packit Service fdd496
	      for (f = 0; f < 2; f++)
Packit Service fdd496
		if (0 <= cmp->file[f].desc)
Packit Service fdd496
		  file_block_read (&cmp->file[f],
Packit Service fdd496
				   buffer_size - cmp->file[f].buffered);
Packit Service fdd496
Packit Service fdd496
	      /* If the buffers differ, the files differ.  */
Packit Service fdd496
	      if (cmp->file[0].buffered != cmp->file[1].buffered
Packit Service fdd496
		  || memcmp (cmp->file[0].buffer,
Packit Service fdd496
			     cmp->file[1].buffer,
Packit Service fdd496
			     cmp->file[0].buffered))
Packit Service fdd496
		{
Packit Service fdd496
		  changes = 1;
Packit Service fdd496
		  break;
Packit Service fdd496
		}
Packit Service fdd496
Packit Service fdd496
	      /* If we reach end of file, the files are the same.  */
Packit Service fdd496
	      if (cmp->file[0].buffered != buffer_size)
Packit Service fdd496
		{
Packit Service fdd496
		  changes = 0;
Packit Service fdd496
		  break;
Packit Service fdd496
		}
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      briefly_report (changes, cmp->file);
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      struct context ctxt;
Packit Service fdd496
      lin diags;
Packit Service fdd496
      lin too_expensive;
Packit Service fdd496
Packit Service fdd496
      /* Allocate vectors for the results of comparison:
Packit Service fdd496
	 a flag for each line of each file, saying whether that line
Packit Service fdd496
	 is an insertion or deletion.
Packit Service fdd496
	 Allocate an extra element, always 0, at each end of each vector.  */
Packit Service fdd496
Packit Service fdd496
      size_t s = cmp->file[0].buffered_lines + cmp->file[1].buffered_lines + 4;
Packit Service fdd496
      char *flag_space = zalloc (s);
Packit Service fdd496
      cmp->file[0].changed = flag_space + 1;
Packit Service fdd496
      cmp->file[1].changed = flag_space + cmp->file[0].buffered_lines + 3;
Packit Service fdd496
Packit Service fdd496
      /* Some lines are obviously insertions or deletions
Packit Service fdd496
	 because they don't match anything.  Detect them now, and
Packit Service fdd496
	 avoid even thinking about them in the main comparison algorithm.  */
Packit Service fdd496
Packit Service fdd496
      discard_confusing_lines (cmp->file);
Packit Service fdd496
Packit Service fdd496
      /* Now do the main comparison algorithm, considering just the
Packit Service fdd496
	 undiscarded lines.  */
Packit Service fdd496
Packit Service fdd496
      ctxt.xvec = cmp->file[0].undiscarded;
Packit Service fdd496
      ctxt.yvec = cmp->file[1].undiscarded;
Packit Service fdd496
      diags = (cmp->file[0].nondiscarded_lines
Packit Service fdd496
	       + cmp->file[1].nondiscarded_lines + 3);
Packit Service fdd496
      ctxt.fdiag = xmalloc (diags * (2 * sizeof *ctxt.fdiag));
Packit Service fdd496
      ctxt.bdiag = ctxt.fdiag + diags;
Packit Service fdd496
      ctxt.fdiag += cmp->file[1].nondiscarded_lines + 1;
Packit Service fdd496
      ctxt.bdiag += cmp->file[1].nondiscarded_lines + 1;
Packit Service fdd496
Packit Service fdd496
      ctxt.heuristic = speed_large_files;
Packit Service fdd496
Packit Service fdd496
      /* Set TOO_EXPENSIVE to be the approximate square root of the
Packit Service fdd496
	 input size, bounded below by 4096.  4096 seems to be good for
Packit Service fdd496
	 circa-2016 CPUs; see Bug#16848 and Bug#24715.  */
Packit Service fdd496
      too_expensive = 1;
Packit Service fdd496
      for (;  diags != 0;  diags >>= 2)
Packit Service fdd496
	too_expensive <<= 1;
Packit Service fdd496
      ctxt.too_expensive = MAX (4096, too_expensive);
Packit Service fdd496
Packit Service fdd496
      files[0] = cmp->file[0];
Packit Service fdd496
      files[1] = cmp->file[1];
Packit Service fdd496
Packit Service fdd496
      compareseq (0, cmp->file[0].nondiscarded_lines,
Packit Service fdd496
		  0, cmp->file[1].nondiscarded_lines, minimal, &ctxt);
Packit Service fdd496
Packit Service fdd496
      free (ctxt.fdiag - (cmp->file[1].nondiscarded_lines + 1));
Packit Service fdd496
Packit Service fdd496
      /* Modify the results slightly to make them prettier
Packit Service fdd496
	 in cases where that can validly be done.  */
Packit Service fdd496
Packit Service fdd496
      shift_boundaries (cmp->file);
Packit Service fdd496
Packit Service fdd496
      /* Get the results of comparison in the form of a chain
Packit Service fdd496
	 of 'struct change's -- an edit script.  */
Packit Service fdd496
Packit Service fdd496
      if (output_style == OUTPUT_ED)
Packit Service fdd496
	script = build_reverse_script (cmp->file);
Packit Service fdd496
      else
Packit Service fdd496
	script = build_script (cmp->file);
Packit Service fdd496
Packit Service fdd496
      /* Set CHANGES if we had any diffs.
Packit Service fdd496
	 If some changes are ignored, we must scan the script to decide.  */
Packit Service fdd496
      if (ignore_blank_lines || ignore_regexp.fastmap)
Packit Service fdd496
	{
Packit Service fdd496
	  struct change *next = script;
Packit Service fdd496
	  changes = 0;
Packit Service fdd496
Packit Service fdd496
	  while (next && changes == 0)
Packit Service fdd496
	    {
Packit Service fdd496
	      struct change *this, *end;
Packit Service fdd496
	      lin first0, last0, first1, last1;
Packit Service fdd496
Packit Service fdd496
	      /* Find a set of changes that belong together.  */
Packit Service fdd496
	      this = next;
Packit Service fdd496
	      end = find_change (next);
Packit Service fdd496
Packit Service fdd496
	      /* Disconnect them from the rest of the changes, making them
Packit Service fdd496
		 a hunk, and remember the rest for next iteration.  */
Packit Service fdd496
	      next = end->link;
Packit Service fdd496
	      end->link = 0;
Packit Service fdd496
Packit Service fdd496
	      /* Determine whether this hunk is really a difference.  */
Packit Service fdd496
	      if (analyze_hunk (this, &first0, &last0, &first1, &last1))
Packit Service fdd496
		changes = 1;
Packit Service fdd496
Packit Service fdd496
	      /* Reconnect the script so it will all be freed properly.  */
Packit Service fdd496
	      end->link = next;
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
      else
Packit Service fdd496
	changes = (script != 0);
Packit Service fdd496
Packit Service fdd496
      if (brief)
Packit Service fdd496
	briefly_report (changes, cmp->file);
Packit Service fdd496
      else
Packit Service fdd496
	{
Packit Service fdd496
	  if (changes || !no_diff_means_no_output)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Record info for starting up output,
Packit Service fdd496
		 to be used if and when we have some output to print.  */
Packit Service fdd496
	      setup_output (file_label[0] ? file_label[0] : cmp->file[0].name,
Packit Service fdd496
			    file_label[1] ? file_label[1] : cmp->file[1].name,
Packit Service fdd496
			    cmp->parent != 0);
Packit Service fdd496
Packit Service fdd496
	      switch (output_style)
Packit Service fdd496
		{
Packit Service fdd496
		case OUTPUT_CONTEXT:
Packit Service fdd496
		  print_context_script (script, false);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_UNIFIED:
Packit Service fdd496
		  print_context_script (script, true);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_ED:
Packit Service fdd496
		  print_ed_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_FORWARD_ED:
Packit Service fdd496
		  pr_forward_ed_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_RCS:
Packit Service fdd496
		  print_rcs_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_NORMAL:
Packit Service fdd496
		  print_normal_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_IFDEF:
Packit Service fdd496
		  print_ifdef_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		case OUTPUT_SDIFF:
Packit Service fdd496
		  print_sdiff_script (script);
Packit Service fdd496
		  break;
Packit Service fdd496
Packit Service fdd496
		default:
Packit Service fdd496
		  abort ();
Packit Service fdd496
		}
Packit Service fdd496
Packit Service fdd496
	      finish_output ();
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      free (cmp->file[0].undiscarded);
Packit Service fdd496
Packit Service fdd496
      free (flag_space);
Packit Service fdd496
Packit Service fdd496
      for (f = 0; f < 2; f++)
Packit Service fdd496
	{
Packit Service fdd496
	  free (cmp->file[f].equivs);
Packit Service fdd496
	  free (cmp->file[f].linbuf + cmp->file[f].linbuf_base);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      for (e = script; e; e = p)
Packit Service fdd496
	{
Packit Service fdd496
	  p = e->link;
Packit Service fdd496
	  free (e);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      if (! ROBUST_OUTPUT_STYLE (output_style))
Packit Service fdd496
	for (f = 0; f < 2; ++f)
Packit Service fdd496
	  if (cmp->file[f].missing_newline)
Packit Service fdd496
	    {
Packit Service fdd496
	      error (0, 0, "%s: %s\n",
Packit Service fdd496
		     file_label[f] ? file_label[f] : cmp->file[f].name,
Packit Service fdd496
		     _("No newline at end of file"));
Packit Service fdd496
	      changes = 2;
Packit Service fdd496
	    }
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (cmp->file[0].buffer != cmp->file[1].buffer)
Packit Service fdd496
    free (cmp->file[0].buffer);
Packit Service fdd496
  free (cmp->file[1].buffer);
Packit Service fdd496
Packit Service fdd496
  return changes;
Packit Service fdd496
}