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