[comp.os.minix] Making context diffs - cdiff

lwall@sdcrdcf.UUCP (06/02/87)

If you use the following program to produce your patches, people will be
able to apply your patches using the patch program even if their line numbers
don't match your line numbers.  This program produces 4.3bsd-style context
diffs, which are considerably more compact than old-style context diffs, yet
are much less error-prone to apply than normal diffs, since patch has more
information to decide if it is putting the patch in the right place.  In
the worst case (that is, an append), a normal diff applied by patch can insert
lines in the wrong place in your program and never tell you about it because
all it has to go on is the line number.

Note that cdiff uses a normal diff as a back end, so you'll still need Erik
Baalbergen's diff program that he just posted if you want to run this under
MINIX.  Don't be confused, however, between his patch program and mine.  His
will only apply normal diffs, and only if you haven't modified your file.

By the way, this is an unobfuscated version of a program I entered in the
Obfuscated C Code Contest.  Exercise #1: How would you go about shrinking this
to under 1K of source?  It's currently over 5K.  Enjoy.

Larry Wall
{allegra,burdvax,cbosgd,hplabs,ihnp4,sdcsvax}!sdcrdcf!lwall

#! /bin/sh

# Make a new directory for the cdiff sources, cd to it, and run kits 1
# thru 1 through sh.  When all 1 kits have been run, read README.

