Commit | Line | Data |
---|---|---|
f6227721 | 1 | #ifndef lint |
88bf5909 | 2 | static char sccsid[] = "@(#)diff3.c 4.4 (Berkeley) %G%"; |
f6227721 | 3 | #endif |
953c3384 BS |
4 | |
5 | #include <stdio.h> | |
953c3384 BS |
6 | |
7 | /* diff3 - 3-way differential file comparison*/ | |
8 | ||
a040b36e | 9 | /* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3] |
953c3384 BS |
10 | * |
11 | * d13 = diff report on f1 vs f3 | |
12 | * d23 = diff report on f2 vs f3 | |
13 | * f1, f2, f3 the 3 files | |
a040b36e VJ |
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). | |
953c3384 BS |
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 | |
a040b36e VJ |
28 | struct diff d13[NC]; |
29 | struct diff d23[NC]; | |
953c3384 BS |
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(!?) | |
a040b36e VJ |
35 | * array overlap indicates which sections in de correspond to |
36 | * lines that are different in all three files. | |
953c3384 | 37 | */ |
953c3384 | 38 | struct diff de[NC]; |
a040b36e VJ |
39 | char overlap[NC]; |
40 | int overlapcnt =0; | |
41 | ||
953c3384 BS |
42 | char line[256]; |
43 | FILE *fp[3]; | |
953c3384 BS |
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; | |
a040b36e | 53 | int oflag; /* indicates whether to mark overlaps (-E or -X)*/ |
953c3384 | 54 | int debug = 0; |
a040b36e VJ |
55 | char f1mark[40], f3mark[40]; /*markers for -E and -X*/ |
56 | ||
953c3384 BS |
57 | |
58 | main(argc,argv) | |
59 | char **argv; | |
60 | { | |
61 | register i,m,n; | |
a040b36e | 62 | eflag=0; oflag=0; |
953c3384 BS |
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; | |
a040b36e VJ |
73 | break; |
74 | case 'E': | |
75 | eflag = 3; | |
76 | oflag = 1; | |
77 | break; | |
78 | case 'X': | |
79 | oflag = eflag = 1; | |
80 | break; | |
953c3384 BS |
81 | } |
82 | argv++; | |
83 | argc--; | |
84 | } | |
85 | if(argc<6) { | |
86 | fprintf(stderr,"diff3: arg count\n"); | |
87 | exit(1); | |
88 | } | |
a040b36e | 89 | if (oflag) { |
88bf5909 VJ |
90 | (void)sprintf(f1mark,"<<<<<<< %s",argc>=7?argv[6]:argv[3]); |
91 | (void)sprintf(f3mark,">>>>>>> %s",argc>=8?argv[7]:argv[5]); | |
a040b36e VJ |
92 | } |
93 | ||
953c3384 BS |
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; | |
88bf5909 | 151 | (void)fclose(fp[0]); |
953c3384 BS |
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); | |
88bf5909 | 219 | keep(2,&d1->new); |
953c3384 BS |
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"); | |
88bf5909 | 229 | keep(1,&d2->new); |
953c3384 BS |
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--; | |
88bf5909 VJ |
316 | (void)skip(i,rold->from,(char *)0); |
317 | (void)skip(i,rold->to," "); | |
953c3384 BS |
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 | */ | |
88bf5909 VJ |
341 | keep(i,rnew) |
342 | struct range *rnew; | |
953c3384 BS |
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); | |
88bf5909 VJ |
382 | (void)skip(0,r1->from,(char *)0); |
383 | (void)skip(1,r2->from,(char *)0); | |
953c3384 BS |
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); | |
88bf5909 | 394 | return(0); |
953c3384 BS |
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++) | |
88bf5909 | 406 | (void)fseek(fp[i], (long)-nchar, 1); |
953c3384 BS |
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++; | |
a040b36e VJ |
423 | overlap[j] = !dup; |
424 | if (!dup) overlapcnt++; | |
953c3384 BS |
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--) { | |
a040b36e VJ |
440 | if (!oflag || !overlap[n]) |
441 | prange(&de[n].old); | |
442 | else | |
443 | printf("%da\n=======\n", de[n].old.to -1); | |
88bf5909 | 444 | (void)fseek(fp[2], (long)de[n].new.from, 0); |
953c3384 BS |
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(); | |
88bf5909 | 449 | (void)fwrite(block, 1, j, stdout); |
953c3384 | 450 | } |
a040b36e VJ |
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 | } | |
953c3384 | 457 | } |
a040b36e | 458 | exit(overlapcnt); |
953c3384 | 459 | } |