xref: /openbsd-src/usr.bin/rcs/diff3.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: diff3.c,v 1.27 2009/02/25 23:16:20 ray Exp $	*/
2 
3 /*
4  * Copyright (C) Caldera International Inc.  2001-2002.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code and documentation must retain the above
11  *    copyright notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed or owned by Caldera
18  *	International, Inc.
19  * 4. Neither the name of Caldera International, Inc. nor the names of other
20  *    contributors may be used to endorse or promote products derived from
21  *    this software without specific prior written permission.
22  *
23  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
24  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
28  * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36 /*-
37  * Copyright (c) 1991, 1993
38  *	The Regents of the University of California.  All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. Neither the name of the University nor the names of its contributors
49  *    may be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62  * SUCH DAMAGE.
63  *
64  *	@(#)diff3.c	8.1 (Berkeley) 6/6/93
65  */
66 
67 #ifndef lint
68 static const char copyright[] =
69 "@(#) Copyright (c) 1991, 1993\n\
70 	The Regents of the University of California.  All rights reserved.\n";
71 #endif /* not lint */
72 
73 #ifndef lint
74 static const char rcsid[] =
75     "$OpenBSD: diff3.c,v 1.27 2009/02/25 23:16:20 ray Exp $";
76 #endif /* not lint */
77 
78 #include <ctype.h>
79 #include <err.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <unistd.h>
84 
85 #include "diff.h"
86 #include "rcsprog.h"
87 
88 /* diff3 - 3-way differential file comparison */
89 
90 /* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3]
91  *
92  * d13 = diff report on f1 vs f3
93  * d23 = diff report on f2 vs f3
94  * f1, f2, f3 the 3 files
95  * if changes in f1 overlap with changes in f3, m1 and m3 are used
96  * to mark the overlaps; otherwise, the file names f1 and f3 are used
97  * (only for options E and X).
98  */
99 
100 /*
101  * "from" is first in range of changed lines; "to" is last+1
102  * from=to=line after point of insertion for added lines.
103  */
104 struct range {
105 	int from;
106 	int to;
107 };
108 
109 struct diff {
110 	struct range old;
111 	struct range new;
112 };
113 
114 static size_t szchanges;
115 
116 static struct diff *d13;
117 static struct diff *d23;
118 
119 /*
120  * "de" is used to gather editing scripts.  These are later spewed out in
121  * reverse order.  Its first element must be all zero, the "new" component
122  * of "de" contains line positions or byte positions depending on when you
123  * look (!?).  Array overlap indicates which sections in "de" correspond to
124  * lines that are different in all three files.
125  */
126 static struct diff *de;
127 static char *overlap;
128 static int overlapcnt = 0;
129 static FILE *fp[3];
130 static int cline[3];		/* # of the last-read line in each file (0-2) */
131 
132 /*
133  * the latest known correspondence between line numbers of the 3 files
134  * is stored in last[1-3];
135  */
136 static int last[4];
137 static int eflag = 3;	/* default -E for compatibility with former RCS */
138 static int oflag = 1;	/* default -E for compatibility with former RCS */
139 static int debug  = 0;
140 static char f1mark[256], f3mark[256];	/* markers for -E and -X */
141 
142 static int duplicate(struct range *, struct range *);
143 static int edit(struct diff *, int, int);
144 static char *getchange(FILE *);
145 static char *getline(FILE *, size_t *);
146 static int number(char **);
147 static ssize_t readin(char *, struct diff **);
148 static int skip(int, int, char *);
149 static int edscript(int);
150 static int merge(size_t, size_t);
151 static void change(int, struct range *, int);
152 static void keep(int, struct range *);
153 static void prange(struct range *);
154 static void repos(int);
155 static void separate(const char *);
156 static void increase(void);
157 static int diff3_internal(int, char **, const char *, const char *);
158 
159 int diff3_conflicts = 0;
160 
161 /*
162  * For merge(1).
163  */
164 BUF *
165 merge_diff3(char **av, int flags)
166 {
167 	int argc;
168 	char *argv[5], *dp13, *dp23, *path1, *path2, *path3;
169 	BUF *b1, *b2, *b3, *d1, *d2, *diffb;
170 	u_char *data, *patch;
171 	size_t dlen, plen;
172 
173 	b1 = b2 = b3 = d1 = d2 = diffb = NULL;
174 	dp13 = dp23 = path1 = path2 = path3 = NULL;
175 	data = patch = NULL;
176 
177 	if ((flags & MERGE_EFLAG) && !(flags & MERGE_OFLAG))
178 		oflag = 0;
179 
180 	if ((b1 = rcs_buf_load(av[0], BUF_AUTOEXT)) == NULL)
181 		goto out;
182 	if ((b2 = rcs_buf_load(av[1], BUF_AUTOEXT)) == NULL)
183 		goto out;
184 	if ((b3 = rcs_buf_load(av[2], BUF_AUTOEXT)) == NULL)
185 		goto out;
186 
187 	d1 = rcs_buf_alloc(128, BUF_AUTOEXT);
188 	d2 = rcs_buf_alloc(128, BUF_AUTOEXT);
189 	diffb = rcs_buf_alloc(128, BUF_AUTOEXT);
190 
191 	(void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
192 	(void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
193 	(void)xasprintf(&path3, "%s/diff3.XXXXXXXXXX", rcs_tmpdir);
194 
195 	rcs_buf_write_stmp(b1, path1);
196 	rcs_buf_write_stmp(b2, path2);
197 	rcs_buf_write_stmp(b3, path3);
198 
199 	rcs_buf_free(b2);
200 	b2 = NULL;
201 
202 	if ((diffreg(path1, path3, d1, D_FORCEASCII) == D_ERROR) ||
203 	    (diffreg(path2, path3, d2, D_FORCEASCII) == D_ERROR)) {
204 		rcs_buf_free(diffb);
205 		diffb = NULL;
206 		goto out;
207 	}
208 
209 	(void)xasprintf(&dp13, "%s/d13.XXXXXXXXXX", rcs_tmpdir);
210 	rcs_buf_write_stmp(d1, dp13);
211 
212 	rcs_buf_free(d1);
213 	d1 = NULL;
214 
215 	(void)xasprintf(&dp23, "%s/d23.XXXXXXXXXX", rcs_tmpdir);
216 	rcs_buf_write_stmp(d2, dp23);
217 
218 	rcs_buf_free(d2);
219 	d2 = NULL;
220 
221 	argc = 0;
222 	diffbuf = diffb;
223 	argv[argc++] = dp13;
224 	argv[argc++] = dp23;
225 	argv[argc++] = path1;
226 	argv[argc++] = path2;
227 	argv[argc++] = path3;
228 
229 	diff3_conflicts = diff3_internal(argc, argv, av[0], av[2]);
230 	if (diff3_conflicts < 0) {
231 		rcs_buf_free(diffb);
232 		diffb = NULL;
233 		goto out;
234 	}
235 
236 	plen = rcs_buf_len(diffb);
237 	patch = rcs_buf_release(diffb);
238 	dlen = rcs_buf_len(b1);
239 	data = rcs_buf_release(b1);
240 
241 	if ((diffb = rcs_patchfile(data, dlen, patch, plen, ed_patch_lines)) == NULL)
242 		goto out;
243 
244 	if (!(flags & QUIET) && diff3_conflicts != 0)
245 		warnx("warning: overlaps or other problems during merge");
246 
247 out:
248 	if (b2 != NULL)
249 		rcs_buf_free(b2);
250 	if (b3 != NULL)
251 		rcs_buf_free(b3);
252 	if (d1 != NULL)
253 		rcs_buf_free(d1);
254 	if (d2 != NULL)
255 		rcs_buf_free(d2);
256 
257 	(void)unlink(path1);
258 	(void)unlink(path2);
259 	(void)unlink(path3);
260 	(void)unlink(dp13);
261 	(void)unlink(dp23);
262 
263 	if (path1 != NULL)
264 		xfree(path1);
265 	if (path2 != NULL)
266 		xfree(path2);
267 	if (path3 != NULL)
268 		xfree(path3);
269 	if (dp13 != NULL)
270 		xfree(dp13);
271 	if (dp23 != NULL)
272 		xfree(dp23);
273 	if (data != NULL)
274 		xfree(data);
275 	if (patch != NULL)
276 		xfree(patch);
277 
278 	return (diffb);
279 }
280 
281 BUF *
282 rcs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2, int flags)
283 {
284 	int argc;
285 	char *argv[5], r1[RCS_REV_BUFSZ], r2[RCS_REV_BUFSZ];
286 	char *dp13, *dp23, *path1, *path2, *path3;
287 	BUF *b1, *b2, *b3, *d1, *d2, *diffb;
288 	size_t dlen, plen;
289 	u_char *data, *patch;
290 
291 	b1 = b2 = b3 = d1 = d2 = diffb = NULL;
292 	dp13 = dp23 = path1 = path2 = path3 = NULL;
293 	data = patch = NULL;
294 
295 	if ((flags & MERGE_EFLAG) && !(flags & MERGE_OFLAG))
296 		oflag = 0;
297 
298 	rcsnum_tostr(rev1, r1, sizeof(r1));
299 	rcsnum_tostr(rev2, r2, sizeof(r2));
300 
301 	if ((b1 = rcs_buf_load(workfile, BUF_AUTOEXT)) == NULL)
302 		goto out;
303 
304 	if (!(flags & QUIET))
305 		(void)fprintf(stderr, "retrieving revision %s\n", r1);
306 	if ((b2 = rcs_getrev(rf, rev1)) == NULL)
307 		goto out;
308 
309 	if (!(flags & QUIET))
310 		(void)fprintf(stderr, "retrieving revision %s\n", r2);
311 	if ((b3 = rcs_getrev(rf, rev2)) == NULL)
312 		goto out;
313 
314 	d1 = rcs_buf_alloc(128, BUF_AUTOEXT);
315 	d2 = rcs_buf_alloc(128, BUF_AUTOEXT);
316 	diffb = rcs_buf_alloc(128, BUF_AUTOEXT);
317 
318 	(void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir);
319 	(void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir);
320 	(void)xasprintf(&path3, "%s/diff3.XXXXXXXXXX", rcs_tmpdir);
321 
322 	rcs_buf_write_stmp(b1, path1);
323 	rcs_buf_write_stmp(b2, path2);
324 	rcs_buf_write_stmp(b3, path3);
325 
326 	rcs_buf_free(b2);
327 	b2 = NULL;
328 
329 	if ((diffreg(path1, path3, d1, D_FORCEASCII) == D_ERROR) ||
330 	    (diffreg(path2, path3, d2, D_FORCEASCII) == D_ERROR)) {
331 		rcs_buf_free(diffb);
332 		diffb = NULL;
333 		goto out;
334 	}
335 
336 	(void)xasprintf(&dp13, "%s/d13.XXXXXXXXXX", rcs_tmpdir);
337 	rcs_buf_write_stmp(d1, dp13);
338 
339 	rcs_buf_free(d1);
340 	d1 = NULL;
341 
342 	(void)xasprintf(&dp23, "%s/d23.XXXXXXXXXX", rcs_tmpdir);
343 	rcs_buf_write_stmp(d2, dp23);
344 
345 	rcs_buf_free(d2);
346 	d2 = NULL;
347 
348 	argc = 0;
349 	diffbuf = diffb;
350 	argv[argc++] = dp13;
351 	argv[argc++] = dp23;
352 	argv[argc++] = path1;
353 	argv[argc++] = path2;
354 	argv[argc++] = path3;
355 
356 	diff3_conflicts = diff3_internal(argc, argv, workfile, r2);
357 	if (diff3_conflicts < 0) {
358 		rcs_buf_free(diffb);
359 		diffb = NULL;
360 		goto out;
361 	}
362 
363 	plen = rcs_buf_len(diffb);
364 	patch = rcs_buf_release(diffb);
365 	dlen = rcs_buf_len(b1);
366 	data = rcs_buf_release(b1);
367 
368 	if ((diffb = rcs_patchfile(data, dlen, patch, plen, ed_patch_lines)) == NULL)
369 		goto out;
370 
371 	if (!(flags & QUIET) && diff3_conflicts != 0)
372 		warnx("warning: overlaps or other problems during merge");
373 
374 out:
375 	if (b2 != NULL)
376 		rcs_buf_free(b2);
377 	if (b3 != NULL)
378 		rcs_buf_free(b3);
379 	if (d1 != NULL)
380 		rcs_buf_free(d1);
381 	if (d2 != NULL)
382 		rcs_buf_free(d2);
383 
384 	(void)unlink(path1);
385 	(void)unlink(path2);
386 	(void)unlink(path3);
387 	(void)unlink(dp13);
388 	(void)unlink(dp23);
389 
390 	if (path1 != NULL)
391 		xfree(path1);
392 	if (path2 != NULL)
393 		xfree(path2);
394 	if (path3 != NULL)
395 		xfree(path3);
396 	if (dp13 != NULL)
397 		xfree(dp13);
398 	if (dp23 != NULL)
399 		xfree(dp23);
400 	if (data != NULL)
401 		xfree(data);
402 	if (patch != NULL)
403 		xfree(patch);
404 
405 	return (diffb);
406 }
407 
408 static int
409 diff3_internal(int argc, char **argv, const char *fmark, const char *rmark)
410 {
411 	ssize_t m, n;
412 	int i;
413 
414 	if (argc < 5)
415 		return (-1);
416 
417 	if (oflag) {
418 		i = snprintf(f1mark, sizeof(f1mark), "<<<<<<< %s", fmark);
419 		if (i < 0 || i >= (int)sizeof(f1mark))
420 			errx(1, "diff3_internal: string truncated");
421 
422 		i = snprintf(f3mark, sizeof(f3mark), ">>>>>>> %s", rmark);
423 		if (i < 0 || i >= (int)sizeof(f3mark))
424 			errx(1, "diff3_internal: string truncated");
425 	}
426 
427 	increase();
428 	if ((m = readin(argv[0], &d13)) < 0) {
429 		warn("%s", argv[0]);
430 		return (-1);
431 	}
432 	if ((n = readin(argv[1], &d23)) < 0) {
433 		warn("%s", argv[1]);
434 		return (-1);
435 	}
436 
437 	for (i = 0; i <= 2; i++)
438 		if ((fp[i] = fopen(argv[i + 2], "r")) == NULL) {
439 			warn("%s", argv[i + 2]);
440 			return (-1);
441 		}
442 
443 	return (merge(m, n));
444 }
445 
446 int
447 ed_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines)
448 {
449 	char op, *ep;
450 	struct rcs_line *sort, *lp, *dlp, *ndlp, *insert_after;
451 	int start, end, i, lineno;
452 	u_char tmp;
453 
454 	dlp = TAILQ_FIRST(&(dlines->l_lines));
455 	lp = TAILQ_FIRST(&(plines->l_lines));
456 
457 	end = 0;
458 	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
459 	    lp = TAILQ_NEXT(lp, l_list)) {
460 		/* Skip blank lines */
461 		if (lp->l_len < 2)
462 			continue;
463 		/* NUL-terminate line buffer for strtol() safety. */
464 		tmp = lp->l_line[lp->l_len - 1];
465 		lp->l_line[lp->l_len - 1] = '\0';
466 		/* len - 1 is NUL terminator so we use len - 2 for 'op' */
467 		op = lp->l_line[lp->l_len - 2];
468 		start = (int)strtol(lp->l_line, &ep, 10);
469 		/* Restore the last byte of the buffer */
470 		lp->l_line[lp->l_len - 1] = tmp;
471 		if (op == 'a') {
472 			if (start > dlines->l_nblines ||
473 			    start < 0 || *ep != 'a')
474 				errx(1, "ed_patch_lines");
475 		} else if (op == 'c') {
476 			if (start > dlines->l_nblines ||
477 			    start < 0 || (*ep != ',' && *ep != 'c'))
478 				errx(1, "ed_patch_lines");
479 
480 			if (*ep == ',') {
481 				ep++;
482 				end = (int)strtol(ep, &ep, 10);
483 				if (end < 0 || *ep != 'c')
484 					errx(1, "ed_patch_lines");
485 			} else {
486 				end = start;
487 			}
488 		}
489 
490 
491 		for (;;) {
492 			if (dlp == NULL)
493 				break;
494 			if (dlp->l_lineno == start)
495 				break;
496 			if (dlp->l_lineno > start) {
497 				dlp = TAILQ_PREV(dlp, rcs_tqh, l_list);
498 			} else if (dlp->l_lineno < start) {
499 				ndlp = TAILQ_NEXT(dlp, l_list);
500 				if (ndlp->l_lineno > start)
501 					break;
502 				dlp = ndlp;
503 			}
504 		}
505 
506 		if (dlp == NULL)
507 			errx(1, "ed_patch_lines");
508 
509 
510 		if (op == 'c') {
511 			insert_after = TAILQ_PREV(dlp, rcs_tqh, l_list);
512 			for (i = 0; i <= (end - start); i++) {
513 				ndlp = TAILQ_NEXT(dlp, l_list);
514 				TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
515 				dlp = ndlp;
516 			}
517 			dlp = insert_after;
518 		}
519 
520 		if (op == 'a' || op == 'c') {
521 			for (;;) {
522 				ndlp = lp;
523 				lp = TAILQ_NEXT(lp, l_list);
524 				if (lp == NULL)
525 					errx(1, "ed_patch_lines");
526 
527 				if (!memcmp(lp->l_line, ".", 1))
528 					break;
529 
530 				TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
531 				TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
532 				    lp, l_list);
533 				dlp = lp;
534 
535 				lp->l_lineno = start;
536 				lp = ndlp;
537 			}
538 		}
539 
540 		/*
541 		 * always resort lines as the markers might be put at the
542 		 * same line as we first started editing.
543 		 */
544 		lineno = 0;
545 		TAILQ_FOREACH(sort, &(dlines->l_lines), l_list)
546 			sort->l_lineno = lineno++;
547 		dlines->l_nblines = lineno - 1;
548 	}
549 
550 	return (0);
551 }
552 
553 /*
554  * Pick up the line numbers of all changes from one change file.
555  * (This puts the numbers in a vector, which is not strictly necessary,
556  * since the vector is processed in one sequential pass.
557  * The vector could be optimized out of existence)
558  */
559 static ssize_t
560 readin(char *name, struct diff **dd)
561 {
562 	int a, b, c, d;
563 	char kind, *p;
564 	size_t i;
565 
566 	fp[0] = fopen(name, "r");
567 	if (fp[0] == NULL)
568 		return (-1);
569 	for (i = 0; (p = getchange(fp[0])); i++) {
570 		if (i >= szchanges - 1)
571 			increase();
572 		a = b = number(&p);
573 		if (*p == ',') {
574 			p++;
575 			b = number(&p);
576 		}
577 		kind = *p++;
578 		c = d = number(&p);
579 		if (*p==',') {
580 			p++;
581 			d = number(&p);
582 		}
583 		if (kind == 'a')
584 			a++;
585 		if (kind == 'd')
586 			c++;
587 		b++;
588 		d++;
589 		(*dd)[i].old.from = a;
590 		(*dd)[i].old.to = b;
591 		(*dd)[i].new.from = c;
592 		(*dd)[i].new.to = d;
593 	}
594 
595 	if (i) {
596 		(*dd)[i].old.from = (*dd)[i-1].old.to;
597 		(*dd)[i].new.from = (*dd)[i-1].new.to;
598 	}
599 	(void)fclose(fp[0]);
600 
601 	return (i);
602 }
603 
604 static int
605 number(char **lc)
606 {
607 	int nn;
608 
609 	nn = 0;
610 	while (isdigit((unsigned char)(**lc)))
611 		nn = nn*10 + *(*lc)++ - '0';
612 
613 	return (nn);
614 }
615 
616 static char *
617 getchange(FILE *b)
618 {
619 	char *line;
620 
621 	while ((line = getline(b, NULL))) {
622 		if (isdigit((unsigned char)line[0]))
623 			return (line);
624 	}
625 
626 	return (NULL);
627 }
628 
629 static char *
630 getline(FILE *b, size_t *n)
631 {
632 	char *cp;
633 	size_t len;
634 	static char *buf;
635 	static size_t bufsize;
636 
637 	if ((cp = fgetln(b, &len)) == NULL)
638 		return (NULL);
639 
640 	if (cp[len - 1] != '\n')
641 		len++;
642 	if (len + 1 > bufsize) {
643 		do {
644 			bufsize += 1024;
645 		} while (len + 1 > bufsize);
646 		buf = xrealloc(buf, 1, bufsize);
647 	}
648 	memcpy(buf, cp, len - 1);
649 	buf[len - 1] = '\n';
650 	buf[len] = '\0';
651 	if (n != NULL)
652 		*n = len;
653 
654 	return (buf);
655 }
656 
657 static int
658 merge(size_t m1, size_t m2)
659 {
660 	struct diff *d1, *d2, *d3;
661 	int dpl, j, t1, t2;
662 
663 	d1 = d13;
664 	d2 = d23;
665 	j = 0;
666 	while ((t1 = d1 < d13 + m1) | (t2 = d2 < d23 + m2)) {
667 		if (debug) {
668 			printf("%d,%d=%d,%d %d,%d=%d,%d\n",
669 			d1->old.from, d1->old.to,
670 			d1->new.from, d1->new.to,
671 			d2->old.from, d2->old.to,
672 			d2->new.from, d2->new.to);
673 		}
674 
675 		/* first file is different from others */
676 		if (!t2 || (t1 && d1->new.to < d2->new.from)) {
677 			/* stuff peculiar to 1st file */
678 			if (eflag==0) {
679 				separate("1");
680 				change(1, &d1->old, 0);
681 				keep(2, &d1->new);
682 				change(3, &d1->new, 0);
683 			}
684 			d1++;
685 			continue;
686 		}
687 
688 		/* second file is different from others */
689 		if (!t1 || (t2 && d2->new.to < d1->new.from)) {
690 			if (eflag==0) {
691 				separate("2");
692 				keep(1, &d2->new);
693 				change(2, &d2->old, 0);
694 				change(3, &d2->new, 0);
695 			}
696 			d2++;
697 			continue;
698 		}
699 
700 		/*
701 		 * Merge overlapping changes in first file
702 		 * this happens after extension (see below).
703 		 */
704 		if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
705 			d1[1].old.from = d1->old.from;
706 			d1[1].new.from = d1->new.from;
707 			d1++;
708 			continue;
709 		}
710 
711 		/* merge overlapping changes in second */
712 		if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
713 			d2[1].old.from = d2->old.from;
714 			d2[1].new.from = d2->new.from;
715 			d2++;
716 			continue;
717 		}
718 		/* stuff peculiar to third file or different in all */
719 		if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
720 			dpl = duplicate(&d1->old,&d2->old);
721 			if (dpl == -1)
722 				return (-1);
723 
724 			/*
725 			 * dpl = 0 means all files differ
726 			 * dpl = 1 means files 1 and 2 identical
727 			 */
728 			if (eflag==0) {
729 				separate(dpl ? "3" : "");
730 				change(1, &d1->old, dpl);
731 				change(2, &d2->old, 0);
732 				d3 = d1->old.to > d1->old.from ? d1 : d2;
733 				change(3, &d3->new, 0);
734 			} else
735 				j = edit(d1, dpl, j);
736 			d1++;
737 			d2++;
738 			continue;
739 		}
740 
741 		/*
742 		 * Overlapping changes from file 1 and 2; extend changes
743 		 * appropriately to make them coincide.
744 		 */
745 		if (d1->new.from < d2->new.from) {
746 			d2->old.from -= d2->new.from-d1->new.from;
747 			d2->new.from = d1->new.from;
748 		} else if (d2->new.from < d1->new.from) {
749 			d1->old.from -= d1->new.from-d2->new.from;
750 			d1->new.from = d2->new.from;
751 		}
752 		if (d1->new.to > d2->new.to) {
753 			d2->old.to += d1->new.to - d2->new.to;
754 			d2->new.to = d1->new.to;
755 		} else if (d2->new.to > d1->new.to) {
756 			d1->old.to += d2->new.to - d1->new.to;
757 			d1->new.to = d2->new.to;
758 		}
759 	}
760 
761 	return (edscript(j));
762 }
763 
764 static void
765 separate(const char *s)
766 {
767 	diff_output("====%s\n", s);
768 }
769 
770 /*
771  * The range of lines rold.from thru rold.to in file i is to be changed.
772  * It is to be printed only if it does not duplicate something to be
773  * printed later.
774  */
775 static void
776 change(int i, struct range *rold, int fdup)
777 {
778 	diff_output("%d:", i);
779 	last[i] = rold->to;
780 	prange(rold);
781 	if (fdup || debug)
782 		return;
783 	i--;
784 	(void)skip(i, rold->from, NULL);
785 	(void)skip(i, rold->to, "  ");
786 }
787 
788 /*
789  * print the range of line numbers, rold.from thru rold.to, as n1,n2 or n1
790  */
791 static void
792 prange(struct range *rold)
793 {
794 	if (rold->to <= rold->from)
795 		diff_output("%da\n", rold->from - 1);
796 	else {
797 		diff_output("%d", rold->from);
798 		if (rold->to > rold->from+1)
799 			diff_output(",%d", rold->to - 1);
800 		diff_output("c\n");
801 	}
802 }
803 
804 /*
805  * No difference was reported by diff between file 1 (or 2) and file 3,
806  * and an artificial dummy difference (trange) must be ginned up to
807  * correspond to the change reported in the other file.
808  */
809 static void
810 keep(int i, struct range *rnew)
811 {
812 	int delta;
813 	struct range trange;
814 
815 	delta = last[3] - last[i];
816 	trange.from = rnew->from - delta;
817 	trange.to = rnew->to - delta;
818 	change(i, &trange, 1);
819 }
820 
821 /*
822  * skip to just before line number from in file "i".  If "pr" is non-NULL,
823  * print all skipped stuff with string pr as a prefix.
824  */
825 static int
826 skip(int i, int from, char *pr)
827 {
828 	size_t j, n;
829 	char *line;
830 
831 	for (n = 0; cline[i] < from - 1; n += j) {
832 		if ((line = getline(fp[i], &j)) == NULL)
833 			return (-1);
834 		if (pr != NULL)
835 			diff_output("%s%s", pr, line);
836 		cline[i]++;
837 	}
838 	return ((int) n);
839 }
840 
841 /*
842  * Return 1 or 0 according as the old range (in file 1) contains exactly
843  * the same data as the new range (in file 2).
844  */
845 static int
846 duplicate(struct range *r1, struct range *r2)
847 {
848 	int c,d;
849 	int nchar;
850 	int nline;
851 
852 	if (r1->to-r1->from != r2->to-r2->from)
853 		return (0);
854 	(void)skip(0, r1->from, NULL);
855 	(void)skip(1, r2->from, NULL);
856 	nchar = 0;
857 	for (nline=0; nline < r1->to - r1->from; nline++) {
858 		do {
859 			c = getc(fp[0]);
860 			d = getc(fp[1]);
861 			if (c == -1 || d== -1)
862 				return (-1);
863 			nchar++;
864 			if (c != d) {
865 				repos(nchar);
866 				return (0);
867 			}
868 		} while (c != '\n');
869 	}
870 	repos(nchar);
871 	return (1);
872 }
873 
874 static void
875 repos(int nchar)
876 {
877 	int i;
878 
879 	for (i = 0; i < 2; i++)
880 		(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
881 }
882 
883 /*
884  * collect an editing script for later regurgitation
885  */
886 static int
887 edit(struct diff *diff, int fdup, int j)
888 {
889 	if (((fdup + 1) & eflag) == 0)
890 		return (j);
891 	j++;
892 	overlap[j] = !fdup;
893 	if (!fdup)
894 		overlapcnt++;
895 	de[j].old.from = diff->old.from;
896 	de[j].old.to = diff->old.to;
897 	de[j].new.from = de[j-1].new.to + skip(2, diff->new.from, NULL);
898 	de[j].new.to = de[j].new.from + skip(2, diff->new.to, NULL);
899 	return (j);
900 }
901 
902 /* regurgitate */
903 static int
904 edscript(int n)
905 {
906 	int j, k;
907 	char block[BUFSIZ+1];
908 
909 	for (n = n; n > 0; n--) {
910 		if (!oflag || !overlap[n])
911 			prange(&de[n].old);
912 		else
913 			diff_output("%da\n=======\n", de[n].old.to -1);
914 		(void)fseek(fp[2], (long)de[n].new.from, SEEK_SET);
915 		for (k = de[n].new.to-de[n].new.from; k > 0; k-= j) {
916 			j = k > BUFSIZ ? BUFSIZ : k;
917 			if (fread(block, 1, (size_t)j,
918 			    fp[2]) != (size_t)j)
919 				return (-1);
920 			block[j] = '\0';
921 			diff_output("%s", block);
922 		}
923 
924 		if (!oflag || !overlap[n])
925 			diff_output(".\n");
926 		else {
927 			diff_output("%s\n.\n", f3mark);
928 			diff_output("%da\n%s\n.\n", de[n].old.from - 1, f1mark);
929 		}
930 	}
931 
932 	return (overlapcnt);
933 }
934 
935 static void
936 increase(void)
937 {
938 	size_t newsz, incr;
939 
940 	/* are the memset(3) calls needed? */
941 	newsz = szchanges == 0 ? 64 : 2 * szchanges;
942 	incr = newsz - szchanges;
943 
944 	d13 = xrealloc(d13, newsz, sizeof(*d13));
945 	memset(d13 + szchanges, 0, incr * sizeof(*d13));
946 	d23 = xrealloc(d23, newsz, sizeof(*d23));
947 	memset(d23 + szchanges, 0, incr * sizeof(*d23));
948 	de = xrealloc(de, newsz, sizeof(*de));
949 	memset(de + szchanges, 0, incr * sizeof(*de));
950 	overlap = xrealloc(overlap, newsz, sizeof(*overlap));
951 	memset(overlap + szchanges, 0, incr * sizeof(*overlap));
952 	szchanges = newsz;
953 }
954