148249Sbostic /*-
2*61985Sbostic * Copyright (c) 1991, 1993
3*61985Sbostic * The Regents of the University of California. All rights reserved.
448249Sbostic *
548249Sbostic * %sccs.include.proprietary.c%
648249Sbostic */
748249Sbostic
814484Ssam #ifndef lint
9*61985Sbostic static char copyright[] =
10*61985Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*61985Sbostic The Regents of the University of California. All rights reserved.\n";
1248249Sbostic #endif /* not lint */
1310880Sshannon
1448249Sbostic #ifndef lint
15*61985Sbostic static char sccsid[] = "@(#)diff3.c 8.1 (Berkeley) 06/06/93";
1648249Sbostic #endif /* not lint */
1748249Sbostic
1810880Sshannon #include <stdio.h>
1910880Sshannon
2010880Sshannon /* diff3 - 3-way differential file comparison*/
2110880Sshannon
2224331Svan /* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3]
2310880Sshannon *
2410880Sshannon * d13 = diff report on f1 vs f3
2510880Sshannon * d23 = diff report on f2 vs f3
2610880Sshannon * f1, f2, f3 the 3 files
2724331Svan * if changes in f1 overlap with changes in f3, m1 and m3 are used
2824331Svan * to mark the overlaps; otherwise, the file names f1 and f3 are used
2924331Svan * (only for options E and X).
3010880Sshannon */
3110880Sshannon
3210880Sshannon struct range {int from,to; };
3310880Sshannon /* from is first in range of changed lines
3410880Sshannon * to is last+1
3510880Sshannon * from=to=line after point of insertion
3610880Sshannon * for added lines
3710880Sshannon */
3810880Sshannon struct diff {struct range old, new;};
3910880Sshannon
4010880Sshannon #define NC 200
4124331Svan struct diff d13[NC];
4224331Svan struct diff d23[NC];
4310880Sshannon /* de is used to gather editing scripts,
4410880Sshannon * that are later spewed out in reverse order.
4510880Sshannon * its first element must be all zero
4610880Sshannon * the "new" component of de contains line positions
4710880Sshannon * or byte positions depending on when you look(!?)
4824331Svan * array overlap indicates which sections in de correspond to
4924331Svan * lines that are different in all three files.
5010880Sshannon */
5110880Sshannon struct diff de[NC];
5224331Svan char overlap[NC];
5324331Svan int overlapcnt =0;
5424331Svan
5510880Sshannon char line[256];
5610880Sshannon FILE *fp[3];
5710880Sshannon /* the number of the last-read line in each file
5810880Sshannon * is kept in cline[0-2]
5910880Sshannon */
6010880Sshannon int cline[3];
6110880Sshannon /* the latest known correspondence between line
6210880Sshannon * numbers of the 3 files is stored in last[1-3]
6310880Sshannon */
6410880Sshannon int last[4];
6510880Sshannon int eflag;
6624331Svan int oflag; /* indicates whether to mark overlaps (-E or -X)*/
6710880Sshannon int debug = 0;
6824331Svan char f1mark[40], f3mark[40]; /*markers for -E and -X*/
6910880Sshannon
7024331Svan
main(argc,argv)7110880Sshannon main(argc,argv)
7210880Sshannon char **argv;
7310880Sshannon {
7410880Sshannon register i,m,n;
7524331Svan eflag=0; oflag=0;
7610880Sshannon if(*argv[1]=='-') {
7710880Sshannon switch(argv[1][1]) {
7810880Sshannon default:
7910880Sshannon eflag = 3;
8010880Sshannon break;
8110880Sshannon case '3':
8210880Sshannon eflag = 2;
8310880Sshannon break;
8410880Sshannon case 'x':
8510880Sshannon eflag = 1;
8624331Svan break;
8724331Svan case 'E':
8824331Svan eflag = 3;
8924331Svan oflag = 1;
9024331Svan break;
9124331Svan case 'X':
9224331Svan oflag = eflag = 1;
9324331Svan break;
9410880Sshannon }
9510880Sshannon argv++;
9610880Sshannon argc--;
9710880Sshannon }
9810880Sshannon if(argc<6) {
9910880Sshannon fprintf(stderr,"diff3: arg count\n");
10010880Sshannon exit(1);
10110880Sshannon }
10224331Svan if (oflag) {
10324444Svan (void)sprintf(f1mark,"<<<<<<< %s",argc>=7?argv[6]:argv[3]);
10424444Svan (void)sprintf(f3mark,">>>>>>> %s",argc>=8?argv[7]:argv[5]);
10524331Svan }
10624331Svan
10710880Sshannon m = readin(argv[1],d13);
10810880Sshannon n = readin(argv[2],d23);
10910880Sshannon for(i=0;i<=2;i++)
11010880Sshannon if((fp[i] = fopen(argv[i+3],"r")) == NULL) {
11110880Sshannon printf("diff3: can't open %s\n",argv[i+3]);
11210880Sshannon exit(1);
11310880Sshannon }
11410880Sshannon merge(m,n);
11510880Sshannon }
11610880Sshannon
11710880Sshannon /*pick up the line numbers of allcahnges from
11810880Sshannon * one change file
11910880Sshannon * (this puts the numbers in a vector, which is not
12010880Sshannon * strictly necessary, since the vector is processed
12110880Sshannon * in one sequential pass. The vector could be optimized
12210880Sshannon * out of existence)
12310880Sshannon */
12410880Sshannon
readin(name,dd)12510880Sshannon readin(name,dd)
12610880Sshannon char *name;
12710880Sshannon struct diff *dd;
12810880Sshannon {
12910880Sshannon register i;
13010880Sshannon int a,b,c,d;
13110880Sshannon char kind;
13210880Sshannon char *p;
13310880Sshannon fp[0] = fopen(name,"r");
13410880Sshannon for(i=0;getchange(fp[0]);i++) {
13510880Sshannon if(i>=NC) {
13610880Sshannon fprintf(stderr,"diff3: too many changes\n");
13710880Sshannon exit(0);
13810880Sshannon }
13910880Sshannon p = line;
14010880Sshannon a = b = number(&p);
14110880Sshannon if(*p==',') {
14210880Sshannon p++;
14310880Sshannon b = number(&p);
14410880Sshannon }
14510880Sshannon kind = *p++;
14610880Sshannon c = d = number(&p);
14710880Sshannon if(*p==',') {
14810880Sshannon p++;
14910880Sshannon d = number(&p);
15010880Sshannon }
15110880Sshannon if(kind=='a')
15210880Sshannon a++;
15310880Sshannon if(kind=='d')
15410880Sshannon c++;
15510880Sshannon b++;
15610880Sshannon d++;
15710880Sshannon dd[i].old.from = a;
15810880Sshannon dd[i].old.to = b;
15910880Sshannon dd[i].new.from = c;
16010880Sshannon dd[i].new.to = d;
16110880Sshannon }
16210880Sshannon dd[i].old.from = dd[i-1].old.to;
16310880Sshannon dd[i].new.from = dd[i-1].new.to;
16424444Svan (void)fclose(fp[0]);
16510880Sshannon return(i);
16610880Sshannon }
16710880Sshannon
number(lc)16810880Sshannon number(lc)
16910880Sshannon char **lc;
17010880Sshannon {
17110880Sshannon register nn;
17210880Sshannon nn = 0;
17310880Sshannon while(digit(**lc))
17410880Sshannon nn = nn*10 + *(*lc)++ - '0';
17510880Sshannon return(nn);
17610880Sshannon }
17710880Sshannon
digit(c)17810880Sshannon digit(c)
17910880Sshannon {
18010880Sshannon return(c>='0'&&c<='9');
18110880Sshannon }
18210880Sshannon
getchange(b)18310880Sshannon getchange(b)
18410880Sshannon FILE *b;
18510880Sshannon {
18610880Sshannon while(getline(b))
18710880Sshannon if(digit(line[0]))
18810880Sshannon return(1);
18910880Sshannon return(0);
19010880Sshannon }
19110880Sshannon
getline(b)19210880Sshannon getline(b)
19310880Sshannon FILE *b;
19410880Sshannon {
19510880Sshannon register i, c;
19610880Sshannon for(i=0;i<sizeof(line)-1;i++) {
19710880Sshannon c = getc(b);
19810880Sshannon if(c==EOF)
19910880Sshannon break;
20010880Sshannon line[i] = c;
20110880Sshannon if(c=='\n') {
20210880Sshannon line[++i] = 0;
20310880Sshannon return(i);
20410880Sshannon }
20510880Sshannon }
20610880Sshannon return(0);
20710880Sshannon }
20810880Sshannon
merge(m1,m2)20910880Sshannon merge(m1,m2)
21010880Sshannon {
21110880Sshannon register struct diff *d1, *d2, *d3;
21210880Sshannon int dup;
21310880Sshannon int j;
21410880Sshannon int t1,t2;
21510880Sshannon d1 = d13;
21610880Sshannon d2 = d23;
21710880Sshannon j = 0;
21810880Sshannon for(;(t1 = d1<d13+m1) | (t2 = d2<d23+m2);) {
21910880Sshannon if(debug) {
22010880Sshannon printf("%d,%d=%d,%d %d,%d=%d,%d\n",
22110880Sshannon d1->old.from,d1->old.to,
22210880Sshannon d1->new.from,d1->new.to,
22310880Sshannon d2->old.from,d2->old.to,
22410880Sshannon d2->new.from,d2->new.to);
22510880Sshannon }
22610880Sshannon /* first file is different from others*/
22710880Sshannon if(!t2||t1&&d1->new.to < d2->new.from) {
22810880Sshannon /* stuff peculiar to 1st file */
22910880Sshannon if(eflag==0) {
23010880Sshannon separate("1");
23110880Sshannon change(1,&d1->old,0);
23224444Svan keep(2,&d1->new);
23310880Sshannon change(3,&d1->new,0);
23410880Sshannon }
23510880Sshannon d1++;
23610880Sshannon continue;
23710880Sshannon }
23810880Sshannon /* second file is different from others*/
23910880Sshannon if(!t1||t2&&d2->new.to < d1->new.from) {
24010880Sshannon if(eflag==0) {
24110880Sshannon separate("2");
24224444Svan keep(1,&d2->new);
24310880Sshannon change(2,&d2->old,0);
24410880Sshannon change(3,&d2->new,0);
24510880Sshannon }
24610880Sshannon d2++;
24710880Sshannon continue;
24810880Sshannon }
24910880Sshannon /* merge overlapping changes in first file
25010880Sshannon * this happens after extension see below*/
25110880Sshannon if(d1+1<d13+m1 &&
25210880Sshannon d1->new.to>=d1[1].new.from) {
25310880Sshannon d1[1].old.from = d1->old.from;
25410880Sshannon d1[1].new.from = d1->new.from;
25510880Sshannon d1++;
25610880Sshannon continue;
25710880Sshannon }
25810880Sshannon /* merge overlapping changes in second*/
25910880Sshannon if(d2+1<d23+m2 &&
26010880Sshannon d2->new.to>=d2[1].new.from) {
26110880Sshannon d2[1].old.from = d2->old.from;
26210880Sshannon d2[1].new.from = d2->new.from;
26310880Sshannon d2++;
26410880Sshannon continue;
26510880Sshannon }
26610880Sshannon /* stuff peculiar to third file or different in all*/
26710880Sshannon if(d1->new.from==d2->new.from&&
26810880Sshannon d1->new.to==d2->new.to) {
26910880Sshannon dup = duplicate(&d1->old,&d2->old);
27010880Sshannon /* dup=0 means all files differ
27110880Sshannon * dup =1 meands files 1&2 identical*/
27210880Sshannon if(eflag==0) {
27310880Sshannon separate(dup?"3":"");
27410880Sshannon change(1,&d1->old,dup);
27510880Sshannon change(2,&d2->old,0);
27610880Sshannon d3 = d1->old.to>d1->old.from?d1:d2;
27710880Sshannon change(3,&d3->new,0);
27810880Sshannon } else
27910880Sshannon j = edit(d1,dup,j);
28010880Sshannon d1++;
28110880Sshannon d2++;
28210880Sshannon continue;
28310880Sshannon }
28410880Sshannon /* overlapping changes from file1 & 2
28510880Sshannon * extend changes appropriately to
28610880Sshannon * make them coincide*/
28710880Sshannon if(d1->new.from<d2->new.from) {
28810880Sshannon d2->old.from -= d2->new.from-d1->new.from;
28910880Sshannon d2->new.from = d1->new.from;
29010880Sshannon }
29110880Sshannon else if(d2->new.from<d1->new.from) {
29210880Sshannon d1->old.from -= d1->new.from-d2->new.from;
29310880Sshannon d1->new.from = d2->new.from;
29410880Sshannon }
29510880Sshannon if(d1->new.to >d2->new.to) {
29610880Sshannon d2->old.to += d1->new.to - d2->new.to;
29710880Sshannon d2->new.to = d1->new.to;
29810880Sshannon }
29910880Sshannon else if(d2->new.to >d1->new.to) {
30010880Sshannon d1->old.to += d2->new.to - d1->new.to;
30110880Sshannon d1->new.to = d2->new.to;
30210880Sshannon }
30310880Sshannon }
30410880Sshannon if(eflag)
30510880Sshannon edscript(j);
30610880Sshannon }
30710880Sshannon
separate(s)30810880Sshannon separate(s)
30910880Sshannon char *s;
31010880Sshannon {
31110880Sshannon printf("====%s\n",s);
31210880Sshannon }
31310880Sshannon
31410880Sshannon /* the range of ines rold.from thru rold.to in file i
31510880Sshannon * is to be changed. it is to be printed only if
31610880Sshannon * it does not duplicate something to be printed later
31710880Sshannon */
31810880Sshannon change(i,rold,dup)
31910880Sshannon struct range *rold;
32010880Sshannon {
32110880Sshannon printf("%d:",i);
32210880Sshannon last[i] = rold->to;
32310880Sshannon prange(rold);
32410880Sshannon if(dup)
32510880Sshannon return;
32610880Sshannon if(debug)
32710880Sshannon return;
32810880Sshannon i--;
32924444Svan (void)skip(i,rold->from,(char *)0);
33024444Svan (void)skip(i,rold->to," ");
33110880Sshannon }
33210880Sshannon
33310880Sshannon /* print the range of line numbers, rold.from thru rold.to
33410880Sshannon * as n1,n2 or n1
33510880Sshannon */
33610880Sshannon prange(rold)
33710880Sshannon struct range *rold;
33810880Sshannon {
33910880Sshannon if(rold->to<=rold->from)
34010880Sshannon printf("%da\n",rold->from-1);
34110880Sshannon else {
34210880Sshannon printf("%d",rold->from);
34310880Sshannon if(rold->to > rold->from+1)
34410880Sshannon printf(",%d",rold->to-1);
34510880Sshannon printf("c\n");
34610880Sshannon }
34710880Sshannon }
34810880Sshannon
34910880Sshannon /* no difference was reported by diff between file 1(or 2)
35010880Sshannon * and file 3, and an artificial dummy difference (trange)
35110880Sshannon * must be ginned up to correspond to the change reported
35210880Sshannon * in the other file
35310880Sshannon */
35424444Svan keep(i,rnew)
35524444Svan struct range *rnew;
35610880Sshannon {
35710880Sshannon register delta;
35810880Sshannon struct range trange;
35910880Sshannon delta = last[3] - last[i];
36010880Sshannon trange.from = rnew->from - delta;
36110880Sshannon trange.to = rnew->to - delta;
36210880Sshannon change(i,&trange,1);
36310880Sshannon }
36410880Sshannon
36510880Sshannon /* skip to just befor line number from in file i
36610880Sshannon * if "pr" is nonzero, print all skipped stuff
36710880Sshannon * w with string pr as a prefix
36810880Sshannon */
skip(i,from,pr)36910880Sshannon skip(i,from,pr)
37010880Sshannon char *pr;
37110880Sshannon {
37210880Sshannon register j,n;
37310880Sshannon for(n=0;cline[i]<from-1;n+=j) {
37410880Sshannon if((j=getline(fp[i]))==0)
37510880Sshannon trouble();
37610880Sshannon if(pr)
37710880Sshannon printf("%s%s",pr,line);
37810880Sshannon cline[i]++;
37910880Sshannon }
38010880Sshannon return(n);
38110880Sshannon }
38210880Sshannon
38310880Sshannon /* return 1 or 0 according as the old range
38410880Sshannon * (in file 1) contains exactly the same data
38510880Sshannon * as the new range (in file 2)
38610880Sshannon */
38710880Sshannon duplicate(r1,r2)
38810880Sshannon struct range *r1, *r2;
38910880Sshannon {
39010880Sshannon register c,d;
39110880Sshannon register nchar;
39210880Sshannon int nline;
39310880Sshannon if(r1->to-r1->from != r2->to-r2->from)
39410880Sshannon return(0);
39524444Svan (void)skip(0,r1->from,(char *)0);
39624444Svan (void)skip(1,r2->from,(char *)0);
39710880Sshannon nchar = 0;
39810880Sshannon for(nline=0;nline<r1->to-r1->from;nline++) {
39910880Sshannon do {
40010880Sshannon c = getc(fp[0]);
40110880Sshannon d = getc(fp[1]);
40210880Sshannon if(c== -1||d== -1)
40310880Sshannon trouble();
40410880Sshannon nchar++;
40510880Sshannon if(c!=d) {
40610880Sshannon repos(nchar);
40724444Svan return(0);
40810880Sshannon }
40910880Sshannon } while(c!= '\n');
41010880Sshannon }
41110880Sshannon repos(nchar);
41210880Sshannon return(1);
41310880Sshannon }
41410880Sshannon
repos(nchar)41510880Sshannon repos(nchar)
41610880Sshannon {
41710880Sshannon register i;
41810880Sshannon for(i=0;i<2;i++)
41924444Svan (void)fseek(fp[i], (long)-nchar, 1);
42010880Sshannon }
42110880Sshannon
trouble()42210880Sshannon trouble()
42310880Sshannon {
42410880Sshannon fprintf(stderr,"diff3: logic error\n");
42510880Sshannon abort();
42610880Sshannon }
42710880Sshannon
42810880Sshannon /* collect an editing script for later regurgitation
42910880Sshannon */
43010880Sshannon edit(diff,dup,j)
43110880Sshannon struct diff *diff;
43210880Sshannon {
43310880Sshannon if(((dup+1)&eflag)==0)
43410880Sshannon return(j);
43510880Sshannon j++;
43624331Svan overlap[j] = !dup;
43724331Svan if (!dup) overlapcnt++;
43810880Sshannon de[j].old.from = diff->old.from;
43910880Sshannon de[j].old.to = diff->old.to;
44010880Sshannon de[j].new.from = de[j-1].new.to
44110880Sshannon +skip(2,diff->new.from,(char *)0);
44210880Sshannon de[j].new.to = de[j].new.from
44310880Sshannon +skip(2,diff->new.to,(char *)0);
44410880Sshannon return(j);
44510880Sshannon }
44610880Sshannon
44710880Sshannon /* regurgitate */
edscript(n)44810880Sshannon edscript(n)
44910880Sshannon {
45010880Sshannon register j,k;
45110880Sshannon char block[BUFSIZ];
45210880Sshannon for(n=n;n>0;n--) {
45324331Svan if (!oflag || !overlap[n])
45424331Svan prange(&de[n].old);
45524331Svan else
45624331Svan printf("%da\n=======\n", de[n].old.to -1);
45724444Svan (void)fseek(fp[2], (long)de[n].new.from, 0);
45810880Sshannon for(k=de[n].new.to-de[n].new.from;k>0;k-= j) {
45910880Sshannon j = k>BUFSIZ?BUFSIZ:k;
46010880Sshannon if(fread(block,1,j,fp[2])!=j)
46110880Sshannon trouble();
46224444Svan (void)fwrite(block, 1, j, stdout);
46310880Sshannon }
46424331Svan if (!oflag || !overlap[n])
46524331Svan printf(".\n");
46624331Svan else {
46724331Svan printf("%s\n.\n",f3mark);
46824331Svan printf("%da\n%s\n.\n",de[n].old.from-1,f1mark);
46924331Svan }
47010880Sshannon }
47124331Svan exit(overlapcnt);
47210880Sshannon }
473