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