echo "This is cdiff 1.1 kit 1 (of 1).  If kit 1 is complete, the line"
echo '"'"End of kit 1 (of 1)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
echo Extracting README
sed >README <<'!STUFFY!FUNK!' -e 's/X//'
XThis program runs a normal diff as input to a pipe, and transforms that into
Xa 4.3bsd-style context diff.
X
XTo compile, just type "cc -o cdiff cdiff.c".  You might want to throw in
Xa -Dvoid=int if your C compiler doesn't grok the void type.
X
XTo run, just say "cdiff file1 file2".  Cdiff will run a "diff file1 file2"
Xfor you, and modify that output to make the context diff.  If you don't have
Xa normal diff program, there is a PD version available from Erik Baalbergen
Xat erikb@cs.vu.nl.
!STUFFY!FUNK!
echo Extracting cdiff.c
sed >cdiff.c <<'!STUFFY!FUNK!' -e 's/X//'
Xstatic char rcsid[] = "$Header: cdiff.c,v 1.1 87/06/01 16:05:57 lwall Exp $";
X
X/* cdiff - turns a regular diff into a new-style context diff
X *
X * Usage: cdiff file1 file2
X *
X * $Log:	cdiff.c,v $
X * Revision 1.1  87/06/01  16:05:57  lwall
X * Initial revision
X * 
X */
X
X#include "patchlevel.h"
X#include <stdio.h>
X#include <sys/types.h>
X#include <stat.h>
X#include <ctype.h>
X
Xchar buf[512];
X
XFILE *inputfp, *oldfp, *newfp;
X
Xint oldmin, oldmax, newmin, newmax;
Xint oldbeg, oldend, newbeg, newend;
Xint preoldmax, prenewmax;
Xint preoldbeg, preoldend, prenewbeg, prenewend;
Xint oldwanted, newwanted;
X
Xchar *oldhunk, *newhunk;
Xunsigned oldsize, oldalloc, newsize, newalloc;
X
Xvoid dumphunk();
Xchar *getold();
Xchar *getnew();
Xchar *malloc();
Xchar *realloc();
Xchar *fgets();
XFILE *popen();
X
X#define Nullfp (FILE*)0
X#define Nullch (char*)0
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X    char *old, *new;
X    int context = 3;
X    struct stat statbuf;
X    register char *s;
X    char op;
X    char *newmark, *oldmark;
X    int len;
X    char *line;
X    int i;
X
X    oldalloc = 512;
X    oldhunk = malloc(oldalloc);
X    newalloc = 512;
X    newhunk = malloc(newalloc);
X
X    for (argc--,argv++; argc; argc--,argv++) {
X	if (argv[0][0] != '-')
X	    break;
X	if (argv[0][1] == 'v') {
X	    printf("%s\n	Patch level: %d\n",rcsid,PATCHLEVEL);
X	    exit(0);
X	}
X	if (argv[0][1] == 'c')
X	    context = atoi(argv[0]+2);
X    }
X
X    if (argc != 2) {
X	fprintf(stderr,"cdiff old new\n");
X	exit(1);
X    }
X
X    old = argv[0];
X    new = argv[1];
X
X    sprintf(buf,"diff %s %s", old, new);
X    inputfp = popen(buf, "r");
X    if (!inputfp) {
X	fprintf(stderr, "Can't execute diff %s %s\n", old, new);
X	exit(1);
X    }
X
X    oldfp = fopen(old,"r");
X    if (!oldfp) {
X	fprintf(stderr, "Can't open %s\n", old);
X	exit(1);
X    }
X    newfp = fopen(new,"r");
X    if (!newfp) {
X	fprintf(stderr, "Can't open %s\n", new);
X	exit(1);
X    }
X
X    fstat(fileno(oldfp),&statbuf);
X    printf("*** %s\t%s", old, ctime(&statbuf.st_mtime));
X    fstat(fileno(newfp),&statbuf);
X    printf("--- %s\t%s", new, ctime(&statbuf.st_mtime));
X
X    preoldend = -1000;
X
X    while (fgets(buf, sizeof buf, inputfp) != Nullch) {
X	if (isdigit(*buf)) {
X	    oldmin = atoi(buf);
X	    for (s = buf; isdigit(*s); s++) ;
X	    if (*s == ',') {
X		s++;
X		oldmax = atoi(s);
X		for ( ; isdigit(*s); s++) ;
X	    }
X	    else {
X		oldmax = oldmin;
X	    }
X	    if (*s != 'a' && *s != 'd' && *s != 'c') {
X		fprintf(stderr, "Unparseable input: %s", s);
X		exit(1);
X	    }
X	    op = *s;
X	    s++;
X	    newmin = atoi(s);
X	    for ( ; isdigit(*s); s++) ;
X	    if (*s == ',') {
X		s++;
X		newmax = atoi(s);
X		for ( ; isdigit(*s); s++) ;
X	    }
X	    else {
X		newmax = newmin;
X	    }
X	    if (*s != '\n' && *s != ' ') {
X		fprintf(stderr, "Unparseable input: %s", s);
X		exit(1);
X	    }
X
X	    newmark = oldmark = "! ";
X	    if (op == 'a') {
X		oldmin++;
X		newmark = "+ ";
X	    }
X	    if (op == 'd') {
X		newmin++;
X		oldmark = "- ";
X	    }
X
X	    oldbeg = oldmin - context;
X	    oldend = oldmax + context;
X	    newbeg = newmin - context;
X	    newend = newmax + context;
X
X	    if (preoldend < newbeg - 1) {
X		if (preoldend >= 0) {
X		    dumphunk();
X		}
X		preoldbeg = oldbeg;
X		prenewbeg = newbeg;
X		oldwanted = newwanted = 0;
X		oldsize = newsize = 0;
X	    } else {	/* we want to append to previous hunk */
X		oldbeg = preoldmax + 1;
X		newbeg = prenewmax + 1;
X	    }
X
X	    for (i = oldbeg; i <= oldmax; i++) {
X		line = getold(i);
X		if (!*line) {
X		    oldend = oldmax = i - 1;
X		    break;
X		}
X		len = strlen(line) + 2;
X		if (oldsize + len + 1 >= oldalloc) {
X		    oldalloc *= 2;
X		    oldhunk = realloc(oldhunk, oldalloc);
X		}
X		if (i >= oldmin) {
X		    strcpy(oldhunk+oldsize, oldmark);
X		    oldwanted++;
X		}
X		else {
X		    strcpy(oldhunk+oldsize, "  ");
X		}
X		strcpy(oldhunk+oldsize+2,line);
X		oldsize += len;
X	    }
X	    preoldmax = oldmax;
X	    preoldend = oldend;
X
X	    for (i = newbeg; i <= newmax; i++) {
X		line = getnew(i);
X		if (!*line) {
X		    newend = newmax = i - 1;
X		    break;
X		}
X		len = strlen(line) + 2;
X		if (newsize + len + 1 >= newalloc) {
X		    newalloc *= 2;
X		    newhunk = realloc(newhunk, newalloc);
X		}
X		if (i >= newmin) {
X		    strcpy(newhunk+newsize, newmark);
X		    newwanted++;
X		}
X		else {
X		    strcpy(newhunk+newsize, "  ");
X		}
X		strcpy(newhunk+newsize+2,line);
X		newsize += len;
X	    }
X	    prenewmax = newmax;
X	    prenewend = newend;
X	}
X    }
X
X    if (preoldend >= 0) {
X	dumphunk();
X    }
X}
X
Xvoid
Xdumphunk()
X{
X    int i;
X    char *line;
X    int len;
X
X    for (i = preoldmax + 1; i <= preoldend; i++) {
X	line = getold(i);
X	if (!line) {
X	    preoldend = i - 1;
X	    break;
X	}
X	len = strlen(line) + 2;
X	if (oldsize + len + 1 >= oldalloc) {
X	    oldalloc *= 2;
X	    oldhunk = realloc(oldhunk, oldalloc);
X	}
X	strcpy(oldhunk+oldsize, "  ");
X	strcpy(oldhunk+oldsize+2, line);
X	oldsize += len;
X    }
X    for (i = prenewmax + 1; i <= prenewend; i++) {
X	line = getnew(i);
X	if (!line) {
X	    prenewend = i - 1;
X	    break;
X	}
X	len = strlen(line) + 2;
X	if (newsize + len + 1 >= newalloc) {
X	    newalloc *= 2;
X	    newhunk = realloc(newhunk, newalloc);
X	}
X	strcpy(newhunk+newsize, "  ");
X	strcpy(newhunk+newsize+2, line);
X	newsize += len;
X    }
X    fputs("***************\n",stdout);
X    if (preoldbeg >= preoldend) {
X	printf("*** %d ****\n", preoldend);
X    } else {
X	printf("*** %d,%d ****\n", preoldbeg, preoldend);
X    }
X    if (oldwanted) {
X	fputs(oldhunk,stdout);
X    }
X    oldsize = 0;
X    *oldhunk = '\0';
X    if (prenewbeg >= prenewend) {
X	printf("--- %d ----\n", prenewend);
X    } else {
X	printf("--- %d,%d ----\n", prenewbeg, prenewend);
X    }
X    if (newwanted) {
X	fputs(newhunk,stdout);
X    }
X    newsize = 0;
X    *newhunk = '\0';
X}
X
Xchar *
Xgetold(targ)
Xint targ;
X{
X    static int oldline = 0;
X
X    while (fgets(buf, sizeof buf, oldfp) != Nullch) {
X	oldline++;
X	if (oldline == targ)
X	    return buf;
X    }
X    return Nullch;
X}
X
Xchar *
Xgetnew(targ)
Xint targ;
X{
X    static int newline = 0;
X
X    while (fgets(buf, sizeof buf, newfp) != Nullch) {
X	newline++;
X	if (newline == targ)
X	    return buf;
X    }
X    return Nullch;
X}
!STUFFY!FUNK!
echo Extracting MANIFEST
sed >MANIFEST <<'!STUFFY!FUNK!' -e 's/X//'
XAfter all the cdiff kits are run you should have the following files:
X
XFilename		Kit Description
X--------		--- -----------
XMANIFEST		1   This file.        
XREADME			1   Instructions.       
Xcdiff.c		1   The program.       
Xpatchlevel.h		1   The patch level of cdiff.   
!STUFFY!FUNK!
echo Extracting patchlevel.h
sed >patchlevel.h <<'!STUFFY!FUNK!' -e 's/X//'
X#define PATCHLEVEL 0
!STUFFY!FUNK!
echo ""
echo "End of kit 1 (of 1)"
: Someone might mail this, so...
exit

