Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Message from discussion v89i217: rcs - revision control system, Part02/14
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Bob Page  
View profile  
 More options Nov 19 1989, 4:24 am
Newsgroups: comp.sources.amiga
From: page%s...@Sun.COM (Bob Page)
Date: 19 Nov 89 09:24:20 GMT
Local: Sun, Nov 19 1989 4:24 am
Subject: v89i217: rcs - revision control system, Part02/14
Submitted-by: r...@cbmvax.commodore.com (Raymond S. Brand)
Posting-number: Volume 89, Issue 217
Archive-name: unix/rcs.02

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#       diff/analyze.c
#       diff/context.c
#       diff/diff.c
#       diff/diff3.c
# This is archive 2 of a 14-part kit.
# This archive created: Sun Nov 19 01:12:04 1989
if `test ! -d diff`
then
  mkdir diff
  echo "mkdir diff"
fi
echo "extracting diff/analyze.c"
sed 's/^X//' << \SHAR_EOF > diff/analyze.c
X/* Analyze file differences for GNU DIFF.
X   Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU DIFF.
X
XGNU DIFF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU DIFF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU DIFF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X/* The basic algorithm is described in:
X   "An O(ND) Difference Algorithm and its Variations", Eugene Myers,
X   Algorithmica Vol. 1 No. 2, 1986, p 251.  */
X
X#include "regex.h"
X#include "diff.h"
X
Xextern int no_discards;
X
Xstatic int *xvec, *yvec;       /* Vectors being compared. */
Xstatic int *fdiag;             /* Vector, indexed by diagonal, containing
X                                  the X coordinate of the point furthest
X                                  along the given diagonal in the forward
X                                  search of the edit matrix. */
Xstatic int *bdiag;             /* Vector, indexed by diagonal, containing
X                                  the X coordinate of the point furthest
X                                  along the given diagonal in the backward
X                                  search of the edit matrix. */
X
X/* Find the midpoint of the shortest edit script for a specified
X   portion of the two files.
X
X   We scan from the beginnings of the files, and simultaneously from the ends,
X   doing a breadth-first search through the space of edit-sequence.
X   When the two searches meet, we have found the midpoint of the shortest
X   edit sequence.
X
X   The value returned is the number of the diagonal on which the midpoint lies.
X   The diagonal number equals the number of inserted lines minus the number
X   of deleted lines (counting only lines before the midpoint).
X   The edit cost is stored into *COST; this is the total number of
X   lines inserted or deleted (counting only lines before the midpoint).
X
X   This function assumes that the first lines of the specified portions
X   of the two files do not match, and likewise that the last lines do not
X   match.  The caller must trim matching lines from the beginning and end
X   of the portions it is going to specify.
X
X   Note that if we return the "wrong" diagonal value, or if
X   the value of bdiag at that diagonal is "wrong",
X   the worst this can do is cause suboptimal diff output.
X   It cannot cause incorrect diff output.  */
X
Xstatic int
Xdiag (xoff, xlim, yoff, ylim, cost)
X     int xoff, xlim, yoff, ylim;
X     int *cost;
X{
X  int *const fd = fdiag;       /* Give the compiler a chance. */
X  int *const bd = bdiag;       /* Additional help for the compiler. */
X  int *const xv = xvec;                /* Still more help for the comiler. */
X  int *const yv = yvec;                /* And more and more . . . */
X  const int dmin = xoff - ylim;        /* Minimum valid diagonal. */
X  const int dmax = xlim - yoff;        /* Maximum valid diagonal. */
X  const int fmid = xoff - yoff;        /* Center diagonal of top-down search. */
X  const int bmid = xlim - ylim;        /* Center diagonal of bottom-up search. */
X  int fmin = fmid, fmax = fmid;        /* Limits of top-down search. */
X  int bmin = bmid, bmax = bmid;        /* Limits of bottom-up search. */
X  int c;                       /* Cost. */
X  int odd = fmid - bmid & 1;       /* True if southeast corner is on an odd
X                                  diagonal with respect to the northwest. */
X
X  fd[fmid] = xoff;
X  bd[bmid] = xlim;
X
X  for (c = 1;; ++c)
X    {
X      int d;                   /* Active diagonal. */
X      int big_snake = 0;
X
X      /* Extend the top-down search by an edit step in each diagonal. */
X      fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin;
X      fmax < dmax ? fd[++fmax + 1] = -1 : --fmax;
X      for (d = fmax; d >= fmin; d -= 2)
X       {
X         int x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1];
X
X         if (tlo >= thi)
X           x = tlo + 1;
X         else
X           x = thi;
X         oldx = x;
X         y = x - d;
X         while (x < xlim && y < ylim && xv[x] == yv[y])
X           ++x, ++y;
X         if (x - oldx > 20)
X           big_snake = 1;
X         fd[d] = x;
X         if (odd && bmin <= d && d <= bmax && bd[d] <= fd[d])
X           {
X             *cost = 2 * c - 1;
X             return d;
X           }
X       }
X
X      /* Similar extend the bottom-up search. */
X      bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin;
X      bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax;
X      for (d = bmax; d >= bmin; d -= 2)
X       {
X         int x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1];
X
X         if (tlo < thi)
X           x = tlo;
X         else
X           x = thi - 1;
X         oldx = x;
X         y = x - d;
X         while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1])
X           --x, --y;
X         if (oldx - x > 20)
X           big_snake = 1;
X         bd[d] = x;
X         if (!odd && fmin <= d && d <= fmax && bd[d] <= fd[d])
X           {
X             *cost = 2 * c;
X             return d;
X           }
X       }
X
X      /* Heuristic: check occasionally for a diagonal that has made
X        lots of progress compared with the edit distance.
X        If we have any such, find the one that has made the most
X        progress and return it as if it had succeeded.
X
X        With this heuristic, for files with a constant small density
X        of changes, the algorithm is linear in the file size.  */
X
X      if (c > 200 && big_snake && heuristic)
X       {
X         int best;
X         int bestpos;
X
X         best = 0;
X         for (d = fmax; d >= fmin; d -= 2)
X           {
X             int dd = d - fmid;
X             if ((fd[d] - xoff)*2 - dd > 12 * (c + (dd > 0 ? dd : -dd)))
X               {
X                 if (fd[d] * 2 - dd > best
X                     && fd[d] - xoff > 20
X                     && fd[d] - d - yoff > 20)
X                   {
X                     int k;
X                     int x = fd[d];
X
X                     /* We have a good enough best diagonal;
X                        now insist that it end with a significant snake.  */
X                     for (k = 1; k <= 20; k++)
X                       if (xvec[x - k] != yvec[x - d - k])
X                         break;
X
X                     if (k == 21)
X                       {
X                         best = fd[d] * 2 - dd;
X                         bestpos = d;
X                       }
X                   }
X               }
X           }
X         if (best > 0)
X           {
X             *cost = 2 * c - 1;
X             return bestpos;
X           }
X
X         best = 0;
X         for (d = bmax; d >= bmin; d -= 2)
X           {
X             int dd = d - bmid;
X             if ((xlim - bd[d])*2 + dd > 12 * (c + (dd > 0 ? dd : -dd)))
X               {
X                 if ((xlim - bd[d]) * 2 + dd > best
X                     && xlim - bd[d] > 20
X                     && ylim - (bd[d] - d) > 20)
X                   {
X                     /* We have a good enough best diagonal;
X                        now insist that it end with a significant snake.  */
X                     int k;
X                     int x = bd[d];
X
X                     for (k = 0; k < 20; k++)
X                       if (xvec[x + k] != yvec[x - d + k])
X                         break;
X                     if (k == 20)
X                       {
X                         best = (xlim - bd[d]) * 2 + dd;
X                         bestpos = d;
X                       }
X                   }
X               }
X           }
X         if (best > 0)
X           {
X             *cost = 2 * c - 1;
X             return bestpos;
X           }
X       }
X    }
X}
X
X/* Compare in detail contiguous subsequences of the two files
X   which are known, as a whole, to match each other.
X
X   The results are recorded in the vectors files[N].changed_flag, by
X   storing a 1 in the element for each line that is an insertion or deletion.
X
X   The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
X
X   Note that XLIM, YLIM are exclusive bounds.
X   All line numbers are origin-0 and discarded lines are not counted.  */
X
Xstatic void
Xcompareseq (xoff, xlim, yoff, ylim)
X     int xoff, xlim, yoff, ylim;
X{
X  /* Slide down the bottom initial diagonal. */
X  while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff])
X    ++xoff, ++yoff;
X  /* Slide up the top initial diagonal. */
X  while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1])
X    --xlim, --ylim;
X  
X  /* Handle simple cases. */
X  if (xoff == xlim)
X    while (yoff < ylim)
X      files[1].changed_flag[files[1].realindexes[yoff++]] = 1;
X  else if (yoff == ylim)
X    while (xoff < xlim)
X      files[0].changed_flag[files[0].realindexes[xoff++]] = 1;
X  else
X    {
X      int c, d, f, b;
X
X      /* Find a point of correspondence in the middle of the files.  */
X
X      d = diag (xoff, xlim, yoff, ylim, &c);
X      f = fdiag[d];
X      b = bdiag[d];
X
X      if (c == 1)
X       {
X         /* This should be impossible, because it implies that
X            one of the two subsequences is empty,
X            and that case was handled above without calling `diag'.
X            Let's verify that this is true.  */
X         abort ();
X#if 0
X         /* The two subsequences differ by a single insert or delete;
X            record it and we are done.  */
X         if (d < xoff - yoff)
X           files[1].changed_flag[files[1].realindexes[b - d - 1]] = 1;
X         else
X           files[0].changed_flag[files[0].realindexes[b]] = 1;
X#endif
X       }
X      else
X       {
X         /* Use that point to split this problem into two subproblems.  */
X         compareseq (xoff, b, yoff, b - d);
X         /* This used to use f instead of b,
X            but that is incorrect!
X            It is not necessarily the case that diagonal d
X            has a snake from b to f.  */
X         compareseq (b, xlim, b - d, ylim);
X       }
X    }
X}
X
X/* Discard lines from one file that have no matches in the other file.
X
X   A line which is discarded will not be considered by the actual
X   comparison algorithm; it will be as if that line were not in the file.
X   The file's `realindexes' table maps virtual line numbers
X   (which don't count the discarded lines) into real line numbers;
X   this is how the actual comparison algorithm produces results
X   that are comprehensible when the discarded lines are counted.
X
X   When we discard a line, we also mark it as a deletion or insertion
X   so that it will be printed in the output.  */
X
Xvoid
Xdiscard_confusing_lines (filevec)
X     struct file_data filevec[];
X{
X  int f, i, j;
X  char *discarded[2];
X  int *equiv_count[2];
X
X  /* Allocate our results.  */
X  for (f = 0; f < 2; f++)
X    {
X      filevec[f].undiscarded
X       = (int *) xmalloc (filevec[f].buffered_lines * sizeof (int));
X      filevec[f].realindexes
X       = (int *) xmalloc (filevec[f].buffered_lines * sizeof (int));
X    }
X
X  /* Set up equiv_count[F][I] as the number of lines in file F
X     that fall in equivalence class I.  */
X
X  equiv_count[0] = (int *) xmalloc (filevec[0].equiv_max * sizeof (int));
X  bzero (equiv_count[0], filevec[0].equiv_max * sizeof (int));
X  equiv_count[1] = (int *) xmalloc (filevec[1].equiv_max * sizeof (int));
X  bzero (equiv_count[1], filevec[1].equiv_max * sizeof (int));
X
X  for (i = 0; i < filevec[0].buffered_lines; ++i)
X    ++equiv_count[0][filevec[0].equivs[i]];
X  for (i = 0; i < filevec[1].buffered_lines; ++i)
X    ++equiv_count[1][filevec[1].equivs[i]];
X
X  /* Set up tables of which lines are going to be discarded.  */
X
X  discarded[0] = (char *) xmalloc (filevec[0].buffered_lines);
X  discarded[1] = (char *) xmalloc (filevec[1].buffered_lines);
X  bzero (discarded[0], filevec[0].buffered_lines);
X  bzero (discarded[1], filevec[1].buffered_lines);
X
X  /* Mark to be discarded each line that matches no line of the other file.
X     If a line matches many lines, mark it as provisionally discardable.  */
X
X  for (f = 0; f < 2; f++)
X    {
X      int end = filevec[f].buffered_lines;
X      char *discards = discarded[f];
X      int *counts = equiv_count[1 - f];
X      int *equivs = filevec[f].equivs;
X      for (i = 0; i < end; i++)
X       {
X         int nmatch = counts[equivs[i]];
X         if (equivs[i] == 0)
X           continue;
X         if (nmatch == 0)
X           discards[i] = 1;
X         else if (nmatch > 5)
X           discards[i] = 2;
X       }
X    }
X
X  /* Don't really discard the provisional lines if there are less than ten
X     discardable lines in a row.  */
X
X  for (f = 0; f < 2; f++)
X    {
X      int end = filevec[f].buffered_lines;
X      char *discards = discarded[f];
X
X      for (i = 0; i < end; i++)
X       if (discards[i])
X         {
X           register int j;
X           int length;
X           int provisional = 0;
X
X           /* Cancel provisional discards at the beginning.  */
X           while (i < end && discards[i] == 2)
X             discards[i] = 0;
X
X           /* Find end of this run of discardable lines.
X              Count how many are provisionally discardable.  */
X           for (j = i; j < end; j++)
X             {
X               if (discards[j] == 0)
X                 break;
X               if (discards[j] == 2)
X                 ++provisional;
X             }
X
X           /* Cancel provisional discards at the end, and shrink the run.  */
X           while (j > i && discards[j - 1] == 2)
X             discards[j - 1] = 0, --provisional;
X
X           /* Now we have the length of a run of discardable lines
X              whose first and last are not provisional.  */
X           length = j - i;
X
X           /* If half the lines in the run are provisional,
X              cancel discarding of all provisional lines in the run.  */
X           if (provisional * 2 > length)
X             {
X               while (j > i)
X                 if (discards[--j] == 2)
X                   discards[j] = 0;
X             }
X           else
X             {
X               /* Cancel provisional discards that are near either end.  */
X               for (j = 0; j < 5 && j < length; j++)
X                 if (discards[i + j] == 2)
X                   discards[i + j] = 0;
X               /* Meanwhile, I advances to the last line of the run.  */
X               i += length - 1;
X               length -= 5;
X               for (j = 0; j < 5 && j < length; j++)
X                 if (discards[i - j] == 2)
X                   discards[i - j] = 0;
X             }
X         }
X    }
X
X  /* Discard from file 0. */
X  for (f = 0; f < 2; f++)
X    {
X      char *discards = discarded[f];
X      int end = filevec[f].buffered_lines;
X      j = 0;
X      for (i = 0; i < end; ++i)
X       if (no_discards || discards[i] == 0)
X         {
X           filevec[f].undiscarded[j] = filevec[f].equivs[i];
X           filevec[f].realindexes[j++] = i;
X         }
X       else
X         filevec[f].changed_flag[i] = 1;
X      filevec[f].nondiscarded_lines = j;
X    }
X
X  free (discarded[1]);
X  free (discarded[0]);
X  free (equiv_count[1]);
X  free (equiv_count[0]);
X}
X
X/* Adjust inserts/deletes of blank lines to join changes
X   as much as possible.
X
X   We do something when a run of changed lines include a blank
X   line at one end and have an excluded blank line at the other.
X   We are free to choose which blank line is included.
X   `compareseq' always chooses the one at the beginning,
X   but usually it is cleaner to consider the following blank line
X   to be the "change".  The only exception is if the preceding blank line
X   would join this change to other changes.  */
X
Xint inhibit;
X
Xstatic void
Xshift_boundaries (filevec)
X     struct file_data filevec[];
X{
X  int f;
X
X  if (inhibit)
X    return;
X
X  for (f = 0; f < 2; f++)
X    {
X      char *changed = filevec[f].changed_flag;
X      char *other_changed = filevec[1-f].changed_flag;
X      int i = 0;
X      int j = 0;
X      int i_end = filevec[f].buffered_lines;
X      int preceeding = -1;
X      int other_preceeding = -1;
X
X      while (1)
X       {
X         int start, end, other_start;
X
X         /* Scan forwards to find beginning of another run of changes.
X            Also keep track of the corresponding point in the other file.  */
X
X         while (i < i_end && changed[i] == 0)
X           {
X             while (other_changed[j++])
X               /* Non-corresponding lines in the other file
X                  will count as the preceeding batch of changes.  */
X               other_preceeding = j;
X             i++;
X           }
X
X         if (i == i_end)
X           break;
X
X         start = i;
X         other_start = j;
X
X         while (1)
X           {
X             /* Now find the end of this run of changes.  */
X
X             while (i < i_end && changed[i] != 0) i++;
X             end = i;
X
X             /* If the first changed line matches the following unchanged one,
X                and this run does not follow right after a previous run,
X                and there are no lines deleted from the other file here,
X                then classify the first changed line as unchanged
X                and the following line as changed in its place.  */
X
X             /* You might ask, how could this run follow right after another?
X                Only because the previous run was shifted here.  */
X
X             if (files[f].equivs[start] == files[f].equivs[end]
X                 && !other_changed[j]
X                 && end != i_end
X                 && !((preceeding >= 0 && start == preceeding)
X                      || (other_preceeding >= 0
X                          && other_start == other_preceeding)))
X               {
X                 changed[end++] = 1;
X                 changed[start++] = 0;
X                 ++i;
X                 /* Since one line-that-matches is now before this run
X                    instead of after, we must advance in the other file
X                    to keep in synch.  */
X                 ++j;
X               }
X             else
X               break;
X           }
X
X         preceeding = i;
X         other_preceeding = j;
X       }
X    }
X}
X
X/* Cons an additional entry onto the front of an edit script OLD.
X   LINE0 and LINE1 are the first affected lines in the two files (origin 0).
X   DELETED is the number of lines deleted here from file 0.
X   INSERTED is the number of lines inserted here in file 1.
X
X   If DELETED is 0 then LINE0 is the number of the line before
X   which the insertion was done; vice versa for INSERTED and LINE1.  */
X
Xstatic struct change *
Xadd_change (line0, line1, deleted, inserted, old)
X     int line0, line1, deleted, inserted;
X     struct change *old;
X{
X  struct change *new = (struct change *) xmalloc (sizeof (struct change));
X
X  new->line0 = line0;
X  new->line1 = line1;
X  new->inserted = inserted;
X  new->deleted = deleted;
X  new->link = old;
X  return new;
X}
X
X/* Scan the tables of which lines are inserted and deleted,
X   producing an edit script in reverse order.  */
X
Xstatic struct change *
Xbuild_reverse_script (filevec)
X     struct file_data filevec[];
X{
X  struct change *script = 0;
X  char *changed0 = filevec[0].changed_flag;
X  char *changed1 = filevec[1].changed_flag;
X  int len0 = filevec[0].buffered_lines;
X  int len1 = filevec[1].buffered_lines;
X
X  /* Note that changedN[len0] does exist, and contains 0.  */
X
X  int i0 = 0, i1 = 0;
X
X  while (i0 < len0 || i1 < len1)
X    {
X      if (changed0[i0] || changed1[i1])
X       {
X         int line0 = i0, line1 = i1;
X
X         /* Find # lines changed here in each file.  */
X         while (changed0[i0]) ++i0;
X         while (changed1[i1]) ++i1;
X
X         /* Record this change.  */
X         script = add_change (line0, line1, i0 - line0, i1 - line1, script);
X       }
X
X      /* We have reached lines in the two files that match each other.  */
X      i0++, i1++;
X    }
X
X  return script;
X}
X
X/* Scan the tables of which lines are inserted and deleted,
X   producing an edit script in forward order.  */
X
Xstatic struct change *
Xbuild_script (filevec)
X     struct file_data filevec[];
X{
X  struct change *script = 0;
X  char *changed0 = filevec[0].changed_flag;
X  char *changed1 = filevec[1].changed_flag;
X  int len0 = filevec[0].buffered_lines;
X  int len1 = filevec[1].buffered_lines;
X
X  /* Note that changedN[-1] does exist, and contains 0.  */
X
X  int i0 = len0, i1 = len1;
X
X  while (i0 >= 0 || i1 >= 0)
X    {
X      if (changed0[i0 - 1] || changed1[i1 - 1])
X       {
X         int line0 = i0, line1 = i1;
X
X         /* Find # lines changed here in each file.  */
X         while (changed0[i0 - 1]) --i0;
X         while (changed1[i1 - 1]) --i1;
X
X         /* Record this change.  */
X         script = add_change (i0, i1, line0 - i0, line1 - i1, script);
X       }
X
X      /* We have reached lines in the two files that match each other.  */
X      i0--, i1--;
X    }
X
X  return script;
X}
X
X/* Report the differences of two files.  DEPTH is the current directory
X   depth. */
Xint
Xdiff_2_files (filevec, depth)
X     struct file_data filevec[];
X     int depth;
X{
X  int diags;
X  int i;
X  struct change *e, *p;
X  struct change *script;
X  int binary;
X
X  /* See if the two named files are actually the same physical file.
X     If so, we know they are identical without actually reading them.  */
X
X#ifndef AMIGA
X  if (filevec[0].stat.st_ino == filevec[1].stat.st_ino
X      && filevec[0].stat.st_dev == filevec[1].stat.st_dev)
X    return 0;
X#endif
X
X  binary = read_files (filevec);
X
X  /* If we have detected that file 0 is a binary file,
X     compare the two files as binary.  This can happen
X     only when the first chunk is read.  */
X
X  if (binary)
X    {
X      int differs = (filevec[0].buffered_chars != filevec[1].buffered_chars
X                    || bcmp (filevec[0].buffer, filevec[1].buffer,
X                             filevec[1].buffered_chars));
X      if (differs)
X       message ("Binary files %s and %s differ\n",
X                filevec[0].name, filevec[1].name);
X
X      for (i = 0; i < 2; ++i)
X       if (filevec[i].buffer)
X         free (filevec[i].buffer);
X      return differs;
X    }
X
X  if (filevec[0].missing_newline != filevec[1].missing_newline)
X    {
X      if (filevec[0].missing_newline)
X       error ("No newline at end of file %s\n", filevec[0].name);
X      if (filevec[1].missing_newline)
X       error ("No newline at end of file %s\n", filevec[1].name);
X    }
X
X  /* Allocate vectors for the results of comparison:
X     a flag for each line of each file, saying whether that line
X     is an insertion or deletion.
X     Allocate an extra element, always zero, at each end of each vector.  */
X
X  filevec[0].changed_flag = (char *) xmalloc (filevec[0].buffered_lines + 2);
X  filevec[1].changed_flag = (char *) xmalloc (filevec[1].buffered_lines + 2);
X  bzero (filevec[0].changed_flag, filevec[0].buffered_lines + 2);
X  bzero (filevec[1].changed_flag, filevec[1].buffered_lines + 2);
X  filevec[0].changed_flag++;
X  filevec[1].changed_flag++;
X
X  /* Some lines are obviously insertions or deletions
X     because they don't match anything.  Detect them now,
X     and avoid even thinking about them in the main comparison algorithm.  */
X
X  discard_confusing_lines (filevec);
X
X  /* Now do the main comparison algorithm, considering just the
X     undiscarded lines.  */
X
X  xvec = filevec[0].undiscarded;
X  yvec = filevec[1].undiscarded;
X  diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3;
X  fdiag = (int *) xmalloc (diags * sizeof (int));
X  fdiag += filevec[1].nondiscarded_lines + 1;
X  bdiag = (int *) xmalloc (diags * sizeof (int));
X  bdiag += filevec[1].nondiscarded_lines + 1;
X
X  files[0] = filevec[0];
X  files[1] = filevec[1];
X
X  compareseq (0, filevec[0].nondiscarded_lines,
X             0, filevec[1].nondiscarded_lines);
X
X  bdiag -= filevec[1].nondiscarded_lines + 1;
X  free (bdiag);
X  fdiag -= filevec[1].nondiscarded_lines + 1;
X  free (fdiag);
X
X  /* Modify the results slightly to make them prettier
X     in cases where that can validly be done.  */
X
X  shift_boundaries (filevec);
X
X  /* Get the results of comparison in the form of a chain
X     of `struct change's -- an edit script.  */
X
X  if (output_style == OUTPUT_ED)
X    script = build_reverse_script (filevec);
X  else
X    script = build_script (filevec);
X
X  if (script)
X    {
X      setup_output (files[0].name, files[1].name, depth);
X      if (output_style == OUTPUT_CONTEXT)
X       print_context_header (files);
X
X      switch (output_style)
X       {
X       case OUTPUT_CONTEXT:
X         print_context_script (script);
X         break;
X
X       case OUTPUT_ED:
X         print_ed_script (script);
X         break;
X
X       case OUTPUT_FORWARD_ED:
X         pr_forward_ed_script (script);
X         break;
X
X       case OUTPUT_RCS:
X         print_rcs_script (script);
X         break;
X
X       case OUTPUT_NORMAL:
X         print_normal_script (script);
X         break;
X       }
X
X      finish_output ();
X    }
X
X  for (i = 1; i >= 0; --i)
X    {
X      free (filevec[i].realindexes);
X      free (filevec[i].undiscarded);
X    }
X
X  for (i = 1; i >= 0; --i)
X    free (--filevec[i].changed_flag);
X
X  for (i = 1; i >= 0; --i)
X    free (filevec[i].equivs);
X
X  for (i = 0; i < 2; ++i)
X    {
X      if (filevec[i].buffer != 0)
X       free (filevec[i].buffer);
X      free (filevec[i].linbuf);
X    }
X
X  for (e = script; e; e = p)
X    {
X      p = e->link;
X      free (e);
X    }
X
X  return script ? 1 : 0;
X}
SHAR_EOF
echo "extracting diff/context.c"
sed 's/^X//' << \SHAR_EOF > diff/context.c
X/* Context-format output routines for GNU DIFF.
X   Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU DIFF.
X
XGNU DIFF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU DIFF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU DIFF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X#include "diff.h"
X
Xstatic void pr_context_hunk ();
Xstatic struct change *find_hunk ();
Xstatic void mark_ignorable ();
Xstatic int find_function ();
X
X/* Last place find_function started searching from.  */
Xstatic int find_function_last_search;
X
X/* The value find_function returned when it started searching there.  */
Xstatic int find_function_last_match;
X
X/* Print a header for a context diff, with the file names and dates.  */
X
Xvoid
Xprint_context_header (inf)
X     struct file_data *inf;
X{
X  fprintf (outfile, "*** %s\t%s", inf[0].name,
X          ctime (&inf[0].stat.st_mtime));
X  fprintf (outfile, "--- %s\t%s", inf[1].name,
X          ctime (&inf[1].stat.st_mtime));
X}
X
X/* Print an edit script in context format.  */
X
Xvoid
Xprint_context_script (script)
X     struct change *script;
X{
X  if (ignore_blank_lines_flag || ignore_regexp)
X    mark_ignorable (script);
X  else
X    {
X      struct change *e;
X      for (e = script; e; e = e->link)
X       e->ignore = 0;
X    }
X
X  find_function_last_search = 0;
X  find_function_last_match = -1;
X
X  print_script (script, find_hunk, pr_context_hunk);
X}
X
X/* Print a pair of line numbers with a comma, translated for file FILE.
X   If the second number is smaller, use the first in place of it.
X
X   Args A and B are internal line numbers.
X   We print the translated (real) line numbers.  */
X
Xstatic void
Xprint_context_number_range (file, a, b)
X     struct file_data *file;
X     int a, b;
X{
X  int trans_a, trans_b;
X  translate_range (file, a, b, &trans_a, &trans_b);
X
X  /* Note: we can have B < A in the case of a range of no lines.
X     In this case, we should print the line number before the range,
X     which is B.  */
X  if (trans_b >= trans_a)
X    fprintf (outfile, "%d,%d", trans_a, trans_b);
X  else
X    fprintf (outfile, "%d", trans_b);
X}
X
X/* Print a portion of an edit script in context format.
X   HUNK is the beginning of the portion to be printed.
X   The end is marked by a `link' that has been nulled out.
X
X   Prints out lines from both files, and precedes each
X   line with the appropriate flag-character.  */
X
Xstatic void
Xpr_context_hunk (hunk)
X     struct change *hunk;
X{
X  int first0, last0, first1, last1, show_from, show_to, i;
X  struct change *next;
X  char *prefix;
X  char *function;
X  int function_length;
X
X  /* Determine range of line numbers involved in each file.  */
X
X  analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
X
X  if (!show_from && !show_to)
X    return;
X
X  /* Include a context's width before and after.  */
X
X  first0 = max (first0 - context, 0);
X  first1 = max (first1 - context, 0);
X  last0 = min (last0 + context, files[0].buffered_lines - 1);
X  last1 = min (last1 + context, files[1].buffered_lines - 1);
X
X  /* If desired, find the preceding function definition line in file 0.  */
X  function = 0;
X  if (function_regexp)
X    find_function (&files[0], first0, &function, &function_length);
X
X  /* If we looked for and found a function this is part of,
X     include its name in the header of the diff section.  */
X  fprintf (outfile, "***************");
X
X  if (function)
X    {
X      fprintf (outfile, " ");
X      fwrite (function, 1, min (function_length - 1, 40), outfile);
X    }
X
X  fprintf (outfile, "\n*** ");
X  print_context_number_range (&files[0], first0, last0);
X  fprintf (outfile, " ****\n");
X
X  if (show_from)
X    {
X      next = hunk;
X
X      for (i = first0; i <= last0; i++)
X       {
X         /* Skip past changes that apply (in file 0)
X            only to lines before line I.  */
X
X         while (next && next->line0 + next->deleted <= i)
X           next = next->link;
X
X         /* Compute the marking for line I.  */
X
X         prefix = " ";
X         if (next && next->line0 <= i)
X           /* The change NEXT covers this line.
X              If lines were inserted here in file 1, this is "changed".
X              Otherwise it is "deleted".  */
X           prefix = (next->inserted > 0 ? "!" : "-");
X
X         print_1_line (prefix, &files[0].linbuf[i]);
X       }
X    }
X
X  fprintf (outfile, "--- ");
X  print_context_number_range (&files[1], first1, last1);
X  fprintf (outfile, " ----\n");
X
X  if (show_to)
X    {
X      next = hunk;
X
X      for (i = first1; i <= last1; i++)
X       {
X         /* Skip past changes that apply (in file 1)
X            only to lines before line I.  */
X
X         while (next && next->line1 + next->inserted <= i)
X           next = next->link;
X
X         /* Compute the marking for line I.  */
X
X         prefix = " ";
X         if (next && next->line1 <= i)
X           /* The change NEXT covers this line.
X              If lines were deleted here in file 0, this is "changed".
X              Otherwise it is "inserted".  */
X           prefix = (next->deleted > 0 ? "!" : "+");
X
X         print_1_line (prefix, &files[1].linbuf[i]);
X       }
X    }
X}
X
X/* Scan a (forward-ordered) edit script for the first place that at least
X   2*CONTEXT unchanged lines appear, and return a pointer
X   to the `struct change' for the last change before those lines.  */
X
Xstatic struct change *
Xfind_hunk (start)
X     struct change *start;
X{
X  struct change *prev;
X  int top0, top1;
X  int thresh;
X
X  do
X    {
X      /* Computer number of first line in each file beyond this changed.  */
X      top0 = start->line0 + start->deleted;
X      top1 = start->line1 + start->inserted;
X      prev = start;
X      start = start->link;
X      /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
X        but only CONTEXT if one is ignorable.  */
X      thresh = ((prev->ignore || (start && start->ignore))
X               ? context
X               : 2 * context);
X      /* It is not supposed to matter which file we check in the end-test.
X        If it would matter, crash.  */
X      if (start && start->line0 - top0 != start->line1 - top1)
X       abort ();
X    } while (start
X            /* Keep going if less than THRESH lines
X               elapse before the affected line.  */
X            && start->line0 < top0 + thresh);
X
X  return prev;
X}
X
X/* Set the `ignore' flag properly in each change in SCRIPT.
X   It should be 1 if all the lines inserted or deleted in that change
X   are ignorable lines.  */
X
Xstatic void
Xmark_ignorable (script)
X     struct change *script;
X{
X  while (script)
X    {
X      struct change *next = script->link;
X      int first0, last0, first1, last1, deletes, inserts;
X
X      /* Turn this change into a hunk: detach it from the others.  */
X      script->link = 0;
X
X      /* Determine whether this change is ignorable.  */
X      analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
X      /* Reconnect the chain as before.  */
X      script->link = next;
X
X      /* If the change is ignorable, mark it.  */
X      script->ignore = (!deletes && !inserts);
X
X      /* Advance to the following change.  */
X      script = next;
X    }
X}
X
X/* Find the last function-header line in FILE prior to line number LINENUM.
X   This is a line containing a match for the regexp in `function_regexp'.
X   Store the address of the line text into LINEP and the length of the
X   line into LENP.
X   Do not store anything if no function-header is found.  */
X
Xstatic int
Xfind_function (file, linenum, linep, lenp)
X     struct file_data *file;
X     int linenum;
X     char **linep;
X     int *lenp;
X{
X  int i = linenum;
X  int last = find_function_last_search;
X  find_function_last_search = i;
X
X  while (--i >= last)
X    {
X      /* See if this line is what we want.  */
X
X      if (0 <= re_search (&function_regexp_compiled,
X                         files[0].linbuf[i].text,
X                         files[0].linbuf[i].length,
X                         0, files[0].linbuf[i].length,
X                         0))
X       {
X         *linep = files[0].linbuf[i].text;
X         *lenp = files[0].linbuf[i].length;
X         find_function_last_match = i;
X         return 1;
X       }
X    }
X  /* If we search back to where we started searching the previous time,
X     find the line we found last time.  */
X  if (find_function_last_match >= 0)
X    {
X      i = find_function_last_match;
X      *linep = files[0].linbuf[i].text;
X      *lenp = files[0].linbuf[i].length;
X      return 1;
X    }
X  return 0;
X}
SHAR_EOF
echo "extracting diff/diff.c"
sed 's/^X//' << \SHAR_EOF > diff/diff.c
X/* GNU DIFF main routine.
X   Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU DIFF.
X
XGNU DIFF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU DIFF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU DIFF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X/* GNU DIFF was written by Mike Haertel, David Hayes,
X   Richard Stallman and Len Tower.  */
X
X#define GDIFF_MAIN
X#include "regex.h"
X#include "diff.h"
X
X
X/* Nonzero for -r: if comparing two directories,
X   compare their common subdirectories recursively.  */
X
Xint recursive;
X
X/* For debugging: don't do discard_confusing_lines.  */
X
Xint no_discards;
X
Xvoid specify_style();
X
X/* Return a string containing the command options with which diff was invoked.
X   Spaces appear between what were separate ARGV-elements.
X   There is a space at the beginning but none at the end.
X   If there were no options, the result is an empty string.
X
X   Arguments: VECTOR, a vector containing separate ARGV-elements, and COUNT,
X   the length of that vector.  */
X
Xstatic char *
Xoption_list (vector, count)
X     char **vector;
X     int count;
X{
X  int i;
X  int length = 0;
X  char *result;
X
X  for (i = 0; i < count; i++)
X    length += strlen (vector[i]) + 1;
X
X  result = (char *) xmalloc (length + 1);
X  result[0] = 0;
X
X  for (i = 0; i < count; i++)
X    {
X      strcat (result, " ");
X      strcat (result, vector[i]);
X    }
X
X  return result;
X}
X
Xmain (argc, argv)
X     int argc;
X     char *argv[];
X{
X  int val;
X  int c;
X  int prev = -1;
X
X  extern int optind;
X  extern char *optarg;
X
X  program = argv[0];
X
X  /* Do our initializations. */
X  output_style = OUTPUT_NORMAL;
X  always_text_flag = FALSE;
X  ignore_space_change_flag = FALSE;
X  ignore_all_space_flag = FALSE;
X  length_varies = FALSE;
X  ignore_case_flag = FALSE;
X  ignore_blank_lines_flag = FALSE;
X  ignore_regexp = 0;
X  function_regexp = 0;
X  print_file_same_flag = FALSE;
X  entire_new_file_flag = FALSE;
X  context = -1;
X  line_end_char = '\n';
X  tab_align_flag = FALSE;
X  tab_expand_flag = FALSE;
X  recursive = FALSE;
X  paginate_flag = FALSE;
X  heuristic = FALSE;
X  dir_start_file = NULL;
X  msg_chain = NULL;
X  msg_chain_end = NULL;
X  no_discards = 0;
X
X  /* Decode the options.  */
X
X  while ((c = getopt (argc, argv, "0123456789abBcC:defF:hHiI:lnNprsS:tTw"))
X        != EOF)
X    {
X      switch (c)
X       {
X         /* All digits combine in decimal to specify the context-size.  */
X       case '1':
X       case '2':
X       case '3':
X       case '4':
X       case '5':
X       case '6':
X       case '7':
X       case '8':
X       case '9':
X       case '0':
X         if (context == -1)
X           context = 0;
X         /* If a context length has already been specified,
X            more digits allowed only if they follow right after the others.
X            Reject two separate runs of digits, or digits after -C.  */
X         else if (prev < '0' || prev > '9')
X           fatal ("context length specified twice");
X
X         context = context * 10 + c - '0';
X         break;
X
X       case 'a':
X         /* Treat all files as text files; never treat as binary.  */
X         always_text_flag = 1;
X         break;
X
X       case 'b':
X         /* Ignore changes in amount of whitespace.  */
X         ignore_space_change_flag = 1;
X         length_varies = 1;
X         break;
X
X       case 'B':
X         /* Ignore changes affecting only blank lines.  */
X         ignore_blank_lines_flag = 1;
X         break;
X
X       case 'c':
X         /* Make context-style output.  */
X         specify_style (OUTPUT_CONTEXT);
X         break;
X
X       case 'C':
X         if (context >= 0)
X           fatal ("context length specified twice");
X         {
X           char *p;
X           for (p = optarg; *p; p++)
X             if (*p < '0' || *p > '9')
X               fatal ("invalid context length argument (-C option)");
X         }
X         context = atoi (optarg);
X         break;
X
X       case 'd':
X         /* Don't discard lines.  This makes things slower (sometimes much
X            slower) but will find a guaranteed minimal set of changes.  */
X         no_discards = 1;
X         break;
X
X       case 'e':
X         /* Make output that is a valid `ed' script.  */
X         specify_style (OUTPUT_ED);
X         break;
X
X       case 'f':
X         /* Make output that looks vaguely like an `ed' script
X            but has changes in the order they appear in the file.  */
X         specify_style (OUTPUT_FORWARD_ED);
X         break;
X
X       case 'F':
X         /* Show, for each set of changes, the previous line that
X            matches the specified regexp.  Currently affects only
X            context-style output.  */
X         function_regexp = optarg;
X         break;
X
X       case 'h':
X         /* Split the files into chunks of around 1500 lines
X            for faster processing.  Usually does not change the result.
X
X            This currently has no effect.  */
X         break;
X
X       case 'H':
X         /* Turn on heuristics that speed processing of large files
X            with a small density of changes.  */
X         heuristic = 1;
X         break;
X
X       case 'i':
X         /* Ignore changes in case.  */
X         ignore_case_flag = 1;
X         break;
X
X       case 'I':
X         /* Ignore changes affecting only lines that match the
X            specified regexp.  */
X         ignore_regexp = optarg;
X         break;
X
X       case 'l':
X         /* Pass the output through `pr' to paginate it.  */
X         paginate_flag = 1;
X         break;
X
X       case 'n':
X         /* Output RCS-style diffs, like `-f' except that each command
X            specifies the number of lines affected.  */
X         specify_style (OUTPUT_RCS);
X         break;
X
X       case 'N':
X         /* When comparing directories, if a file appears only in one
X            directory, treat it as present but empty in the other.  */
X         entire_new_file_flag = 1;
X         break;
X
X       case 'p':
X         /* Make context-style output and show name of last C function.  */
X         specify_style (OUTPUT_CONTEXT);
X         function_regexp = "^[_a-zA-Z]";
X         break;
X
X       case 'r':
X         /* When comparing directories,
X            recursively compare any subdirectories found.  */
X         recursive = 1;
X         break;
X
X       case 's':
X         /* Print a message if the files are the same.  */
X         print_file_same_flag = 1;
X         break;
X
X       case 'S':
X         /* When comparing directories, start with the specified
X            file name.  This is used for resuming an aborted comparison.  */
X         dir_start_file = optarg;
X         break;
X
X       case 't':
X         /* Expand tabs to spaces in the output so that it preserves
X            the alignment of the input files.  */
X         tab_expand_flag = 1;
X         break;
X
X       case 'T':
X         /* Use a tab in the output, rather than a space, before the
X            text of an input line, so as to keep the proper alignment
X            in the input line without changing the characters in it.  */
X         tab_align_flag = 1;
X         break;
X
X       case 'w':
X         /* Ignore horizontal whitespace when comparing lines.  */
X         ignore_all_space_flag = 1;
X         length_varies = 1;
X         break;
X       }
X      prev = c;
X    }
X
X  if (optind != argc - 2)
X    fatal ("requires two file names.  Usage: diff [-options] file1 file2");
X
X  /*
X   * @@ need more complicated usage string for directory options??
X   * Note three liner at top of BSD documentation, and John Gilmore
X   * message in his public domain tar being used by GNU.  
X   */
X
X  if (ignore_regexp)
X    {
X      char *val;
X      bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled);
X      val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp),
X                               &ignore_regexp_compiled);
X      if (val != 0)
X       error ("-I option: ", val);
X      ignore_regexp_compiled.fastmap = (char *) xmalloc (256);
X    }
X
X  if (function_regexp)
X    {
X      char *val;
X      bzero (&function_regexp_compiled, sizeof function_regexp_compiled);
X      val = re_compile_pattern (function_regexp, strlen (function_regexp),
X                               &function_regexp_compiled);
X      if (val != 0)
X       error ("-F option: ", val);
X      function_regexp_compiled.fastmap = (char *) xmalloc (256);
X    }
X
X  if (output_style != OUTPUT_CONTEXT)
X    context = 0;
X  else if (context == -1)
X    /* Default amount of context for -c.  */
X    context = 3;
X
X  switch_string = option_list (argv + 1, optind - 1);
X
X  val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
X
X  /* Print any messages that were saved up for last.  */
X  print_message_queue ();
X
X  exit (val);
X}
X
Xvoid specify_style (style)
X     enum output_style style;
X{
X  if (output_style != OUTPUT_NORMAL
X      && output_style != style)
X    error ("conflicting specifications of output style");
X  output_style = style;
X}
X
X/* Compare two files (or dirs) with specified names
X   DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
X   (if DIR0 is 0, then the name is just NAME0, etc.)
X   This is self-contained; it opens the files and closes them.
X
X   Value is 0 if files are identical, 1 if different,
X   2 if there is a problem opening them.  */
X
Xint
Xcompare_files (dir0, name0, dir1, name1, depth)
X     char *dir0, *dir1;
X     char *name0, *name1;
X     int depth;
X{
X  struct file_data inf[2];
X  register int i;
X  int val;
X  int errorcount = 0;
X  int stat_result[2];
X
X  /* If this is directory comparison, perhaps we have a file
X     that exists only in one of the directories.
X     If so, just print a message to that effect.  */
X
X  if (! entire_new_file_flag && (name0 == 0 || name1 == 0))
X    {
X      char *name = name0 == 0 ? name1 : name0;
X      char *dir = name0 == 0 ? dir1 : dir0;
X      message ("Only in %s: %s\n", dir, name);
X      /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
X      return 1;
X    }
X
X  /* Mark any nonexistent file with -1 in the desc field.  */
X
X  inf[0].desc = name0 == 0 ? -1 : 0;
X  inf[1].desc = name1 == 0 ? -1 : 0;
X
X  /* Now record the full name of each file, including nonexistent ones.  */
X
X  if (name0 == 0)
X    name0 = name1;
X  if (name1 == 0)
X    name1 = name0;
X
X  inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
X  inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
X
X  /* Stat the files.  Record whether they are directories.
X     Record in stat_result whether stat fails.  */
X
X  for (i = 0; i <= 1; i++)
X    {
X      inf[i].stat.st_size = 0;
X      inf[i].stat.st_mtime = 0;
X      inf[i].dir_p = 0;
X      stat_result[i] = 0;
X
X      if (inf[i].desc != -1
X         && strcmp (inf[i].name, "-"))
X       {
X         char *filename = inf[i].name;
X
X         stat_result[i] = stat (filename, &inf[i].stat);
X         if (stat_result[i] < 0)
X           {
X             perror_with_name (filename);
X             errorcount = 1;
X           }
X         else
X#ifdef AMIGA
X           inf[i].dir_p = (inf[i].stat.st_type > 0);
X#else
X           inf[i].dir_p = (S_IFDIR == (inf[i].stat.st_mode & S_IFMT));
X#endif
X       }
X    }
X
X  /* See if the two named files are actually the same physical file.
X     If so, we know they are identical without actually reading them.  */
X
X#ifndef AMIGA
X  if (inf[0].stat.st_ino == inf[1].stat.st_ino
X      && inf[0].stat.st_dev == inf[1].stat.st_dev
X      && stat_result[0] == 0
X      && stat_result[1] == 0)
X    {
X      val = 0;
X      goto done;
X    }
X#endif
X
X  if (name0 == 0)
X    inf[0].dir_p = inf[1].dir_p;
X  if (name1 == 0)
X    inf[1].dir_p = inf[0].dir_p;
X
X  /* Open the files and record their descriptors.  */
X
X  for (i = 0; i <= 1; i++)
X    {
X      if (inf[i].desc == -1)
X       ;
X      else if (!strcmp (inf[i].name, "-"))
X       {
X         inf[i].desc = fileno(stdin);
X         inf[i].name = "Standard Input";
X#ifdef AMIGA
X         inf[i].stat.st_type = -1;
X#endif
X       }
X      /* Don't bother opening if stat already failed.  */
X      else if (stat_result[i] == 0 && ! inf[i].dir_p)
X       {
X         char *filename = inf[i].name;
X
X         inf[i].desc = open (filename, O_RDONLY, 0);
X         if (0 > inf[i].desc)
X           {
X             perror_with_name (filename);
X             errorcount = 1;
X           }
X       }
X    }
X
X  if (errorcount)
X    {
X
X      /* If either file should exist but fails to be opened, return 2.  */
X
X      val = 2;
X
X    }
X  else if (inf[0].dir_p && inf[1].dir_p)
X    {
X
X      /* If both are directories, compare the files in them.  */
X
X      if (depth > 0 && !recursive)
X       {
X         /* But don't compare dir contents one level down
X            unless -r was specified.  */
X         message ("Common subdirectories: %s and %s\n",
X                  inf[0].name, inf[1].name);
X         val = 0;
X       }
X      else
X       {
X         val = diff_dirs (inf[0].name, inf[1].name,
X                          compare_files, depth, 0, 0);
X       }
X
X    }
X  else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p))
X    {
X
X      /* If only one is a directory, and it was specified in the command line,
X        use the file in that dir whose basename matches the other file.  */
X
X      int dir_arg = (inf[0].dir_p ? 0 : 1);
X      int fnm_arg = (inf[0].dir_p ? 1 : 0);
X      char *p = strrchr(inf[fnm_arg].name, '/');
X      char *filename = concat (inf[dir_arg].name,  "/",
X                              (p ? p+1 : inf[fnm_arg].name));
X
X#ifdef AMIGA
X      inf[dir_arg].dir_p = (getfa(filename) == 1);
X      if (!inf[dir_arg].dir_p)
X        {
X        if (stat (filename,&inf[dir_arg].stat) < 0)
X         pfatal_with_name (filename);
X        inf[dir_arg].desc = open (filename, O_RDONLY, 0);
X        if (0 > inf[dir_arg].desc)
X          {
X            perror_with_name (filename);
X            val = 2;
X          }
X/*     free (inf[dir_arg].name); */
X       inf[dir_arg].name = filename;
X       val = diff_2_files (inf, depth);
X        }
X      else
X       {
X         error ("%s is a directory but %s is not",
X          inf[dir_arg].name, inf[fnm_arg].name);
X         val = 2;
X       }
X#else
X      inf[dir_arg].desc = open (filename, O_RDONLY, 0);
X
X      if (0 > inf[dir_arg].desc)
X       {
X         perror_with_name (filename);
X         val = 2;
X       }
X      else
X       {
X         /* JF: patch from the net to check and make sure we can really free
X            this.  If it's from argv[], freeing it is a *really* bad idea */
X         if (0 != (dir_arg ? dir1 : dir0))
X           free (inf[dir_arg].name);
X         inf[dir_arg].name = filename;
X         if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0)
X           pfatal_with_name (inf[dir_arg].name);
X
X         inf[dir_arg].dir_p
X           = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFMT));
X         if (inf[dir_arg].dir_p)
X           {
X             error ("%s is a directory but %s is not",
X                    inf[dir_arg].name, inf[fnm_arg].name);
X             val = 1;
X           }
X         else
X           val = diff_2_files (inf, depth);
X       }
X#endif
X    }
X  else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p))
X    {
X      /* Perhaps we have a subdirectory that exists only in one directory.
X        If so, just print a message to that effect.  */
X
X      if (inf[0].desc == -1 || inf[1].desc == -1)
X       {
X         if (entire_new_file_flag && recursive)
X           val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth,
X                            inf[0].desc == -1, inf[1].desc == -1);
X         else
X           {
X             char *dir = (inf[0].desc == -1) ? dir1 : dir0;
X             message ("Only in %s: %s\n", dir, name0);
X             val = 1;
X           }
X       }
X      else
X       {
X         /* We have a subdirectory in one directory
X            and a file in the other.  */
X
X         if (inf[0].dir_p)
X           message ("%s is a directory but %s is not\n",
X                    inf[0].name, inf[1].name);
X         else
X           message ("%s is a directory but %s is not\n",
X                    inf[1].name, inf[0].name);
X         /* This is a difference.  */
X         val = 1;
X       }
X    }
X  else
X    {
X
X      /* Both exist and both are ordinary files.  */
X
X      val = diff_2_files (inf, depth);
X
X    }
X
X  /* Now the comparison has been done, if no error prevented it,
X     and VAL is the value this function will return.  */
X
X  if (inf[0].desc > 0)
X    close (inf[0].desc);
X  if (inf[1].desc > 0)
X    close (inf[1].desc);
X
X done:
X  if (val == 0 && print_file_same_flag && !inf[0].dir_p)
X    message ("Files %s and %s are identical\n",
X            inf[0].name, inf[1].name);
X
X  fflush (stdout);
X
X  if (dir0 != 0)
X    free (inf[0].name);
X  if (dir1 != 0)
X    free (inf[1].name);
X
X  return val;
X}
SHAR_EOF
echo "extracting diff/diff3.c"
sed 's/^X//' << \SHAR_EOF > diff/diff3.c
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/*
X * Include files.
X */
X#include <stdio.h>
X#include <ctype.h>
X
X#if defined(USG) || defined(AMIGA)
X/* Define needed BSD functions in terms of sysV library.  */
X
X#define bcopy(s,d,n)   memcpy((d),(s),(n))
X#define bcmp(s1,s2,n)  memcmp((s1),(s2),(n))
X#define bzero(s,n)     memset((s),0,(n))
X#endif
X
X#ifdef USG
X#define dup2(f,t)      (close(t),fcntl((f),F_DUPFD,(t)))
X
X#define vfork  fork
X#define index  strchr
X#define rindex strrchr
X#endif
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];             /* The lengths of the lines (since nulls) */
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];             /* The lengths of the lines (since nulls) */
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 absolute line number
X * (i.e. line number within the original file).  Note that these are
X * lvalues and can be used for assignment.
X */
X#define        D_LINENUM(diff, filenum, linenum)       \
X  (*((diff)->lines[filenum] + linenum - D_LOWLINE((diff), (filenum))))
X#define        D_LINELEN(diff, filenum, linenum)       \
X  (*((diff)->lengths[filenum] + linenum - D_LOWLINE((diff), (filenum))))
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 * 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#define DIFF_PROGRAM "rcs:diff"
X
Xint edscript;
Xint flagging;
Xint dont_write_overlap;
Xint dont_write_simple;
Xint finalwrite;
Xint overlaps;
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 ();
Xvoid output_diff3_edscript ();
Xvoid usage ();
Xvoid fatal ();
Xvoid perror_with_exit ();
X
Xstruct diff3_block *using_to_diff3_block ();
Xint copy_stringlist ();
Xstruct diff3_block *create_diff3_block ();
Xint compare_line_list ();
X
Xint 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
X/*
X * No options take arguments.  "i" is my own addition; it stands for
X * "include write command", to emulate system V behavior.
X */
X#define        ARGSTRING       "eix3EX"
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;
X  int mapping [3];
X  int shiftmap;
X  int incompat;
X  struct diff_block *thread1, *thread2;
X  struct diff3_block *diff;
X
X  edscript = flagging = dont_write_overlap
X    = dont_write_simple = finalwrite = 0;
X  incompat = shiftmap = 0;
X
X  argv0 = argv[0];
X  
X  while ((c = getopt (argc, argv, ARGSTRING)) != EOF)
X    {
X      edscript = 1;
X      switch (c)
X       {
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         incompat++;
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 '?':
X       default:
X         usage ();
X         /* NOTREACHED */
X       }
X    }
X
X  if (incompat > 1)         /* Make sure you only have one of a */
X                               /* set of arguments */
X    usage ();
X  
X  if (argc - optind != 3)
X    usage ();
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      mapping [0] = 1;
X      mapping [1] = 2;
X      mapping [2] = 0;
X      shiftmap = 1;
X    }
X  else
X    {
X      /* Normal, what you'd expect */
X      mapping [0] = 0;
X      mapping [1] = 1;
X      mapping [2] = 2;
X      shiftmap = 0;
X    }
X
X  if (shiftmap)
X    {
X      thread1 = process_diff (argv[optind + 2], argv[optind]);
X      thread2 = process_diff (argv[optind + 2], argv[optind + 1]);
X    }
X  else
X    {
X      thread1 = process_diff (argv[optind], argv[optind + 1]);
X      thread2 = process_diff (argv[optind], argv[optind + 2]);
X    }
X  diff = make_3way_diff (thread1, thread2);
X  if (edscript)
X    output_diff3_edscript (stdout, diff, mapping, argv[optind],
X                          argv[optind + 1], argv[optind + 2]);
X  else
X    output_diff3 (stdout, diff, mapping);
X  exit (overlaps);
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 ] file1 file2 file3\n",
X          argv0);
X  fprintf (stderr, "\tOnly one of [exEX3] allowed\n");
X  exit (1);
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    base_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)
X          > D_LOWLINE (current[1], FILE0));
X      high_water_thread = base_water_thread;
X      
X      high_water_diff = current[high_water_thread];
X      
X      /* low and high waters start off same diff */
X      base_water_mark = D_LOWLINE (high_water_diff, FILE0);
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       {
X         if (*fl != *tl || bcmp (*f, *t, *fl))
X            return 0;
X        }
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
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;          /* I hope this is here */
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  int diff_size;
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_size = 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_contents < diff_size)
X    {
X      bptr = ALLOCATE (1, struct diff_block);
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) fatal ("Bad format in diff output");
X      if (*scan_diff != '\n') fatal ("Bad format in diff output");
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_contents + diff_size,
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_contents + diff_size,
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  if (scan_diff - diff_contents != diff_size)
X    fatal ("bad diff format; incomplete last line");
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
Xint
Xread_diff (filea, fileb, output_placement)
X     char *filea, *fileb;
X     char **output_placement;
X{
X  char *cmd;
X  void *malloc();
X  int pipefd;
X  FILE *pipefp, *popenl();
X  char *diff_result;
X  long current_chunk_size;
X  int bytes;
X  int total;
X
X  cmd = (char *)malloc(strlen(diff_program) + strlen(filea) + strlen(fileb) + 2);
X  if (cmd == NULL) {
X       printf("Couldn't obtain memory for diff command\n");
X       exit(1);
X       }
X
X  pipefp = popenl(diff_program, filea, fileb, NULL, "r");
X  if (pipefp == NULL) {
X       printf("Couldn't open pipe to diff program\n");
X       exit(1);
X       }
X  pipefd = fileno(pipefp);
X  current_chunk_size = DIFF_CHUNK_SIZE;
X  diff_result = (char *) xmalloc (current_chunk_size);
X  total = 0;
X  do {
X    bytes = myread (pipefd,
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  *output_placement = diff_result;
X  pclose(pipefp);
X  return 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 = scan_ptr + 2;
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;
X  while (*line_ptr != '\n') line_ptr++;
X  
X  if (line_ptr >= limit)
X    fatal ("Bad diff format; overflow in parse");
X
X  /* Don't include newline, but do return the beginning of the
X     next line */
X  *set_length = line_ptr - *set_start;
X
X  return line_ptr + 1;
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 */
Xvoid
Xoutput_diff3 (outputfile, diff, mapping)
X     FILE *outputfile;
X     struct diff3_block *diff;
X     int mapping[3];
X{
X  int rev_mapping[3];
X  static int eliminate[3] = { 1, 0, 0};
X  int i;
X  int oddoneout;
X  char *cp;
X  struct diff3_block *ptr;
X  int line;
X  int dontprint;
X  static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
X
X  for (i = 0; i < 3; i++)
X    rev_mapping [mapping[i]] = i;
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 = eliminate [oddoneout];
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             for (cp = D_RELNUM (ptr, realfile, line);
X                  cp < (D_RELNUM (ptr, realfile, line)
X                        + D_RELLEN (ptr, realfile, line));
X                  cp++)
X               putc (*cp, outputfile);
X             putc ('\n', outputfile);
X           }
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 *
X * Also, occasionally this routine needs the real names of the files
X * on which it works.  Thus file0, file1, and file2 are the filenames
X * passed on the command line.
X *
X * See options.h for documentation on the global variables which this
X * routine pays attention to.
X */
Xvoid
Xoutput_diff3_edscript (outputfile, diff, mapping, file0, file1, file2)
X     FILE *outputfile;
X     struct diff3_block *diff;
X     int mapping[3];
X     char *file0, *file1, *file2;
X{
X  int rev_mapping[3];
X  int i;
X  int leading_dot;
X  struct diff3_block *newblock, *thisblock;
X  char *cp;
X
X  leading_dot = 0;
X
X  for (i = 0; i < 3; i++)
X    rev_mapping [mapping [i]] = i;
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         overlaps++;
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             for (cp = D_RELNUM (thisblock, mapping [FILE2], i);
X                  cp < (D_RELNUM (thisblock, mapping [FILE2], i)
X                        + D_RELLEN (thisblock, mapping [FILE2], i));
X                  cp++)
X               putc (*cp, outputfile);
X             putc ('\n', outputfile);
X           }
X         fprintf (outputfile, ">>>>>>> %s\n.\n", file2);
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             for (cp = D_RELNUM (thisblock, mapping [FILE2], i);
X                  cp < (D_RELNUM (thisblock, mapping [FILE2], i)
X                        + D_RELLEN (thisblock, mapping [FILE2], i));
X                  cp++)
X               putc (*cp, outputfile);
X             putc ('\n', 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}
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 *) calloc (1,size + 100);
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);
X  if (!result)
X    fatal ("Realloc failed\n");
X  return result;
X}
X
Xvoid fatal (string)
X     char *string;
X{
X  printf("%s: %s\n",argv0, string);
X  exit (1);
X}
Xvoid perror_with_exit (string)
X     char *string;
X{
X  perror (string);
X  exit (1);
X}
SHAR_EOF
echo "End of archive 2 (of 14)"
# if you want to concatenate archives, remove anything after this line
exit


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.