[comp.sources.unix] v24i023: GNU Diff, version 1.15, Part08/08

rsalz@uunet.uu.net (Rich Salz) (02/26/91)

Submitted-by: Paul Eggert <eggert@twinsun.com>
Posting-number: Volume 24, Issue 23
Archive-name: gnudiff1.15/part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 8)."
# Contents:  diff3.c
# Wrapped by eggert@ata on Mon Jan  7 11:25:32 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'diff3.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'diff3.c'\"
else
echo shar: Extracting \"'diff3.c'\" \(46642 characters\)
sed "s/^X//" >'diff3.c' <<'END_OF_FILE'
X/* Three-way file comparison program (diff3) for Project GNU
X   Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X
X/* Written by Randy Smith */
X
X#ifdef __STDC__
X#define VOID void
X#else
X#define VOID char
X#endif
X
X/* 
X * Include files.
X */
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#ifdef USG
X#include <fcntl.h>
X
X/* Define needed BSD functions in terms of sysV library.  */
X
X#define bcmp(s1,s2,n)	memcmp((s1),(s2),(n))
X#define bzero(s,n)	memset((s),0,(n))
X
X#ifndef XENIX
X#define dup2(f,t)	(close(t),fcntl((f),F_DUPFD,(t)))
X#endif
X
X#define vfork	fork
X
X#else /* not USG */
X#include <sys/wait.h>
X#endif /* not USG */
X
X#ifndef WEXITSTATUS
X#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
X#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix.  */
X#endif
X#ifndef WIFEXITED
X#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
X#endif
X
X#ifdef sparc
X/* vfork clobbers registers on the Sparc, so don't use it.  */
X#define vfork fork
X#endif
X
X/*
X * Internal data structures and macros for the diff3 program; includes
X * data structures for both diff3 diffs and normal diffs.
X */
X
X/*
X * Different files within a diff
X */
X#define	FILE0	0
X#define	FILE1	1
X#define	FILE2	2
X
X/*
X * Three way diffs are build out of two two-way diffs; the file which
X * the two two-way diffs share is:
X */
X#define	FILEC	FILE0
X
X/* The ranges are indexed by */
X#define	START	0
X#define	END	1
X
Xenum diff_type {
X  ERROR,			/* Should not be used */
X  ADD,				/* Two way diff add */
X  CHANGE,			/* Two way diff change */
X  DELETE,			/* Two way diff delete */
X  DIFF_ALL,			/* All three are different */
X  DIFF_1ST,			/* Only the first is different */
X  DIFF_2ND,			/* Only the second */
X  DIFF_3RD			/* Only the third */
X};
X
X/* Two-way diff */
Xstruct diff_block {
X  int ranges[2][2];	    	/* Ranges are inclusive */
X  char **lines[2];		/* The actual lines (may contain nulls) */
X  int *lengths[2];		/* Line lengths (including newlines, if any) */
X  struct diff_block *next;
X};
X
X/* Three-way diff */
X
Xstruct diff3_block {
X  enum diff_type correspond;	/* Type of diff */
X  int ranges[3][2];	    	/* Ranges are inclusive */
X  char **lines[3];		/* The actual lines (may contain nulls) */
X  int *lengths[3];		/* Line lengths (including newlines, if any) */
X  struct diff3_block *next;
X};
X
X/*
X * Access the ranges on a diff block.
X */
X#define	D_LOWLINE(diff, filenum)	\
X  ((diff)->ranges[filenum][START])
X#define	D_HIGHLINE(diff, filenum)	\
X  ((diff)->ranges[filenum][END])
X#define	D_NUMLINES(diff, filenum)	\
X  (D_HIGHLINE((diff), (filenum)) - D_LOWLINE((diff), (filenum)) + 1)
X
X/*
X * Access the line numbers in a file in a diff by relative line
X * numbers (i.e. line number within the diff itself).  Note that these
X * are lvalues and can be used for assignment.
X */
X#define	D_RELNUM(diff, filenum, linenum)	\
X  (*((diff)->lines[filenum] + linenum))
X#define	D_RELLEN(diff, filenum, linenum)	\
X  (*((diff)->lengths[filenum] + linenum))
X
X/*
X * And get at them directly, when that should be necessary.
X */
X#define	D_LINEARRAY(diff, filenum)	\
X  ((diff)->lines[filenum])
X#define	D_LENARRAY(diff, filenum)	\
X  ((diff)->lengths[filenum])
X
X/*
X * Next block.
X */
X#define	D_NEXT(diff)	((diff)->next)
X
X/*
X * Access the type of a diff3 block.
X */
X#define	D3_TYPE(diff)	((diff)->correspond)
X
X/*
X * Line mappings based on diffs.  The first maps off the top of the
X * diff, the second off of the bottom.
X */
X#define	D_HIGH_MAPLINE(diff, fromfile, tofile, lineno)	\
X  ((lineno)						\
X   - D_HIGHLINE ((diff), (fromfile))			\
X   + D_HIGHLINE ((diff), (tofile)))
X
X#define	D_LOW_MAPLINE(diff, fromfile, tofile, lineno)	\
X  ((lineno)						\
X   - D_LOWLINE ((diff), (fromfile))			\
X   + D_LOWLINE ((diff), (tofile)))
X
X/*
X * General memory allocation function.
X */
X#define	ALLOCATE(number, type)	\
X  (type *) xmalloc ((number) * sizeof (type))
X
X/*
X * Options variables for flags set on command line.
X *
X * ALWAYS_TEXT: Treat all files as text files; never treat as binary.
X *
X * EDSCRIPT: Write out an ed script instead of the standard diff3 format.
X *
X * FLAGGING: Indicates that in the case of overlapping diffs (type
X * DIFF_ALL), the lines which would normally be deleted from file 1
X * should be preserved with a special flagging mechanism.
X *
X * DONT_WRITE_OVERLAP: 1 if information for overlapping diffs should
X * not be output.
X *
X * DONT_WRITE_SIMPLE: 1 if information for non-overlapping diffs
X * should not be output. 
X *
X * FINALWRITE: 1 if a :wq should be included at the end of the script
X * to write out the file being edited.
X *
X * MERGE: output a merged file.
X */
Xint always_text;
Xint edscript;
Xint flagging;
Xint dont_write_overlap;
Xint dont_write_simple;
Xint finalwrite;
Xint merge;
X
Xextern int optind;
X
Xchar *argv0;
X
X/*
X * Forward function declarations.
X */
Xstruct diff_block *process_diff ();
Xstruct diff3_block *make_3way_diff ();
Xvoid output_diff3 ();
Xint output_diff3_edscript ();
Xint output_diff3_merge ();
Xvoid usage ();
X
Xstruct diff3_block *using_to_diff3_block ();
Xint copy_stringlist ();
Xstruct diff3_block *create_diff3_block ();
Xint compare_line_list ();
X
Xchar *read_diff ();
Xenum diff_type process_diff_control ();
Xchar *scan_diff_line ();
X
Xstruct diff3_block *reverse_diff3_blocklist ();
X
XVOID *xmalloc ();
XVOID *xrealloc ();
X
Xchar diff_program[] = DIFF_PROGRAM;
X
X/*
X * Main program.  Calls diff twice on two pairs of input files,
X * combines the two diffs, and outputs them.
X */
Xmain (argc, argv)
X     int argc;
X     char **argv;
X{
X  int c, i;
X  int mapping[3];
X  int rev_mapping[3];
X  int incompat;
X  int overlaps_found;
X  struct diff_block *thread1, *thread2;
X  struct diff3_block *diff;
X  int tag_count = 0;
X  /* Element 0 is for file 0, element 1 is for file 2.  */
X  char *tag_strings[2];
X  extern char *optarg;
X  char *commonname;
X  struct stat statb;
X
X  incompat = 0;
X  tag_strings[0] = tag_strings[1] = 0;
X
X  argv0 = argv[0];
X  
X  while ((c = getopt (argc, argv, "aeimx3EXL:")) != EOF)
X    {
X      switch (c)
X	{
X	case 'a':
X	  always_text = 1;
X	  break;
X	case 'x':
X	  dont_write_simple = 1;
X	  incompat++;
X	  break;
X	case '3':
X	  dont_write_overlap = 1;
X	  incompat++;
X	  break;
X	case 'i':
X	  finalwrite = 1;
X	  break;
X	case 'm':
X	  merge = 1;
X	  break;
X	case 'X':
X	  dont_write_simple = 1;
X	  /* Falls through */
X	case 'E':
X	  flagging = 1;
X	  /* Falls through */
X	case 'e':
X	  incompat++;
X	  break;
X	case 'L':
X	  /* Handle one or two -L arguments.  */
X	  if (tag_count < 2)
X	    {
X	      tag_strings[tag_count++] = optarg;
X	      break;
X	    }
X	  /* Falls through */
X	case '?':
X	default:
X	  usage ();
X	  /* NOTREACHED */
X	}
X    }
X
X  edscript = incompat & ~merge;  /* -eExX3 without -m implies ed script.  */
X  flagging |= ~incompat & merge;  /* -m without -eExX3 implies -E.  */
X
X  if (incompat > 1  /* Ensure at most one of -eExX3.  */
X      || finalwrite & (~incompat | merge)
X	    /* -i needs one of -eExX3; -i -m would rewrite input file.  */
X      || tag_count && ! flagging /* -L requires one of -EX.  */
X      || argc - optind != 3)
X    usage ();
X
X  if (tag_strings[0] == 0)
X    tag_strings[0] = argv[optind];
X  if (tag_strings[1] == 0)
X    tag_strings[1] = argv[optind + 2];
X
X  if (*argv[optind] == '-' && *(argv[optind] + 1) == '\0')
X    {
X      /* Sigh.  We've got standard input as the first arg. We can't */
X      /* call diff twice on stdin */
X      if (! strcmp (argv[optind + 1], "-") || ! strcmp (argv[optind + 2], "-"))
X	fatal ("`-' specified for more than one input file");
X      mapping[0] = 1;
X      mapping[1] = 2;
X      mapping[2] = 0;
X      rev_mapping[1] = 0;
X      rev_mapping[2] = 1;
X      rev_mapping[0] = 2;
X    }
X  else
X    {
X      /* Normal, what you'd expect */
X      mapping[0] = 0;
X      mapping[1] = 1;
X      mapping[2] = 2;
X      rev_mapping[0] = 0;
X      rev_mapping[1] = 1;
X      rev_mapping[2] = 2;
X    }
X
X  for (i = 0; i < 3; i++)
X    if (argv[optind + i][0] != '-' || argv[optind + i][1] != '\0')
X      if (stat (argv[optind + i], &statb) < 0)
X	perror_with_exit (argv[optind + i]);
X      else if ((statb.st_mode & S_IFMT) == S_IFDIR)
X	{
X	  fprintf (stderr, "%s: %s: Is a directory\n", argv0,
X		   argv[optind + i]);
X	  exit (2);
X	}
X  
X
X  commonname = argv[optind + rev_mapping[0]];
X  thread1 = process_diff (commonname, argv[optind + rev_mapping[1]]);
X  thread2 = process_diff (commonname, argv[optind + rev_mapping[2]]);
X  diff = make_3way_diff (thread1, thread2);
X  if (edscript)
X    overlaps_found
X      = output_diff3_edscript (stdout, diff, mapping, rev_mapping,
X			       tag_strings[0], argv[optind+1], tag_strings[1]);
X  else if (merge)
X    {
X      if (! freopen (commonname, "r", stdin))
X	perror_with_exit (commonname);
X      overlaps_found
X	= output_diff3_merge (stdin, stdout, diff, mapping, rev_mapping,
X			      tag_strings[0], argv[optind+1], tag_strings[1]);
X      if (ferror (stdin))
X	fatal ("read error");
X    }
X  else
X    {
X      output_diff3 (stdout, diff, mapping, rev_mapping);
X      overlaps_found = 0;
X    }
X
X  if (ferror (stdout) || fflush (stdout) != 0)
X    fatal ("write error");
X  exit (overlaps_found);
X}
X      
X/*
X * Explain, patiently and kindly, how to use this program.  Then exit.
X */
Xvoid
Xusage ()
X{
X  fprintf (stderr, "Usage:\t%s [-exEX3 [-i | -m] [-L label1 -L label3]] file1 file2 file3\n",
X	   argv0);
X  fprintf (stderr, "\tOnly one of [exEX3] allowed\n");
X  exit (2);
X}
X
X/*
X * Routines that combine the two diffs together into one.  The
X * algorithm used follows:
X *
X *   File0 is shared in common between the two diffs.
X *   Diff01 is the diff between 0 and 1.
X *   Diff02 is the diff between 0 and 2.
X *
X * 	1) Find the range for the first block in File0.
X *  	    a) Take the lowest of the two ranges (in File0) in the two
X *  	       current blocks (one from each diff) as being the low
X *  	       water mark.  Assign the upper end of this block as
X *  	       being the high water mark and move the current block up
X *  	       one.  Mark the block just moved over as to be used.
X *	    b) Check the next block in the diff that the high water
X *  	       mark is *not* from.  
X *	       
X *	       *If* the high water mark is above
X *  	       the low end of the range in that block, 
X * 
X *	           mark that block as to be used and move the current
X *  	           block up.  Set the high water mark to the max of
X *  	           the high end of this block and the current.  Repeat b.
X * 
X *  	 2) Find the corresponding ranges in Files1 (from the blocks
X *  	    in diff01; line per line outside of diffs) and in File2.
X *  	    Create a diff3_block, reserving space as indicated by the ranges.
X *	    
X *	 3) Copy all of the pointers for file0 in.  At least for now,
X *  	    do bcmp's between corresponding strings in the two diffs.
X *	    
X *	 4) Copy all of the pointers for file1 and 2 in.  Get what you
X *  	    need from file0 (when there isn't a diff block, it's
X *  	    identical to file0 within the range between diff blocks).
X *	    
X *	 5) If the diff blocks you used came from only one of the two
X * 	    strings of diffs, then that file (i.e. the one other than
X * 	    file 0 in that diff) is the odd person out.  If you used
X * 	    diff blocks from both sets, check to see if files 1 and 2 match:
X *	    
X *	        Same number of lines?  If so, do a set of bcmp's (if a
X *  	    bcmp matches; copy the pointer over; it'll be easier later
X *  	    if you have to do any compares).  If they match, 1 & 2 are
X *  	    the same.  If not, all three different.
X * 
X *   Then you do it again, until you run out of blocks. 
X * 
X */
X
X/* 
X * This routine makes a three way diff (chain of diff3_block's) from two
X * two way diffs (chains of diff_block's).  It is assumed that each of
X * the two diffs passed are off of the same file (i.e. that each of the
X * diffs were made "from" the same file).  The three way diff pointer
X * returned will have numbering 0--the common file, 1--the other file
X * in diff1, and 2--the other file in diff2.
X */
Xstruct diff3_block *
Xmake_3way_diff (thread1, thread2)
X     struct diff_block *thread1, *thread2;
X{
X/*
X * This routine works on the two diffs passed to it as threads.
X * Thread number 0 is diff1, thread number 1 is diff2.  The USING
X * array is set to the base of the list of blocks to be used to
X * construct each block of the three way diff; if no blocks from a
X * particular thread are to be used, that element of the using array
X * is set to 0.  The elements LAST_USING array are set to the last
X * elements on each of the using lists.
X *
X * The HIGH_WATER_MARK is set to the highest line number in File 0
X * described in any of the diffs in either of the USING lists.  The
X * HIGH_WATER_THREAD names the thread.  Similarly the BASE_WATER_MARK
X * and BASE_WATER_THREAD describe the lowest line number in File 0
X * described in any of the diffs in either of the USING lists.  The
X * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was
X * taken. 
X *
X * The HIGH_WATER_DIFF should always be equal to LAST_USING
X * [HIGH_WATER_THREAD].  The OTHER_DIFF is the next diff to check for
X * higher water, and should always be equal to
X * CURRENT[HIGH_WATER_THREAD ^ 0x1].  The OTHER_THREAD is the thread
X * in which the OTHER_DIFF is, and hence should always be equal to
X * HIGH_WATER_THREAD ^ 0x1.
X *
X * The variable LAST_DIFF is kept set to the last diff block produced
X * by this routine, for line correspondence purposes between that diff
X * and the one currently being worked on.  It is initialized to
X * ZERO_DIFF before any blocks have been created.
X */
X
X  struct diff_block
X    *using[2],
X    *last_using[2],
X    *current[2];
X
X  int
X    high_water_mark;
X
X  int
X    high_water_thread,
X    base_water_thread,
X    other_thread;
X
X  struct diff_block
X    *high_water_diff,
X    *other_diff;
X
X  struct diff3_block
X    *result,
X    *tmpblock,
X    *result_last,
X    *last_diff;
X
X  static struct diff3_block zero_diff = {
X      ERROR,
X      { {0, 0}, {0, 0}, {0, 0} },
X      { (char **) 0, (char **) 0, (char **) 0 },
X      { (int *) 0, (int *) 0, (int *) 0 },
X      (struct diff3_block *) 0
X      };
X
X  /* Initialization */
X  result = result_last = (struct diff3_block *) 0;
X  current[0] = thread1; current[1] = thread2;
X  last_diff = &zero_diff;
X
X  /* Sniff up the threads until we reach the end */
X
X  while (current[0] || current[1])
X    {
X      using[0] = using[1] = last_using[0] = last_using[1] =
X	(struct diff_block *) 0;
X
X      /* Setup low and high water threads, diffs, and marks.  */
X      if (!current[0])
X	base_water_thread = 1;
X      else if (!current[1])
X	base_water_thread = 0;
X      else
X	base_water_thread =
X	  (D_LOWLINE (current[0], FILE0) > D_LOWLINE (current[1], FILE0));
X
X      high_water_thread = base_water_thread;
X      
X      high_water_diff = current[high_water_thread];
X	
X#if 0
X      /* low and high waters start off same diff */
X      base_water_mark = D_LOWLINE (high_water_diff, FILE0);
X#endif
X
X      high_water_mark = D_HIGHLINE (high_water_diff, FILE0);
X
X      /* Make the diff you just got info from into the using class */
X      using[high_water_thread]
X	= last_using[high_water_thread]
X	= high_water_diff;
X      current[high_water_thread] = high_water_diff->next;
X      last_using[high_water_thread]->next
X	= (struct diff_block *) 0;
X
X      /* And mark the other diff */
X      other_thread = high_water_thread ^ 0x1;
X      other_diff = current[other_thread];
X
X      /* Shuffle up the ladder, checking the other diff to see if it
X         needs to be incorporated */
X      while (other_diff
X	     && D_LOWLINE (other_diff, FILE0) <= high_water_mark + 1)
X	{
X
X	  /* Incorporate this diff into the using list.  Note that
X	     this doesn't take it off the current list */
X	  if (using[other_thread])
X	    last_using[other_thread]->next = other_diff;
X	  else
X	    using[other_thread] = other_diff;
X	  last_using[other_thread] = other_diff;
X
X	  /* Take it off the current list.  Note that this following
X	     code assumes that other_diff enters it equal to
X	     current[high_water_thread ^ 0x1] */
X	  current[other_thread]
X	    = current[other_thread]->next;
X	  other_diff->next
X	    = (struct diff_block *) 0;
X
X	  /* Set the high_water stuff
X	     If this comparison is equal, then this is the last pass
X	     through this loop; since diff blocks within a given
X	     thread cannot overlap, the high_water_mark will be
X	     *below* the range_start of either of the next diffs. */
X
X	  if (high_water_mark < D_HIGHLINE (other_diff, FILE0))
X	    {
X	      high_water_thread ^= 1;
X	      high_water_diff = other_diff;
X	      high_water_mark = D_HIGHLINE (other_diff, FILE0);
X	    }
X
X	  /* Set the other diff */
X	  other_thread = high_water_thread ^ 0x1;
X	  other_diff = current[other_thread];
X	}
X
X      /* The using lists contain a list of all of the blocks to be
X         included in this diff3_block.  Create it.  */
X
X      tmpblock = using_to_diff3_block (using, last_using,
X				       base_water_thread, high_water_thread,
X				       last_diff);
X
X      if (!tmpblock)
X	fatal ("internal: screwup in format of diff blocks");
X
X      /* Put it on the list */
X      if (result)
X	result_last->next = tmpblock;
X      else
X	result = tmpblock;
X      result_last = tmpblock;
X
X      /* Setup corresponding lines correctly */
X      last_diff = tmpblock;
X    }
X  return result;
X}
X
X/*
X * using_to_diff3_block:
X *   This routine takes two lists of blocks (from two separate diff
X * threads) and puts them together into one diff3 block.
X * It then returns a pointer to this diff3 block or 0 for failure.
X *
X * All arguments besides using are for the convenience of the routine;
X * they could be derived from the using array.
X * LAST_USING is a pair of pointers to the last blocks in the using
X * structure.
X * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest
X * and highest line numbers for File0.
X * last_diff contains the last diff produced in the calling routine.
X * This is used for lines mappings which would still be identical to
X * the state that diff ended in.
X *
X * A distinction should be made in this routine between the two diffs
X * that are part of a normal two diff block, and the three diffs that
X * are part of a diff3_block.
X */
Xstruct diff3_block *
Xusing_to_diff3_block (using, last_using, low_thread, high_thread, last_diff)
X     struct diff_block
X       *using[2],
X       *last_using[2];
X     int low_thread, high_thread;
X     struct diff3_block *last_diff;
X{
X  int lowc, highc, low1, high1, low2, high2;
X  struct diff3_block *result;
X  struct diff_block *ptr;
X  int i;
X  int current0line;
X  
X  /* Find the range in file0 */
X  lowc = using[low_thread]->ranges[0][START];
X  highc = last_using[high_thread]->ranges[0][END];
X
X  /* Find the ranges in the other files.
X     If using[x] is null, that means that the file to which that diff
X     refers is equivalent to file 0 over this range */
X  
X  if (using[0])
X    {
X      low1 = D_LOW_MAPLINE (using[0], FILE0, FILE1, lowc);
X      high1 = D_HIGH_MAPLINE (last_using[0], FILE0, FILE1, highc); 
X    }
X  else
X    {
X      low1 = D_HIGH_MAPLINE (last_diff, FILEC, FILE1, lowc);
X      high1 = D_HIGH_MAPLINE (last_diff, FILEC, FILE1, highc);
X    }
X
X  /*
X   * Note that in the following, we use file 1 relative to the diff,
X   * and file 2 relative to the corresponding lines struct.
X   */
X  if (using[1])
X    {
X      low2 = D_LOW_MAPLINE (using[1], FILE0, FILE1, lowc);
X      high2 = D_HIGH_MAPLINE (last_using[1], FILE0, FILE1, highc); 
X    }
X  else
X    {
X      low2 = D_HIGH_MAPLINE (last_diff, FILEC, FILE2, lowc);
X      high2 = D_HIGH_MAPLINE (last_diff, FILEC, FILE2, highc);
X    }
X
X  /* Create a block with the appropriate sizes */
X  result = create_diff3_block (lowc, highc, low1, high1, low2, high2);
X
X  /* Copy over all of the information for File 0.  Return with a zero
X     if any of the compares failed. */
X  for (ptr = using[0]; ptr; ptr = D_NEXT (ptr))
X    {
X      int result_offset = D_LOWLINE (ptr, FILE0) - lowc;
X      int copy_size
X	= D_HIGHLINE (ptr, FILE0) - D_LOWLINE (ptr, FILE0) + 1;
X      
X      if (!copy_stringlist (D_LINEARRAY (ptr, FILE0),
X			    D_LENARRAY (ptr, FILE0),
X			    D_LINEARRAY (result, FILEC) + result_offset,
X			    D_LENARRAY (result, FILEC) + result_offset,
X			    copy_size))
X	return 0;
X    }
X
X  for (ptr = using[1]; ptr; ptr = D_NEXT (ptr))
X    {
X      int result_offset = D_LOWLINE (ptr, FILEC) - lowc;
X      int copy_size
X	= D_HIGHLINE (ptr, FILEC) - D_LOWLINE (ptr, FILEC) + 1;
X      
X      if (!copy_stringlist (D_LINEARRAY (ptr, FILE0),
X			    D_LENARRAY (ptr, FILE0),
X			    D_LINEARRAY (result, FILEC) + result_offset,
X			    D_LENARRAY (result, FILEC) + result_offset,
X			    copy_size))
X	return 0;
X    }
X
X  /* Copy stuff for file 1.  First deal with anything that might be
X     before the first diff. */
X
X  for (i = 0;
X       i + low1 < (using[0] ? D_LOWLINE (using[0], FILE1) : high1 + 1);
X       i++)
X    {
X      D_RELNUM (result, FILE1, i) = D_RELNUM (result, FILEC, i);
X      D_RELLEN (result, FILE1, i) = D_RELLEN (result, FILEC, i);
X    }
X  
X  for (ptr = using[0]; ptr; ptr = D_NEXT (ptr))
X    {
X      int result_offset = D_LOWLINE (ptr, FILE1) - low1;
X      int copy_size
X	= D_HIGHLINE (ptr, FILE1) - D_LOWLINE (ptr, FILE1) + 1;
X
X      if (!copy_stringlist (D_LINEARRAY (ptr, FILE1),
X			    D_LENARRAY (ptr, FILE1),
X			    D_LINEARRAY (result, FILE1) + result_offset,
X			    D_LENARRAY (result, FILE1) + result_offset,
X			    copy_size))
X	return 0;
X
X      /* Catch the lines between here and the next diff */
X      current0line = D_HIGHLINE (ptr, FILE0) + 1 - lowc;
X      for (i = D_HIGHLINE (ptr, FILE1) + 1 - low1;
X	   i < (D_NEXT (ptr) ?
X		D_LOWLINE (D_NEXT (ptr), FILE1) :
X		high1 + 1) - low1;
X	   i++)
X	{
X	  D_RELNUM (result, FILE1, i)
X	    = D_RELNUM (result, FILEC, current0line);
X	  D_RELLEN (result, FILE1, i)
X	    = D_RELLEN (result, FILEC, current0line++);
X	}
X    }
X
X  /* Copy stuff for file 2.  First deal with anything that might be
X     before the first diff. */
X
X  for (i = 0;
X       i + low2 < (using[1] ? D_LOWLINE (using[1], FILE1) : high2 + 1);
X       i++)
X    {
X      D_RELNUM (result, FILE2, i) = D_RELNUM (result, FILEC, i);
X      D_RELLEN (result, FILE2, i) = D_RELLEN (result, FILEC, i);
X    }
X  
X  for (ptr = using[1]; ptr; ptr = D_NEXT (ptr))
X    {
X      int result_offset = D_LOWLINE (ptr, FILE1) - low2;
X      int copy_size
X	= D_HIGHLINE (ptr, FILE1) - D_LOWLINE (ptr, FILE1) + 1;
X
X      if (!copy_stringlist (D_LINEARRAY (ptr, FILE1),
X			    D_LENARRAY (ptr, FILE1),
X			    D_LINEARRAY (result, FILE2) + result_offset,
X			    D_LENARRAY (result, FILE2) + result_offset,
X			    copy_size))
X	return 0;
X
X      /* Catch the lines between here and the next diff */
X      current0line = D_HIGHLINE (ptr, FILE0) + 1 - lowc;
X      for (i = D_HIGHLINE (ptr, FILE1) + 1 - low2;
X	   i < (D_NEXT (ptr) ?
X		D_LOWLINE (D_NEXT (ptr), FILE1) :
X		high2 + 1) - low2;
X	   i++)
X	{
X	  D_RELNUM (result, FILE2, i)
X	    = D_RELNUM (result, FILEC, current0line);
X	  D_RELLEN (result, FILE2, i)
X	    = D_RELLEN (result, FILEC, current0line++);
X	}
X    }
X
X  /* Set correspond */
X  if (!using[0])
X    D3_TYPE (result) = DIFF_3RD;
X  else if (!using[1])
X    D3_TYPE (result) = DIFF_2ND;
X  else
X    {
X      int nl1
X	= D_HIGHLINE (result, FILE1) - D_LOWLINE (result, FILE1) + 1;
X      int nl2
X	= D_HIGHLINE (result, FILE2) - D_LOWLINE (result, FILE2) + 1;
X
X      if (nl1 != nl2
X	  || !compare_line_list (D_LINEARRAY (result, FILE1),
X				 D_LENARRAY (result, FILE1),
X				 D_LINEARRAY (result, FILE2),
X				 D_LENARRAY (result, FILE2),
X				 nl1))
X	D3_TYPE (result) = DIFF_ALL;
X      else
X	D3_TYPE (result) = DIFF_1ST;
X    }
X  
X  return result;
X}
X
X/*
X * This routine copies pointers from a list of strings to a different list
X * of strings.  If a spot in the second list is already filled, it
X * makes sure that it is filled with the same string; if not it
X * returns 0, the copy incomplete.
X * Upon successful completion of the copy, it returns 1.
X */
Xint
Xcopy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum)
X     char *fromptrs[], *toptrs[];
X     int *fromlengths, *tolengths;
X     int copynum;
X{
X  register char
X    **f = fromptrs,
X    **t = toptrs;
X  register int
X    *fl = fromlengths,
X    *tl = tolengths;
X  
X  while (copynum--)
X    {
X      if (*t)
X	{ if (*fl != *tl || bcmp (*f, *t, *fl)) return 0; }
X      else
X	{ *t = *f ; *tl = *fl; }
X
X      t++; f++; tl++; fl++;
X    }
X  return 1;
X}
X
X/*
X * Create a diff3_block, with ranges as specified in the arguments.
X * Allocate the arrays for the various pointers (and zero them) based
X * on the arguments passed.  Return the block as a result.
X */
Xstruct diff3_block *
Xcreate_diff3_block (low0, high0, low1, high1, low2, high2)
X     register int low0, high0, low1, high1, low2, high2;
X{
X  struct diff3_block *result = ALLOCATE (1, struct diff3_block);
X  int numlines;
X
X  D3_TYPE (result) = ERROR;
X  D_NEXT (result) = 0;
X
X  /* Assign ranges */
X  D_LOWLINE (result, FILE0) = low0;
X  D_HIGHLINE (result, FILE0) = high0;
X  D_LOWLINE (result, FILE1) = low1;
X  D_HIGHLINE (result, FILE1) = high1;
X  D_LOWLINE (result, FILE2) = low2;
X  D_HIGHLINE (result, FILE2) = high2;
X
X  /* Allocate and zero space */
X  numlines = D_NUMLINES (result, FILE0);
X  if (numlines)
X    {
X      D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *);
X      D_LENARRAY (result, FILE0) = ALLOCATE (numlines, int);
X      bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
X      bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (int)));
X    }
X  else
X    {
X      D_LINEARRAY (result, FILE0) = (char **) 0;
X      D_LENARRAY (result, FILE0) = (int *) 0;
X    }
X
X  numlines = D_NUMLINES (result, FILE1);
X  if (numlines)
X    {
X      D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
X      D_LENARRAY (result, FILE1) = ALLOCATE (numlines, int);
X      bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
X      bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (int)));
X    }
X  else
X    {
X      D_LINEARRAY (result, FILE1) = (char **) 0;
X      D_LENARRAY (result, FILE1) = (int *) 0;
X    }
X
X  numlines = D_NUMLINES (result, FILE2);
X  if (numlines)
X    {
X      D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
X      D_LENARRAY (result, FILE2) = ALLOCATE (numlines, int);
X      bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
X      bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (int)));
X    }
X  else
X    {
X      D_LINEARRAY (result, FILE2) = (char **) 0;
X      D_LENARRAY (result, FILE2) = (int *) 0;
X    }
X
X  /* Return */
X  return result;
X}
X
X/*
X * Compare two lists of lines of text.
X * Return 1 if they are equivalent, 0 if not.
X */
Xint
Xcompare_line_list (list1, lengths1, list2, lengths2, nl)
X     char *list1[], *list2[];
X     int *lengths1, *lengths2;
X     int nl;
X{
X  char
X    **l1 = list1,
X    **l2 = list2;
X  int
X    *lgths1 = lengths1,
X    *lgths2 = lengths2;
X  
X  while (nl--)
X    if (!*l1 || !*l2 || *lgths1 != *lgths2++
X	|| bcmp (*l1++, *l2++, *lgths1++))
X      return 0;
X  return 1;
X}
X
X/* 
X * Routines to input and parse two way diffs.
X */
X
Xextern char **environ;
X
X#define	DIFF_CHUNK_SIZE	10000
X
Xstruct diff_block *
Xprocess_diff (filea, fileb)
X     char *filea, *fileb;
X{
X  char *diff_contents;
X  char *diff_limit;
X  char *scan_diff;
X  enum diff_type dt;
X  int i;
X  struct diff_block *block_list, *block_list_end, *bptr;
X
X  diff_limit = read_diff (filea, fileb, &diff_contents);
X  scan_diff = diff_contents;
X  bptr = block_list_end = block_list = (struct diff_block *) 0;
X
X  while (scan_diff < diff_limit)
X    {
X      bptr = ALLOCATE (1, struct diff_block);
X      bptr->next = 0;
X      bptr->lines[0] = bptr->lines[1] = (char **) 0;
X      bptr->lengths[0] = bptr->lengths[1] = (int *) 0;
X      
X      dt = process_diff_control (&scan_diff, bptr);
X      if (dt == ERROR || *scan_diff != '\n')
X	{
X	  fprintf (stderr, "%s: diff error: ", argv0);
X	  do
X	    {
X	      putc (*scan_diff, stderr);
X	    }
X	  while (*scan_diff++ != '\n');
X	  exit (2);
X	}
X      scan_diff++;
X      
X      /* Force appropriate ranges to be null, if necessary */
X      switch (dt)
X	{
X	case ADD:
X	  bptr->ranges[0][0]++;
X	  break;
X	case DELETE:
X	  bptr->ranges[1][0]++;
X	  break;
X	case CHANGE:
X	  break;
X	default:
X	  fatal ("internal: Bad diff type in process_diff");
X	  break;
X	}
X      
X      /* Allocate space for the pointers for the lines from filea, and
X	 parcel them out among these pointers */
X      if (dt != ADD)
X	{
X	  bptr->lines[0] = ALLOCATE ((bptr->ranges[0][END]
X				      - bptr->ranges[0][START] + 1),
X				     char *);
X	  bptr->lengths[0] = ALLOCATE ((bptr->ranges[0][END]
X					- bptr->ranges[0][START] + 1),
X				       int);
X	  for (i = 0; i <= (bptr->ranges[0][END]
X			    - bptr->ranges[0][START]); i++)
X	    scan_diff = scan_diff_line (scan_diff,
X					&(bptr->lines[0][i]),
X					&(bptr->lengths[0][i]),
X					diff_limit,
X					'<');
X	}
X      
X      /* Get past the separator for changes */
X      if (dt == CHANGE)
X	{
X	  if (strncmp (scan_diff, "---\n", 4))
X	    fatal ("Bad diff format: bad change separator");
X	  scan_diff += 4;
X	}
X      
X      /* Allocate space for the pointers for the lines from fileb, and
X	 parcel them out among these pointers */
X      if (dt != DELETE)
X	{
X	  bptr->lines[1] = ALLOCATE ((bptr->ranges[1][END]
X				      - bptr->ranges[1][START] + 1),
X				     char *);
X	  bptr->lengths[1] = ALLOCATE ((bptr->ranges[1][END]
X					- bptr->ranges[1][START] + 1),
X				       int);
X	  for (i = 0; i <= (bptr->ranges[1][END]
X			    - bptr->ranges[1][START]); i++)
X	    scan_diff = scan_diff_line (scan_diff,
X					&(bptr->lines[1][i]),
X					&(bptr->lengths[1][i]),
X					diff_limit,
X					'>');
X	}
X      
X      /* Place this block on the blocklist */
X      if (block_list_end)
X	block_list_end->next = bptr;
X      else
X	block_list = bptr;
X      
X      block_list_end = bptr;
X      
X    }
X
X  return block_list;
X}
X
X/*
X * This routine will parse a normal format diff control string.  It
X * returns the type of the diff (ERROR if the format is bad).  All of
X * the other important information is filled into to the structure
X * pointed to by db, and the string pointer (whose location is passed
X * to this routine) is updated to point beyond the end of the string
X * parsed.  Note that only the ranges in the diff_block will be set by
X * this routine.
X *
X * If some specific pair of numbers has been reduced to a single
X * number, then both corresponding numbers in the diff block are set
X * to that number.  In general these numbers are interpetted as ranges
X * inclusive, unless being used by the ADD or DELETE commands.  It is
X * assumed that these will be special cased in a superior routine.  
X */
X
Xenum diff_type
Xprocess_diff_control (string, db)
X     char **string;
X     struct diff_block *db;
X{
X  char *s = *string;
X  int holdnum;
X  enum diff_type type;
X
X/* These macros are defined here because they can use variables
X   defined in this function.  Don't try this at home kids, we're
X   trained professionals!
X
X   Also note that SKIPWHITE only recognizes tabs and spaces, and
X   that READNUM can only read positive, integral numbers */
X
X#define	SKIPWHITE(s)	{ while (*s == ' ' || *s == '\t') s++; }
X#define	READNUM(s, num)	\
X	{ if (!isdigit (*s)) return ERROR; holdnum = 0;	\
X	  do { holdnum = (*s++ - '0' + holdnum * 10); }	\
X	  while (isdigit (*s)); (num) = holdnum; }
X
X  /* Read first set of digits */
X  SKIPWHITE (s);
X  READNUM (s, db->ranges[0][START]);
X
X  /* Was that the only digit? */
X  SKIPWHITE(s);
X  if (*s == ',')
X    {
X      /* Get the next digit */
X      s++;
X      READNUM (s, db->ranges[0][END]);
X    }
X  else
X    db->ranges[0][END] = db->ranges[0][START];
X
X  /* Get the letter */
X  SKIPWHITE (s);
X  switch (*s)
X    {
X    case 'a':
X      type = ADD;
X      break;
X    case 'c':
X      type = CHANGE;
X      break;
X    case 'd':
X      type = DELETE;
X      break;
X    default:
X      return ERROR;			/* Bad format */
X    }
X  s++;				/* Past letter */
X  
X  /* Read second set of digits */
X  SKIPWHITE (s);
X  READNUM (s, db->ranges[1][START]);
X
X  /* Was that the only digit? */
X  SKIPWHITE(s);
X  if (*s == ',')
X    {
X      /* Get the next digit */
X      s++;
X      READNUM (s, db->ranges[1][END]);
X      SKIPWHITE (s);		/* To move to end */
X    }
X  else
X    db->ranges[1][END] = db->ranges[1][START];
X
X  *string = s;
X  return type;
X}
X
Xchar *
Xread_diff (filea, fileb, output_placement)
X     char *filea, *fileb;
X     char **output_placement;
X{
X  char *argv[6];
X  char **ap;
X  int fds[2];
X  char *diff_result;
X  int current_chunk_size;
X  int bytes;
X  int total;
X  int pid, w;
X  int wstatus;
X
X  ap = argv;
X  *ap++ = diff_program;
X  if (always_text)
X    *ap++ = "-a";
X  *ap++ = "--";
X  *ap++ = filea;
X  *ap++ = fileb;
X  *ap = (char *) 0;
X
X  if (pipe (fds) < 0)
X    perror_with_exit ("Pipe failed");
X
X  pid = vfork ();
X  if (pid == 0)
X    {
X      /* Child */
X      close (fds[0]);
X      if (fds[1] != fileno (stdout))
X	{
X	  dup2 (fds[1], fileno (stdout));
X	  close (fds[1]);
X	}
X      execve (diff_program, argv, environ);
X      /* Avoid stdio, because the parent process's buffers are inherited. */
X      write (fileno (stderr), diff_program, strlen (diff_program));
X      write (fileno (stderr), ": not found\n", 12);
X      _exit (2);
X    }
X
X  if (pid == -1)
X    perror_with_exit ("Fork failed");
X
X  close (fds[1]);		/* Prevent erroneous lack of EOF */
X  current_chunk_size = DIFF_CHUNK_SIZE;
X  diff_result = (char *) xmalloc (current_chunk_size);
X  total = 0;
X  do {
X    bytes = myread (fds[0],
X		    diff_result + total,
X		    current_chunk_size - total);
X    total += bytes;
X    if (total == current_chunk_size)
X      diff_result = (char *) xrealloc (diff_result, (current_chunk_size *= 2));
X  } while (bytes);
X
X  if (total != 0 && diff_result[total-1] != '\n')
X    fatal ("bad diff format; incomplete last line");
X
X  *output_placement = diff_result;
X
X  do
X    if ((w = wait (&wstatus)) == -1)
X      perror_with_exit ("Wait failed");
X  while (w != pid);
X
X  if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
X    fatal ("Subsidiary diff failed");
X
X  return diff_result + total;
X}
X
X
X/*
X * Scan a regular diff line (consisting of > or <, followed by a
X * space, followed by text (including nulls) up to a newline.
X *
X * This next routine began life as a macro and many parameters in it
X * are used as call-by-reference values.
X */
Xchar *
Xscan_diff_line (scan_ptr, set_start, set_length, limit, firstchar)
X     char *scan_ptr, **set_start;
X     int *set_length;
X     char *limit;
X     char firstchar;
X{
X  char *line_ptr;
X
X  if (!(scan_ptr[0] == (firstchar)
X	&& scan_ptr[1] == ' '))
X    fatal ("Bad diff format; incorrect leading line chars");
X
X  *set_start = line_ptr = scan_ptr + 2;
X  while (*line_ptr++ != '\n')
X    ;
X
X  /* Include newline if the original line ended in a newline,
X     or if an edit script is being generated.
X     Copy any missing newline message to stderr if an edit script is being
X     generated, because edit scripts cannot handle missing newlines.
X     Return the beginning of the next line.  */
X  *set_length = line_ptr - *set_start;
X  if (line_ptr < limit && *line_ptr == '\\')
X    {
X      if (edscript)
X	fprintf (stderr, "%s:", argv0);
X      else
X	--*set_length;
X      line_ptr++;
X      do
X	{
X	  if (edscript)
X	    putc (*line_ptr, stderr);
X	}
X      while (*line_ptr++ != '\n');
X    }
X
X  return line_ptr;
X}
X
X/*
X * This routine outputs a three way diff passed as a list of
X * diff3_block's.
X * The argument MAPPING is indexed by external file number (in the
X * argument list) and contains the internal file number (from the
X * diff passed).  This is important because the user expects his
X * outputs in terms of the argument list number, and the diff passed
X * may have been done slightly differently (if the first argument in
X * the argument list was the standard input, for example).
X * REV_MAPPING is the inverse of MAPPING.
X */
Xvoid
Xoutput_diff3 (outputfile, diff, mapping, rev_mapping)
X     FILE *outputfile;
X     struct diff3_block *diff;
X     int mapping[3], rev_mapping[3];
X{
X  int i;
X  int oddoneout;
X  char *cp;
X  struct diff3_block *ptr;
X  int line;
X  int length;
X  int dontprint;
X  static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
X
X  for (ptr = diff; ptr; ptr = D_NEXT (ptr))
X    {
X      char x[2];
X
X      switch (ptr->correspond)
X	{
X	case DIFF_ALL:
X	  x[0] = '\0';
X	  dontprint = 3;	/* Print them all */
X	  oddoneout = 3;	/* Nobody's odder than anyone else */
X	  break;
X	case DIFF_1ST:
X	case DIFF_2ND:
X	case DIFF_3RD:
X	  oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST];
X	    
X	  x[0] = oddoneout + '1';
X	  x[1] = '\0';
X	  dontprint = oddoneout==0;
X	  break;
X	default:
X	  fatal ("internal: Bad diff type passed to output");
X	}
X      fprintf (outputfile, "====%s\n", x);
X
X      /* Go 0, 2, 1 if the first and third outputs are equivalent. */
X      for (i = 0; i < 3;
X	   i = (oddoneout == 1 ? skew_increment[i] : i + 1))
X	{
X	  int realfile = mapping[i];
X	  int
X	    lowt = D_LOWLINE (ptr, realfile),
X	    hight = D_HIGHLINE (ptr, realfile);
X	  
X	  fprintf (outputfile, "%d:", i + 1);
X	  switch (lowt - hight)
X	    {
X	    case 1:
X	      fprintf (outputfile, "%da\n", lowt - 1);
X	      break;
X	    case 0:
X	      fprintf (outputfile, "%dc\n", lowt);
X	      break;
X	    default:
X	      fprintf (outputfile, "%d,%dc\n", lowt, hight);
X	      break;
X	    }
X
X	  if (i == dontprint) continue;
X
X	  for (line = 0; line < hight - lowt + 1; line++)
X	    {
X	      fprintf (outputfile, "  ");
X	      cp = D_RELNUM (ptr, realfile, line);
X	      length = D_RELLEN (ptr, realfile, line);
X	      fwrite (cp, sizeof (char), length, outputfile);
X	    }
X	  if (line != 0 && cp[length - 1] != '\n')
X	    fprintf (outputfile, "\n\\ No newline at end of file\n");
X	}
X    }
X}
X
X/*
X * This routine outputs a diff3 set of blocks as an ed script.  This
X * script applies the changes between file's 2 & 3 to file 1.  It
X * takes the precise format of the ed script to be output from global
X * variables set during options processing.  Note that it does
X * destructive things to the set of diff3 blocks it is passed; it
X * reverses their order (this gets around the problems involved with
X * changing line numbers in an ed script).
X *
X * Note that this routine has the same problem of mapping as the last
X * one did; the variable MAPPING maps from file number according to
X * the argument list to file number according to the diff passed.  All
X * files listed below are in terms of the argument list.
X * REV_MAPPING is the inverse of MAPPING.
X *
X * The arguments FILE0, FILE1 and FILE2 are the strings to print
X * as the names of the three files.  These may be the actual names,
X * or may be the arguments specified with -L.
X *
X * Returns 1 if overlaps were found.
X */
X
Xint
Xoutput_diff3_edscript (outputfile, diff, mapping, rev_mapping,
X		       file0, file1, file2)
X     FILE *outputfile;
X     struct diff3_block *diff;
X     int mapping[3], rev_mapping[3];
X     char *file0, *file1, *file2;
X{
X  int i;
X  int leading_dot;
X  int overlaps_found = 0;
X  struct diff3_block *newblock, *thisblock;
X
X  leading_dot = 0;
X
X  newblock = reverse_diff3_blocklist (diff);
X
X  for (thisblock = newblock; thisblock; thisblock = thisblock->next)
X    {
X      /* Must do mapping correctly.  */
X      enum diff_type type
X	= ((thisblock->correspond == DIFF_ALL) ?
X	   DIFF_ALL :
X	   ((enum diff_type)
X	    (((int) DIFF_1ST)
X	     + rev_mapping[(int) thisblock->correspond - (int) DIFF_1ST])));
X
X      /* If we aren't supposed to do this output block, skip it */
X      if (type == DIFF_2ND || type == DIFF_1ST
X	  || (type == DIFF_3RD && dont_write_simple)
X	  || (type == DIFF_ALL && dont_write_overlap))
X	continue;
X
X      if (flagging && type == DIFF_ALL)
X	/* Do special flagging */
X	{
X
X	  /* Put in lines from FILE2 with bracket */
X	  fprintf (outputfile, "%da\n",
X		   D_HIGHLINE (thisblock, mapping[FILE0]));
X	  fprintf (outputfile, "=======\n");
X	  for (i = 0;
X	       i < D_NUMLINES (thisblock, mapping[FILE2]);
X	       i++)
X	    {
X	      if (D_RELNUM (thisblock, mapping[FILE2], i)[0] == '.')
X		{ leading_dot = 1; fprintf(outputfile, "."); }
X	      fwrite (D_RELNUM (thisblock, mapping[FILE2], i), sizeof (char),
X		      D_RELLEN (thisblock, mapping[FILE2], i), outputfile);
X	    }
X	  fprintf (outputfile, ">>>>>>> %s\n.\n", file2);
X	  overlaps_found = 1;
X
X	  /* Add in code to take care of leading dots, if necessary. */
X	  if (leading_dot)
X	    {
X	      fprintf (outputfile, "%d,%ds/^\\.\\./\\./\n",
X		       D_HIGHLINE (thisblock, mapping[FILE0]) + 1,
X		       (D_HIGHLINE (thisblock, mapping[FILE0])
X			+ D_NUMLINES (thisblock, mapping[FILE2])));
X	      leading_dot = 0;
X	    }
X
X	  /* Put in code to do initial bracket of lines from FILE0  */
X	  fprintf (outputfile, "%da\n<<<<<<< %s\n.\n",
X		   D_LOWLINE (thisblock, mapping[FILE0]) - 1,
X		   file0);
X	}
X      else if (D_NUMLINES (thisblock, mapping[FILE2]) == 0)
X	/* Write out a delete */
X	{
X	  if (D_NUMLINES (thisblock, mapping[FILE0]) == 1)
X	    fprintf (outputfile, "%dd\n",
X		     D_LOWLINE (thisblock, mapping[FILE0]));
X	  else
X	    fprintf (outputfile, "%d,%dd\n",
X		     D_LOWLINE (thisblock, mapping[FILE0]),
X		     D_HIGHLINE (thisblock, mapping[FILE0]));
X	}
X      else
X	/* Write out an add or change */
X	{
X	  switch (D_NUMLINES (thisblock, mapping[FILE0]))
X	    {
X	    case 0:
X	      fprintf (outputfile, "%da\n",
X		       D_HIGHLINE (thisblock, mapping[FILE0]));
X	      break;
X	    case 1:
X	      fprintf (outputfile, "%dc\n",
X		       D_HIGHLINE (thisblock, mapping[FILE0]));
X	      break;
X	    default:
X	      fprintf (outputfile, "%d,%dc\n",
X		       D_LOWLINE (thisblock, mapping[FILE0]),
X		       D_HIGHLINE (thisblock, mapping[FILE0]));
X	      break;
X	    }
X	  for (i = 0;
X	       i < D_NUMLINES (thisblock, mapping[FILE2]);
X	       i++)
X	    {
X	      if (D_RELNUM (thisblock, mapping[FILE2], i)[0] == '.')
X		{ leading_dot = 1; fprintf (outputfile, "."); }
X	      fwrite (D_RELNUM (thisblock, mapping[FILE2], i), sizeof (char),
X		      D_RELLEN (thisblock, mapping[FILE2], i), outputfile);
X	    }
X	  fprintf (outputfile, ".\n");
X	  
X	  /* Add in code to take care of leading dots, if necessary. */
X	  if (leading_dot)
X	    {
X	      fprintf (outputfile, "%d,%ds/^\\.\\./\\./\n",
X		       D_HIGHLINE (thisblock, mapping[FILE0]) + 1,
X		       (D_HIGHLINE (thisblock, mapping[FILE0])
X			+ D_NUMLINES (thisblock, mapping[FILE2])));
X	      leading_dot = 0;
X	    }
X	}
X    }
X  if (finalwrite) fprintf (outputfile, "w\nq\n");
X  return overlaps_found;
X}
X
X/*
X * Read from COMMONFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF
X * as a merged file.  This acts like 'ed file0 <[output_diff3_edscript]',
X * except that it works even for binary data or incomplete lines.
X *
X * As before, MAPPING maps from arg list file number to diff file number,
X * REV_MAPPING is its inverse,
X * and FILE0, FILE1, and FILE2 are the names of the files.
X *
X * Returns 1 if overlaps were found.
X */
X
Xint
Xoutput_diff3_merge (commonfile, outputfile, diff, mapping, rev_mapping,
X		    file0, file1, file2)
X     FILE *commonfile, *outputfile;
X     struct diff3_block *diff;
X     int mapping[3], rev_mapping[3];
X     char *file0, *file1, *file2;
X{
X  int c, i;
X  int overlaps_found = 0;
X  struct diff3_block *b;
X  int linesread = 0;
X
X  for (b = diff; b; b = b->next)
X    {
X      /* Must do mapping correctly */
X      enum diff_type type
X	= ((b->correspond == DIFF_ALL) ?
X	   DIFF_ALL :
X	   ((enum diff_type)
X	    (((int) DIFF_1ST)
X	     + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
X
X      /* If we aren't supposed to do this output block, skip it.  */
X      if (type == DIFF_2ND || type == DIFF_1ST
X	  || (type == DIFF_3RD && dont_write_simple)
X	  || (type == DIFF_ALL && dont_write_overlap))
X	continue;
X
X      /* Copy I lines from common file.  */
X      i = D_LOWLINE (b, FILE0) - linesread - 1;
X      linesread += i;
X      while (0 <= --i)
X	{
X	  while ((c = getc(commonfile)) != '\n')
X	    {
X	      if (c == EOF)
X		fatal ("input file shrank");
X	      putc (c, outputfile);
X	    }
X	  putc (c, outputfile);
X	}
X
X      if (flagging && type == DIFF_ALL)
X	/* Do special flagging.  */
X	{
X	  /* Put in lines from FILE0 with bracket.  */
X	  fprintf (outputfile, "<<<<<<< %s\n", file0);
X	  for (i = 0;
X	       i < D_NUMLINES (b, mapping[FILE0]);
X	       i++)
X	    fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
X		    D_RELLEN (b, mapping[FILE0], i), outputfile);
X	  fprintf (outputfile, "=======\n");
X	  overlaps_found = 1;
X	}
X
X      /* Put in lines from FILE2.  */
X      for (i = 0;
X	   i < D_NUMLINES (b, mapping[FILE2]);
X	   i++)
X	fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
X		D_RELLEN (b, mapping[FILE2], i), outputfile);
X
X      if (flagging && type == DIFF_ALL)
X	fprintf (outputfile, ">>>>>>> %s\n", file2);
X
X      /* Skip I lines in common file.  */
X      i = D_NUMLINES (b, FILE0);
X      linesread += i;
X      while (0 <= --i)
X	while ((c = getc(commonfile)) != '\n')
X	  if (c == EOF)
X	    {
X	      if (i || b->next)
X		fatal ("input file shrank");
X	      return overlaps_found;
X	    }
X    }
X  /* Copy rest of common file.  */
X  while ((c = getc (commonfile)) != EOF)
X    putc (c, outputfile);
X  return overlaps_found;
X}
X
X/*
X * Reverse the order of the list of diff3 blocks.
X */
Xstruct diff3_block *
Xreverse_diff3_blocklist (diff)
X     struct diff3_block *diff;
X{
X  register struct diff3_block *tmp, *next, *prev;
X
X  for (tmp = diff, prev = (struct diff3_block *) 0;
X       tmp; tmp = next)
X    {
X      next = tmp->next;
X      tmp->next = prev;
X      prev = tmp;
X    }
X  
X  return prev;
X}
X
Xint
Xmyread (fd, ptr, size)
X     int fd, size;
X     char *ptr;
X{
X  int result = read (fd, ptr, size);
X  if (result < 0)
X    perror_with_exit ("Read failed");
X  return result;
X}
X
XVOID *
Xxmalloc (size)
X     int size;
X{
X  VOID *result = (VOID *) malloc (size ? size : 1);
X  if (!result)
X    fatal ("Malloc failed");
X  return result;
X}
X
XVOID *
Xxrealloc (ptr, size)
X     VOID *ptr;
X     int size;
X{
X  VOID *result = (VOID *) realloc (ptr, size ? size : 1);
X  if (!result)
X    fatal ("Malloc failed");
X  return result;
X}
X
Xfatal (string)
X     char *string;
X{
X  fprintf (stderr, "%s: %s\n", argv0, string);
X  exit (2);
X}
X
Xperror_with_exit (string)
X     char *string;
X{
X  perror (string);
X  exit (2);
X}
END_OF_FILE
if test 46642 -ne `wc -c <'diff3.c'`; then
    echo shar: \"'diff3.c'\" unpacked with wrong size!
fi
# end of 'diff3.c'
fi
echo shar: End of archive 8 \(of 8\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.