xref: /csrg-svn/usr.bin/diff/diff3/diff3.c (revision 24331)
114484Ssam #ifndef lint
2*24331Svan static char sccsid[] = "@(#)diff3.c	4.3 (Berkeley) 08/18/85";
314484Ssam #endif
410880Sshannon 
510880Sshannon #include <stdio.h>
610880Sshannon 
710880Sshannon /* diff3 - 3-way differential file comparison*/
810880Sshannon 
9*24331Svan /* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3]
1010880Sshannon  *
1110880Sshannon  * d13 = diff report on f1 vs f3
1210880Sshannon  * d23 = diff report on f2 vs f3
1310880Sshannon  * f1, f2, f3 the 3 files
14*24331Svan  * if changes in f1 overlap with changes in f3, m1 and m3 are used
15*24331Svan  * to mark the overlaps; otherwise, the file names f1 and f3 are used
16*24331Svan  * (only for options E and X).
1710880Sshannon */
1810880Sshannon 
1910880Sshannon struct  range {int from,to; };
2010880Sshannon 	/* from is first in range of changed lines
2110880Sshannon 	 * to is last+1
2210880Sshannon 	 * from=to=line after point of insertion
2310880Sshannon 	* for added lines
2410880Sshannon 	*/
2510880Sshannon struct diff {struct range old, new;};
2610880Sshannon 
2710880Sshannon #define NC 200
28*24331Svan struct diff d13[NC];
29*24331Svan struct diff d23[NC];
3010880Sshannon /* de is used to gather editing scripts,
3110880Sshannon  * that are later spewed out in reverse order.
3210880Sshannon  * its first element must be all zero
3310880Sshannon  * the "new" component of de contains line positions
3410880Sshannon  * or byte positions depending on when you look(!?)
35*24331Svan  * array overlap indicates which sections in de correspond to
36*24331Svan  * lines that are different in all three files.
3710880Sshannon */
3810880Sshannon struct diff de[NC];
39*24331Svan char overlap[NC];
40*24331Svan int  overlapcnt =0;
41*24331Svan 
4210880Sshannon char line[256];
4310880Sshannon FILE *fp[3];
4410880Sshannon int linct[3] = {0,0,0};
4510880Sshannon /*	the number of the last-read line in each file
4610880Sshannon  *	is kept in cline[0-2]
4710880Sshannon */
4810880Sshannon int cline[3];
4910880Sshannon /*	the latest known correspondence between line
5010880Sshannon  *	numbers of the 3 files is stored in last[1-3]
5110880Sshannon */
5210880Sshannon int last[4];
5310880Sshannon int eflag;
54*24331Svan int oflag;      /* indicates whether to mark overlaps (-E or -X)*/
5510880Sshannon int debug  = 0;
56*24331Svan char f1mark[40], f3mark[40]; /*markers for -E and -X*/
5710880Sshannon 
58*24331Svan 
5910880Sshannon main(argc,argv)
6010880Sshannon char **argv;
6110880Sshannon {
6210880Sshannon 	register i,m,n;
63*24331Svan         eflag=0; oflag=0;
6410880Sshannon 	if(*argv[1]=='-') {
6510880Sshannon 		switch(argv[1][1]) {
6610880Sshannon 		default:
6710880Sshannon 			eflag = 3;
6810880Sshannon 			break;
6910880Sshannon 		case '3':
7010880Sshannon 			eflag = 2;
7110880Sshannon 			break;
7210880Sshannon 		case 'x':
7310880Sshannon 			eflag = 1;
74*24331Svan                         break;
75*24331Svan                 case 'E':
76*24331Svan                         eflag = 3;
77*24331Svan                         oflag = 1;
78*24331Svan                         break;
79*24331Svan                 case 'X':
80*24331Svan                         oflag = eflag = 1;
81*24331Svan                         break;
8210880Sshannon 		}
8310880Sshannon 		argv++;
8410880Sshannon 		argc--;
8510880Sshannon 	}
8610880Sshannon 	if(argc<6) {
8710880Sshannon 		fprintf(stderr,"diff3: arg count\n");
8810880Sshannon 		exit(1);
8910880Sshannon 	}
90*24331Svan         if (oflag) {
91*24331Svan                 sprintf(f1mark,"<<<<<<< %s",argc>=7?argv[6]:argv[3]);
92*24331Svan                 sprintf(f3mark,">>>>>>> %s",argc>=8?argv[7]:argv[5]);
93*24331Svan         }
94*24331Svan 
9510880Sshannon 	m = readin(argv[1],d13);
9610880Sshannon 	n = readin(argv[2],d23);
9710880Sshannon 	for(i=0;i<=2;i++)
9810880Sshannon 		if((fp[i] = fopen(argv[i+3],"r")) == NULL) {
9910880Sshannon 			printf("diff3: can't open %s\n",argv[i+3]);
10010880Sshannon 			exit(1);
10110880Sshannon 		}
10210880Sshannon 	merge(m,n);
10310880Sshannon }
10410880Sshannon 
10510880Sshannon /*pick up the line numbers of allcahnges from
10610880Sshannon  * one change file
10710880Sshannon  * (this puts the numbers in a vector, which is not
10810880Sshannon  * strictly necessary, since the vector is processed
10910880Sshannon  * in one sequential pass. The vector could be optimized
11010880Sshannon  * out of existence)
11110880Sshannon */
11210880Sshannon 
11310880Sshannon readin(name,dd)
11410880Sshannon char *name;
11510880Sshannon struct diff *dd;
11610880Sshannon {
11710880Sshannon 	register i;
11810880Sshannon 	int a,b,c,d;
11910880Sshannon 	char kind;
12010880Sshannon 	char *p;
12110880Sshannon 	fp[0] = fopen(name,"r");
12210880Sshannon 	for(i=0;getchange(fp[0]);i++) {
12310880Sshannon 		if(i>=NC) {
12410880Sshannon 			fprintf(stderr,"diff3: too many changes\n");
12510880Sshannon 			exit(0);
12610880Sshannon 		}
12710880Sshannon 		p = line;
12810880Sshannon 		a = b = number(&p);
12910880Sshannon 		if(*p==',') {
13010880Sshannon 			p++;
13110880Sshannon 			b = number(&p);
13210880Sshannon 		}
13310880Sshannon 		kind = *p++;
13410880Sshannon 		c = d = number(&p);
13510880Sshannon 		if(*p==',') {
13610880Sshannon 			p++;
13710880Sshannon 			d = number(&p);
13810880Sshannon 		}
13910880Sshannon 		if(kind=='a')
14010880Sshannon 			a++;
14110880Sshannon 		if(kind=='d')
14210880Sshannon 			c++;
14310880Sshannon 		b++;
14410880Sshannon 		d++;
14510880Sshannon 		dd[i].old.from = a;
14610880Sshannon 		dd[i].old.to = b;
14710880Sshannon 		dd[i].new.from = c;
14810880Sshannon 		dd[i].new.to = d;
14910880Sshannon 	}
15010880Sshannon 	dd[i].old.from = dd[i-1].old.to;
15110880Sshannon 	dd[i].new.from = dd[i-1].new.to;
15210880Sshannon 	fclose(fp[0]);
15310880Sshannon 	return(i);
15410880Sshannon }
15510880Sshannon 
15610880Sshannon number(lc)
15710880Sshannon char **lc;
15810880Sshannon {
15910880Sshannon 	register nn;
16010880Sshannon 	nn = 0;
16110880Sshannon 	while(digit(**lc))
16210880Sshannon 		nn = nn*10 + *(*lc)++ - '0';
16310880Sshannon 	return(nn);
16410880Sshannon }
16510880Sshannon 
16610880Sshannon digit(c)
16710880Sshannon {
16810880Sshannon 	return(c>='0'&&c<='9');
16910880Sshannon }
17010880Sshannon 
17110880Sshannon getchange(b)
17210880Sshannon FILE *b;
17310880Sshannon {
17410880Sshannon 	while(getline(b))
17510880Sshannon 		if(digit(line[0]))
17610880Sshannon 			return(1);
17710880Sshannon 	return(0);
17810880Sshannon }
17910880Sshannon 
18010880Sshannon getline(b)
18110880Sshannon FILE *b;
18210880Sshannon {
18310880Sshannon 	register i, c;
18410880Sshannon 	for(i=0;i<sizeof(line)-1;i++) {
18510880Sshannon 		c = getc(b);
18610880Sshannon 		if(c==EOF)
18710880Sshannon 			break;
18810880Sshannon 		line[i] = c;
18910880Sshannon 		if(c=='\n') {
19010880Sshannon 			line[++i] = 0;
19110880Sshannon 			return(i);
19210880Sshannon 		}
19310880Sshannon 	}
19410880Sshannon 	return(0);
19510880Sshannon }
19610880Sshannon 
19710880Sshannon merge(m1,m2)
19810880Sshannon {
19910880Sshannon 	register struct diff *d1, *d2, *d3;
20010880Sshannon 	int dup;
20110880Sshannon 	int j;
20210880Sshannon 	int t1,t2;
20310880Sshannon 	d1 = d13;
20410880Sshannon 	d2 = d23;
20510880Sshannon 	j = 0;
20610880Sshannon 	for(;(t1 = d1<d13+m1) | (t2 = d2<d23+m2);) {
20710880Sshannon 		if(debug) {
20810880Sshannon 			printf("%d,%d=%d,%d %d,%d=%d,%d\n",
20910880Sshannon 			d1->old.from,d1->old.to,
21010880Sshannon 			d1->new.from,d1->new.to,
21110880Sshannon 			d2->old.from,d2->old.to,
21210880Sshannon 			d2->new.from,d2->new.to);
21310880Sshannon 		}
21410880Sshannon /*			first file is different from others*/
21510880Sshannon 		if(!t2||t1&&d1->new.to < d2->new.from) {
21610880Sshannon /*			stuff peculiar to 1st file */
21710880Sshannon 			if(eflag==0) {
21810880Sshannon 				separate("1");
21910880Sshannon 				change(1,&d1->old,0);
22010880Sshannon 				keep(2,&d1->old,&d1->new);
22110880Sshannon 				change(3,&d1->new,0);
22210880Sshannon 			}
22310880Sshannon 			d1++;
22410880Sshannon 			continue;
22510880Sshannon 		}
22610880Sshannon /*			second file is different from others*/
22710880Sshannon 		if(!t1||t2&&d2->new.to < d1->new.from) {
22810880Sshannon 			if(eflag==0) {
22910880Sshannon 				separate("2");
23010880Sshannon 				keep(1,&d2->old,&d2->new);
23110880Sshannon 				change(2,&d2->old,0);
23210880Sshannon 				change(3,&d2->new,0);
23310880Sshannon 			}
23410880Sshannon 			d2++;
23510880Sshannon 			continue;
23610880Sshannon 		}
23710880Sshannon /*			merge overlapping changes in first file
23810880Sshannon  *			this happens after extension see below*/
23910880Sshannon 		if(d1+1<d13+m1 &&
24010880Sshannon 		   d1->new.to>=d1[1].new.from) {
24110880Sshannon 			d1[1].old.from = d1->old.from;
24210880Sshannon 			d1[1].new.from = d1->new.from;
24310880Sshannon 			d1++;
24410880Sshannon 			continue;
24510880Sshannon 		}
24610880Sshannon /*			merge overlapping changes in second*/
24710880Sshannon 		if(d2+1<d23+m2 &&
24810880Sshannon 		   d2->new.to>=d2[1].new.from) {
24910880Sshannon 			d2[1].old.from = d2->old.from;
25010880Sshannon 			d2[1].new.from = d2->new.from;
25110880Sshannon 			d2++;
25210880Sshannon 			continue;
25310880Sshannon 		}
25410880Sshannon /*			stuff peculiar to third file or different in all*/
25510880Sshannon 		if(d1->new.from==d2->new.from&&
25610880Sshannon 		   d1->new.to==d2->new.to) {
25710880Sshannon 			dup = duplicate(&d1->old,&d2->old);
25810880Sshannon /*				dup=0 means all files differ
25910880Sshannon  *				dup =1 meands files 1&2 identical*/
26010880Sshannon 			if(eflag==0) {
26110880Sshannon 				separate(dup?"3":"");
26210880Sshannon 				change(1,&d1->old,dup);
26310880Sshannon 				change(2,&d2->old,0);
26410880Sshannon 				d3 = d1->old.to>d1->old.from?d1:d2;
26510880Sshannon 				change(3,&d3->new,0);
26610880Sshannon 			} else
26710880Sshannon 				j = edit(d1,dup,j);
26810880Sshannon 			d1++;
26910880Sshannon 			d2++;
27010880Sshannon 			continue;
27110880Sshannon 		}
27210880Sshannon /*			overlapping changes from file1 & 2
27310880Sshannon  *			extend changes appropriately to
27410880Sshannon  *			make them coincide*/
27510880Sshannon 		 if(d1->new.from<d2->new.from) {
27610880Sshannon 			d2->old.from -= d2->new.from-d1->new.from;
27710880Sshannon 			d2->new.from = d1->new.from;
27810880Sshannon 		}
27910880Sshannon 		else if(d2->new.from<d1->new.from) {
28010880Sshannon 			d1->old.from -= d1->new.from-d2->new.from;
28110880Sshannon 			d1->new.from = d2->new.from;
28210880Sshannon 		}
28310880Sshannon 		if(d1->new.to >d2->new.to) {
28410880Sshannon 			d2->old.to += d1->new.to - d2->new.to;
28510880Sshannon 			d2->new.to = d1->new.to;
28610880Sshannon 		}
28710880Sshannon 		else if(d2->new.to >d1->new.to) {
28810880Sshannon 			d1->old.to += d2->new.to - d1->new.to;
28910880Sshannon 			d1->new.to = d2->new.to;
29010880Sshannon 		}
29110880Sshannon 	}
29210880Sshannon 	if(eflag)
29310880Sshannon 		edscript(j);
29410880Sshannon }
29510880Sshannon 
29610880Sshannon separate(s)
29710880Sshannon char *s;
29810880Sshannon {
29910880Sshannon 	printf("====%s\n",s);
30010880Sshannon }
30110880Sshannon 
30210880Sshannon /*	the range of ines rold.from thru rold.to in file i
30310880Sshannon  *	is to be changed. it is to be printed only if
30410880Sshannon  *	it does not duplicate something to be printed later
30510880Sshannon */
30610880Sshannon change(i,rold,dup)
30710880Sshannon struct range *rold;
30810880Sshannon {
30910880Sshannon 	printf("%d:",i);
31010880Sshannon 	last[i] = rold->to;
31110880Sshannon 	prange(rold);
31210880Sshannon 	if(dup)
31310880Sshannon 		return;
31410880Sshannon 	if(debug)
31510880Sshannon 		return;
31610880Sshannon 	i--;
31710880Sshannon 	skip(i,rold->from,(char *)0);
31810880Sshannon 	skip(i,rold->to,"  ");
31910880Sshannon }
32010880Sshannon 
32110880Sshannon /*	print the range of line numbers, rold.from  thru rold.to
32210880Sshannon  *	as n1,n2 or n1
32310880Sshannon */
32410880Sshannon prange(rold)
32510880Sshannon struct range *rold;
32610880Sshannon {
32710880Sshannon 	if(rold->to<=rold->from)
32810880Sshannon 		printf("%da\n",rold->from-1);
32910880Sshannon 	else {
33010880Sshannon 		printf("%d",rold->from);
33110880Sshannon 		if(rold->to > rold->from+1)
33210880Sshannon 			printf(",%d",rold->to-1);
33310880Sshannon 		printf("c\n");
33410880Sshannon 	}
33510880Sshannon }
33610880Sshannon 
33710880Sshannon /*	no difference was reported by diff between file 1(or 2)
33810880Sshannon  *	and file 3, and an artificial dummy difference (trange)
33910880Sshannon  *	must be ginned up to correspond to the change reported
34010880Sshannon  *	in the other file
34110880Sshannon */
34210880Sshannon keep(i,rold,rnew)
34310880Sshannon struct range *rold, *rnew;
34410880Sshannon {
34510880Sshannon 	register delta;
34610880Sshannon 	struct range trange;
34710880Sshannon 	delta = last[3] - last[i];
34810880Sshannon 	trange.from = rnew->from - delta;
34910880Sshannon 	trange.to = rnew->to - delta;
35010880Sshannon 	change(i,&trange,1);
35110880Sshannon }
35210880Sshannon 
35310880Sshannon /*	skip to just befor line number from in file i
35410880Sshannon  *	if "pr" is nonzero, print all skipped stuff
35510880Sshannon  * w	with string pr as a prefix
35610880Sshannon */
35710880Sshannon skip(i,from,pr)
35810880Sshannon char *pr;
35910880Sshannon {
36010880Sshannon 	register j,n;
36110880Sshannon 	for(n=0;cline[i]<from-1;n+=j) {
36210880Sshannon 		if((j=getline(fp[i]))==0)
36310880Sshannon 			trouble();
36410880Sshannon 		if(pr)
36510880Sshannon 			printf("%s%s",pr,line);
36610880Sshannon 		cline[i]++;
36710880Sshannon 	}
36810880Sshannon 	return(n);
36910880Sshannon }
37010880Sshannon 
37110880Sshannon /*	return 1 or 0 according as the old range
37210880Sshannon  *	(in file 1) contains exactly the same data
37310880Sshannon  *	as the new range (in file 2)
37410880Sshannon */
37510880Sshannon duplicate(r1,r2)
37610880Sshannon struct range *r1, *r2;
37710880Sshannon {
37810880Sshannon 	register c,d;
37910880Sshannon 	register nchar;
38010880Sshannon 	int nline;
38110880Sshannon 	if(r1->to-r1->from != r2->to-r2->from)
38210880Sshannon 		return(0);
38310880Sshannon 	skip(0,r1->from,(char *)0);
38410880Sshannon 	skip(1,r2->from,(char *)0);
38510880Sshannon 	nchar = 0;
38610880Sshannon 	for(nline=0;nline<r1->to-r1->from;nline++) {
38710880Sshannon 		do {
38810880Sshannon 			c = getc(fp[0]);
38910880Sshannon 			d = getc(fp[1]);
39010880Sshannon 			if(c== -1||d== -1)
39110880Sshannon 				trouble();
39210880Sshannon 			nchar++;
39310880Sshannon 			if(c!=d) {
39410880Sshannon 				repos(nchar);
39510880Sshannon 				return;
39610880Sshannon 			}
39710880Sshannon 		} while(c!= '\n');
39810880Sshannon 	}
39910880Sshannon 	repos(nchar);
40010880Sshannon 	return(1);
40110880Sshannon }
40210880Sshannon 
40310880Sshannon repos(nchar)
40410880Sshannon {
40510880Sshannon 	register i;
40610880Sshannon 	for(i=0;i<2;i++)
40710880Sshannon 		fseek(fp[i], (long)-nchar, 1);
40810880Sshannon }
40910880Sshannon 
41010880Sshannon trouble()
41110880Sshannon {
41210880Sshannon 	fprintf(stderr,"diff3: logic error\n");
41310880Sshannon 	abort();
41410880Sshannon }
41510880Sshannon 
41610880Sshannon /*	collect an editing script for later regurgitation
41710880Sshannon */
41810880Sshannon edit(diff,dup,j)
41910880Sshannon struct diff *diff;
42010880Sshannon {
42110880Sshannon 	if(((dup+1)&eflag)==0)
42210880Sshannon 		return(j);
42310880Sshannon 	j++;
424*24331Svan         overlap[j] = !dup;
425*24331Svan         if (!dup) overlapcnt++;
42610880Sshannon 	de[j].old.from = diff->old.from;
42710880Sshannon 	de[j].old.to = diff->old.to;
42810880Sshannon 	de[j].new.from = de[j-1].new.to
42910880Sshannon 	    +skip(2,diff->new.from,(char *)0);
43010880Sshannon 	de[j].new.to = de[j].new.from
43110880Sshannon 	    +skip(2,diff->new.to,(char *)0);
43210880Sshannon 	return(j);
43310880Sshannon }
43410880Sshannon 
43510880Sshannon /*		regurgitate */
43610880Sshannon edscript(n)
43710880Sshannon {
43810880Sshannon 	register j,k;
43910880Sshannon 	char block[BUFSIZ];
44010880Sshannon 	for(n=n;n>0;n--) {
441*24331Svan                 if (!oflag || !overlap[n])
442*24331Svan                         prange(&de[n].old);
443*24331Svan                 else
444*24331Svan                         printf("%da\n=======\n", de[n].old.to -1);
44510880Sshannon 		fseek(fp[2], (long)de[n].new.from, 0);
44610880Sshannon 		for(k=de[n].new.to-de[n].new.from;k>0;k-= j) {
44710880Sshannon 			j = k>BUFSIZ?BUFSIZ:k;
44810880Sshannon 			if(fread(block,1,j,fp[2])!=j)
44910880Sshannon 				trouble();
45010880Sshannon 			fwrite(block, 1, j, stdout);
45110880Sshannon 		}
452*24331Svan                 if (!oflag || !overlap[n])
453*24331Svan                         printf(".\n");
454*24331Svan                 else {
455*24331Svan                         printf("%s\n.\n",f3mark);
456*24331Svan                         printf("%da\n%s\n.\n",de[n].old.from-1,f1mark);
457*24331Svan                 }
45810880Sshannon 	}
459*24331Svan         exit(overlapcnt);
46010880Sshannon }
461