[comp.sources.misc] v05i006: ediff - translate diff output to plain English

edf@ROCKY2.ROCKEFELLER.EDU (David MacKenzie) (10/28/88)

Posting-number: Volume 5, Issue 6
Submitted-by: "David MacKenzie" <edf@ROCKY2.ROCKEFELLER.EDU>
Archive-name: ediff

/*
% cc -O ediff.c -o ediff; strip ediff
 *
 * ediff - translate diff output into plain English
 *
 * Translates only the normal diff output (not -c or -e/-f).
 * Use as a filter.  Copies non-diff lines verbatim.  Deletes NULs.
 *
 * The Unix variant that runs on our Charles River Data Systems
 * machines, UNOS, comes with, in addition to the standard diff program,
 * a program called "difference" that produces more human-readable
 * output.  Ediff provides the advantages of that program to users of other
 * versions of Unix (soon to include us, as we're hoping to get rid of
 * the CRDS machines before too long).
 *
 * Thanks to Mike Haertel for the following information.
 * The exact meaning of the normal diff symbols is as follows
 * (all line numbers are the ORIGINAL line numbers in each file);
 * 
 * 	5,7c8,10
 * Change lines 5-7 of file 1 to read as lines 8-10 of file 2 (or, if changing
 * file 2 into file 1, change lines 8-10 of file 2 to read as lines 5-7
 * of file 1).
 * 
 * 	5,7d3
 * Delete lines 5-7 of file 1 (alternatively, append lines 5-7 of file 1
 * after line 3 of file 2).
 * 
 * 	8a12,15
 * Append lines 12-15 of file 2 after line 8 of file 1 (alternatively,
 * delete lines 12-15 of file 2).
 *
 * David MacKenzie
 * Latest revision: 10/19/88
 */ 

#include <stdio.h>

#define UNUSED -1L

#ifdef __STDC__
#define P(x) x
#else
#define P(x) ()
#endif

void change P((long, long, long, long));
void delete P((long, long, long));
void append P((long, long, long));
void copylines P((long, int));
int readline P((char *, int, FILE *));

char buf[BUFSIZ]; /* Input line buffer. */

main ()
{
  long f1n1, f1n2, f2n1, f2n2;  /* File 1, line number 1; etc. */
  int res;  /* Result of readline. */
    
  while (res = readline (buf, BUFSIZ, stdin))
    {
      f1n2 = f2n2 = UNUSED;
      if
        (sscanf (buf, "%ld,%ldc%ld,%ld\n", &f1n1, &f1n2, &f2n1, &f2n2) == 4 ||
        sscanf (buf, "%ld,%ldc%ld\n", &f1n1, &f1n2, &f2n1) == 3 ||
        sscanf (buf, "%ldc%ld,%ld\n", &f1n1, &f2n1, &f2n2) == 3 ||
        sscanf (buf, "%ldc%ld\n", &f1n1, &f2n1) == 2)
          change (f1n1, f1n2, f2n1, f2n2);
      else if
        (sscanf (buf, "%ld,%ldd%ld\n", &f1n1, &f1n2, &f2n1) == 3 ||
        sscanf (buf, "%ldd%ld\n", &f1n1, &f2n1) == 2)
          delete (f1n1, f1n2, f2n1);
      else if
        (sscanf (buf, "%lda%ld,%ld\n", &f1n1, &f2n1, &f2n2) == 3 ||
        sscanf (buf, "%lda%ld\n", &f1n1, &f2n1) == 2)
          append (f1n1, f2n1, f2n2);
      else
        {
          /* The line wasn't the start of a diff.  Copy it verbatim. */
          fputs (buf, stdout);
          /* If it was a long line, copy the remainder. */
          while (res == -1)
            {
              res = readline (buf, BUFSIZ, stdin);
              fputs (buf, stdout);
            }
        }
    }
}

void change (f1n1, f1n2, f2n1, f2n2)
  long f1n1, f1n2, f2n1, f2n2;
{
  printf ("\n-------- ");
  if (f1n2 == UNUSED && f2n2 == UNUSED)
    printf ("1 line changed at %ld from:\n",
      f1n1); /* 1c1 */
  else if (f1n2 == UNUSED)
    printf ("1 line changed to %ld lines at %ld from:\n",
      f2n2 - f2n1 + 1L, f1n1); /* 1c1,2 */
  else if (f2n2 == UNUSED)
    printf ("%ld lines changed to 1 line at %ld-%ld from:\n",
      f1n2 - f1n1 + 1L, f1n1, f1n2); /* 1,2c1 */
  else if (f1n2 - f1n1 == f2n2 - f2n1)
    printf ("%ld lines changed at %ld-%ld from:\n",
      f1n2 - f1n1 + 1L, f1n1, f1n2); /* 1,2c1,2 */
  else
    printf ("%ld lines changed to %ld lines at %ld-%ld from:\n",
      f1n2 - f1n1 + 1L, f2n2 - f2n1 + 1L, f1n1, f1n2); /* 1,2c1,3 */

  if (f1n2 == UNUSED)
    copylines (1, 2); /* Skip the "< ". */
  else
    copylines (f1n2 - f1n1 + 1L, 2);

  printf ("-------- to:\n");
  (void) readline (buf, BUFSIZ, stdin); /* Eat the "---" line. */
  if (f2n2 == UNUSED)
    copylines (1, 2); /* Skip the "> ". */
  else
    copylines (f2n2 - f2n1 + 1L, 2);
}

/* ARGSUSED */
void delete (f1n1, f1n2, f2n1)
  long f1n1, f1n2, f2n1;
{
  printf ("\n-------- ");
  if (f1n2 == UNUSED)
    {
      printf ("1 line deleted at %ld:\n", f1n1); /* 1d1 */
      copylines (1, 2); /* Skip the "< ". */
    }
  else
    {
      printf ("%ld lines deleted at %ld:\n", f1n2 - f1n1 + 1L, f1n1); /* 1,2d1 */
      copylines (f1n2 - f1n1 + 1L, 2);
    }
}

void append (f1n1, f2n1, f2n2)
  long f1n1, f2n1, f2n2;
{
  printf ("\n-------- ");
  if (f2n2 == UNUSED)
    {
      printf ("1 line added at %ld:\n", f1n1); /* 1a1 */
      copylines (1, 2); /* Skip the "> ". */
    }
  else
    {
      printf ("%ld lines added at %ld:\n", f2n2 - f2n1 + 1L, f1n1); /* 1a1,2 */
      copylines (f2n2 - f2n1 + 1L, 2);
    }
}

/*
 * Copy nlines lines from stdin to stdout; start writing at position skip.
 */
void copylines (nlines, skip)
  long nlines;
  int skip;
{
  int res;
  
  while (nlines-- > 0L)
    {
      res = readline (buf, BUFSIZ, stdin);
      fputs (&buf[skip], stdout);
      /* If it was a long line, copy the remainder. */
      while (res == -1)
        {
          res = readline (buf, BUFSIZ, stdin);
          fputs (buf, stdout);
        }
    }
}

/*
 * Front end to fgets.
 * Exit if EOF; return 1 if end of line read, -1 if partial line read.
 */
int readline (s, len, fp)
  char *s;
  int len;
  FILE *fp;
{
  if (!fgets (s, len, fp))
    exit (0);
  if (s[strlen (s) - 1] == '\n')
    return 1;
  else
    return -1;
}