#include # /* diff3 - 3-way differential file comparison*/ /* diff3 [-e] d13 d23 f1 f2 f3 * * d13 = diff report on f1 vs f3 * d23 = diff report on f2 vs f3 * f1, f2, f3 the 3 files */ struct range {int from,to; }; /* from is first in range of changed lines * to is last+1 * from=to=line after point of insertion * for added lines */ struct diff {struct range old, new;}; #define NC 200 /* de is used to gather editing scripts, * that are later spewed out in reverse order. * its first element must be all zero * the "new" component of de contains line positions * or byte positions depending on when you look(!?) */ struct diff d13[NC]; struct diff d23[NC]; struct diff de[NC]; char line[256]; FILE *fp[3]; int linct[3] = {0,0,0}; /* the number of the last-read line in each file * is kept in cline[0-2] */ int cline[3]; /* the latest known correspondence between line * numbers of the 3 files is stored in last[1-3] */ int last[4]; int eflag; int debug = 0; main(argc,argv) char **argv; { register i,m,n; if(*argv[1]=='-') { switch(argv[1][1]) { default: eflag = 3; break; case '3': eflag = 2; break; case 'x': eflag = 1; } argv++; argc--; } if(argc<6) { fprintf(stderr,"diff3: arg count\n"); exit(1); } m = readin(argv[1],d13); n = readin(argv[2],d23); for(i=0;i<=2;i++) if((fp[i] = fopen(argv[i+3],"r")) == NULL) { printf("diff3: can't open %s\n",argv[i+3]); exit(1); } merge(m,n); } /*pick up the line numbers of allcahnges from * one change file * (this puts the numbers in a vector, which is not * strictly necessary, since the vector is processed * in one sequential pass. The vector could be optimized * out of existence) */ readin(name,dd) char *name; struct diff *dd; { register i; int a,b,c,d; char kind; char *p; fp[0] = fopen(name,"r"); for(i=0;getchange(fp[0]);i++) { if(i>=NC) { fprintf(stderr,"diff3: too many changes\n"); exit(0); } p = line; a = b = number(&p); if(*p==',') { p++; b = number(&p); } kind = *p++; c = d = number(&p); if(*p==',') { p++; d = number(&p); } if(kind=='a') a++; if(kind=='d') c++; b++; d++; dd[i].old.from = a; dd[i].old.to = b; dd[i].new.from = c; dd[i].new.to = d; } dd[i].old.from = dd[i-1].old.to; dd[i].new.from = dd[i-1].new.to; fclose(fp[0]); return(i); } number(lc) char **lc; { register nn; nn = 0; while(digit(**lc)) nn = nn*10 + *(*lc)++ - '0'; return(nn); } digit(c) { return(c>='0'&&c<='9'); } getchange(b) FILE *b; { while(getline(b)) if(digit(line[0])) return(1); return(0); } getline(b) FILE *b; { register i, c; for(i=0;iold.from,d1->old.to, d1->new.from,d1->new.to, d2->old.from,d2->old.to, d2->new.from,d2->new.to); } /* first file is different from others*/ if(!t2||t1&&d1->new.to < d2->new.from) { /* stuff peculiar to 1st file */ if(eflag==0) { separate("1"); change(1,&d1->old,0); keep(2,&d1->old,&d1->new); change(3,&d1->new,0); } d1++; continue; } /* second file is different from others*/ if(!t1||t2&&d2->new.to < d1->new.from) { if(eflag==0) { separate("2"); keep(1,&d2->old,&d2->new); change(2,&d2->old,0); change(3,&d2->new,0); } d2++; continue; } /* merge overlapping changes in first file * this happens after extension see below*/ if(d1+1new.to>=d1[1].new.from) { d1[1].old.from = d1->old.from; d1[1].new.from = d1->new.from; d1++; continue; } /* merge overlapping changes in second*/ if(d2+1new.to>=d2[1].new.from) { d2[1].old.from = d2->old.from; d2[1].new.from = d2->new.from; d2++; continue; } /* stuff peculiar to third file or different in all*/ if(d1->new.from==d2->new.from&& d1->new.to==d2->new.to) { dup = duplicate(&d1->old,&d2->old); /* dup=0 means all files differ * dup =1 meands files 1&2 identical*/ if(eflag==0) { separate(dup?"3":""); change(1,&d1->old,dup); change(2,&d2->old,0); d3 = d1->old.to>d1->old.from?d1:d2; change(3,&d3->new,0); } else j = edit(d1,dup,j); d1++; d2++; continue; } /* overlapping changes from file1 & 2 * extend changes appropriately to * make them coincide*/ if(d1->new.fromnew.from) { d2->old.from -= d2->new.from-d1->new.from; d2->new.from = d1->new.from; } else if(d2->new.fromnew.from) { d1->old.from -= d1->new.from-d2->new.from; d1->new.from = d2->new.from; } if(d1->new.to >d2->new.to) { d2->old.to += d1->new.to - d2->new.to; d2->new.to = d1->new.to; } else if(d2->new.to >d1->new.to) { d1->old.to += d2->new.to - d1->new.to; d1->new.to = d2->new.to; } } if(eflag) edscript(j); } separate(s) char *s; { printf("====%s\n",s); } /* the range of ines rold.from thru rold.to in file i * is to be changed. it is to be printed only if * it does not duplicate something to be printed later */ change(i,rold,dup) struct range *rold; { printf("%d:",i); last[i] = rold->to; prange(rold); if(dup) return; if(debug) return; i--; skip(i,rold->from,(char *)0); skip(i,rold->to," "); } /* print the range of line numbers, rold.from thru rold.to * as n1,n2 or n1 */ prange(rold) struct range *rold; { if(rold->to<=rold->from) printf("%da\n",rold->from-1); else { printf("%d",rold->from); if(rold->to > rold->from+1) printf(",%d",rold->to-1); printf("c\n"); } } /* no difference was reported by diff between file 1(or 2) * and file 3, and an artificial dummy difference (trange) * must be ginned up to correspond to the change reported * in the other file */ keep(i,rold,rnew) struct range *rold, *rnew; { register delta; struct range trange; delta = last[3] - last[i]; trange.from = rnew->from - delta; trange.to = rnew->to - delta; change(i,&trange,1); } /* skip to just befor line number from in file i * if "pr" is nonzero, print all skipped stuff * w with string pr as a prefix */ skip(i,from,pr) char *pr; { register j,n; for(n=0;cline[i]to-r1->from != r2->to-r2->from) return(0); skip(0,r1->from,(char *)0); skip(1,r2->from,(char *)0); nchar = 0; for(nline=0;nlineto-r1->from;nline++) { do { c = getc(fp[0]); d = getc(fp[1]); if(c== -1||d== -1) trouble(); nchar++; if(c!=d) { repos(nchar); return; } } while(c!= '\n'); } repos(nchar); return(1); } repos(nchar) { register i; for(i=0;i<2;i++) fseek(fp[i], (long)-nchar, 1); } trouble() { fprintf(stderr,"diff3: logic error\n"); abort(); } /* collect an editing script for later regurgitation */ edit(diff,dup,j) struct diff *diff; { if(((dup+1)&eflag)==0) return(j); j++; de[j].old.from = diff->old.from; de[j].old.to = diff->old.to; de[j].new.from = de[j-1].new.to +skip(2,diff->new.from,(char *)0); de[j].new.to = de[j].new.from +skip(2,diff->new.to,(char *)0); return(j); } /* regurgitate */ edscript(n) { register j,k; char block[512]; for(n=n;n>0;n--) { prange(&de[n].old); fseek(fp[2], (long)de[n].new.from, 0); for(k=de[n].new.to-de[n].new.from;k>0;k-= j) { j = k>512?512:k; if(fread(block,1,j,fp[2])!=j) trouble(); fwrite(block, 1, j, stdout); } printf(".\n"); } }