diff options
Diffstat (limited to 'usr.bin/diff3')
28 files changed, 1767 insertions, 0 deletions
diff --git a/usr.bin/diff3/Makefile b/usr.bin/diff3/Makefile new file mode 100644 index 000000000000..828743d092c7 --- /dev/null +++ b/usr.bin/diff3/Makefile @@ -0,0 +1,8 @@ +.include <src.opts.mk> + +PROG= diff3 + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + +.include <bsd.prog.mk> diff --git a/usr.bin/diff3/diff3.1 b/usr.bin/diff3/diff3.1 new file mode 100644 index 000000000000..9286a79e6ec6 --- /dev/null +++ b/usr.bin/diff3/diff3.1 @@ -0,0 +1,221 @@ +.\" $OpenBSD: diff3.1,v 1.7 2007/05/31 19:20:09 jmc Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 23, 2022 +.Dt DIFF3 1 +.Os +.Sh NAME +.Nm diff3 +.Nd 3-way differential file comparison +.Sh SYNOPSIS +.Nm +.Op Fl 3AaEeimTXx +.Op Fl Fl diff-program Ar program +.Op Fl Fl strip-trailing-cr +.Op Fl L | Fl Fl label Ar label1 +.Op Fl L | Fl Fl label Ar label2 +.Op Fl L | Fl Fl label Ar label3 +.Ar file1 file2 file3 +.Nm +.Op Fl Fl help +.Op Fl Fl version +.Sh DESCRIPTION +The +.Nm +utility compares the contents of three different versions of a file, +.Ar file1 , +.Ar file2 +and +.Ar file3 , +writing the result to the standard output. +The options describe different methods of merging and +purging +the separate versions into a new file. +.Nm +is used by +.Xr rcs 1 +to merge specific versions or create +new versions. +.Pp +The options are as follows: +.Bl -tag -width "-E, -X" +.It Fl 3 , Fl Fl easy-only +Produces an output script suitable for +.Xr ed 1 +with changes +specific only to +.Ar file3 . +.It Fl A Fl Fl show-all +Output all changes, bracketing conflicts. +.It Fl a , Fl Fl text +Treat all files as ASCII. +.It Fl E , Fl Fl show-overlap +.It Fl X +Similar to +.Fl e +and +.Fl x , +respectively, but treat overlapping changes (i.e., changes that would +be noted with ==== in the normal listing) differently. +The overlapping lines from both files will be inserted by the edit script, +bracketed by "<<<<<<" and ">>>>>>" lines. +.It Fl e , Fl Fl ed +Produces output in a form suitable as an input script for the +.Xr ed 1 +utility. +The script may then be used to merge differences common between all +three files and differences specific to +.Ar file1 +and +.Ar file3 . +In other words, the +.Fl e +option ignores differences specific to +.Ar file1 +and +.Ar file2 , +and those specific to +.Ar file2 +and +.Ar file3 . +It is useful for backing out changes specific to +.Ar file2 +only. +.It Fl Fl help +Prints usage information and exits. +.It Fl i +Appends 'w' and 'q' +.Xr ed 1 +commands. +.It Fl L , Fl Fl label +Defines labels to print instead of file names +.Ar file1 , +.Ar file2 +and +.Ar file3 . +.It Fl m, Fl Fl merge +Merge output instead of generating ed script. +.It Fl T, Fl Fl initial-tab +In the normal listing, +use a tab instead of two spaces +at the beginning of each line. +In modes that produce an +.Xr ed 1 +script, this option changes nothing. +.It Fl x, Fl Fl overlap-only +Produces an output script suitable for +.Xr ed 1 +with changes +specific only to all three versions. +.It Fl Fl diff-program Ar program +Use +.Ar program +instead of the default +.Xr diff 1 +to compare files. +.It Fl Fl strip-trailing-cr +Strip trailing carriage return on input files. +.It Fl Fl version +Prints version information and exits. +.El +.Pp +The +.Fl E +option is used by +.Tn RCS +.Xr merge 1 +to ensure that overlapping changes in the merged files are preserved +and brought to someone's attention. +.Pp +For example, suppose lines 7-8 are changed in both +.Ar file1 +and +.Ar file2 . +Applying the edit script generated by the command +.Pp +.Dl $ diff3 -E file1 file2 file3 +.Pp +to +.Ar file1 +results in the file: +.Bd -literal -offset indent +lines 1-6 +of file1 +<<<<<<< file1 +lines 7-8 +of file1 +======= +lines 7-8 +of file3 +>>>>>>> file3 +rest of file1 +.Ed +.Pp +The default output of +.Nm +makes notation of the differences between all files, and those +differences specific to each pair of files. +The changes are described by the commands necessary for +.Xr ed 1 +to create the desired target from the different versions. +See +.Xr diff 1 +for a description of the commands. +.Bl -tag -width "=====" +.It Li \&==== +The lines beneath this notation are ranges of lines which are different +between all files. +.It \&==== Ns Va n +The lines beneath this notation are ranges of lines which are exclusively +different in file +.Va n . +.El +.Sh SEE ALSO +.Xr diff 1 , +.Xr ed 1 , +.Xr merge 1 , +.Xr rcs 1 , +.Xr sdiff 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . +.Sh BUGS +The +.Fl e +option cannot catch and change lines which have +.Ql \&. +as the first and only character on the line. +The resulting script will fail on that line +as +.Ql \&. +is an +.Xr ed 1 +editing command. diff --git a/usr.bin/diff3/diff3.c b/usr.bin/diff3/diff3.c new file mode 100644 index 000000000000..39523f6e6b38 --- /dev/null +++ b/usr.bin/diff3/diff3.c @@ -0,0 +1,1115 @@ +/* $OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (C) Caldera International Inc. 2001-2002. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code and documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * 4. Neither the name of Caldera International, Inc. nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, + * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/capsicum.h> +#include <sys/procdesc.h> +#include <sys/types.h> +#include <sys/event.h> +#include <sys/wait.h> + +#include <capsicum_helpers.h> +#include <ctype.h> +#include <err.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +/* + * "from" is first in range of changed lines; "to" is last+1 + * from=to=line after point of insertion for added lines. + */ +struct range { + int from; + int to; +}; + +struct diff { +#define DIFF_TYPE1 1 +#define DIFF_TYPE2 2 +#define DIFF_TYPE3 3 + int type; +#if DEBUG + char *line; +#endif /* DEBUG */ + + /* Ranges as lines */ + struct range old; + struct range new; +}; + +#define EFLAG_NONE 0 +#define EFLAG_OVERLAP 1 +#define EFLAG_NOOVERLAP 2 +#define EFLAG_UNMERGED 3 + +static size_t szchanges; + +static struct diff *d13; +static struct diff *d23; +/* + * "de" is used to gather editing scripts. These are later spewed out in + * reverse order. Its first element must be all zero, the "old" and "new" + * components of "de" contain line positions. Array overlap indicates which + * sections in "de" correspond to lines that are different in all three files. + */ +static struct diff *de; +static char *overlap; +static int overlapcnt; +static FILE *fp[3]; +static int cline[3]; /* # of the last-read line in each file (0-2) */ +/* + * The latest known correspondence between line numbers of the 3 files + * is stored in last[1-3]; + */ +static int last[4]; +static int Aflag, eflag, iflag, mflag, Tflag; +static int oflag; /* indicates whether to mark overlaps (-E or -X) */ +static int strip_cr; +static char *f1mark, *f2mark, *f3mark; +static const char *oldmark = "<<<<<<<"; +static const char *orgmark = "|||||||"; +static const char *newmark = ">>>>>>>"; +static const char *divider = "======="; + +static bool duplicate(struct range *, struct range *); +static int edit(struct diff *, bool, int, int); +static char *getchange(FILE *); +static char *get_line(FILE *, size_t *); +static int readin(int fd, struct diff **); +static int skip(int, int, const char *); +static void change(int, struct range *, bool); +static void keep(int, struct range *); +static void merge(int, int); +static void prange(struct range *, bool); +static void repos(int); +static void separate(const char *); +static void edscript(int) __dead2; +static void Ascript(int) __dead2; +static void mergescript(int) __dead2; +static void increase(void); +static void usage(void); +static void printrange(FILE *, struct range *); + +static const char diff3_version[] = "FreeBSD diff3 20240925"; + +enum { + DIFFPROG_OPT, + STRIPCR_OPT, + HELP_OPT, + VERSION_OPT +}; + +#define DIFF_PATH "/usr/bin/diff" + +#define OPTIONS "3aAeEiL:mTxX" +static struct option longopts[] = { + { "ed", no_argument, NULL, 'e' }, + { "show-overlap", no_argument, NULL, 'E' }, + { "overlap-only", no_argument, NULL, 'x' }, + { "initial-tab", no_argument, NULL, 'T' }, + { "text", no_argument, NULL, 'a' }, + { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, + { "show-all", no_argument, NULL, 'A' }, + { "easy-only", no_argument, NULL, '3' }, + { "merge", no_argument, NULL, 'm' }, + { "label", required_argument, NULL, 'L' }, + { "diff-program", required_argument, NULL, DIFFPROG_OPT }, + { "help", no_argument, NULL, HELP_OPT}, + { "version", no_argument, NULL, VERSION_OPT} +}; + +static void +usage(void) +{ + fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] " + "[-L label3] file1 file2 file3\n"); +} + +static int +strtoi(char *str, char **end) +{ + intmax_t num; + + errno = 0; + num = strtoimax(str, end, 10); + if ((end != NULL && *end == str) || + num < 0 || num > INT_MAX || + errno == EINVAL || errno == ERANGE) + err(1, "error in diff output"); + return (int)num; +} + +/* + * Read diff hunks into the array pointed to by *dd. + * + * The output from `diff foo bar` consists of a series of hunks describing + * an addition (lines in bar not present in foo), change (lines in bar + * different from lines in foo), or deletion (lines in foo not present in + * bar). Each record starts with a line of the form: + * + * a[,b]xc[,d] + * + * where a, b, c, and d are nonnegative integers (b and d are printed only + * if they differ from a and c, respectively), and x is either 'a' for an + * addition, 'c' for a change, or 'd' for a deletion. This is then + * followed by a series of lines (which we ignore) giving the added, + * changed, or deleted text. + * + * For an addition, a == b is the last line in 'foo' before the addition, + * while c through d is the range of lines in 'bar' to be added to 'foo'. + * + * For a change, a through b is the range of lines in 'foo' to be replaced + * and c through d is the range of lines in 'bar' to replace them with. + * + * For a deletion, a through b is the range of lines in 'foo' to remove + * and c == d is the line in 'bar' which corresponds to the last line + * before the deletion. + * + * The observant reader will have noticed that x is not really needed and + * that we can fully describe any hunk using only a, b, c, and d: + * + * - an addition replaces a zero-length range in one file with a + * non-zero-length range from the other + * + * - a change replaces a non-zero-length range in one file with a + * non-zero-length range from the other + * + * - a deletion replaces a non-zero-length range in one file with a + * zero-length range from the other + */ +static int +readin(int fd, struct diff **dd) +{ + int a, b, c, d; + int i; + char kind, *p; + FILE *f; + + f = fdopen(fd, "r"); + if (f == NULL) + err(2, "fdopen"); + for (i = 0; (p = getchange(f)) != NULL; i++) { + if ((size_t)i >= szchanges - 1) + increase(); +#if DEBUG + (*dd)[i].line = strdup(p); +#endif /* DEBUG */ + + a = b = strtoi(p, &p); + if (*p == ',') + b = strtoi(p + 1, &p); + kind = *p++; + c = d = strtoi(p, &p); + if (*p == ',') + d = strtoi(p + 1, &p); + if (*p != '\n') + errx(1, "error in diff output"); + if (kind == 'a') + a++; + else if (kind == 'c') + /* nothing */ ; + else if (kind == 'd') + c++; + else + errx(1, "error in diff output"); + b++; + d++; + if (b < a || d < c) + errx(1, "error in diff output"); + (*dd)[i].old.from = a; + (*dd)[i].old.to = b; + (*dd)[i].new.from = c; + (*dd)[i].new.to = d; + if (i > 0) { + if ((*dd)[i].old.from < (*dd)[i - 1].old.to || + (*dd)[i].new.from < (*dd)[i - 1].new.to) + errx(1, "diff output out of order"); + } + } + if (i > 0) { + (*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to; + (*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to; + } + fclose(f); + return (i); +} + +static int +diffexec(const char *diffprog, char **diffargv, int fd[]) +{ + int pd; + + switch (pdfork(&pd, PD_CLOEXEC)) { + case 0: + close(fd[0]); + if (dup2(fd[1], STDOUT_FILENO) == -1) + err(2, "child could not duplicate descriptor"); + close(fd[1]); + execvp(diffprog, diffargv); + err(2, "could not execute diff: %s", diffprog); + break; + case -1: + err(2, "could not fork"); + break; + } + close(fd[1]); + return (pd); +} + +static char * +getchange(FILE *b) +{ + char *line; + + while ((line = get_line(b, NULL)) != NULL) { + if (isdigit((unsigned char)line[0])) + return (line); + } + return (NULL); +} + + +static char * +get_line(FILE *b, size_t *n) +{ + ssize_t len; + static char *buf = NULL; + static size_t bufsize = 0; + + if ((len = getline(&buf, &bufsize, b)) < 0) + return (NULL); + + if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) { + buf[len - 2] = '\n'; + buf[len - 1] = '\0'; + len--; + } + + if (n != NULL) + *n = len; + + return (buf); +} + +static void +merge(int m1, int m2) +{ + struct diff *d1, *d2, *d3; + int j, t1, t2; + bool dup = false; + + d1 = d13; + d2 = d23; + j = 0; + + for (;;) { + t1 = (d1 < d13 + m1); + t2 = (d2 < d23 + m2); + if (!t1 && !t2) + break; + + /* first file is different from the others */ + if (!t2 || (t1 && d1->new.to < d2->new.from)) { + /* stuff peculiar to 1st file */ + if (eflag == EFLAG_NONE) { + separate("1"); + change(1, &d1->old, false); + keep(2, &d1->new); + change(3, &d1->new, false); + } else if (eflag == EFLAG_OVERLAP) { + j = edit(d2, dup, j, DIFF_TYPE1); + } + d1++; + continue; + } + /* second file is different from others */ + if (!t1 || (t2 && d2->new.to < d1->new.from)) { + if (eflag == EFLAG_NONE) { + separate("2"); + keep(1, &d2->new); + change(3, &d2->new, false); + change(2, &d2->old, false); + } else if (Aflag || mflag) { + // XXX-THJ: What does it mean for the second file to differ? + if (eflag == EFLAG_UNMERGED) + j = edit(d2, dup, j, DIFF_TYPE2); + } + d2++; + continue; + } + /* + * Merge overlapping changes in first file + * this happens after extension (see below). + */ + if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) { + d1[1].old.from = d1->old.from; + d1[1].new.from = d1->new.from; + d1++; + continue; + } + + /* merge overlapping changes in second */ + if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) { + d2[1].old.from = d2->old.from; + d2[1].new.from = d2->new.from; + d2++; + continue; + } + /* stuff peculiar to third file or different in all */ + if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) { + dup = duplicate(&d1->old, &d2->old); + /* + * dup = 0 means all files differ + * dup = 1 means files 1 and 2 identical + */ + if (eflag == EFLAG_NONE) { + separate(dup ? "3" : ""); + change(1, &d1->old, dup); + change(2, &d2->old, false); + d3 = d1->old.to > d1->old.from ? d1 : d2; + change(3, &d3->new, false); + } else { + j = edit(d1, dup, j, DIFF_TYPE3); + } + dup = false; + d1++; + d2++; + continue; + } + /* + * Overlapping changes from file 1 and 2; extend changes + * appropriately to make them coincide. + */ + if (d1->new.from < d2->new.from) { + d2->old.from -= d2->new.from - d1->new.from; + d2->new.from = d1->new.from; + } else if (d2->new.from < d1->new.from) { + d1->old.from -= d1->new.from - d2->new.from; + d1->new.from = d2->new.from; + } + if (d1->new.to > d2->new.to) { + d2->old.to += d1->new.to - d2->new.to; + d2->new.to = d1->new.to; + } else if (d2->new.to > d1->new.to) { + d1->old.to += d2->new.to - d1->new.to; + d1->new.to = d2->new.to; + } + } + + if (mflag) + mergescript(j); + else if (Aflag) + Ascript(j); + else if (eflag) + edscript(j); +} + +static void +separate(const char *s) +{ + printf("====%s\n", s); +} + +/* + * The range of lines rold.from thru rold.to in file i is to be changed. + * It is to be printed only if it does not duplicate something to be + * printed later. + */ +static void +change(int i, struct range *rold, bool dup) +{ + + printf("%d:", i); + last[i] = rold->to; + prange(rold, false); + if (dup) + return; + i--; + skip(i, rold->from, NULL); + skip(i, rold->to, " "); +} + +/* + * Print the range of line numbers, rold.from thru rold.to, as n1,n2 or + * n1. + */ +static void +prange(struct range *rold, bool delete) +{ + + if (rold->to <= rold->from) + printf("%da\n", rold->from - 1); + else { + printf("%d", rold->from); + if (rold->to > rold->from + 1) + printf(",%d", rold->to - 1); + if (delete) + printf("d\n"); + else + printf("c\n"); + } +} + +/* + * No difference was reported by diff between file 1 (or 2) and file 3, + * and an artificial dummy difference (trange) must be ginned up to + * correspond to the change reported in the other file. + */ +static void +keep(int i, struct range *rnew) +{ + int delta; + struct range trange; + + delta = last[3] - last[i]; + trange.from = rnew->from - delta; + trange.to = rnew->to - delta; + change(i, &trange, true); +} + +/* + * skip to just before line number from in file "i". If "pr" is non-NULL, + * print all skipped stuff with string pr as a prefix. + */ +static int +skip(int i, int from, const char *pr) +{ + size_t j, n; + char *line; + + for (n = 0; cline[i] < from - 1; n += j) { + if ((line = get_line(fp[i], &j)) == NULL) + errx(EXIT_FAILURE, "logic error"); + if (pr != NULL) + printf("%s%s", Tflag == 1 ? "\t" : pr, line); + cline[i]++; + } + return ((int) n); +} + +/* + * Return 1 or 0 according as the old range (in file 1) contains exactly + * the same data as the new range (in file 2). + */ +static bool +duplicate(struct range *r1, struct range *r2) +{ + int c, d; + int nchar; + int nline; + + if (r1->to-r1->from != r2->to-r2->from) + return (0); + skip(0, r1->from, NULL); + skip(1, r2->from, NULL); + nchar = 0; + for (nline = 0; nline < r1->to - r1->from; nline++) { + do { + c = getc(fp[0]); + d = getc(fp[1]); + if (c == -1 && d == -1) + break; + if (c == -1 || d == -1) + errx(EXIT_FAILURE, "logic error"); + nchar++; + if (c != d) { + repos(nchar); + return (0); + } + } while (c != '\n'); + } + repos(nchar); + return (1); +} + +static void +repos(int nchar) +{ + int i; + + for (i = 0; i < 2; i++) + (void)fseek(fp[i], (long)-nchar, SEEK_CUR); +} + +/* + * collect an editing script for later regurgitation + */ +static int +edit(struct diff *diff, bool dup, int j, int difftype) +{ + if (!(eflag == EFLAG_UNMERGED || + (!dup && eflag == EFLAG_OVERLAP ) || + (dup && eflag == EFLAG_NOOVERLAP))) { + return (j); + } + j++; + overlap[j] = !dup; + if (!dup) + overlapcnt++; + + de[j].type = difftype; +#if DEBUG + de[j].line = strdup(diff->line); +#endif /* DEBUG */ + + de[j].old.from = diff->old.from; + de[j].old.to = diff->old.to; + de[j].new.from = diff->new.from; + de[j].new.to = diff->new.to; + return (j); +} + +static void +printrange(FILE *p, struct range *r) +{ + char *line = NULL; + size_t len = 0; + int i = 1; + + /* We haven't been asked to print anything */ + if (r->from == r->to) + return; + + if (r->from > r->to) + errx(EXIT_FAILURE, "invalid print range"); + + /* + * XXX-THJ: We read through all of the file for each range printed. + * This duplicates work and will probably impact performance on large + * files with lots of ranges. + */ + fseek(p, 0L, SEEK_SET); + while (getline(&line, &len, p) > 0) { + if (i >= r->from) + printf("%s", line); + if (++i > r->to - 1) + break; + } + free(line); +} + +/* regurgitate */ +static void +edscript(int n) +{ + bool delete; + struct range *new, *old; + + for (; n > 0; n--) { + new = &de[n].new; + old = &de[n].old; + + delete = (new->from == new->to); + if (de[n].type == DIFF_TYPE1) { + if (delete) + printf("%dd\n", new->from - 1); + else if (old->from == new->from && old->to == new->to) { + printf("%dc\n", old->from); + printrange(fp[2], old); + printf(".\n"); + } + continue; + } else { + if (!oflag || !overlap[n]) { + prange(old, delete); + } else { + printf("%da\n", old->to - 1); + printf("%s\n", divider); + } + printrange(fp[2], new); + if (!oflag || !overlap[n]) { + if (!delete) + printf(".\n"); + } else { + printf("%s %s\n.\n", newmark, f3mark); + printf("%da\n%s %s\n.\n", old->from - 1, + oldmark, f1mark); + } + } + } + if (iflag) + printf("w\nq\n"); + + exit(eflag == EFLAG_NONE ? overlapcnt : 0); +} + +/* + * Output an edit script to turn mine into yours, when there is a conflict + * between the 3 files bracket the changes. Regurgitate the diffs in reverse + * order to allow the ed script to track down where the lines are as changes + * are made. + */ +static void +Ascript(int n) +{ + int startmark; + bool deletenew; + bool deleteold; + + struct range *new, *old; + + for (; n > 0; n--) { + new = &de[n].new; + old = &de[n].old; + deletenew = (new->from == new->to); + deleteold = (old->from == old->to); + + if (de[n].type == DIFF_TYPE2) { + if (!oflag || !overlap[n]) { + prange(old, deletenew); + printrange(fp[2], new); + } else { + startmark = new->to - 1; + + printf("%da\n", startmark); + printf("%s %s\n", newmark, f3mark); + + printf(".\n"); + + printf("%da\n", startmark - + (new->to - new->from)); + printf("%s %s\n", oldmark, f2mark); + if (!deleteold) + printrange(fp[1], old); + printf("%s\n.\n", divider); + } + + } else if (de[n].type == DIFF_TYPE3) { + startmark = old->to - 1; + + if (!oflag || !overlap[n]) { + prange(old, deletenew); + printrange(fp[2], new); + } else { + printf("%da\n", startmark); + printf("%s %s\n", orgmark, f2mark); + + if (deleteold) { + struct range r; + r.from = old->from-1; + r.to = new->to; + printrange(fp[1], &r); + } else + printrange(fp[1], old); + + printf("%s\n", divider); + printrange(fp[2], new); + } + + if (!oflag || !overlap[n]) { + if (!deletenew) + printf(".\n"); + } else { + printf("%s %s\n.\n", newmark, f3mark); + + /* + * Go to the start of the conflict in original + * file and append lines + */ + printf("%da\n%s %s\n.\n", + startmark - (old->to - old->from), + oldmark, f1mark); + } + } + } + if (iflag) + printf("w\nq\n"); + + exit(overlapcnt > 0); +} + +/* + * Output the merged file directly (don't generate an ed script). When + * regurgitating diffs we need to walk forward through the file and print any + * inbetween lines. + */ +static void +mergescript(int i) +{ + struct range r, *new, *old; + int n; + bool delete = false; + + r.from = 1; + r.to = 1; + + for (n = 1; n <= i; n++) { + new = &de[n].new; + old = &de[n].old; + + /* + * Print any lines leading up to here. If we are merging don't + * print deleted ranges. + */ + delete = (new->from == new->to); + if (de[n].type == DIFF_TYPE1 && delete) + r.to = new->from - 1; + else if (de[n].type == DIFF_TYPE3 && (old->from == old->to)) { + r.from = old->from - 1; + r.to = new->from; + } else + r.to = old->from; + + printrange(fp[0], &r); + switch (de[n].type) { + case DIFF_TYPE1: + /* If this isn't a delete print it */ + if (!delete) + printrange(fp[2], new); + break; + case DIFF_TYPE2: + printf("%s %s\n", oldmark, f2mark); + printrange(fp[1], old); + printf("%s\n", divider); + printrange(fp[2], new); + printf("%s %s\n", newmark, f3mark); + break; + case DIFF_TYPE3: + if (!oflag || !overlap[n]) { + printrange(fp[2], new); + } else { + + printf("%s %s\n", oldmark, f1mark); + printrange(fp[0], old); + + if (eflag != EFLAG_OVERLAP) { + printf("%s %s\n", orgmark, f2mark); + if (old->from == old->to) { + struct range or; + or.from = old->from - 1; + or.to = new->to; + printrange(fp[1], &or); + } else { + printrange(fp[1], old); + } + } + + printf("%s\n", divider); + + printrange(fp[2], new); + printf("%s %s\n", newmark, f3mark); + } + break; + default: + printf("Error: Unhandled diff type - exiting\n"); + exit(EXIT_FAILURE); + } + + if (old->from == old->to) + r.from = new->to; + else + r.from = old->to; + } + + /* + * Print from the final range to the end of 'myfile'. Any deletions or + * additions to this file should have been handled by now. + * + * If the ranges are the same we need to rewind a line. + * If the new range is 0 length (from == to), we need to use the old + * range. + */ + new = &de[n-1].new; + old = &de[n-1].old; + + if (old->from == new->from && old->to == new->to) + r.from--; + else if (new->from == new->to) + r.from = old->from; + + r.to = INT_MAX; + printrange(fp[2], &r); + exit(overlapcnt > 0); +} + +static void +increase(void) +{ + struct diff *p; + char *q; + size_t newsz, incr; + + /* are the memset(3) calls needed? */ + newsz = szchanges == 0 ? 64 : 2 * szchanges; + incr = newsz - szchanges; + + p = reallocarray(d13, newsz, sizeof(*p)); + if (p == NULL) + err(1, NULL); + memset(p + szchanges, 0, incr * sizeof(*p)); + d13 = p; + p = reallocarray(d23, newsz, sizeof(*p)); + if (p == NULL) + err(1, NULL); + memset(p + szchanges, 0, incr * sizeof(*p)); + d23 = p; + p = reallocarray(de, newsz, sizeof(*p)); + if (p == NULL) + err(1, NULL); + memset(p + szchanges, 0, incr * sizeof(*p)); + de = p; + q = reallocarray(overlap, newsz, 1); + if (q == NULL) + err(1, NULL); + memset(q + szchanges, 0, incr * 1); + overlap = q; + szchanges = newsz; +} + + +int +main(int argc, char **argv) +{ + int ch, nblabels, status, m, n, kq, nke, nleft, i; + char *labels[] = { NULL, NULL, NULL }; + const char *diffprog = DIFF_PATH; + char *file1, *file2, *file3; + char *diffargv[7]; + int diffargc = 0; + int fd13[2], fd23[2]; + int pd13, pd23; + cap_rights_t rights_ro; + struct kevent *e; + + nblabels = 0; + eflag = EFLAG_NONE; + oflag = 0; + diffargv[diffargc++] = __DECONST(char *, diffprog); + while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) { + switch (ch) { + case '3': + eflag = EFLAG_NOOVERLAP; + break; + case 'a': + diffargv[diffargc++] = __DECONST(char *, "-a"); + break; + case 'A': + Aflag = 1; + break; + case 'e': + eflag = EFLAG_UNMERGED; + break; + case 'E': + eflag = EFLAG_OVERLAP; + oflag = 1; + break; + case 'i': + iflag = 1; + break; + case 'L': + oflag = 1; + if (nblabels >= 3) + errx(2, "too many file label options"); + labels[nblabels++] = optarg; + break; + case 'm': + Aflag = 1; + oflag = 1; + mflag = 1; + break; + case 'T': + Tflag = 1; + break; + case 'x': + eflag = EFLAG_OVERLAP; + break; + case 'X': + oflag = 1; + eflag = EFLAG_OVERLAP; + break; + case DIFFPROG_OPT: + diffprog = optarg; + break; + case STRIPCR_OPT: + strip_cr = 1; + diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr"); + break; + case HELP_OPT: + usage(); + exit(0); + case VERSION_OPT: + printf("%s\n", diff3_version); + exit(0); + } + } + argc -= optind; + argv += optind; + + if (Aflag) { + if (eflag == EFLAG_NONE) + eflag = EFLAG_UNMERGED; + oflag = 1; + } + + if (argc != 3) { + usage(); + exit(2); + } + + if (caph_limit_stdio() == -1) + err(2, "unable to limit stdio"); + + cap_rights_init(&rights_ro, CAP_READ, CAP_FSTAT, CAP_SEEK); + + kq = kqueue(); + if (kq == -1) + err(2, "kqueue"); + + e = malloc(2 * sizeof(*e)); + if (e == NULL) + err(2, "malloc"); + + /* TODO stdio */ + file1 = argv[0]; + file2 = argv[1]; + file3 = argv[2]; + + if (oflag) { + asprintf(&f1mark, "%s", + labels[0] != NULL ? labels[0] : file1); + if (f1mark == NULL) + err(2, "asprintf"); + asprintf(&f2mark, "%s", + labels[1] != NULL ? labels[1] : file2); + if (f2mark == NULL) + err(2, "asprintf"); + asprintf(&f3mark, "%s", + labels[2] != NULL ? labels[2] : file3); + if (f3mark == NULL) + err(2, "asprintf"); + } + fp[0] = fopen(file1, "r"); + if (fp[0] == NULL) + err(2, "Can't open %s", file1); + if (caph_rights_limit(fileno(fp[0]), &rights_ro) < 0) + err(2, "unable to limit rights on: %s", file1); + + fp[1] = fopen(file2, "r"); + if (fp[1] == NULL) + err(2, "Can't open %s", file2); + if (caph_rights_limit(fileno(fp[1]), &rights_ro) < 0) + err(2, "unable to limit rights on: %s", file2); + + fp[2] = fopen(file3, "r"); + if (fp[2] == NULL) + err(2, "Can't open %s", file3); + if (caph_rights_limit(fileno(fp[2]), &rights_ro) < 0) + err(2, "unable to limit rights on: %s", file3); + + if (pipe(fd13)) + err(2, "pipe"); + if (pipe(fd23)) + err(2, "pipe"); + + diffargv[diffargc] = file1; + diffargv[diffargc + 1] = file3; + diffargv[diffargc + 2] = NULL; + + nleft = 0; + pd13 = diffexec(diffprog, diffargv, fd13); + EV_SET(e + nleft , pd13, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL); + if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) + err(2, "kevent1"); + nleft++; + + diffargv[diffargc] = file2; + pd23 = diffexec(diffprog, diffargv, fd23); + EV_SET(e + nleft , pd23, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, NULL); + if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) + err(2, "kevent2"); + nleft++; + + caph_cache_catpages(); + if (caph_enter() < 0) + err(2, "unable to enter capability mode"); + + /* parse diffs */ + increase(); + m = readin(fd13[0], &d13); + n = readin(fd23[0], &d23); + + /* waitpid cooked over pdforks */ + while (nleft > 0) { + nke = kevent(kq, NULL, 0, e, nleft, NULL); + if (nke == -1) + err(2, "kevent"); + for (i = 0; i < nke; i++) { + status = e[i].data; + if (WIFEXITED(status) && WEXITSTATUS(status) >= 2) + errx(2, "diff exited abnormally"); + else if (WIFSIGNALED(status)) + errx(2, "diff killed by signal %d", + WTERMSIG(status)); + } + nleft -= nke; + } + free(e); + merge(m, n); + + return (EXIT_SUCCESS); +} diff --git a/usr.bin/diff3/tests/1.out b/usr.bin/diff3/tests/1.out new file mode 100644 index 000000000000..9b872bf3a880 --- /dev/null +++ b/usr.bin/diff3/tests/1.out @@ -0,0 +1,14 @@ +==== +1:2,3c + which multiple lines + and a few typ0s to fix +2:2,3c + which multiple lines + and a few typos to fix +3:2c + and a few typ0s to fix +====2 +1:4a +3:3a +2:5c + New line diff --git a/usr.bin/diff3/tests/1.txt b/usr.bin/diff3/tests/1.txt new file mode 100644 index 000000000000..f0cd8062a6f1 --- /dev/null +++ b/usr.bin/diff3/tests/1.txt @@ -0,0 +1,6 @@ +This is a more complete test +which multiple lines +and a few typ0s to fix +also I plan to add few lines + and to remove +other lines diff --git a/usr.bin/diff3/tests/1cr.txt b/usr.bin/diff3/tests/1cr.txt new file mode 100644 index 000000000000..c24bf241e68a --- /dev/null +++ b/usr.bin/diff3/tests/1cr.txt @@ -0,0 +1,6 @@ +This is a more complete test
+which multiple lines
+and a few typ0s to fix
+also I plan to add few lines
+ and to remove
+other lines
diff --git a/usr.bin/diff3/tests/1t.out b/usr.bin/diff3/tests/1t.out new file mode 100644 index 000000000000..fc54ce50286f --- /dev/null +++ b/usr.bin/diff3/tests/1t.out @@ -0,0 +1,14 @@ +==== +1:2,3c + which multiple lines + and a few typ0s to fix +2:2,3c + which multiple lines + and a few typos to fix +3:2c + and a few typ0s to fix +====2 +1:4a +3:3a +2:5c + New line diff --git a/usr.bin/diff3/tests/2.out b/usr.bin/diff3/tests/2.out new file mode 100644 index 000000000000..420dc4784b80 --- /dev/null +++ b/usr.bin/diff3/tests/2.out @@ -0,0 +1,3 @@ +2,3c +and a few typ0s to fix +. diff --git a/usr.bin/diff3/tests/2.txt b/usr.bin/diff3/tests/2.txt new file mode 100644 index 000000000000..990aa6f345c6 --- /dev/null +++ b/usr.bin/diff3/tests/2.txt @@ -0,0 +1,7 @@ +This is a more complete test +which multiple lines +and a few typos to fix +also I plan to add few lines +New line + and to remove +other lines diff --git a/usr.bin/diff3/tests/3.out b/usr.bin/diff3/tests/3.out new file mode 100644 index 000000000000..81509d4ca510 --- /dev/null +++ b/usr.bin/diff3/tests/3.out @@ -0,0 +1,8 @@ +3a +======= +and a few typ0s to fix +>>>>>>> 3 +. +1a +<<<<<<< 1 +. diff --git a/usr.bin/diff3/tests/3.txt b/usr.bin/diff3/tests/3.txt new file mode 100644 index 000000000000..104466e49690 --- /dev/null +++ b/usr.bin/diff3/tests/3.txt @@ -0,0 +1,5 @@ +This is a more complete test +and a few typ0s to fix +also I plan to add few lines + and to remove +other lines diff --git a/usr.bin/diff3/tests/4.out b/usr.bin/diff3/tests/4.out new file mode 100644 index 000000000000..81509d4ca510 --- /dev/null +++ b/usr.bin/diff3/tests/4.out @@ -0,0 +1,8 @@ +3a +======= +and a few typ0s to fix +>>>>>>> 3 +. +1a +<<<<<<< 1 +. diff --git a/usr.bin/diff3/tests/5.out b/usr.bin/diff3/tests/5.out new file mode 100644 index 000000000000..420dc4784b80 --- /dev/null +++ b/usr.bin/diff3/tests/5.out @@ -0,0 +1,3 @@ +2,3c +and a few typ0s to fix +. diff --git a/usr.bin/diff3/tests/6.out b/usr.bin/diff3/tests/6.out new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/usr.bin/diff3/tests/6.out diff --git a/usr.bin/diff3/tests/7.out b/usr.bin/diff3/tests/7.out new file mode 100644 index 000000000000..9b872bf3a880 --- /dev/null +++ b/usr.bin/diff3/tests/7.out @@ -0,0 +1,14 @@ +==== +1:2,3c + which multiple lines + and a few typ0s to fix +2:2,3c + which multiple lines + and a few typos to fix +3:2c + and a few typ0s to fix +====2 +1:4a +3:3a +2:5c + New line diff --git a/usr.bin/diff3/tests/8.out b/usr.bin/diff3/tests/8.out new file mode 100644 index 000000000000..5d02326ed7d1 --- /dev/null +++ b/usr.bin/diff3/tests/8.out @@ -0,0 +1,19 @@ +4a +>>>>>>> 3 +. +4a +<<<<<<< 2 +New line +======= +. +3a +||||||| 2 +which multiple lines +and a few typos to fix +======= +and a few typ0s to fix +>>>>>>> 3 +. +1a +<<<<<<< 1 +. diff --git a/usr.bin/diff3/tests/9.out b/usr.bin/diff3/tests/9.out new file mode 100644 index 000000000000..2071bab4ce07 --- /dev/null +++ b/usr.bin/diff3/tests/9.out @@ -0,0 +1,17 @@ +This is a more complete test +<<<<<<< 1 +which multiple lines +and a few typ0s to fix +||||||| 2 +which multiple lines +and a few typos to fix +======= +and a few typ0s to fix +>>>>>>> 3 +also I plan to add few lines +<<<<<<< 2 +New line +======= +>>>>>>> 3 + and to remove +other lines diff --git a/usr.bin/diff3/tests/Makefile b/usr.bin/diff3/tests/Makefile new file mode 100644 index 000000000000..864f27beede8 --- /dev/null +++ b/usr.bin/diff3/tests/Makefile @@ -0,0 +1,28 @@ +PACKAGE= tests + +ATF_TESTS_SH= diff3_test +${PACKAGE}FILES+= \ + 1.txt \ + 1cr.txt \ + 2.txt \ + 3.txt \ + long-m.txt \ + long-o.txt \ + long-y.txt \ + 1.out \ + 1t.out \ + 2.out \ + 3.out \ + 4.out \ + 5.out \ + 6.out \ + 7.out \ + 8.out \ + 9.out \ + long-ed.out \ + long-A.out \ + long-merge.out \ + fbsdid1.txt \ + fbsdid2.txt + +.include <bsd.test.mk> diff --git a/usr.bin/diff3/tests/Makefile.depend b/usr.bin/diff3/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/usr.bin/diff3/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.bin/diff3/tests/diff3_test.sh b/usr.bin/diff3/tests/diff3_test.sh new file mode 100755 index 000000000000..3cbd7dac1ed9 --- /dev/null +++ b/usr.bin/diff3/tests/diff3_test.sh @@ -0,0 +1,108 @@ + +atf_test_case diff3 +atf_test_case diff3_lesssimple +atf_test_case diff3_ed +atf_test_case diff3_A +atf_test_case diff3_merge +atf_test_case diff3_E_merge + +diff3_body() +{ + atf_check -o file:$(atf_get_srcdir)/1.out \ + diff3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/1.out \ + diff3 --strip-trailing-cr $(atf_get_srcdir)/1cr.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/1t.out \ + diff3 -T $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/2.out \ + diff3 -e $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/3.out \ + diff3 -E -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/4.out \ + diff3 -X -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/5.out \ + diff3 -x $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/6.out \ + diff3 -3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -o file:$(atf_get_srcdir)/7.out \ + diff3 -i $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt +} + +diff3_lesssimple_body() +{ + atf_check -s exit:1 -o file:$(atf_get_srcdir)/10.out \ + diff3 -m -L 1 -L 2 -L 3 $(atf_get_srcdir)/4.txt $(atf_get_srcdir)/5.txt $(atf_get_srcdir)/6.txt +} + +diff3_ed_body() +{ + atf_check -s exit:0 -o file:$(atf_get_srcdir)/long-ed.out \ + diff3 -e $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt +} + +diff3_A_body() +{ + atf_check -s exit:1 -o file:$(atf_get_srcdir)/8.out \ + diff3 -A -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -s exit:1 -o file:$(atf_get_srcdir)/long-A.out \ + diff3 -A -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt +} + + +diff3_merge_body() +{ + atf_check -s exit:1 -o file:$(atf_get_srcdir)/9.out \ + diff3 -m -L 1 -L 2 -L 3 $(atf_get_srcdir)/1.txt $(atf_get_srcdir)/2.txt $(atf_get_srcdir)/3.txt + + atf_check -s exit:1 -o file:$(atf_get_srcdir)/long-merge.out \ + diff3 -m -L long-m.txt -L long-o.txt -L long-y.txt $(atf_get_srcdir)/long-m.txt $(atf_get_srcdir)/long-o.txt $(atf_get_srcdir)/long-y.txt +} + +diff3_E_merge_body() +{ + +expected="<<<<<<< 2 +# \$FreeBSD: head/local 12000 jhb \$ +======= +# \$FreeBSD: head/local 12345 jhb \$ +>>>>>>> 3 +# \$FreeBSD: head/local 12345 jhb \$ + +this is a file + +these are some local mods to the file +" + # first test the regular test + cp $(atf_get_srcdir)/fbsdid2.txt out.txt + atf_check -s exit:1 -o inline:"${expected}" \ + diff3 -m -L 1 -L 2 -L 3 out.txt $(atf_get_srcdir)/fbsdid1.txt $(atf_get_srcdir)/fbsdid2.txt + +merged="# \$FreeBSD: head/local 12345 jhb \$ + +this is a file + +these are some local mods to the file +" + atf_check -s exit:0 -o inline:"${merged}" \ + diff3 -E -m out.txt $(atf_get_srcdir)/fbsdid1.txt $(atf_get_srcdir)/fbsdid2.txt +} + + +atf_init_test_cases() +{ + atf_add_test_case diff3 +# atf_add_test_case diff3_lesssimple + atf_add_test_case diff3_ed + atf_add_test_case diff3_A + atf_add_test_case diff3_merge + atf_add_test_case diff3_E_merge +} diff --git a/usr.bin/diff3/tests/fbsdid1.txt b/usr.bin/diff3/tests/fbsdid1.txt new file mode 100644 index 000000000000..9f94660bda97 --- /dev/null +++ b/usr.bin/diff3/tests/fbsdid1.txt @@ -0,0 +1,5 @@ +# $FreeBSD: head/local 12000 jhb $ + +this is a file + +these are some local mods to the file diff --git a/usr.bin/diff3/tests/fbsdid2.txt b/usr.bin/diff3/tests/fbsdid2.txt new file mode 100644 index 000000000000..420ad975868b --- /dev/null +++ b/usr.bin/diff3/tests/fbsdid2.txt @@ -0,0 +1,5 @@ +# $FreeBSD: head/local 12345 jhb $ + +this is a file + +these are some local mods to the file diff --git a/usr.bin/diff3/tests/long-A.out b/usr.bin/diff3/tests/long-A.out new file mode 100644 index 000000000000..2a2e4cbb112d --- /dev/null +++ b/usr.bin/diff3/tests/long-A.out @@ -0,0 +1,22 @@ +24d +16a +||||||| long-o.txt +This line is different in yours and mine, but the different in each +======= +This line is different in yours and mine, but the best in yours +>>>>>>> long-y.txt +. +15a +<<<<<<< long-m.txt +. +11a +>>>>>>> long-y.txt +. +10a +<<<<<<< long-o.txt +This line is different in yours and mine, but the same +======= +. +8c +This line is different in yours, much butter +. diff --git a/usr.bin/diff3/tests/long-ed.out b/usr.bin/diff3/tests/long-ed.out new file mode 100644 index 000000000000..8814a3518d60 --- /dev/null +++ b/usr.bin/diff3/tests/long-ed.out @@ -0,0 +1,7 @@ +24d +16c +This line is different in yours and mine, but the best in yours +. +8c +This line is different in yours, much butter +. diff --git a/usr.bin/diff3/tests/long-m.txt b/usr.bin/diff3/tests/long-m.txt new file mode 100644 index 000000000000..327bf8c225f7 --- /dev/null +++ b/usr.bin/diff3/tests/long-m.txt @@ -0,0 +1,26 @@ +This is a long file +These lines are the same in all three files +These lines are the same in all three files +This line is different in mine, not better +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, the same is in both +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, best change in mine +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is deleted in yours +These lines are the same in all three files +These lines are the same in all three files diff --git a/usr.bin/diff3/tests/long-merge.out b/usr.bin/diff3/tests/long-merge.out new file mode 100644 index 000000000000..5139a48fa429 --- /dev/null +++ b/usr.bin/diff3/tests/long-merge.out @@ -0,0 +1,35 @@ +This is a long file +These lines are the same in all three files +These lines are the same in all three files +This line is different in mine, not better +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours, much butter +These lines are the same in all three files +These lines are the same in all three files +<<<<<<< long-o.txt +This line is different in yours and mine, but the same +======= +This line is different in yours and mine, the same is in both +>>>>>>> long-y.txt +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +<<<<<<< long-m.txt +This line is different in yours and mine, best change in mine +||||||| long-o.txt +This line is different in yours and mine, but the different in each +======= +This line is different in yours and mine, but the best in yours +>>>>>>> long-y.txt +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files diff --git a/usr.bin/diff3/tests/long-o.txt b/usr.bin/diff3/tests/long-o.txt new file mode 100644 index 000000000000..b4085d1d98c0 --- /dev/null +++ b/usr.bin/diff3/tests/long-o.txt @@ -0,0 +1,27 @@ +This is a long file +These lines are the same in all three files +These lines are the same in all three files +This line is different in mine +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, but the same +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, but the different in each +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is deleted in mine +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is deleted in yours +These lines are the same in all three files +These lines are the same in all three files diff --git a/usr.bin/diff3/tests/long-y.txt b/usr.bin/diff3/tests/long-y.txt new file mode 100644 index 000000000000..1bb25004924e --- /dev/null +++ b/usr.bin/diff3/tests/long-y.txt @@ -0,0 +1,26 @@ +This is a long file +These lines are the same in all three files +These lines are the same in all three files +This line is different in mine +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours, much butter +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, the same is in both +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is different in yours and mine, but the best in yours +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +This line is deleted in mine +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files +These lines are the same in all three files |