1/*	$OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt 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
65#include <sys/capsicum.h>
66#include <sys/procdesc.h>
67#include <sys/types.h>
68#include <sys/event.h>
69#include <sys/wait.h>
70
71#include <capsicum_helpers.h>
72#include <ctype.h>
73#include <err.h>
74#include <getopt.h>
75#include <stdio.h>
76#include <stdlib.h>
77#include <limits.h>
78#include <inttypes.h>
79#include <string.h>
80#include <unistd.h>
81
82
83/*
84 * "from" is first in range of changed lines; "to" is last+1
85 * from=to=line after point of insertion for added lines.
86 */
87struct range {
88	int from;
89	int to;
90};
91
92struct diff {
93#define DIFF_TYPE2 2
94#define DIFF_TYPE3 3
95	int type;
96#if DEBUG
97	char *line;
98#endif	/* DEBUG */
99
100	/* Ranges as lines */
101	struct range old;
102	struct range new;
103};
104
105#define EFLAG_NONE 	0
106#define EFLAG_OVERLAP 	1
107#define EFLAG_NOOVERLAP	2
108#define EFLAG_UNMERGED	3
109
110static size_t szchanges;
111
112static struct diff *d13;
113static struct diff *d23;
114/*
115 * "de" is used to gather editing scripts.  These are later spewed out in
116 * reverse order.  Its first element must be all zero, the "old" and "new"
117 * components of "de" contain line positions. Array overlap indicates which
118 * sections in "de" correspond to lines that are different in all three files.
119 */
120static struct diff *de;
121static char *overlap;
122static int  overlapcnt;
123static FILE *fp[3];
124static int cline[3];		/* # of the last-read line in each file (0-2) */
125/*
126 * The latest known correspondence between line numbers of the 3 files
127 * is stored in last[1-3];
128 */
129static int last[4];
130static int Aflag, eflag, iflag, mflag, Tflag;
131static int oflag;		/* indicates whether to mark overlaps (-E or -X) */
132static int strip_cr;
133static char *f1mark, *f2mark, *f3mark;
134static const char *oldmark = "<<<<<<<";
135static const char *orgmark = "|||||||";
136static const char *newmark = ">>>>>>>";
137static const char *divider = "=======";
138
139static bool duplicate(struct range *, struct range *);
140static int edit(struct diff *, bool, int, int);
141static char *getchange(FILE *);
142static char *get_line(FILE *, size_t *);
143static int readin(int fd, struct diff **);
144static int skip(int, int, const char *);
145static void change(int, struct range *, bool);
146static void keep(int, struct range *);
147static void merge(int, int);
148static void prange(struct range *, bool);
149static void repos(int);
150static void edscript(int) __dead2;
151static void Ascript(int) __dead2;
152static void mergescript(int) __dead2;
153static void increase(void);
154static void usage(void);
155static void printrange(FILE *, struct range *);
156
157static const char diff3_version[] = "FreeBSD diff3 20220517";
158
159enum {
160	DIFFPROG_OPT,
161	STRIPCR_OPT,
162	HELP_OPT,
163	VERSION_OPT
164};
165
166#define DIFF_PATH "/usr/bin/diff"
167
168#define OPTIONS "3aAeEiL:mTxX"
169static struct option longopts[] = {
170	{ "ed",			no_argument,		NULL,	'e' },
171	{ "show-overlap",	no_argument,		NULL,	'E' },
172	{ "overlap-only",	no_argument,		NULL,	'x' },
173	{ "initial-tab",	no_argument,		NULL,	'T' },
174	{ "text",		no_argument,		NULL,	'a' },
175	{ "strip-trailing-cr",	no_argument,		NULL,	STRIPCR_OPT },
176	{ "show-all",		no_argument,		NULL,	'A' },
177	{ "easy-only",		no_argument,		NULL,	'3' },
178	{ "merge",		no_argument,		NULL,	'm' },
179	{ "label",		required_argument,	NULL,	'L' },
180	{ "diff-program",	required_argument,	NULL,	DIFFPROG_OPT },
181	{ "help",		no_argument,		NULL,	HELP_OPT},
182	{ "version",		no_argument,		NULL,	VERSION_OPT}
183};
184
185static void
186usage(void)
187{
188	fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
189	    "[-L label3] file1 file2 file3\n");
190}
191
192static int
193readin(int fd, struct diff **dd)
194{
195	int a, b, c, d;
196	size_t i;
197	char kind, *p;
198	FILE *f;
199
200	f = fdopen(fd, "r");
201	if (f == NULL)
202		err(2, "fdopen");
203	for (i = 0; (p = getchange(f)); i++) {
204#if DEBUG
205		(*dd)[i].line = strdup(p);
206#endif	/* DEBUG */
207
208		if (i >= szchanges - 1)
209			increase();
210		a = b = (int)strtoimax(p, &p, 10);
211		if (*p == ',') {
212			p++;
213			b = (int)strtoimax(p, &p, 10);
214		}
215		kind = *p++;
216		c = d = (int)strtoimax(p, &p, 10);
217		if (*p == ',') {
218			p++;
219			d = (int)strtoimax(p, &p, 10);
220		}
221		if (kind == 'a')
222			a++;
223		if (kind == 'd')
224			c++;
225		b++;
226		d++;
227		(*dd)[i].old.from = a;
228		(*dd)[i].old.to = b;
229		(*dd)[i].new.from = c;
230		(*dd)[i].new.to = d;
231	}
232	if (i) {
233		(*dd)[i].old.from = (*dd)[i - 1].old.to;
234		(*dd)[i].new.from = (*dd)[i - 1].new.to;
235	}
236	fclose(f);
237	return (i);
238}
239
240static int
241diffexec(const char *diffprog, char **diffargv, int fd[])
242{
243	int pd;
244
245	switch (pdfork(&pd, PD_CLOEXEC)) {
246	case 0:
247		close(fd[0]);
248		if (dup2(fd[1], STDOUT_FILENO) == -1)
249			err(2, "child could not duplicate descriptor");
250		close(fd[1]);
251		execvp(diffprog, diffargv);
252		err(2, "could not execute diff: %s", diffprog);
253		break;
254	case -1:
255		err(2, "could not fork");
256		break;
257	}
258	close(fd[1]);
259	return (pd);
260}
261
262static char *
263getchange(FILE *b)
264{
265	char *line;
266
267	while ((line = get_line(b, NULL))) {
268		if (isdigit((unsigned char)line[0]))
269			return (line);
270	}
271	return (NULL);
272}
273
274
275static char *
276get_line(FILE *b, size_t *n)
277{
278	ssize_t len;
279	static char *buf = NULL;
280	static size_t bufsize = 0;
281
282	if ((len = getline(&buf, &bufsize, b)) < 0)
283		return (NULL);
284
285	if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
286		buf[len - 2] = '\n';
287		buf[len - 1] = '\0';
288		len--;
289	}
290
291	if (n != NULL)
292		*n = len;
293
294	return (buf);
295}
296
297static void
298merge(int m1, int m2)
299{
300	struct diff *d1, *d2, *d3;
301	int j, t1, t2;
302	bool dup = false;
303
304	d1 = d13;
305	d2 = d23;
306	j = 0;
307
308	while (t1 = d1 < d13 + m1, t2 = d2 < d23 + m2, t1 || t2) {
309		/* first file is different from the others */
310		if (!t2 || (t1 && d1->new.to < d2->new.from)) {
311			/* stuff peculiar to 1st file */
312			if (eflag == EFLAG_NONE) {
313				printf("====1\n");
314				change(1, &d1->old, false);
315				keep(2, &d1->new);
316				change(3, &d1->new, false);
317			}
318			d1++;
319			continue;
320		}
321		/* second file is different from others */
322		if (!t1 || (t2 && d2->new.to < d1->new.from)) {
323			if (eflag == EFLAG_NONE) {
324				printf("====2\n");
325				keep(1, &d2->new);
326				change(3, &d2->new, false);
327				change(2, &d2->old, false);
328			} else if (Aflag || mflag) {
329				// XXX-THJ: What does it mean for the second file to differ?
330				if (eflag == EFLAG_UNMERGED)
331					j = edit(d2, dup, j, DIFF_TYPE2);
332			}
333			d2++;
334			continue;
335		}
336		/*
337		 * Merge overlapping changes in first file
338		 * this happens after extension (see below).
339		 */
340		if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
341			d1[1].old.from = d1->old.from;
342			d1[1].new.from = d1->new.from;
343			d1++;
344			continue;
345		}
346
347		/* merge overlapping changes in second */
348		if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
349			d2[1].old.from = d2->old.from;
350			d2[1].new.from = d2->new.from;
351			d2++;
352			continue;
353		}
354		/* stuff peculiar to third file or different in all */
355		if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
356			dup = duplicate(&d1->old, &d2->old);
357			/*
358			 * dup = 0 means all files differ
359			 * dup = 1 means files 1 and 2 identical
360			 */
361			if (eflag == EFLAG_NONE) {
362				printf("====%s\n", dup ? "3" : "");
363				change(1, &d1->old, dup);
364				change(2, &d2->old, false);
365				d3 = d1->old.to > d1->old.from ? d1 : d2;
366				change(3, &d3->new, false);
367			} else {
368				j = edit(d1, dup, j, DIFF_TYPE3);
369			}
370			dup = false;
371			d1++;
372			d2++;
373			continue;
374		}
375		/*
376		 * Overlapping changes from file 1 and 2; extend changes
377		 * appropriately to make them coincide.
378		 */
379		if (d1->new.from < d2->new.from) {
380			d2->old.from -= d2->new.from - d1->new.from;
381			d2->new.from = d1->new.from;
382		} else if (d2->new.from < d1->new.from) {
383			d1->old.from -= d1->new.from - d2->new.from;
384			d1->new.from = d2->new.from;
385		}
386		if (d1->new.to > d2->new.to) {
387			d2->old.to += d1->new.to - d2->new.to;
388			d2->new.to = d1->new.to;
389		} else if (d2->new.to > d1->new.to) {
390			d1->old.to += d2->new.to - d1->new.to;
391			d1->new.to = d2->new.to;
392		}
393	}
394
395	if (mflag)
396		mergescript(j);
397	else if (Aflag)
398		Ascript(j);
399	else if (eflag)
400		edscript(j);
401}
402
403/*
404 * The range of lines rold.from thru rold.to in file i is to be changed.
405 * It is to be printed only if it does not duplicate something to be
406 * printed later.
407 */
408static void
409change(int i, struct range *rold, bool dup)
410{
411
412	printf("%d:", i);
413	last[i] = rold->to;
414	prange(rold, false);
415	if (dup)
416		return;
417	i--;
418	skip(i, rold->from, NULL);
419	skip(i, rold->to, "  ");
420}
421
422/*
423 * Print the range of line numbers, rold.from thru rold.to, as n1,n2 or
424 * n1.
425 */
426static void
427prange(struct range *rold, bool delete)
428{
429
430	if (rold->to <= rold->from)
431		printf("%da\n", rold->from - 1);
432	else {
433		printf("%d", rold->from);
434		if (rold->to > rold->from + 1)
435			printf(",%d", rold->to - 1);
436		if (delete)
437			printf("d\n");
438		else
439			printf("c\n");
440	}
441}
442
443/*
444 * No difference was reported by diff between file 1 (or 2) and file 3,
445 * and an artificial dummy difference (trange) must be ginned up to
446 * correspond to the change reported in the other file.
447 */
448static void
449keep(int i, struct range *rnew)
450{
451	int delta;
452	struct range trange;
453
454	delta = last[3] - last[i];
455	trange.from = rnew->from - delta;
456	trange.to = rnew->to - delta;
457	change(i, &trange, true);
458}
459
460/*
461 * skip to just before line number from in file "i".  If "pr" is non-NULL,
462 * print all skipped stuff with string pr as a prefix.
463 */
464static int
465skip(int i, int from, const char *pr)
466{
467	size_t j, n;
468	char *line;
469
470	for (n = 0; cline[i] < from - 1; n += j) {
471		if ((line = get_line(fp[i], &j)) == NULL)
472			errx(EXIT_FAILURE, "logic error");
473		if (pr != NULL)
474			printf("%s%s", Tflag == 1 ? "\t" : pr, line);
475		cline[i]++;
476	}
477	return ((int) n);
478}
479
480/*
481 * Return 1 or 0 according as the old range (in file 1) contains exactly
482 * the same data as the new range (in file 2).
483 */
484static bool
485duplicate(struct range *r1, struct range *r2)
486{
487	int c, d;
488	int nchar;
489	int nline;
490
491	if (r1->to-r1->from != r2->to-r2->from)
492		return (0);
493	skip(0, r1->from, NULL);
494	skip(1, r2->from, NULL);
495	nchar = 0;
496	for (nline = 0; nline < r1->to - r1->from; nline++) {
497		do {
498			c = getc(fp[0]);
499			d = getc(fp[1]);
500			if (c == -1 && d == -1)
501				break;
502			if (c == -1 || d == -1)
503				errx(EXIT_FAILURE, "logic error");
504			nchar++;
505			if (c != d) {
506				repos(nchar);
507				return (0);
508			}
509		} while (c != '\n');
510	}
511	repos(nchar);
512	return (1);
513}
514
515static void
516repos(int nchar)
517{
518	int i;
519
520	for (i = 0; i < 2; i++)
521		(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
522}
523
524/*
525 * collect an editing script for later regurgitation
526 */
527static int
528edit(struct diff *diff, bool dup, int j, int difftype)
529{
530	if (!(eflag == EFLAG_UNMERGED ||
531		(!dup && eflag == EFLAG_OVERLAP ) ||
532		(dup && eflag == EFLAG_NOOVERLAP))) {
533		return (j);
534	}
535	j++;
536	overlap[j] = !dup;
537	if (!dup)
538		overlapcnt++;
539
540	de[j].type = difftype;
541#if DEBUG
542	de[j].line = strdup(diff->line);
543#endif	/* DEBUG */
544
545	de[j].old.from = diff->old.from;
546	de[j].old.to = diff->old.to;
547	de[j].new.from = diff->new.from;
548	de[j].new.to = diff->new.to;
549	return (j);
550}
551
552static void
553printrange(FILE *p, struct range *r)
554{
555	char *line = NULL;
556	size_t len = 0;
557	int i = 1;
558	ssize_t rlen = 0;
559
560	/* We haven't been asked to print anything */
561	if (r->from == r->to)
562		return;
563
564	if (r->from > r->to)
565		errx(EXIT_FAILURE, "invalid print range");
566
567	/*
568	 * XXX-THJ: We read through all of the file for each range printed.
569	 * This duplicates work and will probably impact performance on large
570	 * files with lots of ranges.
571	 */
572	fseek(p, 0L, SEEK_SET);
573	while ((rlen = getline(&line, &len, p)) > 0) {
574		if (i >= r->from)
575			printf("%s", line);
576		if (++i > r->to - 1)
577			break;
578	}
579	free(line);
580}
581
582/* regurgitate */
583static void
584edscript(int n)
585{
586	bool delete;
587	struct range *new, *old;
588
589	for (; n > 0; n--) {
590		new = &de[n].new;
591		old = &de[n].old;
592
593		delete = (new->from == new->to);
594		if (!oflag || !overlap[n]) {
595			prange(old, delete);
596		} else {
597			printf("%da\n", old->to - 1);
598			printf("%s\n", divider);
599		}
600		printrange(fp[2], new);
601		if (!oflag || !overlap[n]) {
602			if (!delete)
603				printf(".\n");
604		} else {
605			printf("%s %s\n.\n", newmark, f3mark);
606			printf("%da\n%s %s\n.\n", old->from - 1,
607				oldmark, f1mark);
608		}
609	}
610	if (iflag)
611		printf("w\nq\n");
612
613	exit(eflag == EFLAG_NONE ? overlapcnt : 0);
614}
615
616/*
617 * Output an edit script to turn mine into yours, when there is a conflict
618 * between the 3 files bracket the changes. Regurgitate the diffs in reverse
619 * order to allow the ed script to track down where the lines are as changes
620 * are made.
621 */
622static void
623Ascript(int n)
624{
625	int startmark;
626	bool deletenew;
627	bool deleteold;
628
629	struct range *new, *old;
630
631	for (; n > 0; n--) {
632		new = &de[n].new;
633		old = &de[n].old;
634		deletenew = (new->from == new->to);
635		deleteold = (old->from == old->to);
636
637		if (de[n].type == DIFF_TYPE2) {
638			if (!oflag || !overlap[n]) {
639				prange(old, deletenew);
640				printrange(fp[2], new);
641			} else {
642				startmark = new->to;
643
644				if (!deletenew)
645					startmark--;
646
647				printf("%da\n", startmark);
648				printf("%s %s\n", newmark, f3mark);
649
650				printf(".\n");
651
652				printf("%da\n", startmark -
653					(new->to - new->from));
654				printf("%s %s\n", oldmark, f2mark);
655				if (!deleteold)
656					printrange(fp[1], old);
657				printf("%s\n.\n", divider);
658			}
659
660		} else if (de[n].type == DIFF_TYPE3) {
661			startmark = old->to - 1;
662
663			if (!oflag || !overlap[n]) {
664				prange(old, deletenew);
665				printrange(fp[2], new);
666			} else {
667				printf("%da\n", startmark);
668				printf("%s %s\n", orgmark, f2mark);
669
670				if (deleteold) {
671					struct range r;
672					r.from = old->from-1;
673					r.to = new->to;
674					printrange(fp[1], &r);
675				} else
676					printrange(fp[1], old);
677
678				printf("%s\n", divider);
679				printrange(fp[2], new);
680			}
681
682			if (!oflag || !overlap[n]) {
683				if (!deletenew)
684					printf(".\n");
685			} else {
686				printf("%s %s\n.\n", newmark, f3mark);
687
688				/*
689				 * Go to the start of the conflict in original
690				 * file and append lines
691				 */
692				printf("%da\n%s %s\n.\n",
693					startmark - (old->to - old->from),
694					oldmark, f1mark);
695			}
696		}
697	}
698	if (iflag)
699		printf("w\nq\n");
700
701	exit(overlapcnt > 0);
702}
703
704/*
705 * Output the merged file directly (don't generate an ed script). When
706 * regurgitating diffs we need to walk forward through the file and print any
707 * inbetween lines.
708 */
709static void
710mergescript(int i)
711{
712	struct range r, *new, *old;
713	int n;
714
715	r.from = 1;
716	r.to = 1;
717
718	for (n = 1; n < i+1; n++) {
719		new = &de[n].new;
720		old = &de[n].old;
721
722		/* print any lines leading up to here */
723		r.to = old->from;
724		printrange(fp[0], &r);
725
726		if (de[n].type == DIFF_TYPE2) {
727			printf("%s %s\n", oldmark, f2mark);
728			printrange(fp[1], old);
729			printf("%s\n", divider);
730			printrange(fp[2], new);
731			printf("%s %s\n", newmark, f3mark);
732		} else if (de[n].type == DIFF_TYPE3) {
733			if (!oflag || !overlap[n]) {
734				printrange(fp[2], new);
735			} else {
736
737				printf("%s %s\n", oldmark, f1mark);
738				printrange(fp[0], old);
739
740				printf("%s %s\n", orgmark, f2mark);
741				if (old->from == old->to) {
742					struct range or;
743					or.from = old->from - 1;
744					or.to = new->to;
745					printrange(fp[1], &or);
746				} else
747					printrange(fp[1], old);
748
749				printf("%s\n", divider);
750
751				printrange(fp[2], new);
752				printf("%s %s\n", newmark, f3mark);
753			}
754		}
755
756		if (old->from == old->to)
757			r.from = new->to;
758		else
759			r.from = old->to;
760	}
761	/*
762	 * Print from the final range to the end of 'myfile'. Any deletions or
763	 * additions to this file should have been handled by now.
764	 *
765	 * If the ranges are the same we need to rewind a line.
766	 * If the new range is 0 length (from == to), we need to use the old
767	 * range.
768	 */
769	new = &de[n-1].new;
770	old = &de[n-1].old;
771	if ((old->from == new->from) &&
772		(old->to == new->to))
773		r.from--;
774	else if (new->from == new->to)
775		r.from = old->from;
776
777	/*
778	 * If the range is a 3 way merge then we need to skip a line in the
779	 * trailing output.
780	 */
781	if (de[n-1].type == DIFF_TYPE3)
782		r.from++;
783
784	r.to = INT_MAX;
785	printrange(fp[0], &r);
786	exit(overlapcnt > 0);
787}
788
789static void
790increase(void)
791{
792	struct diff *p;
793	char *q;
794	size_t newsz, incr;
795
796	/* are the memset(3) calls needed? */
797	newsz = szchanges == 0 ? 64 : 2 * szchanges;
798	incr = newsz - szchanges;
799
800	p = reallocarray(d13, newsz, sizeof(struct diff));
801	if (p == NULL)
802		err(1, NULL);
803	memset(p + szchanges, 0, incr * sizeof(struct diff));
804	d13 = p;
805	p = reallocarray(d23, newsz, sizeof(struct diff));
806	if (p == NULL)
807		err(1, NULL);
808	memset(p + szchanges, 0, incr * sizeof(struct diff));
809	d23 = p;
810	p = reallocarray(de, newsz, sizeof(struct diff));
811	if (p == NULL)
812		err(1, NULL);
813	memset(p + szchanges, 0, incr * sizeof(struct diff));
814	de = p;
815	q = reallocarray(overlap, newsz, sizeof(char));
816	if (q == NULL)
817		err(1, NULL);
818	memset(q + szchanges, 0, incr * sizeof(char));
819	overlap = q;
820	szchanges = newsz;
821}
822
823
824int
825main(int argc, char **argv)
826{
827	int ch, nblabels, status, m, n, kq, nke, nleft, i;
828	char *labels[] = { NULL, NULL, NULL };
829	const char *diffprog = DIFF_PATH;
830	char *file1, *file2, *file3;
831	char *diffargv[7];
832	int diffargc = 0;
833	int fd13[2], fd23[2];
834	int pd13, pd23;
835	cap_rights_t rights_ro;
836	struct kevent *e;
837
838	nblabels = 0;
839	eflag = EFLAG_NONE;
840	oflag = 0;
841	diffargv[diffargc++] = __DECONST(char *, diffprog);
842	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
843		switch (ch) {
844		case '3':
845			eflag = EFLAG_NOOVERLAP;
846			break;
847		case 'a':
848			diffargv[diffargc++] = __DECONST(char *, "-a");
849			break;
850		case 'A':
851			Aflag = 1;
852			break;
853		case 'e':
854			eflag = EFLAG_UNMERGED;
855			break;
856		case 'E':
857			eflag = EFLAG_OVERLAP;
858			oflag = 1;
859			break;
860		case 'i':
861			iflag = 1;
862			break;
863		case 'L':
864			oflag = 1;
865			if (nblabels >= 3)
866				errx(2, "too many file label options");
867			labels[nblabels++] = optarg;
868			break;
869		case 'm':
870			Aflag = 1;
871			oflag = 1;
872			mflag = 1;
873			break;
874		case 'T':
875			Tflag = 1;
876			break;
877		case 'x':
878			eflag = EFLAG_OVERLAP;
879			break;
880		case 'X':
881			oflag = 1;
882			eflag = EFLAG_OVERLAP;
883			break;
884		case DIFFPROG_OPT:
885			diffprog = optarg;
886			break;
887		case STRIPCR_OPT:
888			strip_cr = 1;
889			diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr");
890			break;
891		case HELP_OPT:
892			usage();
893			exit(0);
894		case VERSION_OPT:
895			printf("%s\n", diff3_version);
896			exit(0);
897		}
898	}
899	argc -= optind;
900	argv += optind;
901
902	if (Aflag) {
903		if (eflag == EFLAG_NONE)
904			eflag = EFLAG_UNMERGED;
905		oflag = 1;
906	}
907
908	if (argc != 3) {
909		usage();
910		exit(2);
911	}
912
913	if (caph_limit_stdio() == -1)
914		err(2, "unable to limit stdio");
915
916	cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK);
917
918	kq = kqueue();
919	if (kq == -1)
920		err(2, "kqueue");
921
922	e = malloc(2 * sizeof(struct kevent));
923	if (e == NULL)
924		err(2, "malloc");
925
926	/* TODO stdio */
927	file1 = argv[0];
928	file2 = argv[1];
929	file3 = argv[2];
930
931	if (oflag) {
932		asprintf(&f1mark, "%s",
933		    labels[0] != NULL ? labels[0] : file1);
934		if (f1mark == NULL)
935			err(2, "asprintf");
936		asprintf(&f2mark, "%s",
937		    labels[1] != NULL ? labels[1] : file2);
938		if (f2mark == NULL)
939			err(2, "asprintf");
940		asprintf(&f3mark, "%s",
941		    labels[2] != NULL ? labels[2] : file3);
942		if (f3mark == NULL)
943			err(2, "asprintf");
944	}
945	fp[0] = fopen(file1, "r");
946	if (fp[0] == NULL)
947		err(2, "Can't open %s", file1);
948	if (caph_rights_limit(fileno(fp[0]), &rights_ro) < 0)
949		err(2, "unable to limit rights on: %s", file1);
950
951	fp[1] = fopen(file2, "r");
952	if (fp[1] == NULL)
953		err(2, "Can't open %s", file2);
954	if (caph_rights_limit(fileno(fp[1]), &rights_ro) < 0)
955		err(2, "unable to limit rights on: %s", file2);
956
957	fp[2] = fopen(file3, "r");
958	if (fp[2] == NULL)
959		err(2, "Can't open %s", file3);
960	if (caph_rights_limit(fileno(fp[2]), &rights_ro) < 0)
961		err(2, "unable to limit rights on: %s", file3);
962
963	if (pipe(fd13))
964		err(2, "pipe");
965	if (pipe(fd23))
966		err(2, "pipe");
967
968	diffargv[diffargc] = file1;
969	diffargv[diffargc + 1] = file3;
970	diffargv[diffargc + 2] = NULL;
971
972	nleft = 0;
973	pd13 = diffexec(diffprog, diffargv, fd13);
974	EV_SET(e + nleft , pd13, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL);
975	if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1)
976		err(2, "kevent1");
977	nleft++;
978
979	diffargv[diffargc] = file2;
980	pd23 = diffexec(diffprog, diffargv, fd23);
981	EV_SET(e + nleft , pd23, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL);
982	if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1)
983		err(2, "kevent2");
984	nleft++;
985
986	caph_cache_catpages();
987	if (caph_enter() < 0)
988		err(2, "unable to enter capability mode");
989
990	/* parse diffs */
991	increase();
992	m = readin(fd13[0], &d13);
993	n = readin(fd23[0], &d23);
994
995	/* waitpid cooked over pdforks */
996	while (nleft > 0) {
997		nke = kevent(kq, NULL, 0, e, nleft, NULL);
998		if (nke == -1)
999			err(2, "kevent");
1000		for (i = 0; i < nke; i++) {
1001			status = e[i].data;
1002			if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
1003				errx(2, "diff exited abnormally");
1004			else if (WIFSIGNALED(status))
1005				errx(2, "diff killed by signal %d",
1006				    WTERMSIG(status));
1007		}
1008		nleft -= nke;
1009	}
1010	merge(m, n);
1011
1012	return (EXIT_SUCCESS);
1013}
1014