xref: /openbsd-src/usr.bin/diff3/diff3prog.c (revision 2e6633a8ffcb18d5d77f5bbe43cfaabaa81cd9c0)
1*2e6633a8Stobias /*	$OpenBSD: diff3prog.c,v 1.10 2008/02/27 18:10:05 tobias Exp $	*/
2d407ca27Smillert 
3d407ca27Smillert /*
4d407ca27Smillert  * Copyright (C) Caldera International Inc.  2001-2002.
5d407ca27Smillert  * All rights reserved.
6d407ca27Smillert  *
7d407ca27Smillert  * Redistribution and use in source and binary forms, with or without
8d407ca27Smillert  * modification, are permitted provided that the following conditions
9d407ca27Smillert  * are met:
10d407ca27Smillert  * 1. Redistributions of source code and documentation must retain the above
11d407ca27Smillert  *    copyright notice, this list of conditions and the following disclaimer.
12d407ca27Smillert  * 2. Redistributions in binary form must reproduce the above copyright
13d407ca27Smillert  *    notice, this list of conditions and the following disclaimer in the
14d407ca27Smillert  *    documentation and/or other materials provided with the distribution.
15d407ca27Smillert  * 3. All advertising materials mentioning features or use of this software
16d407ca27Smillert  *    must display the following acknowledgement:
17d407ca27Smillert  *	This product includes software developed or owned by Caldera
18d407ca27Smillert  *	International, Inc.
19d407ca27Smillert  * 4. Neither the name of Caldera International, Inc. nor the names of other
20d407ca27Smillert  *    contributors may be used to endorse or promote products derived from
21d407ca27Smillert  *    this software without specific prior written permission.
22d407ca27Smillert  *
23d407ca27Smillert  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
24d407ca27Smillert  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
25d407ca27Smillert  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26d407ca27Smillert  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27d407ca27Smillert  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
28d407ca27Smillert  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29d407ca27Smillert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30d407ca27Smillert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31d407ca27Smillert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32d407ca27Smillert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33d407ca27Smillert  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34d407ca27Smillert  * POSSIBILITY OF SUCH DAMAGE.
35d407ca27Smillert  */
36d407ca27Smillert /*-
37d407ca27Smillert  * Copyright (c) 1991, 1993
38d407ca27Smillert  *	The Regents of the University of California.  All rights reserved.
39d407ca27Smillert  *
40d407ca27Smillert  * Redistribution and use in source and binary forms, with or without
41d407ca27Smillert  * modification, are permitted provided that the following conditions
42d407ca27Smillert  * are met:
43d407ca27Smillert  * 1. Redistributions of source code must retain the above copyright
44d407ca27Smillert  *    notice, this list of conditions and the following disclaimer.
45d407ca27Smillert  * 2. Redistributions in binary form must reproduce the above copyright
46d407ca27Smillert  *    notice, this list of conditions and the following disclaimer in the
47d407ca27Smillert  *    documentation and/or other materials provided with the distribution.
48d407ca27Smillert  * 3. Neither the name of the University nor the names of its contributors
49d407ca27Smillert  *    may be used to endorse or promote products derived from this software
50d407ca27Smillert  *    without specific prior written permission.
51d407ca27Smillert  *
52d407ca27Smillert  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53d407ca27Smillert  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54d407ca27Smillert  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55d407ca27Smillert  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56d407ca27Smillert  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57d407ca27Smillert  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58d407ca27Smillert  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59d407ca27Smillert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60d407ca27Smillert  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61d407ca27Smillert  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62d407ca27Smillert  * SUCH DAMAGE.
63d407ca27Smillert  *
64d407ca27Smillert  *	@(#)diff3.c	8.1 (Berkeley) 6/6/93
65d407ca27Smillert  */
66d407ca27Smillert 
67d407ca27Smillert #ifndef lint
68d407ca27Smillert static const char copyright[] =
69d407ca27Smillert "@(#) Copyright (c) 1991, 1993\n\
70d407ca27Smillert 	The Regents of the University of California.  All rights reserved.\n";
71d407ca27Smillert #endif /* not lint */
72d407ca27Smillert 
73d407ca27Smillert #ifndef lint
74*2e6633a8Stobias static const char rcsid[] = "$OpenBSD: diff3prog.c,v 1.10 2008/02/27 18:10:05 tobias Exp $";
75d407ca27Smillert #endif /* not lint */
76d407ca27Smillert 
77d407ca27Smillert #include <stdio.h>
78d407ca27Smillert #include <stdlib.h>
79ad7191bdSmillert #include <string.h>
80d407ca27Smillert #include <ctype.h>
81d407ca27Smillert #include <err.h>
82d407ca27Smillert 
83d407ca27Smillert /* diff3 - 3-way differential file comparison */
84d407ca27Smillert 
85d407ca27Smillert /* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3]
86d407ca27Smillert  *
87d407ca27Smillert  * d13 = diff report on f1 vs f3
88d407ca27Smillert  * d23 = diff report on f2 vs f3
89d407ca27Smillert  * f1, f2, f3 the 3 files
90d407ca27Smillert  * if changes in f1 overlap with changes in f3, m1 and m3 are used
91d407ca27Smillert  * to mark the overlaps; otherwise, the file names f1 and f3 are used
92d407ca27Smillert  * (only for options E and X).
93d407ca27Smillert  */
94d407ca27Smillert 
95d407ca27Smillert /*
96d407ca27Smillert  * "from" is first in range of changed lines; "to" is last+1
97d407ca27Smillert  * from=to=line after point of insertion for added lines.
98d407ca27Smillert  */
99d407ca27Smillert struct  range {
100d407ca27Smillert 	int from;
101d407ca27Smillert 	int to;
102d407ca27Smillert };
103d407ca27Smillert struct diff {
104d407ca27Smillert 	struct range old;
105d407ca27Smillert 	struct range new;
106d407ca27Smillert };
107d407ca27Smillert 
10868496fbfSotto size_t szchanges;
10968496fbfSotto 
11068496fbfSotto struct diff *d13;
11168496fbfSotto struct diff *d23;
112d407ca27Smillert /*
113d407ca27Smillert  * "de" is used to gather editing scripts.  These are later spewed out in
114d407ca27Smillert  * reverse order.  Its first element must be all zero, the "new" component
115d407ca27Smillert  * of "de" contains line positions or byte positions depending on when you
116d407ca27Smillert  * look (!?).  Array overlap indicates which sections in "de" correspond to
117d407ca27Smillert  * lines that are different in all three files.
118d407ca27Smillert  */
11968496fbfSotto struct diff *de;
12068496fbfSotto char *overlap;
121d407ca27Smillert int  overlapcnt;
122d407ca27Smillert FILE *fp[3];
123d407ca27Smillert int cline[3];		/* # of the last-read line in each file (0-2) */
124d407ca27Smillert /*
125d407ca27Smillert  * the latest known correspondence between line numbers of the 3 files
126d407ca27Smillert  * is stored in last[1-3];
127d407ca27Smillert  */
128d407ca27Smillert int last[4];
129d407ca27Smillert int eflag;
130d407ca27Smillert int oflag;		/* indicates whether to mark overlaps (-E or -X)*/
131d407ca27Smillert int debug  = 0;
132d407ca27Smillert char f1mark[40], f3mark[40];	/* markers for -E and -X */
133d407ca27Smillert 
134d407ca27Smillert int duplicate(struct range *, struct range *);
135d407ca27Smillert int edit(struct diff *, int, int);
136ad7191bdSmillert char *getchange(FILE *);
137ad7191bdSmillert char *getline(FILE *, size_t *);
138d407ca27Smillert int number(char **);
13968496fbfSotto int readin(char *, struct diff **);
140d407ca27Smillert int skip(int, int, char *);
141d407ca27Smillert void change(int, struct range *, int);
142d407ca27Smillert void keep(int, struct range *);
143d407ca27Smillert void merge(int, int);
144d407ca27Smillert void prange(struct range *);
145d407ca27Smillert void repos(int);
146d407ca27Smillert void separate(const char *);
147d407ca27Smillert __dead void edscript(int);
148d407ca27Smillert __dead void trouble(void);
14968496fbfSotto void increase(void);
150d407ca27Smillert __dead void usage(void);
151d407ca27Smillert 
152d407ca27Smillert int
153d407ca27Smillert main(int argc, char **argv)
154d407ca27Smillert {
155d407ca27Smillert 	int ch, i, m, n;
156d407ca27Smillert 
157d407ca27Smillert 	eflag = 0;
158d407ca27Smillert 	oflag = 0;
159d407ca27Smillert 	while ((ch = getopt(argc, argv, "EeXx3")) != -1) {
160d407ca27Smillert 		switch (ch) {
161d407ca27Smillert 		case 'E':
162d407ca27Smillert 			eflag = 3;
163d407ca27Smillert 			oflag = 1;
164d407ca27Smillert 			break;
165d407ca27Smillert 		case 'e':
166d407ca27Smillert 			eflag = 3;
167d407ca27Smillert 			break;
168d407ca27Smillert 		case 'X':
169d407ca27Smillert 			oflag = eflag = 1;
170d407ca27Smillert 			break;
171d407ca27Smillert 		case 'x':
172d407ca27Smillert 			eflag = 1;
173d407ca27Smillert 			break;
174d407ca27Smillert 		case '3':
175d407ca27Smillert 			eflag = 2;
176d407ca27Smillert 			break;
177d407ca27Smillert 		}
178d407ca27Smillert 	}
179d407ca27Smillert 	argc -= optind;
180d407ca27Smillert 	argv += optind;
181d407ca27Smillert 	/* XXX - argc usage seems wrong here */
182d407ca27Smillert 	if (argc < 5)
183d407ca27Smillert 		usage();
184d407ca27Smillert 
185d407ca27Smillert 	if (oflag) {
186d407ca27Smillert 		(void)snprintf(f1mark, sizeof(f1mark), "<<<<<<< %s",
187d407ca27Smillert 		    argc >= 6 ? argv[5] : argv[2]);
188d407ca27Smillert 		(void)snprintf(f3mark, sizeof(f3mark), ">>>>>>> %s",
189d407ca27Smillert 		    argc >= 7 ? argv[6] : argv[4]);
190d407ca27Smillert 	}
191d407ca27Smillert 
19268496fbfSotto 	increase();
19368496fbfSotto 	m = readin(argv[0], &d13);
19468496fbfSotto 	n = readin(argv[1], &d23);
195d407ca27Smillert 	for (i = 0; i <= 2; i++) {
196*2e6633a8Stobias 		if ((fp[i] = fopen(argv[i + 2], "r")) == NULL)
197*2e6633a8Stobias 			err(EXIT_FAILURE, "can't open %s", argv[i + 2]);
198d407ca27Smillert 	}
199d407ca27Smillert 	merge(m, n);
200d407ca27Smillert 	exit(EXIT_SUCCESS);
201d407ca27Smillert }
202d407ca27Smillert 
203d407ca27Smillert /*
2048fa21293Sotto  * Pick up the line numbers of all changes from one change file.
205d407ca27Smillert  * (This puts the numbers in a vector, which is not strictly necessary,
206d407ca27Smillert  * since the vector is processed in one sequential pass.
207d407ca27Smillert  * The vector could be optimized out of existence)
208d407ca27Smillert  */
209d407ca27Smillert int
21068496fbfSotto readin(char *name, struct diff **dd)
211d407ca27Smillert {
212ad7191bdSmillert 	int a, b, c, d, i;
213ad7191bdSmillert 	char kind, *p;
214ad7191bdSmillert 
215d407ca27Smillert 	fp[0] = fopen(name, "r");
216*2e6633a8Stobias 	if (fp[0] == NULL)
217*2e6633a8Stobias 		err(EXIT_FAILURE, "can't open %s", name);
218ad7191bdSmillert 	for (i=0; (p = getchange(fp[0])); i++) {
21968496fbfSotto 		if (i >= szchanges - 1)
22068496fbfSotto 			increase();
221d407ca27Smillert 		a = b = number(&p);
222d407ca27Smillert 		if (*p == ',') {
223d407ca27Smillert 			p++;
224d407ca27Smillert 			b = number(&p);
225d407ca27Smillert 		}
226d407ca27Smillert 		kind = *p++;
227d407ca27Smillert 		c = d = number(&p);
228d407ca27Smillert 		if (*p==',') {
229d407ca27Smillert 			p++;
230d407ca27Smillert 			d = number(&p);
231d407ca27Smillert 		}
232d407ca27Smillert 		if (kind == 'a')
233d407ca27Smillert 			a++;
234d407ca27Smillert 		if (kind == 'd')
235d407ca27Smillert 			c++;
236d407ca27Smillert 		b++;
237d407ca27Smillert 		d++;
23868496fbfSotto 		(*dd)[i].old.from = a;
23968496fbfSotto 		(*dd)[i].old.to = b;
24068496fbfSotto 		(*dd)[i].new.from = c;
24168496fbfSotto 		(*dd)[i].new.to = d;
242d407ca27Smillert 	}
2436daeeae1Smarkus 	if (i) {
24468496fbfSotto 		(*dd)[i].old.from = (*dd)[i-1].old.to;
24568496fbfSotto 		(*dd)[i].new.from = (*dd)[i-1].new.to;
2466daeeae1Smarkus 	}
247d407ca27Smillert 	(void)fclose(fp[0]);
248d407ca27Smillert 	return (i);
249d407ca27Smillert }
250d407ca27Smillert 
251d407ca27Smillert int
252d407ca27Smillert number(char **lc)
253d407ca27Smillert {
254d407ca27Smillert 	int nn;
255d407ca27Smillert 	nn = 0;
256d407ca27Smillert 	while (isdigit((unsigned char)(**lc)))
257d407ca27Smillert 		nn = nn*10 + *(*lc)++ - '0';
258d407ca27Smillert 	return (nn);
259d407ca27Smillert }
260d407ca27Smillert 
261ad7191bdSmillert char *
262d407ca27Smillert getchange(FILE *b)
263d407ca27Smillert {
264ad7191bdSmillert 	char *line;
265ad7191bdSmillert 
266ad7191bdSmillert 	while ((line = getline(b, NULL))) {
267d407ca27Smillert 		if (isdigit((unsigned char)line[0]))
268ad7191bdSmillert 			return (line);
269d407ca27Smillert 	}
270ad7191bdSmillert 	return (NULL);
271d407ca27Smillert }
272d407ca27Smillert 
273ad7191bdSmillert char *
274ad7191bdSmillert getline(FILE *b, size_t *n)
275d407ca27Smillert {
276ad7191bdSmillert 	char *cp;
277ad7191bdSmillert 	size_t len;
278ad7191bdSmillert 	static char *buf;
279ad7191bdSmillert 	static size_t bufsize;
280d407ca27Smillert 
281ad7191bdSmillert 	if ((cp = fgetln(b, &len)) == NULL)
282ad7191bdSmillert 		return (NULL);
283ad7191bdSmillert 
284ad7191bdSmillert 	if (cp[len - 1] != '\n')
285ad7191bdSmillert 		len++;
286ad7191bdSmillert 	if (len + 1 > bufsize) {
287ad7191bdSmillert 		do {
288ad7191bdSmillert 			bufsize += 1024;
289ad7191bdSmillert 		} while (len + 1 > bufsize);
290ad7191bdSmillert 		if ((buf = realloc(buf, bufsize)) == NULL)
291ad7191bdSmillert 			err(EXIT_FAILURE, NULL);
292d407ca27Smillert 	}
293ad7191bdSmillert 	memcpy(buf, cp, len - 1);
294ad7191bdSmillert 	buf[len - 1] = '\n';
295ad7191bdSmillert 	buf[len] = '\0';
296ad7191bdSmillert 	if (n != NULL)
297ad7191bdSmillert 		*n = len;
298ad7191bdSmillert 	return (buf);
299d407ca27Smillert }
300d407ca27Smillert 
301d407ca27Smillert void
302d407ca27Smillert merge(int m1, int m2)
303d407ca27Smillert {
304d407ca27Smillert 	struct diff *d1, *d2, *d3;
305d407ca27Smillert 	int dup, j, t1, t2;
306d407ca27Smillert 
307d407ca27Smillert 	d1 = d13;
308d407ca27Smillert 	d2 = d23;
309d407ca27Smillert 	j = 0;
310d407ca27Smillert 	while ((t1 = d1 < d13 + m1) | (t2 = d2 < d23 + m2)) {
311d407ca27Smillert 		if (debug) {
312d407ca27Smillert 			printf("%d,%d=%d,%d %d,%d=%d,%d\n",
313d407ca27Smillert 			d1->old.from,d1->old.to,
314d407ca27Smillert 			d1->new.from,d1->new.to,
315d407ca27Smillert 			d2->old.from,d2->old.to,
316d407ca27Smillert 			d2->new.from,d2->new.to);
317d407ca27Smillert 		}
318d407ca27Smillert 		/* first file is different from others */
3190b1c43dfScanacar 		if (!t2 || (t1 && d1->new.to < d2->new.from)) {
320d407ca27Smillert 			/* stuff peculiar to 1st file */
321d407ca27Smillert 			if (eflag==0) {
322d407ca27Smillert 				separate("1");
323d407ca27Smillert 				change(1, &d1->old, 0);
324d407ca27Smillert 				keep(2, &d1->new);
325d407ca27Smillert 				change(3, &d1->new, 0);
326d407ca27Smillert 			}
327d407ca27Smillert 			d1++;
328d407ca27Smillert 			continue;
329d407ca27Smillert 		}
330d407ca27Smillert 		/* second file is different from others */
3310b1c43dfScanacar 		if (!t1 || (t2 && d2->new.to < d1->new.from)) {
332d407ca27Smillert 			if (eflag==0) {
333d407ca27Smillert 				separate("2");
334d407ca27Smillert 				keep(1, &d2->new);
335d407ca27Smillert 				change(2, &d2->old, 0);
336d407ca27Smillert 				change(3, &d2->new, 0);
337d407ca27Smillert 			}
338d407ca27Smillert 			d2++;
339d407ca27Smillert 			continue;
340d407ca27Smillert 		}
341d407ca27Smillert 		/*
342d407ca27Smillert 		 * Merge overlapping changes in first file
343d407ca27Smillert 		 * this happens after extension (see below).
344d407ca27Smillert 		 */
345d407ca27Smillert 		if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
346d407ca27Smillert 			d1[1].old.from = d1->old.from;
347d407ca27Smillert 			d1[1].new.from = d1->new.from;
348d407ca27Smillert 			d1++;
349d407ca27Smillert 			continue;
350d407ca27Smillert 		}
351d407ca27Smillert 
352d407ca27Smillert 		/* merge overlapping changes in second */
353d407ca27Smillert 		if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
354d407ca27Smillert 			d2[1].old.from = d2->old.from;
355d407ca27Smillert 			d2[1].new.from = d2->new.from;
356d407ca27Smillert 			d2++;
357d407ca27Smillert 			continue;
358d407ca27Smillert 		}
359d407ca27Smillert 		/* stuff peculiar to third file or different in all */
360d407ca27Smillert 		if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
361d407ca27Smillert 			dup = duplicate(&d1->old,&d2->old);
362d407ca27Smillert 			/*
363d407ca27Smillert 			 * dup = 0 means all files differ
3648fa21293Sotto 			 * dup = 1 means files 1 and 2 identical
365d407ca27Smillert 			 */
366d407ca27Smillert 			if (eflag==0) {
367d407ca27Smillert 				separate(dup ? "3" : "");
368d407ca27Smillert 				change(1, &d1->old, dup);
369d407ca27Smillert 				change(2, &d2->old, 0);
370d407ca27Smillert 				d3 = d1->old.to > d1->old.from ? d1 : d2;
371d407ca27Smillert 				change(3, &d3->new, 0);
372d407ca27Smillert 			} else
373d407ca27Smillert 				j = edit(d1, dup, j);
374d407ca27Smillert 			d1++;
375d407ca27Smillert 			d2++;
376d407ca27Smillert 			continue;
377d407ca27Smillert 		}
378d407ca27Smillert 		/*
379d407ca27Smillert 		 * Overlapping changes from file 1 and 2; extend changes
380d407ca27Smillert 		 * appropriately to make them coincide.
381d407ca27Smillert 		 */
382d407ca27Smillert 		if (d1->new.from < d2->new.from) {
383d407ca27Smillert 			d2->old.from -= d2->new.from-d1->new.from;
384d407ca27Smillert 			d2->new.from = d1->new.from;
385d407ca27Smillert 		} else if (d2->new.from < d1->new.from) {
386d407ca27Smillert 			d1->old.from -= d1->new.from-d2->new.from;
387d407ca27Smillert 			d1->new.from = d2->new.from;
388d407ca27Smillert 		}
389d407ca27Smillert 		if (d1->new.to > d2->new.to) {
390d407ca27Smillert 			d2->old.to += d1->new.to - d2->new.to;
391d407ca27Smillert 			d2->new.to = d1->new.to;
392d407ca27Smillert 		} else if (d2->new.to > d1->new.to) {
393d407ca27Smillert 			d1->old.to += d2->new.to - d1->new.to;
394d407ca27Smillert 			d1->new.to = d2->new.to;
395d407ca27Smillert 		}
396d407ca27Smillert 	}
397d407ca27Smillert 	if (eflag)
398d407ca27Smillert 		edscript(j);
399d407ca27Smillert }
400d407ca27Smillert 
401d407ca27Smillert void
402d407ca27Smillert separate(const char *s)
403d407ca27Smillert {
404d407ca27Smillert 	printf("====%s\n", s);
405d407ca27Smillert }
406d407ca27Smillert 
407d407ca27Smillert /*
408d407ca27Smillert  * The range of lines rold.from thru rold.to in file i is to be changed.
409d407ca27Smillert  * It is to be printed only if it does not duplicate something to be
410d407ca27Smillert  * printed later.
411d407ca27Smillert  */
412d407ca27Smillert void
413d407ca27Smillert change(int i, struct range *rold, int dup)
414d407ca27Smillert {
415d407ca27Smillert 	printf("%d:", i);
416d407ca27Smillert 	last[i] = rold->to;
417d407ca27Smillert 	prange(rold);
418d407ca27Smillert 	if (dup || debug)
419d407ca27Smillert 		return;
420d407ca27Smillert 	i--;
421d407ca27Smillert 	(void)skip(i, rold->from, NULL);
422d407ca27Smillert 	(void)skip(i, rold->to, "  ");
423d407ca27Smillert }
424d407ca27Smillert 
425d407ca27Smillert /*
426d407ca27Smillert  * print the range of line numbers, rold.from thru rold.to, as n1,n2 or n1
427d407ca27Smillert  */
428d407ca27Smillert void
429d407ca27Smillert prange(struct range *rold)
430d407ca27Smillert {
431d407ca27Smillert 	if (rold->to <= rold->from)
432d407ca27Smillert 		printf("%da\n", rold->from - 1);
433d407ca27Smillert 	else {
434d407ca27Smillert 		printf("%d", rold->from);
435d407ca27Smillert 		if (rold->to > rold->from+1)
436d407ca27Smillert 			printf(",%d", rold->to - 1);
437d407ca27Smillert 		printf("c\n");
438d407ca27Smillert 	}
439d407ca27Smillert }
440d407ca27Smillert 
441d407ca27Smillert /*
442d407ca27Smillert  * No difference was reported by diff between file 1 (or 2) and file 3,
443d407ca27Smillert  * and an artificial dummy difference (trange) must be ginned up to
444d407ca27Smillert  * correspond to the change reported in the other file.
445d407ca27Smillert  */
446d407ca27Smillert void
447d407ca27Smillert keep(int i, struct range *rnew)
448d407ca27Smillert {
449d407ca27Smillert 	int delta;
450d407ca27Smillert 	struct range trange;
451d407ca27Smillert 
452d407ca27Smillert 	delta = last[3] - last[i];
453d407ca27Smillert 	trange.from = rnew->from - delta;
454d407ca27Smillert 	trange.to = rnew->to - delta;
455d407ca27Smillert 	change(i, &trange, 1);
456d407ca27Smillert }
457d407ca27Smillert 
458d407ca27Smillert /*
4598fa21293Sotto  * skip to just before line number from in file "i".  If "pr" is non-NULL,
460d407ca27Smillert  * print all skipped stuff with string pr as a prefix.
461d407ca27Smillert  */
462d407ca27Smillert int
463d407ca27Smillert skip(int i, int from, char *pr)
464d407ca27Smillert {
465ad7191bdSmillert 	size_t j, n;
466ad7191bdSmillert 	char *line;
467d407ca27Smillert 
468d407ca27Smillert 	for (n = 0; cline[i] < from - 1; n += j) {
469ad7191bdSmillert 		if ((line = getline(fp[i], &j)) == NULL)
470d407ca27Smillert 			trouble();
471d407ca27Smillert 		if (pr != NULL)
472d407ca27Smillert 			printf("%s%s", pr, line);
473d407ca27Smillert 		cline[i]++;
474d407ca27Smillert 	}
475ad7191bdSmillert 	return ((int) n);
476d407ca27Smillert }
477d407ca27Smillert 
478d407ca27Smillert /*
479d407ca27Smillert  * Return 1 or 0 according as the old range (in file 1) contains exactly
480d407ca27Smillert  * the same data as the new range (in file 2).
481d407ca27Smillert  */
482d407ca27Smillert int
483d407ca27Smillert duplicate(struct range *r1, struct range *r2)
484d407ca27Smillert {
485d407ca27Smillert 	int c,d;
486d407ca27Smillert 	int nchar;
487d407ca27Smillert 	int nline;
488d407ca27Smillert 
489d407ca27Smillert 	if (r1->to-r1->from != r2->to-r2->from)
490d407ca27Smillert 		return (0);
491d407ca27Smillert 	(void)skip(0, r1->from, NULL);
492d407ca27Smillert 	(void)skip(1, r2->from, NULL);
493d407ca27Smillert 	nchar = 0;
494d407ca27Smillert 	for (nline=0; nline < r1->to - r1->from; nline++) {
495d407ca27Smillert 		do {
496d407ca27Smillert 			c = getc(fp[0]);
497d407ca27Smillert 			d = getc(fp[1]);
498d407ca27Smillert 			if (c == -1 || d== -1)
499d407ca27Smillert 				trouble();
500d407ca27Smillert 			nchar++;
501d407ca27Smillert 			if (c != d) {
502d407ca27Smillert 				repos(nchar);
503d407ca27Smillert 				return (0);
504d407ca27Smillert 			}
505d407ca27Smillert 		} while (c != '\n');
506d407ca27Smillert 	}
507d407ca27Smillert 	repos(nchar);
508d407ca27Smillert 	return (1);
509d407ca27Smillert }
510d407ca27Smillert 
511d407ca27Smillert void
512d407ca27Smillert repos(int nchar)
513d407ca27Smillert {
514d407ca27Smillert 	int i;
515d407ca27Smillert 
516d407ca27Smillert 	for (i = 0; i < 2; i++)
517a52c79cdStobias 		(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
518d407ca27Smillert }
519d407ca27Smillert 
520d407ca27Smillert __dead void
521d407ca27Smillert trouble(void)
522d407ca27Smillert {
523d407ca27Smillert 	errx(EXIT_FAILURE, "logic error");
524d407ca27Smillert }
525d407ca27Smillert 
526d407ca27Smillert /*
527d407ca27Smillert  * collect an editing script for later regurgitation
528d407ca27Smillert  */
529d407ca27Smillert int
530d407ca27Smillert edit(struct diff *diff, int dup, int j)
531d407ca27Smillert {
532d407ca27Smillert 	if (((dup + 1) & eflag) == 0)
533d407ca27Smillert 		return (j);
534d407ca27Smillert 	j++;
535d407ca27Smillert 	overlap[j] = !dup;
536d407ca27Smillert 	if (!dup)
537d407ca27Smillert 		overlapcnt++;
538d407ca27Smillert 	de[j].old.from = diff->old.from;
539d407ca27Smillert 	de[j].old.to = diff->old.to;
540d407ca27Smillert 	de[j].new.from = de[j-1].new.to + skip(2, diff->new.from, NULL);
541d407ca27Smillert 	de[j].new.to = de[j].new.from + skip(2, diff->new.to, NULL);
542d407ca27Smillert 	return (j);
543d407ca27Smillert }
544d407ca27Smillert 
545d407ca27Smillert /* regurgitate */
546d407ca27Smillert __dead void
547d407ca27Smillert edscript(int n)
548d407ca27Smillert {
549d407ca27Smillert 	int j,k;
550d407ca27Smillert 	char block[BUFSIZ];
551d407ca27Smillert 
552d407ca27Smillert 	for (n = n; n > 0; n--) {
553d407ca27Smillert 		if (!oflag || !overlap[n])
554d407ca27Smillert 			prange(&de[n].old);
555d407ca27Smillert 		else
556d407ca27Smillert 			printf("%da\n=======\n", de[n].old.to -1);
557a52c79cdStobias 		(void)fseek(fp[2], (long)de[n].new.from, SEEK_SET);
558d407ca27Smillert 		for (k = de[n].new.to-de[n].new.from; k > 0; k-= j) {
559d407ca27Smillert 			j = k > BUFSIZ ? BUFSIZ : k;
560d407ca27Smillert 			if (fread(block, 1, j, fp[2]) != j)
561d407ca27Smillert 				trouble();
562d407ca27Smillert 			(void)fwrite(block, 1, j, stdout);
563d407ca27Smillert 		}
564d407ca27Smillert 		if (!oflag || !overlap[n])
565d407ca27Smillert 			printf(".\n");
566d407ca27Smillert 		else {
567d407ca27Smillert 			printf("%s\n.\n", f3mark);
568d407ca27Smillert 			printf("%da\n%s\n.\n", de[n].old.from - 1, f1mark);
569d407ca27Smillert 		}
570d407ca27Smillert 	}
571d407ca27Smillert 	exit(overlapcnt);
572d407ca27Smillert }
573d407ca27Smillert 
57468496fbfSotto void
57568496fbfSotto increase(void)
57668496fbfSotto {
57768496fbfSotto 	struct diff *p;
57868496fbfSotto 	char *q;
57968496fbfSotto 	size_t newsz, incr;
58068496fbfSotto 
58168496fbfSotto 	/* are the memset(3) calls needed? */
58268496fbfSotto 	newsz = szchanges == 0 ? 64 : 2 * szchanges;
58368496fbfSotto 	incr = newsz - szchanges;
58468496fbfSotto 
58568496fbfSotto 	p = realloc(d13, newsz * sizeof(struct diff));
58668496fbfSotto 	if (p == NULL)
58768496fbfSotto 		err(1, NULL);
58868496fbfSotto 	memset(p + szchanges, 0, incr * sizeof(struct diff));
58968496fbfSotto 	d13 = p;
59068496fbfSotto 	p = realloc(d23, newsz * sizeof(struct diff));
59168496fbfSotto 	if (p == NULL)
59268496fbfSotto 		err(1, NULL);
59368496fbfSotto 	memset(p + szchanges, 0, incr * sizeof(struct diff));
59468496fbfSotto 	d23 = p;
59568496fbfSotto 	p = realloc(de, newsz * sizeof(struct diff));
59668496fbfSotto 	if (p == NULL)
59768496fbfSotto 		err(1, NULL);
59868496fbfSotto 	memset(p + szchanges, 0, incr * sizeof(struct diff));
59968496fbfSotto 	de = p;
60068496fbfSotto 	q = realloc(overlap, newsz * sizeof(char));
60168496fbfSotto 	if (q == NULL)
60268496fbfSotto 		err(1, NULL);
60368496fbfSotto 	memset(q + szchanges, 0, incr * sizeof(char));
60468496fbfSotto 	overlap = q;
60568496fbfSotto 	szchanges = newsz;
60668496fbfSotto }
60768496fbfSotto 
60868496fbfSotto 
609d407ca27Smillert __dead void
610d407ca27Smillert usage(void)
611d407ca27Smillert {
612d407ca27Smillert 	extern char *__progname;
613d407ca27Smillert 
614d407ca27Smillert 	fprintf(stderr, "usage: %s [-exEX3] /tmp/d3a.?????????? "
615d407ca27Smillert 	    "/tmp/d3b.?????????? file1 file2 file3\n", __progname);
616d407ca27Smillert 	exit(EXIT_FAILURE);
617d407ca27Smillert }
618