aeusemrs@csun.UUCP (06/03/87)

Larry, I am having a little problem with your cdiff program.  We are
on a 3b5, System V R2.0, using the normal diff, everything works
fine. But I compiled your program, and it doesn't produce coherent output.
Is there a problem with the output format of our diff, or with cdiff?

$ diff /tmp/ojove.c /tmp/jove.c
370a371,372
> #ifdef SYSV
>   int proc_child();
371a374,375
>   signal(SIGCHLD, SIG_DFL);
> #else SYSV
374a379
> #endif SYSV
380a386
> #ifndef SYSV
383a390
> #endif SYSV
391a399,401
> #ifdef SYSV
>   signal(SIGCHLD, proc_child);
> #else SYSV
394a405
> #endif SYSV
401c412
< int    OKXonXoff = 0,     /* ^S and ^Q initially DON'T work */
---
> int    OKXonXoff = 1,     /* ^S and ^Q initially DON'T work */
499a511,512
>   if (OKXonXoff)
>       sg2.c_iflag &= ~IXON; /* disable tty xon/xoff processing */
$ cdiff /tmp/ojove.c /tmp/jove.c
*** /tmp/ojove.c	Tue Jun  2 18:35:52 1987
--- /tmp/jove.c	Tue Jun  2 18:35:27 1987
***************
*** 368,374 ****
--- 368,378 ----
    int pid,
            (*old_int)() = signal(SIGINT, SIG_IGN),
        (*old_quit)() = signal(SIGQUIT, SIG_IGN);
+ #ifdef SYSV
+   int proc_child();

+   signal(SIGCHLD, SIG_DFL);
+ #else SYSV
  #ifdef IPROCS
    sighold(SIGCHLD);
  #endif
***************
*** 371 ****
--- 375 ----
***************
*** 377 ****
--- 382 ----
***************
*** 380 ****
--- 386 ----
***************
*** 388 ****
--- 395 ----
***************
*** 391 ****
--- 401 ----
***************
*** 397 ****
--- 408 ----
***************
*** 496 ****
--- 507 ----
$ exit
-- 
Mike Stump, Cal State Univ, Northridge Comp Sci Department
uucp: {sdcrdcf, ihnp4, hplabs, ttidca, psivax, csustan}!csun!aeusemrs

clark@b14.UUCP (06/13/87)

In article <4663@sdcrdcf.UUCP>, lwall@sdcrdcf.UUCP (Larry Wall) writes:

> Note that cdiff uses a normal diff as a back end, so you'll still need Erik
> Baalbergen's diff program that he just posted if you want to run this under


Our site seems to have missed Erik's posting. Would some kind soul please
mail me the sources for his diff?

------------------------------------------------------------------------------
Clark Williams				
Intergraph Corp.	  		UUCP:   ihnp4!ingr!b14!clark
One Madison Industrial Park <<<--------	USnail: mail stop DB1402
Huntsville, AL 35807