Blame src/io.c

Packit Service fdd496
/* File I/O for GNU DIFF.
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
Packit Service fdd496
   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 <binary-io.h>
Packit Service fdd496
#include <cmpbuf.h>
Packit Service fdd496
#include <file-type.h>
Packit Service fdd496
#include <xalloc.h>
Packit Service 1d0110
#include <assert.h>
Packit Service fdd496
Packit Service fdd496
/* Rotate an unsigned value to the left.  */
Packit Service fdd496
#define ROL(v, n) ((v) << (n) | (v) >> (sizeof (v) * CHAR_BIT - (n)))
Packit Service fdd496
Packit Service fdd496
/* Given a hash value and a new character, return a new hash value.  */
Packit Service fdd496
#define HASH(h, c) ((c) + ROL (h, 7))
Packit Service fdd496

Packit Service fdd496
/* The type of a hash value.  */
Packit Service fdd496
typedef size_t hash_value;
Packit Service fdd496
verify (! TYPE_SIGNED (hash_value));
Packit Service fdd496
Packit Service fdd496
/* Lines are put into equivalence classes of lines that match in lines_differ.
Packit Service fdd496
   Each equivalence class is represented by one of these structures,
Packit Service fdd496
   but only while the classes are being computed.
Packit Service fdd496
   Afterward, each class is represented by a number.  */
