4.3BSD beta release manual page
[unix-history] / usr / src / usr.bin / diff / diff3 / diff3.c
CommitLineData
f6227721
SL
1#ifndef lint
2static char sccsid[] = "@(#)diff3.c 4.2 (Berkeley) %G%";
3#endif
953c3384
BS
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
17struct 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 */
23struct 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*/
32struct diff d13[NC];
33struct diff d23[NC];
34struct diff de[NC];
35char line[256];
36FILE *fp[3];
37int linct[3] = {0,0,0};
38/* the number of the last-read line in each file
39 * is kept in cline[0-2]
40*/
41int cline[3];
42/* the latest known correspondence between line
43 * numbers of the 3 files is stored in last[1-3]
44*/
45int last[4];
46int eflag;
47int debug = 0;
48
49main(argc,argv)
50char **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
89readin(name,dd)
90char *name;
91struct 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
132number(lc)
133char **lc;
134{
135 register nn;
136 nn = 0;
137 while(digit(**lc))
138 nn = nn*10 + *(*lc)++ - '0';
139 return(nn);
140}
141
142digit(c)
143{
144 return(c>='0'&&c<='9');
145}
146
147getchange(b)
148FILE *b;
149{
150 while(getline(b))
151 if(digit(line[0]))
152 return(1);
153 return(0);
154}
155
156getline(b)
157FILE *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
173merge(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
272separate(s)
273char *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*/
282change(i,rold,dup)
283struct 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*/
300prange(rold)
301struct 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*/
318keep(i,rold,rnew)
319struct 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*/
333skip(i,from,pr)
334char *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*/
351duplicate(r1,r2)
352struct 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
379repos(nchar)
380{
381 register i;
382 for(i=0;i<2;i++)
383 fseek(fp[i], (long)-nchar, 1);
384}
385
386trouble()
387{
388 fprintf(stderr,"diff3: logic error\n");
389 abort();
390}
391
392/* collect an editing script for later regurgitation
393*/
394edit(diff,dup,j)
395struct 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 */
410edscript(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}