/*	@(#)diffh.c	2.1		*/

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>

#define C 3
#define RANGE 30
#define LEN 255
#define INF 16384

char *text[2][RANGE];
long lineno[2] = {1, 1};	/*no. of 1st stored line in each file*/
int ntext[2];		/*number of stored lines in each*/
long n0,n1;		/*scan pointer in each*/
int bflag;
int debug = 0;
FILE *file[2];

	/* return pointer to line n of file f*/
char *getl(f,n)
long n;
{
	register char *t;
	char *malloc();
	register delta, nt;
again:
	delta = n - lineno[f];
	nt = ntext[f];
	if(delta<0)
		progerr("1");
	if(delta<nt)
		return(text[f][delta]);
	if(delta>nt)
		progerr("2");
	if(nt>=RANGE)
		progerr("3");
	if(feof(file[f]))
		return(NULL);
	t = text[f][nt];
	if(t==0) {
		t = text[f][nt] = malloc(LEN+1);
		if(t==NULL)
			if(hardsynch())
				goto again;
			else
				progerr("5");
	}
	t = fgets(t,LEN,file[f]);
	if(t!=NULL)
		ntext[f]++;
	return(t);
}

	/*remove thru line n of file f from storage*/
clrl(f,n)
long n;
{
	register i,j;
	j = n-lineno[f]+1;
	for(i=0;i+j<ntext[f];i++)
		movstr(text[f][i+j],text[f][i]);
	lineno[f] = n+1;
	ntext[f] -= j;
}

movstr(s,t)
register char *s, *t;
{
	while(*t++= *s++)
		continue;
}

main(argc,argv)
char **argv;
{
	char *s0,*s1;
	FILE *dopen();
	if(*argv[1]=='-') {
		argc--;
		argv++;
		while(*++argv[0])
			if(*argv[0]=='b')
				bflag++;
	}
	if(argc!=3)
		error("must have 2 file arguments","");
	file[0] = dopen(argv[1],argv[2]);
	file[1] = dopen(argv[2],argv[1]);
	for(;;) {
		s0 = getl(0,++n0);
		s1 = getl(1,++n1);
		if(s0==NULL||s1==NULL)
			break;
		if(cmp(s0,s1)!=0) {
			if(!easysynch()&&!hardsynch())
				progerr("5");
		} else {
			clrl(0,n0);
			clrl(1,n1);
		}
	}
	if(s0==NULL&&s1==NULL)
		return;
	if(s0==NULL)
		output(-1,INF);
	if(s1==NULL)
		output(INF,-1);
}

	/* synch on C successive matches*/
easysynch()
{
	int i,j;
	register k,m;
	char *s0,*s1;
	for(i=j=1;i<RANGE&&j<RANGE;i++,j++) {
		s0 = getl(0,n0+i);
		if(s0==NULL)
			return(output(INF,INF));
		for(k=C-1;k<j;k++) {
			for(m=0;m<C;m++)
				if(cmp(getl(0,n0+i-m),
					getl(1,n1+k-m))!=0)
					goto cont1;
			return(output(i-C,k-C));
cont1:			;
		}
		s1 = getl(1,n1+j);
		if(s1==NULL)
			return(output(INF,INF));
		for(k=C-1;k<=i;k++) {
			for(m=0;m<C;m++)
				if(cmp(getl(0,n0+k-m),
					getl(1,n1+j-m))!=0)
					goto cont2;
			return(output(k-C,j-C));
cont2:			;
		}
	}
	return(0);
}

output(a,b)
{
	register i;
	char *s;
	if(a<0)
		change(n0-1,0,n1,b,"a");
	else if(b<0)
		change(n0,a,n1-1,0,"d");
	else
		change(n0,a,n1,b,"c");
	for(i=0;i<=a;i++) {
		s = getl(0,n0+i);
		if(s==NULL)
			break;
		printf("< %s",s);
		clrl(0,n0+i);
	}
	n0 += i-1;
	if(a>=0&&b>=0)
		printf("---\n");
	for(i=0;i<=b;i++) {
		s = getl(1,n1+i);
		if(s==NULL)
			break;
		printf("> %s",s);
		clrl(1,n1+i);
	}
	n1 += i-1;
	return(1);
}

change(a,b,c,d,s)
long a,c;
char *s;
{
	range(a,b);
	printf("%s",s);
	range(c,d);
	printf("\n");
}

range(a,b)
long a;
{
	if(b==INF)
		printf("%ld,$",a);
	else if(b==0)
		printf("%ld",a);
	else
		printf("%ld,%ld",a,a+b);
}

cmp(s,t)
char *s,*t;
{
	if(debug)
		printf("%s:%s\n",s,t);
	for(;;){
		if(bflag&&isspace(*s)&&isspace(*t)) {
			while(isspace(*++s)) ;
			while(isspace(*++t)) ;
		}
		if(*s!=*t||*s==0)
			break;
		s++;
		t++;
	}
	return(*s-*t);
}

FILE *dopen(f1,f2)
char *f1,*f2;
{
	FILE *f;
	char b[100],*bptr,*eptr;
	struct stat statbuf;
	if(cmp(f1,"-")==0)
		if(cmp(f2,"-")==0)
			error("can't do - -","");
		else
			return(stdin);
	if(stat(f1,&statbuf)==-1)
		error("can't access ",f1);
	if((statbuf.st_mode&S_IFMT)==S_IFDIR) {
		for(bptr=b;*bptr= *f1++;bptr++) ;
		*bptr++ = '/';
		for(eptr=f2;*eptr;eptr++)
			if(*eptr=='/'&&eptr[1]!=0&&eptr[1]!='/')
				f2 = eptr+1;
		while(*bptr++= *f2++) ;
		f1 = b;
	}
	f = fopen(f1,"r");
	if(f==NULL)
		error("can't open",f1);
	return(f);
}


progerr(s)
char *s;
{
	error("program error ",s);
}

error(s,t)
char *s,*t;
{
	fprintf(stderr,"diffh: %s%s\n",s,t);
	exit(1);
}

	/*stub for resychronization beyond limits of text buf*/
hardsynch()
{
	change(n0,INF,n1,INF,"c");
	printf("---change record omitted\n");
	error("can't resynchronize","");
	return(0);
}