Packit Service fdd496
struct equivclass
Packit Service fdd496
{
Packit Service fdd496
  lin next;		/* Next item in this bucket.  */
Packit Service fdd496
  hash_value hash;	/* Hash of lines in this class.  */
Packit Service fdd496
  char const *line;	/* A line that fits this class.  */
Packit Service fdd496
  size_t length;	/* That line's length, not counting its newline.  */
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
/* Hash-table: array of buckets, each being a chain of equivalence classes.
Packit Service fdd496
   buckets[-1] is reserved for incomplete lines.  */
Packit Service fdd496
static lin *buckets;
Packit Service fdd496
Packit Service fdd496
/* Number of buckets in the hash table array, not counting buckets[-1].  */
Packit Service fdd496
static size_t nbuckets;
Packit Service fdd496
Packit Service fdd496
/* Array in which the equivalence classes are allocated.
Packit Service fdd496
   The bucket-chains go through the elements in this array.
Packit Service fdd496
   The number of an equivalence class is its index in this array.  */
Packit Service fdd496
static struct equivclass *equivs;
Packit Service fdd496
Packit Service fdd496
/* Index of first free element in the array 'equivs'.  */
Packit Service fdd496
static lin equivs_index;
Packit Service fdd496
Packit Service fdd496
/* Number of elements allocated in the array 'equivs'.  */
Packit Service fdd496
static lin equivs_alloc;
Packit Service fdd496

Packit Service fdd496
/* Read a block of data into a file buffer, checking for EOF and error.  */
Packit Service fdd496
Packit Service fdd496
void
Packit Service fdd496
file_block_read (struct file_data *current, size_t size)
Packit Service fdd496
{
Packit Service fdd496
  if (size && ! current->eof)
Packit Service fdd496
    {
Packit Service fdd496
      size_t s = block_read (current->desc,
Packit Service fdd496
			     FILE_BUFFER (current) + current->buffered, size);
Packit Service fdd496
      if (s == SIZE_MAX)
Packit Service fdd496
	pfatal_with_name (current->name);
Packit Service fdd496
      current->buffered += s;
Packit Service fdd496
      current->eof = s < size;
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Check for binary files and compare them for exact identity.  */
Packit Service fdd496
Packit Service fdd496
/* Return 1 if BUF contains a non text character.
Packit Service fdd496
   SIZE is the number of characters in BUF.  */
Packit Service fdd496
Packit Service fdd496
#define binary_file_p(buf, size) (memchr (buf, 0, size) != 0)
Packit Service fdd496
Packit Service fdd496
/* Get ready to read the current file.
Packit Service fdd496
   Return nonzero if SKIP_TEST is zero,
Packit Service fdd496
   and if it appears to be a binary file.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
sip (struct file_data *current, bool skip_test)
Packit Service fdd496
{
Packit Service fdd496
  /* If we have a nonexistent file at this stage, treat it as empty.  */
Packit Service fdd496
  if (current->desc < 0)
Packit Service fdd496
    {
Packit Service fdd496
      /* Leave room for a sentinel.  */
Packit Service fdd496
      current->bufsize = sizeof (word);
Packit Service fdd496
      current->buffer = xmalloc (current->bufsize);
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      current->bufsize = buffer_lcm (sizeof (word),
Packit Service fdd496
				     STAT_BLOCKSIZE (current->stat),
Packit Service fdd496
				     PTRDIFF_MAX - 2 * sizeof (word));
Packit Service fdd496
      current->buffer = xmalloc (current->bufsize);
Packit Service fdd496
Packit Service fdd496
#ifdef __KLIBC__
Packit Service fdd496
      /* Skip test if seek is not possible */
Packit Service fdd496
      skip_test = skip_test
Packit Service fdd496
		  || (lseek (current->desc, 0, SEEK_CUR) < 0
Packit Service fdd496
		      && errno == ESPIPE);
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
      if (! skip_test)
Packit Service fdd496
	{
Packit Service fdd496
	  /* Check first part of file to see if it's a binary file.  */
Packit Service fdd496
Packit Service fdd496
	  int prev_mode = set_binary_mode (current->desc, O_BINARY);
Packit Service fdd496
	  off_t buffered;
Packit Service fdd496
	  file_block_read (current, current->bufsize);
Packit Service fdd496
	  buffered = current->buffered;
Packit Service fdd496
Packit Service fdd496
	  if (prev_mode != O_BINARY)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Revert to text mode and seek back to the start to reread
Packit Service fdd496
		 the file.  Use relative seek, since file descriptors
Packit Service fdd496
		 like stdin might not start at offset zero.  */
Packit Service fdd496
	      if (lseek (current->desc, - buffered, SEEK_CUR) < 0)
Packit Service fdd496
		pfatal_with_name (current->name);
Packit Service fdd496
	      set_binary_mode (current->desc, prev_mode);
Packit Service fdd496
	      current->buffered = 0;
Packit Service fdd496
	      current->eof = false;
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  return binary_file_p (current->buffer, buffered);
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  current->buffered = 0;
Packit Service fdd496
  current->eof = false;
Packit Service fdd496
  return false;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Slurp the rest of the current file completely into memory.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
slurp (struct file_data *current)
Packit Service fdd496
{
Packit Service fdd496
  size_t cc;
Packit Service fdd496
Packit Service fdd496
  if (current->desc < 0)
Packit Service fdd496
    {
Packit Service fdd496
      /* The file is nonexistent.  */
Packit Service fdd496
      return;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (S_ISREG (current->stat.st_mode))
Packit Service fdd496
    {
Packit Service fdd496
      /* It's a regular file; slurp in the rest all at once.  */
Packit Service fdd496
Packit Service fdd496
      /* Get the size out of the stat block.
Packit Service fdd496
	 Allocate just enough room for appended newline plus word sentinel,
Packit Service fdd496
	 plus word-alignment since we want the buffer word-aligned.  */
Packit Service fdd496
      size_t file_size = current->stat.st_size;
Packit Service fdd496
      cc = file_size + 2 * sizeof (word) - file_size % sizeof (word);
Packit Service fdd496
      if (file_size != current->stat.st_size || cc < file_size
Packit Service fdd496
	  || PTRDIFF_MAX <= cc)
Packit Service fdd496
	xalloc_die ();
Packit Service fdd496
Packit Service fdd496
      if (current->bufsize < cc)
Packit Service fdd496
	{
Packit Service fdd496
	  current->bufsize = cc;
Packit Service fdd496
	  current->buffer = xrealloc (current->buffer, cc);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Try to read at least 1 more byte than the size indicates, to
Packit Service fdd496
	 detect whether the file is growing.  This is a nicety for
Packit Service fdd496
	 users who run 'diff' on files while they are changing.  */
Packit Service fdd496
Packit Service fdd496
      if (current->buffered <= file_size)
Packit Service fdd496
	{
Packit Service fdd496
	  file_block_read (current, file_size + 1 - current->buffered);
Packit Service fdd496
	  if (current->buffered <= file_size)
Packit Service fdd496
	    return;
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* It's not a regular file, or it's a growing regular file; read it,
Packit Service fdd496
     growing the buffer as needed.  */
Packit Service fdd496
Packit Service fdd496
  file_block_read (current, current->bufsize - current->buffered);
Packit Service fdd496
Packit Service fdd496
  if (current->buffered)
Packit Service fdd496
    {
Packit Service fdd496
      while (current->buffered == current->bufsize)
Packit Service fdd496
	{
Packit Service fdd496
	  if (PTRDIFF_MAX / 2 - sizeof (word) < current->bufsize)
Packit Service fdd496
	    xalloc_die ();
Packit Service fdd496
	  current->bufsize *= 2;
Packit Service fdd496
	  current->buffer = xrealloc (current->buffer, current->bufsize);
Packit Service fdd496
	  file_block_read (current, current->bufsize - current->buffered);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Allocate just enough room for appended newline plus word
Packit Service fdd496
	 sentinel, plus word-alignment.  */
Packit Service fdd496
      cc = current->buffered + 2 * sizeof (word);
Packit Service fdd496
      current->bufsize = cc - cc % sizeof (word);
Packit Service fdd496
      current->buffer = xrealloc (current->buffer, current->bufsize);
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Split the file into lines, simultaneously computing the equivalence
Packit Service fdd496
   class for each line.  */
Packit Service 1d0110
#ifdef HANDLE_MULTIBYTE
Packit Service 1d0110
# define MBC2WC(P, END, MBLENGTH, WC, STATE, CONVFAIL)		\
Packit Service 1d0110
do								\
Packit Service 1d0110
  {								\
Packit Service 1d0110
    mbstate_t state_bak = STATE;				\
Packit Service 1d0110
								\
Packit Service 1d0110
    CONVFAIL = 0;						\
Packit Service 1d0110
    MBLENGTH = mbrtowc (&WC, P, END - (char const *)P, &STATE);	\
Packit Service 1d0110
								\
Packit Service 1d0110
    switch (MBLENGTH)						\
Packit Service 1d0110
      {								\
Packit Service 1d0110
      case (size_t)-2:						\
Packit Service 1d0110
      case (size_t)-1:						\
Packit Service 1d0110
	STATE = state_bak;					\
Packit Service 1d0110
	++CONVFAIL;						\
Packit Service 1d0110
	/* Fall through. */					\
Packit Service 1d0110
      case 0:							\
Packit Service 1d0110
	MBLENGTH = 1;						\
Packit Service 1d0110
      }								\
Packit Service 1d0110
  }								\
Packit Service 1d0110
 while (0)
Packit Service 1d0110
#endif
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
find_and_hash_each_line (struct file_data *current)
Packit Service fdd496
{
Packit Service fdd496
  char const *p = current->prefix_end;
Packit Service fdd496
  lin i, *bucket;
Packit Service fdd496
  size_t length;
Packit Service fdd496
Packit Service fdd496
  /* Cache often-used quantities in local variables to help the compiler.  */
Packit Service fdd496
  char const **linbuf = current->linbuf;
Packit Service fdd496
  lin alloc_lines = current->alloc_lines;
Packit Service fdd496
  lin line = 0;
Packit Service fdd496
  lin linbuf_base = current->linbuf_base;
Packit Service fdd496
  lin *cureqs = xmalloc (alloc_lines * sizeof *cureqs);
Packit Service fdd496
  struct equivclass *eqs = equivs;
Packit Service fdd496
  lin eqs_index = equivs_index;
Packit Service fdd496
  lin eqs_alloc = equivs_alloc;
Packit Service fdd496
  char const *suffix_begin = current->suffix_begin;
Packit Service fdd496
  char const *bufend = FILE_BUFFER (current) + current->buffered;
Packit Service fdd496
  bool ig_case = ignore_case;
Packit Service fdd496
  enum DIFF_white_space ig_white_space = ignore_white_space;
Packit Service fdd496
  bool diff_length_compare_anyway =
Packit Service fdd496
    ig_white_space != IGNORE_NO_WHITE_SPACE;
Packit Service fdd496
  bool same_length_diff_contents_compare_anyway =
Packit Service fdd496
    diff_length_compare_anyway | ig_case;
Packit Service fdd496
Packit Service 1d0110
#ifdef HANDLE_MULTIBYTE
Packit Service 1d0110
  wchar_t wc;
Packit Service 1d0110
  size_t mblength;
Packit Service 1d0110
  mbstate_t state;
Packit Service 1d0110
  int convfail;
Packit Service 1d0110
Packit Service 1d0110
  memset (&state, '\0', sizeof (mbstate_t));
Packit Service 1d0110
#endif
Packit Service 1d0110
Packit Service fdd496
  while (p < suffix_begin)
Packit Service fdd496
    {
Packit Service fdd496
      char const *ip = p;
Packit Service fdd496
      hash_value h = 0;
Packit Service fdd496
      unsigned char c;
Packit Service fdd496
Packit Service 1d0110
#ifdef HANDLE_MULTIBYTE
Packit Service 1d0110
      if (MB_CUR_MAX > 1)
Packit Service 1d0110
	{
Packit Service 1d0110
	  wchar_t lo_wc;
Packit Service 1d0110
	  char mbc[MB_LEN_MAX];
Packit Service 1d0110
	  mbstate_t state_wc;
Packit Service 1d0110
Packit Service 1d0110
	  /* Hash this line until we find a newline.  */
Packit Service 1d0110
	  switch (ig_white_space)
Packit Service 1d0110
	    {
Packit Service 1d0110
	    case IGNORE_ALL_SPACE:
Packit Service 1d0110
	      while (1)
Packit Service 1d0110
		{
Packit Service 1d0110
		  if (*p == '\n')
Packit Service 1d0110
		    {
Packit Service 1d0110
		      ++p;
Packit Service 1d0110
		      break;
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
Packit Service 1d0110
		  if (convfail)
Packit Service 1d0110
		    mbc[0] = *p++;
Packit Service 1d0110
		  else if (!iswspace (wc))
Packit Service 1d0110
		    {
Packit Service 1d0110
		      bool flag = 0;
Packit Service 1d0110
Packit Service 1d0110
		      if (ig_case)
Packit Service 1d0110
			{
Packit Service 1d0110
			  lo_wc = towlower (wc);
Packit Service 1d0110
			  if (lo_wc != wc)
Packit Service 1d0110
			    {
Packit Service 1d0110
			      flag = 1;
Packit Service 1d0110
Packit Service 1d0110
			      p += mblength;
Packit Service 1d0110
			      memset (&state_wc, '\0', sizeof(mbstate_t));
Packit Service 1d0110
			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
Packit Service 1d0110
Packit Service 1d0110
			      assert (mblength != (size_t)-1 &&
Packit Service 1d0110
				      mblength != (size_t)-2);
Packit Service 1d0110
Packit Service 1d0110
			      mblength = (mblength < 1) ? 1 : mblength;
Packit Service 1d0110
			    }
Packit Service 1d0110
			}
Packit Service 1d0110
Packit Service 1d0110
		      if (!flag)
Packit Service 1d0110
			{
Packit Service 1d0110
			  for (i = 0; i < mblength; i++)
Packit Service 1d0110
			    mbc[i] = *p++;
Packit Service 1d0110
			}
Packit Service 1d0110
		    }
Packit Service 1d0110
		  else
Packit Service 1d0110
		    {
Packit Service 1d0110
		      p += mblength;
Packit Service 1d0110
		      continue;
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  for (i = 0; i < mblength; i++)
Packit Service 1d0110
		    h = HASH (h, mbc[i]);
Packit Service 1d0110
		}
Packit Service 1d0110
	      break;
Packit Service 1d0110
Packit Service 1d0110
	    case IGNORE_SPACE_CHANGE:
Packit Service 1d0110
	      while (1)
Packit Service 1d0110
		{
Packit Service 1d0110
		  if (*p == '\n')
Packit Service 1d0110
		    {
Packit Service 1d0110
		      ++p;
Packit Service 1d0110
		      break;
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
Packit Service 1d0110
		  if (!convfail && iswspace (wc))
Packit Service 1d0110
		    {
Packit Service 1d0110
		      while (1)
Packit Service 1d0110
			{
Packit Service 1d0110
			  if (*p == '\n')
Packit Service 1d0110
			    {
Packit Service 1d0110
			      ++p;
Packit Service 1d0110
			      goto hashing_done;
Packit Service 1d0110
			    }
Packit Service 1d0110
Packit Service 1d0110
			  p += mblength;
Packit Service 1d0110
			  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
			  if (convfail || !iswspace (wc))
Packit Service 1d0110
			    break;
Packit Service 1d0110
			}
Packit Service 1d0110
		      h = HASH (h, ' ');
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  /* WC is now the first non-space.  */
Packit Service 1d0110
		  if (convfail)
Packit Service 1d0110
		    mbc[0] = *p++;
Packit Service 1d0110
		  else
Packit Service 1d0110
		    {
Packit Service 1d0110
		      bool flag = 0;
Packit Service 1d0110
Packit Service 1d0110
		      if (ignore_case)
Packit Service 1d0110
			{
Packit Service 1d0110
			  lo_wc = towlower (wc);
Packit Service 1d0110
			  if (lo_wc != wc)
Packit Service 1d0110
			    {
Packit Service 1d0110
			      flag = 1;
Packit Service 1d0110
Packit Service 1d0110
			      p += mblength;
Packit Service 1d0110
			      memset (&state_wc, '\0', sizeof(mbstate_t));
Packit Service 1d0110
			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
Packit Service 1d0110
Packit Service 1d0110
			      assert (mblength != (size_t)-1 &&
Packit Service 1d0110
				      mblength != (size_t)-2);
Packit Service 1d0110
Packit Service 1d0110
			      mblength = (mblength < 1) ? 1 : mblength;
Packit Service 1d0110
			    }
Packit Service 1d0110
			}
Packit Service 1d0110
Packit Service 1d0110
		      if (!flag)
Packit Service 1d0110
			{
Packit Service 1d0110
			  for (i = 0; i < mblength; i++)
Packit Service 1d0110
			    mbc[i] = *p++;
Packit Service 1d0110
			}
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  for (i = 0; i < mblength; i++)
Packit Service 1d0110
		    h = HASH (h, mbc[i]);
Packit Service 1d0110
		}
Packit Service 1d0110
	      break;
Packit Service 1d0110
Packit Service 1d0110
	    case IGNORE_TAB_EXPANSION:
Packit Service 1d0110
	    case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
Packit Service 1d0110
	    case IGNORE_TRAILING_SPACE:
Packit Service 1d0110
	      {
Packit Service 1d0110
		size_t column = 0;
Packit Service 1d0110
		while (1)
Packit Service 1d0110
		  {
Packit Service 1d0110
		    if (*p == '\n')
Packit Service 1d0110
		      {
Packit Service 1d0110
			++p;
Packit Service 1d0110
			break;
Packit Service 1d0110
		      }
Packit Service 1d0110
Packit Service 1d0110
		    MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
Packit Service 1d0110
		    if (!convfail
Packit Service 1d0110
			&& ig_white_space & IGNORE_TRAILING_SPACE
Packit Service 1d0110
			&& iswspace (wc))
Packit Service 1d0110
		      {
Packit Service 1d0110
			char const *p1 = p;
Packit Service 1d0110
			while (1)
Packit Service 1d0110
			  {
Packit Service 1d0110
			    if (*p1 == '\n')
Packit Service 1d0110
			      {
Packit Service 1d0110
				p = p1 + 1;
Packit Service 1d0110
				goto hashing_done;
Packit Service 1d0110
			      }
Packit Service 1d0110
Packit Service 1d0110
			    p1 += mblength;
Packit Service 1d0110
			    MBC2WC (p1, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
			    if (convfail || !iswspace (wc))
Packit Service 1d0110
			      break;
Packit Service 1d0110
			  }
Packit Service 1d0110
		      }
Packit Service 1d0110
Packit Service 1d0110
		    size_t repetitions = 1;
Packit Service 1d0110
		    bool no_convert = 0;
Packit Service 1d0110
Packit Service 1d0110
		    if (ig_white_space & IGNORE_TAB_EXPANSION)
Packit Service 1d0110
		      {
Packit Service 1d0110
			if (convfail)
Packit Service 1d0110
			  column++;
Packit Service 1d0110
			else
Packit Service 1d0110
			  switch (wc)
Packit Service 1d0110
			    {
Packit Service 1d0110
			    case L'\b':
Packit Service 1d0110
			      column -= 0 < column;
Packit Service 1d0110
			      break;
Packit Service 1d0110
Packit Service 1d0110
			    case L'\t':
Packit Service 1d0110
			      mbc[0] = ' ';
Packit Service 1d0110
			      mblength = 1;
Packit Service 1d0110
			      no_convert = 1;
Packit Service 1d0110
			      p++;
Packit Service 1d0110
			      assert(mblength == 1);
Packit Service 1d0110
			      repetitions = tabsize - column % tabsize;
Packit Service 1d0110
			      column = (column + repetitions < column
Packit Service 1d0110
					? 0
Packit Service 1d0110
					: column + repetitions);
Packit Service 1d0110
			      break;
Packit Service 1d0110
Packit Service 1d0110
			    case L'\r':
Packit Service 1d0110
			      column = 0;
Packit Service 1d0110
			      break;
Packit Service 1d0110
Packit Service 1d0110
			    default:
Packit Service 1d0110
			      column += wcwidth (wc);
Packit Service 1d0110
			      break;
Packit Service 1d0110
			    }
Packit Service 1d0110
		      }
Packit Service 1d0110
Packit Service 1d0110
		    if (ig_case)
Packit Service 1d0110
		      {
Packit Service 1d0110
			lo_wc = towlower (wc);
Packit Service 1d0110
			if (lo_wc != wc)
Packit Service 1d0110
			  {
Packit Service 1d0110
			    no_convert = 1;
Packit Service 1d0110
			    p += mblength;
Packit Service 1d0110
			    memset (&state_wc, '\0', sizeof(mbstate_t));
Packit Service 1d0110
			    mblength = wcrtomb (mbc, lo_wc, &state_wc);
Packit Service 1d0110
Packit Service 1d0110
			    assert (mblength != (size_t)-1 &&
Packit Service 1d0110
				    mblength != (size_t)-2);
Packit Service 1d0110
Packit Service 1d0110
			    mblength = (mblength < 1) ? 1 : mblength;
Packit Service 1d0110
			  }
Packit Service 1d0110
		      }
Packit Service 1d0110
Packit Service 1d0110
		    if (!no_convert)
Packit Service 1d0110
		      for (i = 0; i < mblength; i++)
Packit Service 1d0110
			mbc[i] = *p++;
Packit Service 1d0110
Packit Service 1d0110
		    do
Packit Service 1d0110
		      {
Packit Service 1d0110
			for (i = 0; i < mblength; i++)
Packit Service 1d0110
			  h = HASH (h, mbc[i]);
Packit Service 1d0110
		      }
Packit Service 1d0110
		    while (--repetitions != 0);
Packit Service 1d0110
		  }
Packit Service 1d0110
	      }
Packit Service 1d0110
	      break;
Packit Service 1d0110
Packit Service 1d0110
	    default:
Packit Service 1d0110
	      while (1)
Packit Service 1d0110
		{
Packit Service 1d0110
		  if (*p == '\n')
Packit Service 1d0110
		    {
Packit Service 1d0110
		      ++p;
Packit Service 1d0110
		      break;
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  MBC2WC (p, suffix_begin, mblength, wc, state, convfail);
Packit Service 1d0110
Packit Service 1d0110
		  if (convfail)
Packit Service 1d0110
		    mbc[0] = *p++;
Packit Service 1d0110
		  else
Packit Service 1d0110
		    {
Packit Service 1d0110
		      int flag = 0;
Packit Service 1d0110
Packit Service 1d0110
		      if (ig_case)
Packit Service 1d0110
			{
Packit Service 1d0110
			  lo_wc = towlower (wc);
Packit Service 1d0110
			  if (lo_wc != wc)
Packit Service 1d0110
			    {
Packit Service 1d0110
			      flag = 1;
Packit Service 1d0110
			      p += mblength;
Packit Service 1d0110
			      memset (&state_wc, '\0', sizeof(mbstate_t));
Packit Service 1d0110
			      mblength = wcrtomb (mbc, lo_wc, &state_wc);
Packit Service 1d0110
Packit Service 1d0110
			      assert (mblength != (size_t)-1 &&
Packit Service 1d0110
				      mblength != (size_t)-2);
Packit Service 1d0110
Packit Service 1d0110
			      mblength = (mblength < 1) ? 1 : mblength;
Packit Service 1d0110
			    }
Packit Service 1d0110
			}
Packit Service 1d0110
Packit Service 1d0110
		      if (!flag)
Packit Service 1d0110
			{
Packit Service 1d0110
			  for (i = 0; i < mblength; i++)
Packit Service 1d0110
			    mbc[i] = *p++;
Packit Service 1d0110
			}
Packit Service 1d0110
		    }
Packit Service 1d0110
Packit Service 1d0110
		  for (i = 0; i < mblength; i++)
Packit Service 1d0110
		    h = HASH (h, mbc[i]);
Packit Service 1d0110
		}
Packit Service 1d0110
	    }
Packit Service 1d0110
	}
Packit Service 1d0110
      else
Packit Service 1d0110
#endif
Packit Service 1d0110
Packit Service fdd496
      /* Hash this line until we find a newline.  */
Packit Service fdd496
      switch (ig_white_space)
Packit Service fdd496
	{
Packit Service fdd496
	case IGNORE_ALL_SPACE:
Packit Service fdd496
	  while ((c = *p++) != '\n')
Packit Service fdd496
	    if (! isspace (c))
Packit Service fdd496
	      h = HASH (h, ig_case ? tolower (c) : c);
Packit Service fdd496
	  break;
Packit Service fdd496
Packit Service fdd496
	case IGNORE_SPACE_CHANGE:
Packit Service fdd496
	  while ((c = *p++) != '\n')
Packit Service fdd496
	    {
Packit Service fdd496
	      if (isspace (c))
Packit Service fdd496
		{
Packit Service fdd496
		  do
Packit Service fdd496
		    if ((c = *p++) == '\n')
Packit Service fdd496
		      goto hashing_done;
Packit Service fdd496
		  while (isspace (c));
Packit Service fdd496
Packit Service fdd496
		  h = HASH (h, ' ');
Packit Service fdd496
		}
Packit Service fdd496
Packit Service fdd496
	      /* C is now the first non-space.  */
Packit Service fdd496
	      h = HASH (h, ig_case ? tolower (c) : c);
Packit Service fdd496
	    }
Packit Service fdd496
	  break;
Packit Service fdd496
Packit Service fdd496
	case IGNORE_TAB_EXPANSION:
Packit Service fdd496
	case IGNORE_TAB_EXPANSION_AND_TRAILING_SPACE:
Packit Service fdd496
	case IGNORE_TRAILING_SPACE:
Packit Service fdd496
	  {
Packit Service fdd496
	    size_t column = 0;
Packit Service fdd496
	    while ((c = *p++) != '\n')
Packit Service fdd496
	      {
Packit Service fdd496
		if (ig_white_space & IGNORE_TRAILING_SPACE
Packit Service fdd496
		    && isspace (c))
Packit Service fdd496
		  {
Packit Service fdd496
		    char const *p1 = p;
Packit Service fdd496
		    unsigned char c1;
Packit Service fdd496
		    do
Packit Service fdd496
		      if ((c1 = *p1++) == '\n')
Packit Service fdd496
			{
Packit Service fdd496
			  p = p1;
Packit Service fdd496
			  goto hashing_done;
Packit Service fdd496
			}
Packit Service fdd496
		    while (isspace (c1));
Packit Service fdd496
		  }
Packit Service fdd496
Packit Service fdd496
		size_t repetitions = 1;
Packit Service fdd496
Packit Service fdd496
		if (ig_white_space & IGNORE_TAB_EXPANSION)
Packit Service fdd496
		  switch (c)
Packit Service fdd496
		    {
Packit Service fdd496
		    case '\b':
Packit Service fdd496
		      column -= 0 < column;
Packit Service fdd496
		      break;
Packit Service fdd496
Packit Service fdd496
		    case '\t':
Packit Service fdd496
		      c = ' ';
Packit Service fdd496
		      repetitions = tabsize - column % tabsize;
Packit Service fdd496
		      column = (column + repetitions < column
Packit Service fdd496
				? 0
Packit Service fdd496
				: column + repetitions);
Packit Service fdd496
		      break;
Packit Service fdd496
Packit Service fdd496
		    case '\r':
Packit Service fdd496
		      column = 0;
Packit Service fdd496
		      break;
Packit Service fdd496
Packit Service fdd496
		    default:
Packit Service fdd496
		      column++;
Packit Service fdd496
		      break;
Packit Service fdd496
		    }
Packit Service fdd496
Packit Service fdd496
		if (ig_case)
Packit Service fdd496
		  c = tolower (c);
Packit Service fdd496
Packit Service fdd496
		do
Packit Service fdd496
		  h = HASH (h, c);
Packit Service fdd496
		while (--repetitions != 0);
Packit Service fdd496
	      }
Packit Service fdd496
	  }
Packit Service fdd496
	  break;
Packit Service fdd496
Packit Service fdd496
	default:
Packit Service fdd496
	  if (ig_case)
Packit Service fdd496
	    while ((c = *p++) != '\n')
Packit Service fdd496
	      h = HASH (h, tolower (c));
Packit Service fdd496
	  else
Packit Service fdd496
	    while ((c = *p++) != '\n')
Packit Service fdd496
	      h = HASH (h, c);
Packit Service fdd496
	  break;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
   hashing_done:;
Packit Service fdd496
Packit Service fdd496
      bucket = &buckets[h % nbuckets];
Packit Service fdd496
      length = p - ip - 1;
Packit Service fdd496
Packit Service fdd496
      if (p == bufend
Packit Service fdd496
	  && current->missing_newline
Packit Service fdd496
	  && ROBUST_OUTPUT_STYLE (output_style))
Packit Service fdd496
	{
Packit Service fdd496
	  /* The last line is incomplete and we do not silently
Packit Service fdd496
	     complete lines.  If the line cannot compare equal to any
Packit Service fdd496
	     complete line, put it into buckets[-1] so that it can
Packit Service fdd496
	     compare equal only to the other file's incomplete line
Packit Service fdd496
	     (if one exists).  */
Packit Service fdd496
	  if (ig_white_space < IGNORE_TRAILING_SPACE)
Packit Service fdd496
	    bucket = &buckets[-1];
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      for (i = *bucket;  ;  i = eqs[i].next)
Packit Service fdd496
	if (!i)
Packit Service fdd496
	  {
Packit Service fdd496
	    /* Create a new equivalence class in this bucket.  */
Packit Service fdd496
	    i = eqs_index++;
Packit Service fdd496
	    if (i == eqs_alloc)
Packit Service fdd496
	      {
Packit Service fdd496
		if (PTRDIFF_MAX / (2 * sizeof *eqs) <= eqs_alloc)
Packit Service fdd496
		  xalloc_die ();
Packit Service fdd496
		eqs_alloc *= 2;
Packit Service fdd496
		eqs = xrealloc (eqs, eqs_alloc * sizeof *eqs);
Packit Service fdd496
	      }
Packit Service fdd496
	    eqs[i].next = *bucket;
Packit Service fdd496
	    eqs[i].hash = h;
Packit Service fdd496
	    eqs[i].line = ip;
Packit Service fdd496
	    eqs[i].length = length;
Packit Service fdd496
	    *bucket = i;
Packit Service fdd496
	    break;
Packit Service fdd496
	  }
Packit Service fdd496
	else if (eqs[i].hash == h)
Packit Service fdd496
	  {
Packit Service fdd496
	    char const *eqline = eqs[i].line;
Packit Service fdd496
Packit Service fdd496
	    /* Reuse existing class if lines_differ reports the lines
Packit Service fdd496
               equal.  */
Packit Service fdd496
	    if (eqs[i].length == length)
Packit Service fdd496
	      {
Packit Service fdd496
		/* Reuse existing equivalence class if the lines are identical.
Packit Service fdd496
		   This detects the common case of exact identity
Packit Service fdd496
		   faster than lines_differ would.  */
Packit Service fdd496
		if (memcmp (eqline, ip, length) == 0)
Packit Service fdd496
		  break;
Packit Service fdd496
		if (!same_length_diff_contents_compare_anyway)
Packit Service fdd496
		  continue;
Packit Service fdd496
	      }
Packit Service fdd496
	    else if (!diff_length_compare_anyway)
Packit Service fdd496
	      continue;
Packit Service fdd496
Packit Service 1d0110
	    if (! lines_differ (eqline, eqs[i].length + 1, ip, length + 1))
Packit Service fdd496
	      break;
Packit Service fdd496
	  }
Packit Service fdd496
Packit Service fdd496
      /* Maybe increase the size of the line table.  */
Packit Service fdd496
      if (line == alloc_lines)
Packit Service fdd496
	{
Packit Service fdd496
	  /* Double (alloc_lines - linbuf_base) by adding to alloc_lines.  */
Packit Service fdd496
	  if (PTRDIFF_MAX / 3 <= alloc_lines
Packit Service fdd496
	      || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
Packit Service fdd496
	      || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
Packit Service fdd496
	    xalloc_die ();
Packit Service fdd496
	  alloc_lines = 2 * alloc_lines - linbuf_base;
Packit Service fdd496
	  cureqs = xrealloc (cureqs, alloc_lines * sizeof *cureqs);
Packit Service fdd496
	  linbuf += linbuf_base;
Packit Service fdd496
	  linbuf = xrealloc (linbuf,
Packit Service fdd496
			     (alloc_lines - linbuf_base) * sizeof *linbuf);
Packit Service fdd496
	  linbuf -= linbuf_base;
Packit Service fdd496
	}
Packit Service fdd496
      linbuf[line] = ip;
Packit Service fdd496
      cureqs[line] = i;
Packit Service fdd496
      ++line;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  current->buffered_lines = line;
Packit Service fdd496
Packit Service fdd496
  for (i = 0;  ;  i++)
Packit Service fdd496
    {
Packit Service fdd496
      /* Record the line start for lines in the suffix that we care about.
Packit Service fdd496
	 Record one more line start than lines,
Packit Service fdd496
	 so that we can compute the length of any buffered line.  */
Packit Service fdd496
      if (line == alloc_lines)
Packit Service fdd496
	{
Packit Service fdd496
	  /* Double (alloc_lines - linbuf_base) by adding to alloc_lines.  */
Packit Service fdd496
	  if (PTRDIFF_MAX / 3 <= alloc_lines
Packit Service fdd496
	      || PTRDIFF_MAX / sizeof *cureqs <= 2 * alloc_lines - linbuf_base
Packit Service fdd496
	      || PTRDIFF_MAX / sizeof *linbuf <= alloc_lines - linbuf_base)
Packit Service fdd496
	    xalloc_die ();
Packit Service fdd496
	  alloc_lines = 2 * alloc_lines - linbuf_base;
Packit Service fdd496
	  linbuf += linbuf_base;
Packit Service fdd496
	  linbuf = xrealloc (linbuf,
Packit Service fdd496
			     (alloc_lines - linbuf_base) * sizeof *linbuf);
Packit Service fdd496
	  linbuf -= linbuf_base;
Packit Service fdd496
	}
Packit Service fdd496
      linbuf[line] = p;
Packit Service fdd496
Packit Service fdd496
      if (p == bufend)
Packit Service fdd496
	{
Packit Service fdd496
	  /* If the last line is incomplete and we do not silently
Packit Service fdd496
	     complete lines, don't count its appended newline.  */
Packit Service fdd496
	  if (current->missing_newline && ROBUST_OUTPUT_STYLE (output_style))
Packit Service fdd496
	    linbuf[line]--;
Packit Service fdd496
	  break;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      if (context <= i && no_diff_means_no_output)
Packit Service fdd496
	break;
Packit Service fdd496
Packit Service fdd496
      line++;
Packit Service fdd496
Packit Service fdd496
      while (*p++ != '\n')
Packit Service fdd496
	continue;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Done with cache in local variables.  */
Packit Service fdd496
  current->linbuf = linbuf;
Packit Service fdd496
  current->valid_lines = line;
Packit Service fdd496
  current->alloc_lines = alloc_lines;
Packit Service fdd496
  current->equivs = cureqs;
Packit Service fdd496
  equivs = eqs;
Packit Service fdd496
  equivs_alloc = eqs_alloc;
Packit Service fdd496
  equivs_index = eqs_index;
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Prepare the text.  Make sure the text end is initialized.
Packit Service fdd496
   Make sure text ends in a newline,
Packit Service fdd496
   but remember that we had to add one.
Packit Service fdd496
   Strip trailing CRs, if that was requested.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
prepare_text (struct file_data *current)
Packit Service fdd496
{
Packit Service fdd496
  size_t buffered = current->buffered;
Packit Service fdd496
  char *p = FILE_BUFFER (current);
Packit Service fdd496
Packit Service fdd496
  if (buffered == 0 || p[buffered - 1] == '\n')
Packit Service fdd496
    current->missing_newline = false;
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      p[buffered++] = '\n';
Packit Service fdd496
      current->missing_newline = true;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (!p)
Packit Service fdd496
    return;
Packit Service fdd496
Packit Service fdd496
  /* Don't use uninitialized storage when planting or using sentinels.  */
Packit Service fdd496
  memset (p + buffered, 0, sizeof (word));
Packit Service fdd496
Packit Service fdd496
  if (strip_trailing_cr)
Packit Service fdd496
    {
Packit Service fdd496
      char *dst;
Packit Service fdd496
      char *srclim = p + buffered;
Packit Service fdd496
      *srclim = '\r';
Packit Service fdd496
      dst = rawmemchr (p, '\r');
Packit Service fdd496
Packit Service fdd496
      if (dst != srclim)
Packit Service fdd496
	{
Packit Service fdd496
	  char const *src = dst;
Packit Service fdd496
	  do
Packit Service fdd496
	    {
Packit Service fdd496
	      *dst = *src++;
Packit Service fdd496
	      dst += ! (*dst == '\r' && *src == '\n');
Packit Service fdd496
	    }
Packit Service fdd496
	  while (src < srclim);
Packit Service fdd496
Packit Service fdd496
	  buffered -= src - dst;
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  current->buffered = buffered;
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* We have found N lines in a buffer of size S; guess the
Packit Service fdd496
   proportionate number of lines that will be found in a buffer of
Packit Service fdd496
   size T.  However, do not guess a number of lines so large that the
Packit Service fdd496
   resulting line table might cause overflow in size calculations.  */
Packit Service fdd496
static lin
Packit Service fdd496
guess_lines (lin n, size_t s, size_t t)
Packit Service fdd496
{
Packit Service fdd496
  size_t guessed_bytes_per_line = n < 10 ? 32 : s / (n - 1);
Packit Service fdd496
  lin guessed_lines = MAX (1, t / guessed_bytes_per_line);
Packit Service fdd496
  return MIN (guessed_lines, PTRDIFF_MAX / (2 * sizeof (char *) + 1) - 5) + 5;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Given a vector of two file_data objects, find the identical
Packit Service fdd496
   prefixes and suffixes of each object.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
find_identical_ends (struct file_data filevec[])
Packit Service fdd496
{
Packit Service fdd496
  word *w0, *w1;
Packit Service fdd496
  char *p0, *p1, *buffer0, *buffer1;
Packit Service fdd496
  char const *end0, *beg0;
Packit Service fdd496
  char const **linbuf0, **linbuf1;
Packit Service fdd496
  lin i, lines;
Packit Service fdd496
  size_t n0, n1;
Packit Service fdd496
  lin alloc_lines0, alloc_lines1;
Packit Service fdd496
  bool prefix_needed;
Packit Service fdd496
  lin buffered_prefix, prefix_count, prefix_mask;
Packit Service fdd496
  lin middle_guess, suffix_guess;
Packit Service fdd496
Packit Service fdd496
  slurp (&filevec[0]);
Packit Service fdd496
  prepare_text (&filevec[0]);
Packit Service fdd496
  if (filevec[0].desc != filevec[1].desc)
Packit Service fdd496
    {
Packit Service fdd496
      slurp (&filevec[1]);
Packit Service fdd496
      prepare_text (&filevec[1]);
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      filevec[1].buffer = filevec[0].buffer;
Packit Service fdd496
      filevec[1].bufsize = filevec[0].bufsize;
Packit Service fdd496
      filevec[1].buffered = filevec[0].buffered;
Packit Service fdd496
      filevec[1].missing_newline = filevec[0].missing_newline;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Find identical prefix.  */
Packit Service fdd496
Packit Service fdd496
  w0 = filevec[0].buffer;
Packit Service fdd496
  w1 = filevec[1].buffer;
Packit Service fdd496
  p0 = buffer0 = (char *) w0;
Packit Service fdd496
  p1 = buffer1 = (char *) w1;
Packit Service fdd496
  n0 = filevec[0].buffered;
Packit Service fdd496
  n1 = filevec[1].buffered;
Packit Service fdd496
Packit Service fdd496
  if (p0 == p1)
Packit Service fdd496
    /* The buffers are the same; sentinels won't work.  */
Packit Service fdd496
    p0 = p1 += n1;
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      /* Insert end sentinels, in this case characters that are guaranteed
Packit Service fdd496
	 to make the equality test false, and thus terminate the loop.  */
Packit Service fdd496
Packit Service fdd496
      if (n0 < n1)
Packit Service fdd496
	p0[n0] = ~p1[n0];
Packit Service fdd496
      else
Packit Service fdd496
	p1[n1] = ~p0[n1];
Packit Service fdd496
Packit Service fdd496
      /* Loop until first mismatch, or to the sentinel characters.  */
Packit Service fdd496
Packit Service fdd496
      /* Compare a word at a time for speed.  */
Packit Service fdd496
      while (*w0 == *w1)
Packit Service fdd496
	w0++, w1++;
Packit Service fdd496
Packit Service fdd496
      /* Do the last few bytes of comparison a byte at a time.  */
Packit Service fdd496
      p0 = (char *) w0;
Packit Service fdd496
      p1 = (char *) w1;
Packit Service fdd496
      while (*p0 == *p1)
Packit Service fdd496
	p0++, p1++;
Packit Service fdd496
Packit Service fdd496
      /* Don't mistakenly count missing newline as part of prefix.  */
Packit Service fdd496
      if (ROBUST_OUTPUT_STYLE (output_style)
Packit Service fdd496
	  && ((buffer0 + n0 - filevec[0].missing_newline < p0)
Packit Service fdd496
	      !=
Packit Service fdd496
	      (buffer1 + n1 - filevec[1].missing_newline < p1)))
Packit Service fdd496
	p0--, p1--;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Now P0 and P1 point at the first nonmatching characters.  */
Packit Service fdd496
Packit Service fdd496
  /* Skip back to last line-beginning in the prefix,
Packit Service fdd496
     and then discard up to HORIZON_LINES lines from the prefix.  */
Packit Service fdd496
  i = horizon_lines;
Packit Service fdd496
  while (p0 != buffer0 && (p0[-1] != '\n' || i--))
Packit Service fdd496
    p0--, p1--;
Packit Service fdd496
Packit Service fdd496
  /* Record the prefix.  */
Packit Service fdd496
  filevec[0].prefix_end = p0;
Packit Service fdd496
  filevec[1].prefix_end = p1;
Packit Service fdd496
Packit Service fdd496
  /* Find identical suffix.  */
Packit Service fdd496
Packit Service fdd496
  /* P0 and P1 point beyond the last chars not yet compared.  */
Packit Service fdd496
  p0 = buffer0 + n0;
Packit Service fdd496
  p1 = buffer1 + n1;
Packit Service fdd496
Packit Service fdd496
  if (! ROBUST_OUTPUT_STYLE (output_style)
Packit Service fdd496
      || filevec[0].missing_newline == filevec[1].missing_newline)
Packit Service fdd496
    {
Packit Service fdd496
      end0 = p0;	/* Addr of last char in file 0.  */
Packit Service fdd496
Packit Service fdd496
      /* Get value of P0 at which we should stop scanning backward:
Packit Service fdd496
	 this is when either P0 or P1 points just past the last char
Packit Service fdd496
	 of the identical prefix.  */
Packit Service fdd496
      beg0 = filevec[0].prefix_end + (n0 < n1 ? 0 : n0 - n1);
Packit Service fdd496
Packit Service fdd496
      /* Scan back until chars don't match or we reach that point.  */
Packit Service fdd496
      while (p0 != beg0)
Packit Service fdd496
	if (*--p0 != *--p1)
Packit Service fdd496
	  {
Packit Service fdd496
	    /* Point at the first char of the matching suffix.  */
Packit Service fdd496
	    ++p0, ++p1;
Packit Service fdd496
	    beg0 = p0;
Packit Service fdd496
	    break;
Packit Service fdd496
	  }
Packit Service fdd496
Packit Service fdd496
      /* Are we at a line-beginning in both files?  If not, add the rest of
Packit Service fdd496
	 this line to the main body.  Discard up to HORIZON_LINES lines from
Packit Service fdd496
	 the identical suffix.  Also, discard one extra line,
Packit Service fdd496
	 because shift_boundaries may need it.  */
Packit Service fdd496
      i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n')
Packit Service fdd496
			    &&
Packit Service fdd496
			    (buffer1 == p1 || p1[-1] == '\n'));
Packit Service fdd496
      while (i-- && p0 != end0)
Packit Service fdd496
	while (*p0++ != '\n')
Packit Service fdd496
	  continue;
Packit Service fdd496
Packit Service fdd496
      p1 += p0 - beg0;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Record the suffix.  */
Packit Service fdd496
  filevec[0].suffix_begin = p0;
Packit Service fdd496
  filevec[1].suffix_begin = p1;
Packit Service fdd496
Packit Service fdd496
  /* Calculate number of lines of prefix to save.
Packit Service fdd496
Packit Service fdd496
     prefix_count == 0 means save the whole prefix;
Packit Service fdd496
     we need this for options like -D that output the whole file,
Packit Service fdd496
     or for enormous contexts (to avoid worrying about arithmetic overflow).
Packit Service fdd496
     We also need it for options like -F that output some preceding line;
Packit Service fdd496
     at least we will need to find the last few lines,
Packit Service fdd496
     but since we don't know how many, it's easiest to find them all.
Packit Service fdd496
Packit Service fdd496
     Otherwise, prefix_count != 0.  Save just prefix_count lines at start
Packit Service fdd496
     of the line buffer; they'll be moved to the proper location later.
Packit Service fdd496
     Handle 1 more line than the context says (because we count 1 too many),
Packit Service fdd496
     rounded up to the next power of 2 to speed index computation.  */
Packit Service fdd496
Packit Service fdd496
  if (no_diff_means_no_output && ! function_regexp.fastmap
Packit Service fdd496
      && context < LIN_MAX / 4 && context < n0)
Packit Service fdd496
    {
Packit Service fdd496
      middle_guess = guess_lines (0, 0, p0 - filevec[0].prefix_end);
Packit Service fdd496
      suffix_guess = guess_lines (0, 0, buffer0 + n0 - p0);
Packit Service fdd496
      for (prefix_count = 1;  prefix_count <= context;  prefix_count *= 2)
Packit Service fdd496
	continue;
Packit Service fdd496
      alloc_lines0 = (prefix_count + middle_guess
Packit Service fdd496
		      + MIN (context, suffix_guess));
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      prefix_count = 0;
Packit Service fdd496
      alloc_lines0 = guess_lines (0, 0, n0);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  prefix_mask = prefix_count - 1;
Packit Service fdd496
  lines = 0;
Packit Service fdd496
  linbuf0 = xmalloc (alloc_lines0 * sizeof *linbuf0);
Packit Service fdd496
  prefix_needed = ! (no_diff_means_no_output
Packit Service fdd496
		     && filevec[0].prefix_end == p0
Packit Service fdd496
		     && filevec[1].prefix_end == p1);
Packit Service fdd496
  p0 = buffer0;
Packit Service fdd496
Packit Service fdd496
  /* If the prefix is needed, find the prefix lines.  */
Packit Service fdd496
  if (prefix_needed)
Packit Service fdd496
    {
Packit Service fdd496
      end0 = filevec[0].prefix_end;
Packit Service fdd496
      while (p0 != end0)
Packit Service fdd496
	{
Packit Service fdd496
	  lin l = lines++ & prefix_mask;
Packit Service fdd496
	  if (l == alloc_lines0)
Packit Service fdd496
	    {
Packit Service fdd496
	      if (PTRDIFF_MAX / (2 * sizeof *linbuf0) <= alloc_lines0)
Packit Service fdd496
		xalloc_die ();
Packit Service fdd496
	      alloc_lines0 *= 2;
Packit Service fdd496
	      linbuf0 = xrealloc (linbuf0, alloc_lines0 * sizeof *linbuf0);
Packit Service fdd496
	    }
Packit Service fdd496
	  linbuf0[l] = p0;
Packit Service fdd496
	  while (*p0++ != '\n')
Packit Service fdd496
	    continue;
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
  buffered_prefix = prefix_count && context < lines ? context : lines;
Packit Service fdd496
Packit Service fdd496
  /* Allocate line buffer 1.  */
Packit Service fdd496
Packit Service fdd496
  middle_guess = guess_lines (lines, p0 - buffer0, p1 - filevec[1].prefix_end);
Packit Service fdd496
  suffix_guess = guess_lines (lines, p0 - buffer0, buffer1 + n1 - p1);
Packit Service fdd496
  alloc_lines1 = buffered_prefix + middle_guess + MIN (context, suffix_guess);
Packit Service fdd496
  if (alloc_lines1 < buffered_prefix
Packit Service fdd496
      || PTRDIFF_MAX / sizeof *linbuf1 <= alloc_lines1)
Packit Service fdd496
    xalloc_die ();
Packit Service fdd496
  linbuf1 = xmalloc (alloc_lines1 * sizeof *linbuf1);
Packit Service fdd496
Packit Service fdd496
  if (buffered_prefix != lines)
Packit Service fdd496
    {
Packit Service fdd496
      /* Rotate prefix lines to proper location.  */
Packit Service fdd496
      for (i = 0;  i < buffered_prefix;  i++)
Packit Service fdd496
	linbuf1[i] = linbuf0[(lines - context + i) & prefix_mask];
Packit Service fdd496
      for (i = 0;  i < buffered_prefix;  i++)
Packit Service fdd496
	linbuf0[i] = linbuf1[i];
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Initialize line buffer 1 from line buffer 0.  */
Packit Service fdd496
  for (i = 0; i < buffered_prefix; i++)
Packit Service fdd496
    linbuf1[i] = linbuf0[i] - buffer0 + buffer1;
Packit Service fdd496
Packit Service fdd496
  /* Record the line buffer, adjusted so that
Packit Service fdd496
     linbuf[0] points at the first differing line.  */
Packit Service fdd496
  filevec[0].linbuf = linbuf0 + buffered_prefix;
Packit Service fdd496
  filevec[1].linbuf = linbuf1 + buffered_prefix;
Packit Service fdd496
  filevec[0].linbuf_base = filevec[1].linbuf_base = - buffered_prefix;
Packit Service fdd496
  filevec[0].alloc_lines = alloc_lines0 - buffered_prefix;
Packit Service fdd496
  filevec[1].alloc_lines = alloc_lines1 - buffered_prefix;
Packit Service fdd496
  filevec[0].prefix_lines = filevec[1].prefix_lines = lines;
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* If 1 < k, then (2**k - prime_offset[k]) is the largest prime less
Packit Service fdd496
   than 2**k.  This table is derived from Chris K. Caldwell's list
Packit Service fdd496
   <http://www.utm.edu/research/primes/lists/2small/>.  */
Packit Service fdd496
Packit Service fdd496
static unsigned char const prime_offset[] =
Packit Service fdd496
{
Packit Service fdd496
  0, 0, 1, 1, 3, 1, 3, 1, 5, 3, 3, 9, 3, 1, 3, 19, 15, 1, 5, 1, 3, 9, 3,
Packit Service fdd496
  15, 3, 39, 5, 39, 57, 3, 35, 1, 5, 9, 41, 31, 5, 25, 45, 7, 87, 21,
Packit Service fdd496
  11, 57, 17, 55, 21, 115, 59, 81, 27, 129, 47, 111, 33, 55, 5, 13, 27,
Packit Service fdd496
  55, 93, 1, 57, 25
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
/* Verify that this host's size_t is not too wide for the above table.  */
Packit Service fdd496
Packit Service fdd496
verify (sizeof (size_t) * CHAR_BIT <= sizeof prime_offset);
Packit Service fdd496
Packit Service fdd496
/* Given a vector of two file_data objects, read the file associated
Packit Service fdd496
   with each one, and build the table of equivalence classes.
Packit Service fdd496
   Return nonzero if either file appears to be a binary file.
Packit Service fdd496
   If PRETEND_BINARY is nonzero, pretend they are binary regardless.  */
Packit Service fdd496
Packit Service fdd496
bool
Packit Service fdd496
read_files (struct file_data filevec[], bool pretend_binary)
Packit Service fdd496
{
Packit Service fdd496
  int i;
Packit Service fdd496
  bool skip_test = text | pretend_binary;
Packit Service fdd496
  bool appears_binary = pretend_binary | sip (&filevec[0], skip_test);
Packit Service fdd496
Packit Service fdd496
  if (filevec[0].desc != filevec[1].desc)
Packit Service fdd496
    appears_binary |= sip (&filevec[1], skip_test | appears_binary);
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      filevec[1].buffer = filevec[0].buffer;
Packit Service fdd496
      filevec[1].bufsize = filevec[0].bufsize;
Packit Service fdd496
      filevec[1].buffered = filevec[0].buffered;
Packit Service fdd496
    }
Packit Service fdd496
  if (appears_binary)
Packit Service fdd496
    {
Packit Service fdd496
      set_binary_mode (filevec[0].desc, O_BINARY);
Packit Service fdd496
      set_binary_mode (filevec[1].desc, O_BINARY);
Packit Service fdd496
      return true;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  find_identical_ends (filevec);
Packit Service fdd496
Packit Service fdd496
  equivs_alloc = filevec[0].alloc_lines + filevec[1].alloc_lines + 1;
Packit Service fdd496
  if (PTRDIFF_MAX / sizeof *equivs <= equivs_alloc)
Packit Service fdd496
    xalloc_die ();
Packit Service fdd496
  equivs = xmalloc (equivs_alloc * sizeof *equivs);
Packit Service fdd496
  /* Equivalence class 0 is permanently safe for lines that were not
Packit Service fdd496
     hashed.  Real equivalence classes start at 1.  */
Packit Service fdd496
  equivs_index = 1;
Packit Service fdd496
Packit Service fdd496
  /* Allocate (one plus) a prime number of hash buckets.  Use a prime
Packit Service fdd496
     number between 1/3 and 2/3 of the value of equiv_allocs,
Packit Service fdd496
     approximately.  */
Packit Service fdd496
  for (i = 9; (size_t) 1 << i < equivs_alloc / 3; i++)
Packit Service fdd496
    continue;
Packit Service fdd496
  nbuckets = ((size_t) 1 << i) - prime_offset[i];
Packit Service fdd496
  if (PTRDIFF_MAX / sizeof *buckets <= nbuckets)
Packit Service fdd496
    xalloc_die ();
Packit Service fdd496
  buckets = zalloc ((nbuckets + 1) * sizeof *buckets);
Packit Service fdd496
  buckets++;
Packit Service fdd496
Packit Service fdd496
  for (i = 0; i < 2; i++)
Packit Service fdd496
    find_and_hash_each_line (&filevec[i]);
Packit Service fdd496
Packit Service fdd496
  filevec[0].equiv_max = filevec[1].equiv_max = equivs_index;
Packit Service fdd496
Packit Service fdd496
  free (equivs);
Packit Service fdd496
  free (buckets - 1);
Packit Service fdd496
Packit Service fdd496
  return false;
Packit Service fdd496
}