xref: /original-bsd/usr.bin/diff/diff3/diff3.c (revision 7ecb520c)
1 #ifndef lint
2 static char sccsid[] = "@(#)diff3.c	4.2 (Berkeley) 08/11/83";
3 #endif
4 
5 #include <stdio.h>
6 #
7 
8 /* diff3 - 3-way differential file comparison*/
9 
10 /* diff3 [-e] d13 d23 f1 f2 f3
11  *
12  * d13 = diff report on f1 vs f3
13  * d23 = diff report on f2 vs f3
14  * f1, f2, f3 the 3 files
15 */
16 
17 struct  range {int from,to; };
18 	/* from is first in range of changed lines
19 	 * to is last+1
20 	 * from=to=line after point of insertion
21 	* for added lines
22 	*/
23 struct diff {struct range old, new;};
24 
25 #define NC 200
26 /* de is used to gather editing scripts,
27  * that are later spewed out in reverse order.
28  * its first element must be all zero
29  * the "new" component of de contains line positions
30  * or byte positions depending on when you look(!?)
31 */
32 struct diff d13[NC];
33 struct diff d23[NC];
34 struct diff de[NC];
35 char line[256];
36 FILE *fp[3];
37 int linct[3] = {0,0,0};
38 /*	the number of the last-read line in each file
39  *	is kept in cline[0-2]
40 */
41 int cline[3];
42 /*	the latest known correspondence between line
43  *	numbers of the 3 files is stored in last[1-3]
44 */
45 int last[4];
46 int eflag;
47 int debug  = 0;
48 
49 main(argc,argv)
50 char **argv;
51 {
52 	register i,m,n;
53 	if(*argv[1]=='-') {
54 		switch(argv[1][1]) {
55 		default:
56 			eflag = 3;
57 			break;
58 		case '3':
59 			eflag = 2;
60 			break;
61 		case 'x':
62 			eflag = 1;
63 		}
64 		argv++;
65 		argc--;
66 	}
67 	if(argc<6) {
68 		fprintf(stderr,"diff3: arg count\n");
69 		exit(1);
70 	}
71 	m = readin(argv[1],d13);
72 	n = readin(argv[2],d23);
73 	for(i=0;i<=2;i++)
74 		if((fp[i] = fopen(argv[i+3],"r")) == NULL) {
75 			printf("diff3: can't open %s\n",argv[i+3]);
76 			exit(1);
77 		}
78 	merge(m,n);
79 }
80 
81 /*pick up the line numbers of allcahnges from
82  * one change file
83  * (this puts the numbers in a vector, which is not
84  * strictly necessary, since the vector is processed
85  * in one sequential pass. The vector could be optimized
86  * out of existence)
87 */
88 
89 readin(name,dd)
90 char *name;
91 struct diff *dd;
92 {
93 	register i;
94 	int a,b,c,d;
95 	char kind;
96 	char *p;
97 	fp[0] = fopen(name,"r");
98 	for(i=0;getchange(fp[0]);i++) {
99 		if(i>=NC) {
100 			fprintf(stderr,"diff3: too many changes\n");
101 			exit(0);
102 		}
103 		p = line;
104 		a = b = number(&p);
105 		if(*p==',') {
106 			p++;
107 			b = number(&p);
108 		}
109 		kind = *p++;
110 		c = d = number(&p);
111 		if(*p==',') {
112 			p++;
113 			d = number(&p);
114 		}
115 		if(kind=='a')
116 			a++;
117 		if(kind=='d')
118 			c++;
119 		b++;
120 		d++;
121 		dd[i].old.from = a;
122 		dd[i].old.to = b;
123 		dd[i].new.from = c;
124 		dd[i].new.to = d;
125 	}
126 	dd[i].old.from = dd[i-1].old.to;
127 	dd[i].new.from = dd[i-1].new.to;
128 	fclose(fp[0]);
129 	return(i);
130 }
131 
132 number(lc)
133 char **lc;
134 {
135 	register nn;
136 	nn = 0;
137 	while(digit(**lc))
138 		nn = nn*10 + *(*lc)++ - '0';
139 	return(nn);
140 }
141 
142 digit(c)
143 {
144 	return(c>='0'&&c<='9');
145 }
146 
147 getchange(b)
148 FILE *b;
149 {
150 	while(getline(b))
151 		if(digit(line[0]))
152 			return(1);
153 	return(0);
154 }
155 
156 getline(b)
157 FILE *b;
158 {
159 	register i, c;
160 	for(i=0;i<sizeof(line)-1;i++) {
161 		c = getc(b);
162 		if(c==EOF)
163 			break;
164 		line[i] = c;
165 		if(c=='\n') {
166 			line[++i] = 0;
167 			return(i);
168 		}
169 	}
170 	return(0);
171 }
172 
173 merge(m1,m2)
174 {
175 	register struct diff *d1, *d2, *d3;
176 	int dup;
177 	int j;
178 	int t1,t2;
179 	d1 = d13;
180 	d2 = d23;
181 	j = 0;
182 	for(;(t1 = d1<d13+m1) | (t2 = d2<d23+m2);) {
183 		if(debug) {
184 			printf("%d,%d=%d,%d %d,%d=%d,%d\n",
185 			d1->old.from,d1->old.to,
186 			d1->new.from,d1->new.to,
187 			d2->old.from,d2->old.to,
188 			d2->new.from,d2->new.to);
189 		}
190 /*			first file is different from others*/
191 		if(!t2||t1&&d1->new.to < d2->new.from) {
192 /*			stuff peculiar to 1st file */
193 			if(eflag==0) {
194 				separate("1");
195 				change(1,&d1->old,0);
196 				keep(2,&d1->old,&d1->new);
197 				change(3,&d1->new,0);
198 			}
199 			d1++;
200 			continue;
201 		}
202 /*			second file is different from others*/
203 		if(!t1||t2&&d2->new.to < d1->new.from) {
204 			if(eflag==0) {
205 				separate("2");
206 				keep(1,&d2->old,&d2->new);
207 				change(2,&d2->old,0);
208 				change(3,&d2->new,0);
209 			}
210 			d2++;
211 			continue;
212 		}
213 /*			merge overlapping changes in first file
214  *			this happens after extension see below*/
215 		if(d1+1<d13+m1 &&
216 		   d1->new.to>=d1[1].new.from) {
217 			d1[1].old.from = d1->old.from;
218 			d1[1].new.from = d1->new.from;
219 			d1++;
220 			continue;
221 		}
222 /*			merge overlapping changes in second*/
223 		if(d2+1<d23+m2 &&
224 		   d2->new.to>=d2[1].new.from) {
225 			d2[1].old.from = d2->old.from;
226 			d2[1].new.from = d2->new.from;
227 			d2++;
228 			continue;
229 		}
230 /*			stuff peculiar to third file or different in all*/
231 		if(d1->new.from==d2->new.from&&
232 		   d1->new.to==d2->new.to) {
233 			dup = duplicate(&d1->old,&d2->old);
234 /*				dup=0 means all files differ
235  *				dup =1 meands files 1&2 identical*/
236 			if(eflag==0) {
237 				separate(dup?"3":"");
238 				change(1,&d1->old,dup);
239 				change(2,&d2->old,0);
240 				d3 = d1->old.to>d1->old.from?d1:d2;
241 				change(3,&d3->new,0);
242 			} else
243 				j = edit(d1,dup,j);
244 			d1++;
245 			d2++;
246 			continue;
247 		}
248 /*			overlapping changes from file1 & 2
249  *			extend changes appropriately to
250  *			make them coincide*/
251 		 if(d1->new.from<d2->new.from) {
252 			d2->old.from -= d2->new.from-d1->new.from;
253 			d2->new.from = d1->new.from;
254 		}
255 		else if(d2->new.from<d1->new.from) {
256 			d1->old.from -= d1->new.from-d2->new.from;
257 			d1->new.from = d2->new.from;
258 		}
259 		if(d1->new.to >d2->new.to) {
260 			d2->old.to += d1->new.to - d2->new.to;
261 			d2->new.to = d1->new.to;
262 		}
263 		else if(d2->new.to >d1->new.to) {
264 			d1->old.to += d2->new.to - d1->new.to;
265 			d1->new.to = d2->new.to;
266 		}
267 	}
268 	if(eflag)
269 		edscript(j);
270 }
271 
272 separate(s)
273 char *s;
274 {
275 	printf("====%s\n",s);
276 }
277 
278 /*	the range of ines rold.from thru rold.to in file i
279  *	is to be changed. it is to be printed only if
280  *	it does not duplicate something to be printed later
281 */
282 change(i,rold,dup)
283 struct range *rold;
284 {
285 	printf("%d:",i);
286 	last[i] = rold->to;
287 	prange(rold);
288 	if(dup)
289 		return;
290 	if(debug)
291 		return;
292 	i--;
293 	skip(i,rold->from,(char *)0);
294 	skip(i,rold->to,"  ");
295 }
296 
297 /*	print the range of line numbers, rold.from  thru rold.to
298  *	as n1,n2 or n1
299 */
300 prange(rold)
301 struct range *rold;
302 {
303 	if(rold->to<=rold->from)
304 		printf("%da\n",rold->from-1);
305 	else {
306 		printf("%d",rold->from);
307 		if(rold->to > rold->from+1)
308 			printf(",%d",rold->to-1);
309 		printf("c\n");
310 	}
311 }
312 
313 /*	no difference was reported by diff between file 1(or 2)
314  *	and file 3, and an artificial dummy difference (trange)
315  *	must be ginned up to correspond to the change reported
316  *	in the other file
317 */
318 keep(i,rold,rnew)
319 struct range *rold, *rnew;
320 {
321 	register delta;
322 	struct range trange;
323 	delta = last[3] - last[i];
324 	trange.from = rnew->from - delta;
325 	trange.to = rnew->to - delta;
326 	change(i,&trange,1);
327 }
328 
329 /*	skip to just befor line number from in file i
330  *	if "pr" is nonzero, print all skipped stuff
331  * w	with string pr as a prefix
332 */
333 skip(i,from,pr)
334 char *pr;
335 {
336 	register j,n;
337 	for(n=0;cline[i]<from-1;n+=j) {
338 		if((j=getline(fp[i]))==0)
339 			trouble();
340 		if(pr)
341 			printf("%s%s",pr,line);
342 		cline[i]++;
343 	}
344 	return(n);
345 }
346 
347 /*	return 1 or 0 according as the old range
348  *	(in file 1) contains exactly the same data
349  *	as the new range (in file 2)
350 */
351 duplicate(r1,r2)
352 struct range *r1, *r2;
353 {
354 	register c,d;
355 	register nchar;
356 	int nline;
357 	if(r1->to-r1->from != r2->to-r2->from)
358 		return(0);
359 	skip(0,r1->from,(char *)0);
360 	skip(1,r2->from,(char *)0);
361 	nchar = 0;
362 	for(nline=0;nline<r1->to-r1->from;nline++) {
363 		do {
364 			c = getc(fp[0]);
365 			d = getc(fp[1]);
366 			if(c== -1||d== -1)
367 				trouble();
368 			nchar++;
369 			if(c!=d) {
370 				repos(nchar);
371 				return;
372 			}
373 		} while(c!= '\n');
374 	}
375 	repos(nchar);
376 	return(1);
377 }
378 
379 repos(nchar)
380 {
381 	register i;
382 	for(i=0;i<2;i++)
383 		fseek(fp[i], (long)-nchar, 1);
384 }
385 
386 trouble()
387 {
388 	fprintf(stderr,"diff3: logic error\n");
389 	abort();
390 }
391 
392 /*	collect an editing script for later regurgitation
393 */
394 edit(diff,dup,j)
395 struct diff *diff;
396 {
397 	if(((dup+1)&eflag)==0)
398 		return(j);
399 	j++;
400 	de[j].old.from = diff->old.from;
401 	de[j].old.to = diff->old.to;
402 	de[j].new.from = de[j-1].new.to
403 	    +skip(2,diff->new.from,(char *)0);
404 	de[j].new.to = de[j].new.from
405 	    +skip(2,diff->new.to,(char *)0);
406 	return(j);
407 }
408 
409 /*		regurgitate */
410 edscript(n)
411 {
412 	register j,k;
413 	char block[BUFSIZ];
414 	for(n=n;n>0;n--) {
415 		prange(&de[n].old);
416 		fseek(fp[2], (long)de[n].new.from, 0);
417 		for(k=de[n].new.to-de[n].new.from;k>0;k-= j) {
418 			j = k>BUFSIZ?BUFSIZ:k;
419 			if(fread(block,1,j,fp[2])!=j)
420 				trouble();
421 			fwrite(block, 1, j, stdout);
422 		}
423 		printf(".\n");
424 	}
425 }
426