commit 60ae6ef

Anto  ·  2026-06-29 16:28:43 +0000 UTC
parents 09e4878, f048161
Merge pull request #3 from shrub900/master

added posix/diff, extra/diff3, extra/sync
14 files changed,  +4848, -10
+26, -2
 1@@ -125,6 +125,7 @@ POSIX_BIN_ALL =\
 2 	cmd/posix/date\
 3 	cmd/posix/dd\
 4 	cmd/posix/df\
 5+	cmd/posix/diff/diff\
 6 	cmd/posix/dirname\
 7 	cmd/posix/du\
 8 	cmd/posix/echo\
 9@@ -276,6 +277,8 @@ PSEUDO_BIN_ALL =\
10 	cmd/extra/blkid\
11 	cmd/extra/lsblk\
12 	cmd/extra/fdisk\
13+	cmd/extra/sync\
14+	cmd/extra/diff3/diff3\
15 	cmd/dev/ar/ar\
16 	cmd/dev/as/as\
17 	cmd/dev/ld/ld\
18@@ -294,6 +297,13 @@ MAKEOBJ =\
19 	cmd/posix/make/posix.o\
20 	cmd/posix/make/rules.o
21 
22+DIFFOBJ =\
23+	cmd/posix/diff/diff.o\
24+	cmd/posix/diff/diffdir.o\
25+	cmd/posix/diff/diffreg.o\
26+	cmd/posix/diff/pr.o\
27+	cmd/posix/diff/xmalloc.o
28+
29 BIN_basename_1 = cmd/posix/basename
30 BIN_cal_1 = cmd/posix/cal
31 BIN_cat_1 = cmd/posix/cat
32@@ -308,6 +318,7 @@ BIN_cut_1 = cmd/posix/cut
33 BIN_date_1 = cmd/posix/date
34 BIN_dd_1 = cmd/posix/dd
35 BIN_df_1 = cmd/posix/df
36+BIN_diff_1 = cmd/posix/diff/diff
37 BIN_dirname_1 = cmd/posix/dirname
38 BIN_du_1 = cmd/posix/du
39 BIN_echo_1 = cmd/posix/echo
40@@ -455,6 +466,8 @@ BIN_b3sum_1 = cmd/extra/b3sum
41 BIN_blkid_1 = cmd/extra/blkid
42 BIN_lsblk_1 = cmd/extra/lsblk
43 BIN_fdisk_1 = cmd/extra/fdisk
44+BIN_sync_1 = cmd/extra/sync
45+BIN_diff3_1 = cmd/extra/diff3/diff3
46 BIN_ar_1 = cmd/dev/ar/ar
47 BIN_as_1 = cmd/dev/as/as
48 BIN_ld_1 = cmd/dev/ld/ld
49@@ -481,6 +494,7 @@ POSIX_BIN = \
50 	$(BIN_date_$(BUILD_POSIX_DATE)) \
51 	$(BIN_dd_$(BUILD_POSIX_DD)) \
52 	$(BIN_df_$(BUILD_POSIX_DF)) \
53+	$(BIN_diff_$(BUILD_POSIX_DIFF)) \
54 	$(BIN_dirname_$(BUILD_POSIX_DIRNAME)) \
55 	$(BIN_du_$(BUILD_POSIX_DU)) \
56 	$(BIN_echo_$(BUILD_POSIX_ECHO)) \
57@@ -632,6 +646,8 @@ PSEUDO_BIN = \
58 	$(BIN_blkid_$(BUILD_PSEUDO_BLKID)) \
59 	$(BIN_lsblk_$(BUILD_PSEUDO_LSBLK)) \
60 	$(BIN_fdisk_$(BUILD_PSEUDO_FDISK)) \
61+	$(BIN_sync_$(BUILD_PSEUDO_SYNC)) \
62+	$(BIN_diff3_$(BUILD_PSEUDO_DIFF3)) \
63 	$(BIN_ar_$(BUILD_DEV_AR)) \
64 	$(BIN_as_$(BUILD_DEV_CC)) \
65 	$(BIN_ld_$(BUILD_DEV_LD)) \
66@@ -671,6 +687,11 @@ $(MAKEOBJ): cmd/posix/make/make.h
67 cmd/posix/make/make: $(MAKEOBJ) $(LIB)
68 	$(CC) $(LDFLAGS) -o $@ $(MAKEOBJ) $(LIB) $(LDLIBS)
69 
70+$(DIFFOBJ): cmd/posix/diff/diff.h cmd/posix/diff/pr.h cmd/posix/diff/xmalloc.h
71+
72+cmd/posix/diff/diff: $(DIFFOBJ) $(LIB)
73+	$(CC) $(LDFLAGS) -o $@ $(DIFFOBJ) $(LIB) $(LDLIBS) -lm
74+
75 shared/libutf/libutf.a: $(LIBUTFOBJ)
76 	$(AR) $(ARFLAGS) $@ $?
77 	$(RANLIB) $@
78@@ -718,9 +739,9 @@ man: scripts/mkman/mkman
79 
80 clean:
81 	rm -f shared/libutf/*.o shared/libutil/*.o shared/libredline/*.o
82-	rm -f cmd/posix/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
83+	rm -f cmd/posix/*.o cmd/posix/diff/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
84 	rm -f cmd/linux/*.o cmd/net/*.o cmd/xsi/*.o cmd/pseudo/*.o
85-	rm -f cmd/extra/*.o cmd/dev/ar/*.o cmd/dev/ld/*.o cmd/dev/cc/*.o cmd/dev/as/*.o cmd/dev/xcutil/*.o
86+	rm -f cmd/extra/*.o cmd/extra/diff3/*.o cmd/dev/ar/*.o cmd/dev/ld/*.o cmd/dev/cc/*.o cmd/dev/as/*.o cmd/dev/xcutil/*.o
87 	rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL) $(LIB)
88 	rm -f cmd/posix/make/make cmd/posix/getconf.h cmd/posix/bc.c
89 	rm -f cmd/posix/awk/awk cmd/posix/awk/maketab cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c
90@@ -827,6 +848,9 @@ cmd/posix/sh/%.o: cmd/posix/sh/%.c
91 cmd/posix/sh/sh: $(SHOBJ) $(LIB)
92 	$(CC) $(LDFLAGS) -o $@ $(SHOBJ) $(LIB) $(LDLIBS)
93 
94+cmd/extra/diff3/diff3: cmd/extra/diff3/diff3.o cmd/posix/diff/xmalloc.o $(LIB)
95+	$(CC) $(LDFLAGS) -o $@ cmd/extra/diff3/diff3.o cmd/posix/diff/xmalloc.o $(LIB) $(LDLIBS)
96+
97 cmd/net/wget: cmd/net/wget.o $(LIB)
98 	$(CC) $(LDFLAGS) -o $@ cmd/net/wget.o $(LIB) $(LDLIBS) $(LDLIBS_TLS)
99 
+37, -6
 1@@ -271,6 +271,9 @@ build_simple_tools() {
 2 	for src in cmd/"$cat"/*.c; do
 3 		[ -f "$src" ] || continue
 4 		base=${src##*/}; base=${base%.c}
 5+		if [ "$cat" = posix ] && [ "$base" = diff ]; then
 6+			continue
 7+		fi
 8 		var=$(cfgvar "$cat" "$base")
 9 		cfg_enabled "$var" || continue
10 		compile_c "$src" "${src%.c}.o"
11@@ -285,6 +288,30 @@ build_simple_tools() {
12 
13 # generated multi-file tools under cmd/posix
14 #
15+build_diff() {
16+	cfg_enabled BUILD_POSIX_DIFF || return 0
17+	local dir=cmd/posix/diff
18+	local objs=""
19+	EXTRA_HDR="$dir/diff.h $dir/pr.h $dir/xmalloc.h"
20+	compile_c "$dir/diff.c" "$dir/diff.o"
21+	compile_c "$dir/diffdir.c" "$dir/diffdir.o"
22+	compile_c "$dir/diffreg.c" "$dir/diffreg.o"
23+	compile_c "$dir/pr.c" "$dir/pr.o"
24+	compile_c "$dir/xmalloc.c" "$dir/xmalloc.o"
25+	EXTRA_HDR=
26+	objs="$dir/diff.o $dir/diffdir.o $dir/diffreg.o $dir/pr.o $dir/xmalloc.o"
27+	link_bin "$dir/diff" $objs -- $LIB -lm
28+}
29+
30+build_diff3() {
31+	cfg_enabled BUILD_PSEUDO_DIFF3 || return 0
32+	local dir=cmd/extra/diff3
33+	EXTRA_HDR="cmd/posix/diff/xmalloc.h"
34+	compile_c "$dir/diff3.c" "$dir/diff3.o"
35+	EXTRA_HDR=
36+	link_bin "$dir/diff3" "$dir/diff3.o" cmd/posix/diff/xmalloc.o -- $LIB
37+}
38+
39 build_awk() {
40 	cfg_enabled BUILD_POSIX_AWK || return 0
41 	local dir=cmd/posix/awk
42@@ -379,6 +406,7 @@ build_posix() {
43 		scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
44 	}
45 	build_simple_tools posix
46+	build_diff
47 	build_awk
48 	build_sh
49 	build_make
50@@ -495,6 +523,8 @@ build_man() {
51 			build_man_for "$(cfgvar "$cat" "$base")" "$src"
52 		done
53 	done
54+	build_man_for BUILD_POSIX_DIFF cmd/posix/diff/diff.c
55+	build_man_for BUILD_PSEUDO_DIFF3 cmd/extra/diff3/diff3.c
56 }
57 
58 # install and clean
59@@ -535,18 +565,19 @@ case "$TARGET" in
60 		build_lib
61 		build_posix
62 		build_dev
63-		build_simple_tools linux
64-		build_simple_tools net
65-		build_simple_tools xsi
66-		build_simple_tools pseudo
67-		build_simple_tools extra
68+	build_simple_tools linux
69+	build_simple_tools net
70+	build_simple_tools xsi
71+	build_simple_tools pseudo
72+	build_simple_tools extra
73+	build_diff3
74 		;;
75 	lib)             build_lib ;;
76 	posix)           build_lib; build_posix ;;
77 	dev)             build_lib; build_dev ;;
78 	make)            build_lib; build_make ;;
79 	linux|net|xsi|pseudo|extra)
80-	                 build_lib; build_simple_tools "$TARGET" ;;
81+	                 build_lib; build_simple_tools "$TARGET"; [ "$TARGET" = extra ] && build_diff3 ;;
82 	man)             build_man ;;
83 	install)         do_install ;;
84 	clean)           do_clean ;;
+1250, -0
   1@@ -0,0 +1,1250 @@
   2+/*	$OpenBSD: diff3prog.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $	*/
   3+
   4+/*
   5+ * Copyright (C) Caldera International Inc.  2001-2002.
   6+ * All rights reserved.
   7+ *
   8+ * Redistribution and use in source and binary forms, with or without
   9+ * modification, are permitted provided that the following conditions
  10+ * are met:
  11+ * 1. Redistributions of source code and documentation must retain the above
  12+ *    copyright notice, this list of conditions and the following disclaimer.
  13+ * 2. Redistributions in binary form must reproduce the above copyright
  14+ *    notice, this list of conditions and the following disclaimer in the
  15+ *    documentation and/or other materials provided with the distribution.
  16+ * 3. All advertising materials mentioning features or use of this software
  17+ *    must display the following acknowledgement:
  18+ *	This product includes software developed or owned by Caldera
  19+ *	International, Inc.
  20+ * 4. Neither the name of Caldera International, Inc. nor the names of other
  21+ *    contributors may be used to endorse or promote products derived from
  22+ *    this software without specific prior written permission.
  23+ *
  24+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
  25+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
  26+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  27+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  28+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
  29+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  34+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35+ * POSSIBILITY OF SUCH DAMAGE.
  36+ */
  37+/*-
  38+ * Copyright (c) 1991, 1993
  39+ *	The Regents of the University of California.  All rights reserved.
  40+ *
  41+ * Redistribution and use in source and binary forms, with or without
  42+ * modification, are permitted provided that the following conditions
  43+ * are met:
  44+ * 1. Redistributions of source code must retain the above copyright
  45+ *    notice, this list of conditions and the following disclaimer.
  46+ * 2. Redistributions in binary form must reproduce the above copyright
  47+ *    notice, this list of conditions and the following disclaimer in the
  48+ *    documentation and/or other materials provided with the distribution.
  49+ * 3. Neither the name of the University nor the names of its contributors
  50+ *    may be used to endorse or promote products derived from this software
  51+ *    without specific prior written permission.
  52+ *
  53+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  54+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  55+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  56+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  57+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  58+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  59+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  60+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  61+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  62+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  63+ * SUCH DAMAGE.
  64+ */
  65+
  66+#include <sys/cdefs.h>
  67+#include <sys/types.h>
  68+#include <sys/wait.h>
  69+
  70+#include <ctype.h>
  71+#include <err.h>
  72+#include <fcntl.h>
  73+#include <getopt.h>
  74+#include <stdbool.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+#include <errno.h>
  82+#include <poll.h>
  83+
  84+#include "util.h"
  85+#include "../../posix/diff/xmalloc.h"
  86+
  87+// ?man diff3: 3-way differential file comparison
  88+// ?man arguments: file1 file2 file3
  89+// ?man synopsis: [-3AaEeimTXx] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
  90+// ?man synopsis: [--help]
  91+// ?man synopsis: [--version]
  92+// ?man ## OPTIONS
  93+// ?man ### -3, --easy-only
  94+// ?man Produces an output script suitable for ed(1) with changes specific only to file3.
  95+// ?man ### -A, --show-all
  96+// ?man Output all changes, bracketing conflicts.
  97+// ?man ### -a, --text
  98+// ?man Treat all files as ASCII.
  99+// ?man ### -E, --show-overlap
 100+// ?man Similar to e and 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.
 101+// ?man ### -X
 102+// ?man Similar to e and 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.
 103+// ?man ### -e, --ed
 104+// ?man Produces output in a form suitable as an input script for the ed(1) utility. The script may then be used to merge differences common between all three files and differences specific to file1 and file3. In other words, the e option ignores differences specific to file1 and file2, and those specific to file2 and file3. It is useful for backing out changes specific to file2 only.
 105+// ?man ### --help
 106+// ?man Prints usage information and exits.
 107+// ?man ### -i
 108+// ?man Appends 'w' and 'q' ed(1) commands.
 109+// ?man ### -L, --label
 110+// ?man Defines labels to print instead of file names file1, file2 and file3.
 111+// ?man ### -m, --merge
 112+// ?man Merge output instead of generating ed script.
 113+// ?man ### -T, --initial-tab
 114+// ?man In the normal listing, use a tab instead of two spaces at the beginning of each line. In modes that produce an ed(1) script, this option changes nothing.
 115+// ?man ### -x, --overlap-only
 116+// ?man Produces an output script suitable for ed(1) with changes specific only to all three versions.
 117+// ?man ### --diff-program program
 118+// ?man Use program instead of the default diff(1) to compare files.
 119+// ?man ### --strip-trailing-cr
 120+// ?man Strip trailing carriage return on input files.
 121+// ?man ### --version
 122+// ?man Prints version information and exits.
 123+// ?man ## DESCRIPTION
 124+// ?man The diff3 utility compares the contents of three different versions of a file, file1, file2 and file3, writing the result to the standard output. The options describe different methods of merging and purging the separate versions into a new file. diff3 is used by rcs(1) to merge specific versions or create new versions.
 125+// ?man
 126+// ?man The E option is used by RCS merge(1) to ensure that overlapping changes in the merged files are preserved and brought to someone's attention.
 127+// ?man
 128+// ?man For example, suppose lines 7-8 are changed in both file1 and file2. Applying the edit script generated by the command
 129+// ?man
 130+// ?man $ diff3 -E file1 file2 file3
 131+// ?man
 132+// ?man to file1 results in the file:
 133+// ?man
 134+// ?man lines 1-6
 135+// ?man of file1
 136+// ?man <<<<<<< file1
 137+// ?man lines 7-8
 138+// ?man of file1
 139+// ?man =======
 140+// ?man lines 7-8
 141+// ?man of file3
 142+// ?man >>>>>>> file3
 143+// ?man rest of file1
 144+// ?man
 145+// ?man The default output of diff3 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 ed(1) to create the desired target from the different versions. See diff(1) for a description of the commands.
 146+// ?man ### ====
 147+// ?man The lines beneath this notation are ranges of lines which are different between all files.
 148+// ?man ### ====n
 149+// ?man The lines beneath this notation are ranges of lines which are exclusively different in file n.
 150+// ?man ## SEE ALSO
 151+// ?man diff(1), ed(1), merge(1), rcs(1), sdiff(1)
 152+// ?man ## HISTORY
 153+// ?man A diff3 command appeared in AT&T Version 7 UNIX.
 154+// ?man ## BUGS
 155+// ?man The e option cannot catch and change lines which have "." as the first and only character on the line. The resulting script will fail on that line as "." is an ed(1) command.
 156+
 157+#ifndef __dead2
 158+#define __dead2 __attribute__((__noreturn__))
 159+#endif
 160+
 161+#ifndef __DECONST
 162+#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
 163+#endif
 164+
 165+/*
 166+ * "from" is first in range of changed lines; "to" is last+1
 167+ * from=to=line after point of insertion for added lines.
 168+ */
 169+struct range {
 170+	int from;
 171+	int to;
 172+};
 173+
 174+struct diff {
 175+#define DIFF_TYPE1 1
 176+#define DIFF_TYPE2 2
 177+#define DIFF_TYPE3 3
 178+	int type;
 179+#if DEBUG
 180+	char *line;
 181+#endif	/* DEBUG */
 182+
 183+	/* Ranges as lines */
 184+	struct range old;
 185+	struct range new;
 186+};
 187+
 188+#define EFLAG_NONE 	0
 189+#define EFLAG_OVERLAP 	1
 190+#define EFLAG_NOOVERLAP	2
 191+#define EFLAG_UNMERGED	3
 192+
 193+static size_t szchanges;
 194+
 195+static struct diff *d13;
 196+static struct diff *d23;
 197+/*
 198+ * "de" is used to gather editing scripts.  These are later spewed out in
 199+ * reverse order.  Its first element must be all zero, the "old" and "new"
 200+ * components of "de" contain line positions. Array overlap indicates which
 201+ * sections in "de" correspond to lines that are different in all three files.
 202+ */
 203+static struct diff *de;
 204+static char *overlap;
 205+static int  overlapcnt;
 206+static FILE *fp[3];
 207+static int cline[3];		/* # of the last-read line in each file (0-2) */
 208+static int sigpipe[2];
 209+/*
 210+ * The latest known correspondence between line numbers of the 3 files
 211+ * is stored in last[1-3];
 212+ */
 213+static int last[4];
 214+static int Aflag, eflag, iflag, mflag, Tflag;
 215+static int oflag;		/* indicates whether to mark overlaps (-E or -X) */
 216+static int strip_cr;
 217+static char *f1mark, *f2mark, *f3mark;
 218+static const char *oldmark = "<<<<<<<";
 219+static const char *orgmark = "|||||||";
 220+static const char *newmark = ">>>>>>>";
 221+static const char *divider = "=======";
 222+
 223+static bool duplicate(struct range *, struct range *);
 224+static int edit(struct diff *, bool, int, int);
 225+static char *getchange(FILE *);
 226+static char *get_line(FILE *, size_t *);
 227+static int readin(int fd, struct diff **);
 228+static int skip(int, int, const char *);
 229+static void change(int, struct range *, bool);
 230+static void keep(int, struct range *);
 231+static void merge(int, int);
 232+static void prange(struct range *, bool);
 233+static void repos(int);
 234+static void separate(const char *);
 235+static void edscript(int) __dead2;
 236+static void Ascript(int) __dead2;
 237+static void mergescript(int) __dead2;
 238+static void increase(void);
 239+static void usage(void);
 240+static void printrange(FILE *, struct range *);
 241+
 242+static const char diff3_version[] = "FreeBSD diff3 20240925";
 243+
 244+enum {
 245+	DIFFPROG_OPT,
 246+	STRIPCR_OPT,
 247+	HELP_OPT,
 248+	VERSION_OPT
 249+};
 250+
 251+#define DIFF_PATH "/usr/bin/diff"
 252+
 253+#define OPTIONS "3aAeEiL:mTxX"
 254+static struct option longopts[] = {
 255+	{ "ed",			no_argument,		NULL,	'e' },
 256+	{ "show-overlap",	no_argument,		NULL,	'E' },
 257+	{ "overlap-only",	no_argument,		NULL,	'x' },
 258+	{ "initial-tab",	no_argument,		NULL,	'T' },
 259+	{ "text",		no_argument,		NULL,	'a' },
 260+	{ "strip-trailing-cr",	no_argument,		NULL,	STRIPCR_OPT },
 261+	{ "show-all",		no_argument,		NULL,	'A' },
 262+	{ "easy-only",		no_argument,		NULL,	'3' },
 263+	{ "merge",		no_argument,		NULL,	'm' },
 264+	{ "label",		required_argument,	NULL,	'L' },
 265+	{ "diff-program",	required_argument,	NULL,	DIFFPROG_OPT },
 266+	{ "help",		no_argument,		NULL,	HELP_OPT},
 267+	{ "version",		no_argument,		NULL,	VERSION_OPT}
 268+};
 269+
 270+static void
 271+usage(void)
 272+{
 273+	fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
 274+	    "[-L label3] file1 file2 file3\n");
 275+}
 276+
 277+// ?man diff3: 3-way differential file comparison
 278+// ?man arguments: file1 file2 file3
 279+// ?man synopsis: [-3AaEeimTXx] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
 280+// ?man synopsis: [--help]
 281+// ?man synopsis: [--version]
 282+// ?man The diff3 utility compares the contents of three different versions of a file, file1, file2 and file3, writing the result to the standard output. The options describe different methods of merging and purging the separate versions into a new file. diff3 is used by rcs(1) to merge specific versions or create new versions.
 283+// ?man ## OPTIONS
 284+// ?man ### -3, --easy-only
 285+// ?man Produces an output script suitable for ed(1) with changes specific only to file3.
 286+// ?man ### -A, --show-all
 287+// ?man Output all changes, bracketing conflicts.
 288+// ?man ### -a, --text
 289+// ?man Treat all files as ASCII.
 290+// ?man ### -E, --show-overlap
 291+// ?man Similar to e and 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.
 292+// ?man ### -X
 293+// ?man Similar to e and 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.
 294+// ?man ### -e, --ed
 295+// ?man Produces output in a form suitable as an input script for the ed(1) utility. The script may then be used to merge differences common between all three files and differences specific to file1 and file3. In other words, the e option ignores differences specific to file1 and file2, and those specific to file2 and file3. It is useful for backing out changes specific to file2 only.
 296+// ?man ### --help
 297+// ?man Prints usage information and exits.
 298+// ?man ### -i
 299+// ?man Appends 'w' and 'q' ed(1) commands.
 300+// ?man ### -L, --label
 301+// ?man Defines labels to print instead of file names file1, file2 and file3.
 302+// ?man ### -m, --merge
 303+// ?man Merge output instead of generating ed script.
 304+// ?man ### -T, --initial-tab
 305+// ?man In the normal listing, use a tab instead of two spaces at the beginning of each line. In modes that produce an ed(1) script, this option changes nothing.
 306+// ?man ### -x, --overlap-only
 307+// ?man Produces an output script suitable for ed(1) with changes specific only to all three versions.
 308+// ?man ### --diff-program program
 309+// ?man Use program instead of the default diff(1) to compare files.
 310+// ?man ### --strip-trailing-cr
 311+// ?man Strip trailing carriage return on input files.
 312+// ?man ### --version
 313+// ?man Prints version information and exits.
 314+// ?man ## DESCRIPTION
 315+// ?man The E option is used by RCS merge(1) to ensure that overlapping changes in the merged files are preserved and brought to someone's attention.
 316+// ?man
 317+// ?man For example, suppose lines 7-8 are changed in both file1 and file2. Applying the edit script generated by the command
 318+// ?man
 319+// ?man $ diff3 -E file1 file2 file3
 320+// ?man
 321+// ?man to file1 results in the file:
 322+// ?man
 323+// ?man lines 1-6
 324+// ?man of file1
 325+// ?man <<<<<<< file1
 326+// ?man lines 7-8
 327+// ?man of file1
 328+// ?man =======
 329+// ?man lines 7-8
 330+// ?man of file3
 331+// ?man >>>>>>> file3
 332+// ?man rest of file1
 333+// ?man
 334+// ?man The default output of diff3 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 ed(1) to create the desired target from the different versions. See diff(1) for a description of the commands.
 335+// ?man ### ====
 336+// ?man The lines beneath this notation are ranges of lines which are different between all files.
 337+// ?man ### ====n
 338+// ?man The lines beneath this notation are ranges of lines which are exclusively different in file n.
 339+// ?man ## SEE ALSO
 340+// ?man diff(1), ed(1), merge(1), rcs(1), sdiff(1)
 341+// ?man ## HISTORY
 342+// ?man A diff3 command appeared in AT&T Version 7 UNIX.
 343+// ?man ## BUGS
 344+// ?man The e option cannot catch and change lines which have "." as the first and only character on the line. The resulting script will fail on that line as "." is an ed(1) command.
 345+
 346+static int
 347+strtoi(char *str, char **end)
 348+{
 349+	intmax_t num;
 350+
 351+	errno = 0;
 352+	num = strtoimax(str, end, 10);
 353+	if ((end != NULL && *end == str) ||
 354+	    num < 0 || num > INT_MAX ||
 355+	    errno == EINVAL || errno == ERANGE)
 356+		err(1, "error in diff output");
 357+	return (int)num;
 358+}
 359+
 360+/*
 361+ * Read diff hunks into the array pointed to by *dd.
 362+ *
 363+ * The output from `diff foo bar` consists of a series of hunks describing
 364+ * an addition (lines in bar not present in foo), change (lines in bar
 365+ * different from lines in foo), or deletion (lines in foo not present in
 366+ * bar).  Each record starts with a line of the form:
 367+ *
 368+ * a[,b]xc[,d]
 369+ *
 370+ * where a, b, c, and d are nonnegative integers (b and d are printed only
 371+ * if they differ from a and c, respectively), and x is either 'a' for an
 372+ * addition, 'c' for a change, or 'd' for a deletion.  This is then
 373+ * followed by a series of lines (which we ignore) giving the added,
 374+ * changed, or deleted text.
 375+ *
 376+ * For an addition, a == b is the last line in 'foo' before the addition,
 377+ * while c through d is the range of lines in 'bar' to be added to 'foo'.
 378+ *
 379+ * For a change, a through b is the range of lines in 'foo' to be replaced
 380+ * and c through d is the range of lines in 'bar' to replace them with.
 381+ *
 382+ * For a deletion, a through b is the range of lines in 'foo' to remove
 383+ * and c == d is the line in 'bar' which corresponds to the last line
 384+ * before the deletion.
 385+ *
 386+ * The observant reader will have noticed that x is not really needed and
 387+ * that we can fully describe any hunk using only a, b, c, and d:
 388+ *
 389+ * - an addition replaces a zero-length range in one file with a
 390+ *   non-zero-length range from the other
 391+ *
 392+ * - a change replaces a non-zero-length range in one file with a
 393+ *   non-zero-length range from the other
 394+ *
 395+ * - a deletion replaces a non-zero-length range in one file with a
 396+ *   zero-length range from the other
 397+ */
 398+static int
 399+readin(int fd, struct diff **dd)
 400+{
 401+	int a, b, c, d;
 402+	int i;
 403+	char kind, *p;
 404+	FILE *f;
 405+
 406+	f = fdopen(fd, "r");
 407+	if (f == NULL)
 408+		err(2, "fdopen");
 409+	for (i = 0; (p = getchange(f)) != NULL; i++) {
 410+		if ((size_t)i >= szchanges - 1)
 411+			increase();
 412+#if DEBUG
 413+		(*dd)[i].line = strdup(p);
 414+#endif	/* DEBUG */
 415+
 416+		a = b = strtoi(p, &p);
 417+		if (*p == ',')
 418+			b = strtoi(p + 1, &p);
 419+		kind = *p++;
 420+		c = d = strtoi(p, &p);
 421+		if (*p == ',')
 422+			d = strtoi(p + 1, &p);
 423+		if (*p != '\n')
 424+			errx(1, "error in diff output");
 425+		if (kind == 'a')
 426+			a++;
 427+		else if (kind == 'c')
 428+			/* nothing */ ;
 429+		else if (kind == 'd')
 430+			c++;
 431+		else
 432+			errx(1, "error in diff output");
 433+		b++;
 434+		d++;
 435+		if (b < a || d < c)
 436+			errx(1, "error in diff output");
 437+		(*dd)[i].old.from = a;
 438+		(*dd)[i].old.to = b;
 439+		(*dd)[i].new.from = c;
 440+		(*dd)[i].new.to = d;
 441+		if (i > 0) {
 442+			if ((*dd)[i].old.from < (*dd)[i - 1].old.to ||
 443+			    (*dd)[i].new.from < (*dd)[i - 1].new.to)
 444+				errx(1, "diff output out of order");
 445+		}
 446+	}
 447+	if (i > 0) {
 448+		(*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to;
 449+		(*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to;
 450+	}
 451+	fclose(f);
 452+	return (i);
 453+}
 454+
 455+static void
 456+diffexec(const char *diffprog, char **diffargv, int fd[])
 457+{
 458+	switch (fork()) {
 459+	case 0:
 460+		close(fd[0]);
 461+		if (dup2(fd[1], STDOUT_FILENO) == -1)
 462+			err(2, "child could not duplicate descriptor");
 463+		close(fd[1]);
 464+		execvp(diffprog, diffargv);
 465+		err(2, "could not execute diff: %s", diffprog);
 466+		break;
 467+	case -1:
 468+		err(2, "could not fork");
 469+		break;
 470+	}
 471+	close(fd[1]);
 472+}
 473+
 474+static char *
 475+getchange(FILE *b)
 476+{
 477+	char *line;
 478+
 479+	while ((line = get_line(b, NULL)) != NULL) {
 480+		if (isdigit((unsigned char)line[0]))
 481+			return (line);
 482+	}
 483+	return (NULL);
 484+}
 485+
 486+
 487+static char *
 488+get_line(FILE *b, size_t *n)
 489+{
 490+	ssize_t len;
 491+	static char *buf = NULL;
 492+	static size_t bufsize = 0;
 493+
 494+	if ((len = getline(&buf, &bufsize, b)) < 0)
 495+		return (NULL);
 496+
 497+	if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
 498+		buf[len - 2] = '\n';
 499+		buf[len - 1] = '\0';
 500+		len--;
 501+	}
 502+
 503+	if (n != NULL)
 504+		*n = len;
 505+
 506+	return (buf);
 507+}
 508+
 509+static void
 510+merge(int m1, int m2)
 511+{
 512+	struct diff *d1, *d2, *d3;
 513+	int j, t1, t2;
 514+	bool dup = false;
 515+
 516+	d1 = d13;
 517+	d2 = d23;
 518+	j = 0;
 519+
 520+	for (;;) {
 521+		t1 = (d1 < d13 + m1);
 522+		t2 = (d2 < d23 + m2);
 523+		if (!t1 && !t2)
 524+			break;
 525+
 526+		/* first file is different from the others */
 527+		if (!t2 || (t1 && d1->new.to < d2->new.from)) {
 528+			/* stuff peculiar to 1st file */
 529+			if (eflag == EFLAG_NONE) {
 530+				separate("1");
 531+				change(1, &d1->old, false);
 532+				keep(2, &d1->new);
 533+				change(3, &d1->new, false);
 534+			} else if (eflag == EFLAG_OVERLAP) {
 535+				j = edit(d2, dup, j, DIFF_TYPE1);
 536+			}
 537+			d1++;
 538+			continue;
 539+		}
 540+		/* second file is different from others */
 541+		if (!t1 || (t2 && d2->new.to < d1->new.from)) {
 542+			if (eflag == EFLAG_NONE) {
 543+				separate("2");
 544+				keep(1, &d2->new);
 545+				change(3, &d2->new, false);
 546+				change(2, &d2->old, false);
 547+			} else if (Aflag || mflag) {
 548+				// XXX-THJ: What does it mean for the second file to differ?
 549+				if (eflag == EFLAG_UNMERGED)
 550+					j = edit(d2, dup, j, DIFF_TYPE2);
 551+			}
 552+			d2++;
 553+			continue;
 554+		}
 555+		/*
 556+		 * Merge overlapping changes in first file
 557+		 * this happens after extension (see below).
 558+		 */
 559+		if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
 560+			d1[1].old.from = d1->old.from;
 561+			d1[1].new.from = d1->new.from;
 562+			d1++;
 563+			continue;
 564+		}
 565+
 566+		/* merge overlapping changes in second */
 567+		if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
 568+			d2[1].old.from = d2->old.from;
 569+			d2[1].new.from = d2->new.from;
 570+			d2++;
 571+			continue;
 572+		}
 573+		/* stuff peculiar to third file or different in all */
 574+		if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
 575+			dup = duplicate(&d1->old, &d2->old);
 576+			/*
 577+			 * dup = 0 means all files differ
 578+			 * dup = 1 means files 1 and 2 identical
 579+			 */
 580+			if (eflag == EFLAG_NONE) {
 581+				separate(dup ? "3" : "");
 582+				change(1, &d1->old, dup);
 583+				change(2, &d2->old, false);
 584+				d3 = d1->old.to > d1->old.from ? d1 : d2;
 585+				change(3, &d3->new, false);
 586+			} else {
 587+				j = edit(d1, dup, j, DIFF_TYPE3);
 588+			}
 589+			dup = false;
 590+			d1++;
 591+			d2++;
 592+			continue;
 593+		}
 594+		/*
 595+		 * Overlapping changes from file 1 and 2; extend changes
 596+		 * appropriately to make them coincide.
 597+		 */
 598+		if (d1->new.from < d2->new.from) {
 599+			d2->old.from -= d2->new.from - d1->new.from;
 600+			d2->new.from = d1->new.from;
 601+		} else if (d2->new.from < d1->new.from) {
 602+			d1->old.from -= d1->new.from - d2->new.from;
 603+			d1->new.from = d2->new.from;
 604+		}
 605+		if (d1->new.to > d2->new.to) {
 606+			d2->old.to += d1->new.to - d2->new.to;
 607+			d2->new.to = d1->new.to;
 608+		} else if (d2->new.to > d1->new.to) {
 609+			d1->old.to += d2->new.to - d1->new.to;
 610+			d1->new.to = d2->new.to;
 611+		}
 612+	}
 613+
 614+	if (mflag)
 615+		mergescript(j);
 616+	else if (Aflag)
 617+		Ascript(j);
 618+	else if (eflag)
 619+		edscript(j);
 620+}
 621+
 622+static void
 623+separate(const char *s)
 624+{
 625+	printf("====%s\n", s);
 626+}
 627+
 628+/*
 629+ * The range of lines rold.from thru rold.to in file i is to be changed.
 630+ * It is to be printed only if it does not duplicate something to be
 631+ * printed later.
 632+ */
 633+static void
 634+change(int i, struct range *rold, bool dup)
 635+{
 636+
 637+	printf("%d:", i);
 638+	last[i] = rold->to;
 639+	prange(rold, false);
 640+	if (dup)
 641+		return;
 642+	i--;
 643+	skip(i, rold->from, NULL);
 644+	skip(i, rold->to, "  ");
 645+}
 646+
 647+/*
 648+ * Print the range of line numbers, rold.from thru rold.to, as n1,n2 or 
 649+ * n1.
 650+ */
 651+static void
 652+prange(struct range *rold, bool delete)
 653+{
 654+
 655+	if (rold->to <= rold->from)
 656+		printf("%da\n", rold->from - 1);
 657+	else {
 658+		printf("%d", rold->from);
 659+		if (rold->to > rold->from + 1)
 660+			printf(",%d", rold->to - 1);
 661+		if (delete)
 662+			printf("d\n");
 663+		else
 664+			printf("c\n");
 665+	}
 666+}
 667+
 668+/*
 669+ * No difference was reported by diff between file 1 (or 2) and file 3,
 670+ * and an artificial dummy difference (trange) must be ginned up to
 671+ * correspond to the change reported in the other file.
 672+ */
 673+static void
 674+keep(int i, struct range *rnew)
 675+{
 676+	int delta;
 677+	struct range trange;
 678+
 679+	delta = last[3] - last[i];
 680+	trange.from = rnew->from - delta;
 681+	trange.to = rnew->to - delta;
 682+	change(i, &trange, true);
 683+}
 684+
 685+/*
 686+ * skip to just before line number from in file "i".  If "pr" is non-NULL,
 687+ * print all skipped stuff with string pr as a prefix.
 688+ */
 689+static int
 690+skip(int i, int from, const char *pr)
 691+{
 692+	size_t j, n;
 693+	char *line;
 694+
 695+	for (n = 0; cline[i] < from - 1; n += j) {
 696+		if ((line = get_line(fp[i], &j)) == NULL)
 697+			errx(EXIT_FAILURE, "logic error");
 698+		if (pr != NULL)
 699+			printf("%s%s", Tflag == 1 ? "\t" : pr, line);
 700+		cline[i]++;
 701+	}
 702+	return ((int) n);
 703+}
 704+
 705+/*
 706+ * Return 1 or 0 according as the old range (in file 1) contains exactly
 707+ * the same data as the new range (in file 2).
 708+ */
 709+static bool
 710+duplicate(struct range *r1, struct range *r2)
 711+{
 712+	int c, d;
 713+	int nchar;
 714+	int nline;
 715+
 716+	if (r1->to-r1->from != r2->to-r2->from)
 717+		return (0);
 718+	skip(0, r1->from, NULL);
 719+	skip(1, r2->from, NULL);
 720+	nchar = 0;
 721+	for (nline = 0; nline < r1->to - r1->from; nline++) {
 722+		do {
 723+			c = getc(fp[0]);
 724+			d = getc(fp[1]);
 725+			if (c == -1 && d == -1)
 726+				break;
 727+			if (c == -1 || d == -1)
 728+				errx(EXIT_FAILURE, "logic error");
 729+			nchar++;
 730+			if (c != d) {
 731+				repos(nchar);
 732+				return (0);
 733+			}
 734+		} while (c != '\n');
 735+	}
 736+	repos(nchar);
 737+	return (1);
 738+}
 739+
 740+static void
 741+repos(int nchar)
 742+{
 743+	int i;
 744+
 745+	for (i = 0; i < 2; i++)
 746+		(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
 747+}
 748+
 749+/*
 750+ * collect an editing script for later regurgitation
 751+ */
 752+static int
 753+edit(struct diff *diff, bool dup, int j, int difftype)
 754+{
 755+	if (!(eflag == EFLAG_UNMERGED ||
 756+		(!dup && eflag == EFLAG_OVERLAP ) ||
 757+		(dup && eflag == EFLAG_NOOVERLAP))) {
 758+		return (j);
 759+	}
 760+	j++;
 761+	overlap[j] = !dup;
 762+	if (!dup)
 763+		overlapcnt++;
 764+
 765+	de[j].type = difftype;
 766+#if DEBUG
 767+	de[j].line = strdup(diff->line);
 768+#endif	/* DEBUG */
 769+
 770+	de[j].old.from = diff->old.from;
 771+	de[j].old.to = diff->old.to;
 772+	de[j].new.from = diff->new.from;
 773+	de[j].new.to = diff->new.to;
 774+	return (j);
 775+}
 776+
 777+static void
 778+printrange(FILE *p, struct range *r)
 779+{
 780+	char *line = NULL;
 781+	size_t len = 0;
 782+	int i = 1;
 783+
 784+	/* We haven't been asked to print anything */
 785+	if (r->from == r->to)
 786+		return;
 787+
 788+	if (r->from > r->to)
 789+		errx(EXIT_FAILURE, "invalid print range");
 790+
 791+	/*
 792+	 * XXX-THJ: We read through all of the file for each range printed.
 793+	 * This duplicates work and will probably impact performance on large
 794+	 * files with lots of ranges.
 795+	 */
 796+	fseek(p, 0L, SEEK_SET);
 797+	while (getline(&line, &len, p) > 0) {
 798+		if (i >= r->from)
 799+			printf("%s", line);
 800+		if (++i > r->to - 1)
 801+			break;
 802+	}
 803+	free(line);
 804+}
 805+
 806+/* regurgitate */
 807+static void
 808+edscript(int n)
 809+{
 810+	bool delete;
 811+	struct range *new, *old;
 812+
 813+	for (; n > 0; n--) {
 814+		new = &de[n].new;
 815+		old = &de[n].old;
 816+
 817+		delete = (new->from == new->to);
 818+		if (de[n].type == DIFF_TYPE1) {
 819+			if (delete)
 820+				printf("%dd\n", new->from - 1);
 821+			else if (old->from == new->from && old->to == new->to) {
 822+				printf("%dc\n", old->from);
 823+				printrange(fp[2], old);
 824+				printf(".\n");
 825+			}
 826+			continue;
 827+		} else {
 828+			if (!oflag || !overlap[n]) {
 829+				prange(old, delete);
 830+			} else {
 831+				printf("%da\n", old->to - 1);
 832+				printf("%s\n", divider);
 833+			}
 834+			printrange(fp[2], new);
 835+			if (!oflag || !overlap[n]) {
 836+				if (!delete)
 837+					printf(".\n");
 838+			} else {
 839+				printf("%s %s\n.\n", newmark, f3mark);
 840+				printf("%da\n%s %s\n.\n", old->from - 1,
 841+					oldmark, f1mark);
 842+			}
 843+		}
 844+	}
 845+	if (iflag)
 846+		printf("w\nq\n");
 847+
 848+	exit(eflag == EFLAG_NONE ? overlapcnt : 0);
 849+}
 850+
 851+/*
 852+ * Output an edit script to turn mine into yours, when there is a conflict
 853+ * between the 3 files bracket the changes. Regurgitate the diffs in reverse
 854+ * order to allow the ed script to track down where the lines are as changes
 855+ * are made.
 856+ */
 857+static void
 858+Ascript(int n)
 859+{
 860+	int startmark;
 861+	bool deletenew;
 862+	bool deleteold;
 863+
 864+	struct range *new, *old;
 865+
 866+	for (; n > 0; n--) {
 867+		new = &de[n].new;
 868+		old = &de[n].old;
 869+		deletenew = (new->from == new->to);
 870+		deleteold = (old->from == old->to);
 871+
 872+		if (de[n].type == DIFF_TYPE2) {
 873+			if (!oflag || !overlap[n]) {
 874+				prange(old, deletenew);
 875+				printrange(fp[2], new);
 876+			} else {
 877+				startmark = new->to - 1;
 878+
 879+				printf("%da\n", startmark);
 880+				printf("%s %s\n", newmark, f3mark);
 881+
 882+				printf(".\n");
 883+
 884+				printf("%da\n", startmark -
 885+					(new->to - new->from));
 886+				printf("%s %s\n", oldmark, f2mark);
 887+				if (!deleteold)
 888+					printrange(fp[1], old);
 889+				printf("%s\n.\n", divider);
 890+			}
 891+
 892+		} else if (de[n].type == DIFF_TYPE3) {
 893+			startmark = old->to - 1;
 894+
 895+			if (!oflag || !overlap[n]) {
 896+				prange(old, deletenew);
 897+				printrange(fp[2], new);
 898+			} else {
 899+				printf("%da\n", startmark);
 900+				printf("%s %s\n", orgmark, f2mark);
 901+
 902+				if (deleteold) {
 903+					struct range r;
 904+					r.from = old->from-1;
 905+					r.to = new->to;
 906+					printrange(fp[1], &r);
 907+				} else
 908+					printrange(fp[1], old);
 909+
 910+				printf("%s\n", divider);
 911+				printrange(fp[2], new);
 912+			}
 913+
 914+			if (!oflag || !overlap[n]) {
 915+				if (!deletenew)
 916+					printf(".\n");
 917+			} else {
 918+				printf("%s %s\n.\n", newmark, f3mark);
 919+
 920+				/*
 921+				 * Go to the start of the conflict in original
 922+				 * file and append lines
 923+				 */
 924+				printf("%da\n%s %s\n.\n",
 925+					startmark - (old->to - old->from),
 926+					oldmark, f1mark);
 927+			}
 928+		}
 929+	}
 930+	if (iflag)
 931+		printf("w\nq\n");
 932+
 933+	exit(overlapcnt > 0);
 934+}
 935+
 936+/*
 937+ * Output the merged file directly (don't generate an ed script). When
 938+ * regurgitating diffs we need to walk forward through the file and print any
 939+ * inbetween lines.
 940+ */
 941+static void
 942+mergescript(int i)
 943+{
 944+	struct range r, *new, *old;
 945+	int n;
 946+	bool delete = false;
 947+
 948+	r.from = 1;
 949+	r.to = 1;
 950+
 951+	for (n = 1; n <= i; n++) {
 952+		new = &de[n].new;
 953+		old = &de[n].old;
 954+
 955+		/*
 956+		 * Print any lines leading up to here. If we are merging don't
 957+		 * print deleted ranges.
 958+		 */
 959+		delete = (new->from == new->to);
 960+		if (de[n].type == DIFF_TYPE1 && delete)
 961+			r.to = new->from - 1;
 962+		else if (de[n].type == DIFF_TYPE3 && (old->from == old->to)) {
 963+			r.from = old->from - 1;
 964+			r.to = new->from;
 965+		} else
 966+			r.to = old->from;
 967+
 968+		printrange(fp[0], &r);
 969+		switch (de[n].type) {
 970+		case DIFF_TYPE1:
 971+			/* If this isn't a delete print it */
 972+			if (!delete)
 973+				printrange(fp[2], new);
 974+			break;
 975+		case DIFF_TYPE2:
 976+			printf("%s %s\n", oldmark, f2mark);
 977+			printrange(fp[1], old);
 978+			printf("%s\n", divider);
 979+			printrange(fp[2], new);
 980+			printf("%s %s\n", newmark, f3mark);
 981+			break;
 982+		case DIFF_TYPE3:
 983+			if (!oflag || !overlap[n]) {
 984+				printrange(fp[2], new);
 985+			} else {
 986+
 987+				printf("%s %s\n", oldmark, f1mark);
 988+				printrange(fp[0], old);
 989+
 990+				if (eflag != EFLAG_OVERLAP) {
 991+					printf("%s %s\n", orgmark, f2mark);
 992+					if (old->from == old->to) {
 993+						struct range or;
 994+						or.from = old->from - 1;
 995+						or.to = new->to;
 996+						printrange(fp[1], &or);
 997+					} else {
 998+						printrange(fp[1], old);
 999+					}
1000+				}
1001+
1002+				printf("%s\n", divider);
1003+
1004+				printrange(fp[2], new);
1005+				printf("%s %s\n", newmark, f3mark);
1006+			}
1007+			break;
1008+		default:
1009+			printf("Error: Unhandled diff type - exiting\n");
1010+			exit(EXIT_FAILURE);
1011+		}
1012+
1013+		if (old->from == old->to)
1014+			r.from = new->to;
1015+		else
1016+			r.from = old->to;
1017+	}
1018+
1019+	/*
1020+	 * Print from the final range to the end of 'myfile'. Any deletions or
1021+	 * additions to this file should have been handled by now.
1022+	 *
1023+	 * If the ranges are the same we need to rewind a line.
1024+	 * If the new range is 0 length (from == to), we need to use the old
1025+	 * range.
1026+	 */
1027+	new = &de[n-1].new;
1028+	old = &de[n-1].old;
1029+
1030+	if (old->from == new->from && old->to == new->to)
1031+		r.from--;
1032+	else if (new->from == new->to)
1033+		r.from = old->from;
1034+
1035+	r.to = INT_MAX;
1036+	printrange(fp[2], &r);
1037+	exit(overlapcnt > 0);
1038+}
1039+
1040+static void
1041+increase(void)
1042+{
1043+	struct diff *p;
1044+	char *q;
1045+	size_t newsz, incr;
1046+
1047+	/* are the memset(3) calls needed? */
1048+	newsz = szchanges == 0 ? 64 : 2 * szchanges;
1049+	incr = newsz - szchanges;
1050+
1051+	p = reallocarray(d13, newsz, sizeof(*p));
1052+	if (p == NULL)
1053+		err(1, NULL);
1054+	memset(p + szchanges, 0, incr * sizeof(*p));
1055+	d13 = p;
1056+	p = reallocarray(d23, newsz, sizeof(*p));
1057+	if (p == NULL)
1058+		err(1, NULL);
1059+	memset(p + szchanges, 0, incr * sizeof(*p));
1060+	d23 = p;
1061+	p = reallocarray(de, newsz, sizeof(*p));
1062+	if (p == NULL)
1063+		err(1, NULL);
1064+	memset(p + szchanges, 0, incr * sizeof(*p));
1065+	de = p;
1066+	q = reallocarray(overlap, newsz, 1);
1067+	if (q == NULL)
1068+		err(1, NULL);
1069+	memset(q + szchanges, 0, incr * 1);
1070+	overlap = q;
1071+	szchanges = newsz;
1072+}
1073+
1074+static void
1075+handle_sig(int signo)
1076+{
1077+    write(sigpipe[1], &signo, sizeof(signo));
1078+}
1079+
1080+int
1081+main(int argc, char **argv)
1082+{
1083+	int ch, nblabels, status, m, n, npe, nleft;
1084+	char *labels[] = { NULL, NULL, NULL };
1085+	const char *diffprog = DIFF_PATH;
1086+	char *file1, *file2, *file3;
1087+	char *diffargv[7];
1088+	int diffargc = 0;
1089+	int fd13[2], fd23[2], signo;
1090+	pid_t wpid;
1091+	struct pollfd pfd;
1092+
1093+	nblabels = 0;
1094+	eflag = EFLAG_NONE;
1095+	oflag = 0;
1096+	diffargv[diffargc++] = __DECONST(char *, diffprog);
1097+	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
1098+		switch (ch) {
1099+		case '3':
1100+			eflag = EFLAG_NOOVERLAP;
1101+			break;
1102+		case 'a':
1103+			diffargv[diffargc++] = __DECONST(char *, "-a");
1104+			break;
1105+		case 'A':
1106+			Aflag = 1;
1107+			break;
1108+		case 'e':
1109+			eflag = EFLAG_UNMERGED;
1110+			break;
1111+		case 'E':
1112+			eflag = EFLAG_OVERLAP;
1113+			oflag = 1;
1114+			break;
1115+		case 'i':
1116+			iflag = 1;
1117+			break;
1118+		case 'L':
1119+			oflag = 1;
1120+			if (nblabels >= 3)
1121+				errx(2, "too many file label options");
1122+			labels[nblabels++] = optarg;
1123+			break;
1124+		case 'm':
1125+			Aflag = 1;
1126+			oflag = 1;
1127+			mflag = 1;
1128+			break;
1129+		case 'T':
1130+			Tflag = 1;
1131+			break;
1132+		case 'x':
1133+			eflag = EFLAG_OVERLAP;
1134+			break;
1135+		case 'X':
1136+			oflag = 1;
1137+			eflag = EFLAG_OVERLAP;
1138+			break;
1139+		case DIFFPROG_OPT:
1140+			diffprog = optarg;
1141+			break;
1142+		case STRIPCR_OPT:
1143+			strip_cr = 1;
1144+			diffargv[diffargc++] = __DECONST(char *, "--strip-trailing-cr");
1145+			break;
1146+		case HELP_OPT:
1147+			usage();
1148+			exit(0);
1149+		case VERSION_OPT:
1150+			printf("%s\n", diff3_version);
1151+			exit(0);
1152+		}
1153+	}
1154+	argc -= optind;
1155+	argv += optind;
1156+
1157+	if (Aflag) {
1158+		if (eflag == EFLAG_NONE)
1159+			eflag = EFLAG_UNMERGED;
1160+		oflag = 1;
1161+	}
1162+
1163+	if (argc != 3) {
1164+		usage();
1165+		exit(2);
1166+	}
1167+
1168+	/* TODO stdio */
1169+	file1 = argv[0];
1170+	file2 = argv[1];
1171+	file3 = argv[2];
1172+
1173+	if (oflag) {
1174+		xasprintf(&f1mark, "%s",
1175+		    labels[0] != NULL ? labels[0] : file1);
1176+		xasprintf(&f2mark, "%s",
1177+		    labels[1] != NULL ? labels[1] : file2);
1178+		xasprintf(&f3mark, "%s",
1179+		    labels[2] != NULL ? labels[2] : file3);
1180+	}
1181+	fp[0] = fopen(file1, "r");
1182+	if (fp[0] == NULL)
1183+		err(2, "Can't open %s", file1);
1184+
1185+	fp[1] = fopen(file2, "r");
1186+	if (fp[1] == NULL)
1187+		err(2, "Can't open %s", file2);
1188+
1189+	fp[2] = fopen(file3, "r");
1190+	if (fp[2] == NULL)
1191+		err(2, "Can't open %s", file3);
1192+
1193+	if (pipe(fd13))
1194+		err(2, "pipe");
1195+	if (pipe(fd23))
1196+		err(2, "pipe");
1197+	if (pipe(sigpipe))
1198+		err(2, "pipe");
1199+	if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC))
1200+		err(2, "fcntl");
1201+	if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC))
1202+		err(2, "fcntl");
1203+
1204+	pfd.fd = sigpipe[0];
1205+	pfd.events = POLLIN;
1206+	pfd.revents = 0;
1207+
1208+	if (signal(SIGCHLD, handle_sig) == SIG_ERR)
1209+		err(2, "signal");
1210+
1211+	diffargv[diffargc] = file1;
1212+	diffargv[diffargc + 1] = file3;
1213+	diffargv[diffargc + 2] = NULL;
1214+
1215+	nleft = 0;
1216+	diffexec(diffprog, diffargv, fd13);
1217+	nleft++;
1218+
1219+	diffargv[diffargc] = file2;
1220+	diffexec(diffprog, diffargv, fd23);
1221+	nleft++;
1222+
1223+	/* parse diffs */
1224+	increase();
1225+	m = readin(fd13[0], &d13);
1226+	n = readin(fd23[0], &d23);
1227+
1228+	/* waitpid */
1229+	while (nleft > 0) {
1230+		npe = poll(&pfd, 1, -1);
1231+		if (npe == -1) {
1232+			if (errno == EINTR) continue;
1233+			err(2, "poll");
1234+		}
1235+		if (pfd.revents != POLLIN)
1236+			continue;
1237+		if (read(pfd.fd, &signo, sizeof(signo)) < 0)
1238+			err(2, "read");
1239+		while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {
1240+			if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
1241+				errx(2, "diff exited abnormally");
1242+			else if (WIFSIGNALED(status))
1243+				errx(2, "diff killed by signal %d",
1244+				    WTERMSIG(status));
1245+			nleft--;
1246+		}
1247+	}
1248+	merge(m, n);
1249+
1250+	return (EXIT_SUCCESS);
1251+}
+32, -0
 1@@ -0,0 +1,32 @@
 2+/* See LICENSE file for copyright and license details. */
 3+#include <unistd.h>
 4+
 5+#include "arg.h"
 6+#include "util.h"
 7+
 8+// ?man sync: flush disk cache
 9+// ?man synopsis:
10+// ?man The sync utility invokes sync(2) to flush all unwritten changes to disk. This is usually done before shutting down, rebooting or halting.
11+// ?man ## SEE ALSO
12+// ?man fsync(2), sync(2)
13+
14+static void
15+usage(void)
16+{
17+	eprintf("usage: %s\n", argv0);
18+}
19+
20+int
21+main(int argc, char *argv[])
22+{
23+	ARGBEGIN {
24+	default:
25+		usage();
26+	} ARGEND
27+
28+	if (argc)
29+		usage();
30+	sync();
31+
32+	return 0;
33+}
+904, -0
  1@@ -0,0 +1,904 @@
  2+/*	$OpenBSD: diff.c,v 1.67 2019/06/28 13:35:00 deraadt Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
  6+ *
  7+ * Permission to use, copy, modify, and distribute this software for any
  8+ * purpose with or without fee is hereby granted, provided that the above
  9+ * copyright notice and this permission notice appear in all copies.
 10+ *
 11+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 13+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 14+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 15+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 16+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 17+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 18+ *
 19+ * Sponsored in part by the Defense Advanced Research Projects
 20+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
 21+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
 22+ */
 23+
 24+#include <sys/cdefs.h>
 25+#include <sys/stat.h>
 26+
 27+#include <ctype.h>
 28+#include <err.h>
 29+#include <errno.h>
 30+#include <getopt.h>
 31+#include <limits.h>
 32+#include <stdio.h>
 33+#include <stdlib.h>
 34+#include <string.h>
 35+#include <unistd.h>
 36+
 37+#include "util.h"
 38+
 39+#include "diff.h"
 40+#include "xmalloc.h"
 41+
 42+#ifndef __dead2
 43+#define __dead2 __attribute__((__noreturn__))
 44+#endif
 45+
 46+static const char diff_version[] = "FreeBSD diff 20240307";
 47+bool	 lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
 48+bool	 ignore_file_case, suppress_common, color, noderef;
 49+static bool help = false;
 50+int	 diff_format, diff_context, diff_algorithm, status;
 51+bool	 diff_algorithm_set;
 52+int	 tabsize = 8, width = 130;
 53+static int	colorflag = COLORFLAG_NEVER;
 54+char	*start, *ifdefname, *diffargs, *label[2];
 55+char	*ignore_pats, *most_recent_pat;
 56+char	*group_format = NULL;
 57+const char	*add_code, *del_code;
 58+struct stat stb1, stb2;
 59+struct excludes *excludes_list;
 60+regex_t	 ignore_re, most_recent_re;
 61+
 62+static struct algorithm {
 63+	const char *name;
 64+	int id;
 65+} algorithms[] = {
 66+	{"stone", D_DIFFSTONE},
 67+	{"myers", D_DIFFMYERS},
 68+	{"patience", D_DIFFPATIENCE},
 69+	{NULL, D_DIFFNONE}
 70+};
 71+
 72+#define	OPTIONS	"0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
 73+enum {
 74+	OPT_TSIZE = CHAR_MAX + 1,
 75+	OPT_STRIPCR,
 76+	OPT_IGN_FN_CASE,
 77+	OPT_NO_IGN_FN_CASE,
 78+	OPT_NORMAL,
 79+	OPT_HELP,
 80+	OPT_HORIZON_LINES,
 81+	OPT_CHANGED_GROUP_FORMAT,
 82+	OPT_SUPPRESS_COMMON,
 83+	OPT_COLOR,
 84+	OPT_NO_DEREFERENCE,
 85+	OPT_VERSION,
 86+};
 87+
 88+static struct option longopts[] = {
 89+	{ "algorithm",			required_argument,	0,	'A' },
 90+	{ "text",			no_argument,		0,	'a' },
 91+	{ "ignore-space-change",	no_argument,		0,	'b' },
 92+	{ "context",			optional_argument,	0,	'C' },
 93+	{ "ifdef",			required_argument,	0,	'D' },
 94+	{ "minimal",			no_argument,		0,	'd' },
 95+	{ "ed",				no_argument,		0,	'e' },
 96+	{ "forward-ed",			no_argument,		0,	'f' },
 97+	{ "show-function-line",		required_argument,	0,	'F' },
 98+	{ "speed-large-files",		no_argument,		NULL,	'H' },
 99+	{ "ignore-blank-lines",		no_argument,		0,	'B' },
100+	{ "ignore-matching-lines",	required_argument,	0,	'I' },
101+	{ "ignore-case",		no_argument,		0,	'i' },
102+	{ "paginate",			no_argument,		NULL,	'l' },
103+	{ "label",			required_argument,	0,	'L' },
104+	{ "new-file",			no_argument,		0,	'N' },
105+	{ "rcs",			no_argument,		0,	'n' },
106+	{ "unidirectional-new-file",	no_argument,		0,	'P' },
107+	{ "show-c-function",		no_argument,		0,	'p' },
108+	{ "brief",			no_argument,		0,	'q' },
109+	{ "recursive",			no_argument,		0,	'r' },
110+	{ "report-identical-files",	no_argument,		0,	's' },
111+	{ "starting-file",		required_argument,	0,	'S' },
112+	{ "expand-tabs",		no_argument,		0,	't' },
113+	{ "initial-tab",		no_argument,		0,	'T' },
114+	{ "unified",			optional_argument,	0,	'U' },
115+	{ "ignore-all-space",		no_argument,		0,	'w' },
116+	{ "width",			required_argument,	0,	'W' },
117+	{ "exclude",			required_argument,	0,	'x' },
118+	{ "exclude-from",		required_argument,	0,	'X' },
119+	{ "side-by-side",		no_argument,		NULL,	'y' },
120+	{ "ignore-file-name-case",	no_argument,		NULL,	OPT_IGN_FN_CASE },
121+	{ "help",			no_argument,		NULL,	OPT_HELP},
122+	{ "horizon-lines",		required_argument,	NULL,	OPT_HORIZON_LINES },
123+	{ "no-dereference",		no_argument,		NULL,	OPT_NO_DEREFERENCE},
124+	{ "no-ignore-file-name-case",	no_argument,		NULL,	OPT_NO_IGN_FN_CASE },
125+	{ "normal",			no_argument,		NULL,	OPT_NORMAL },
126+	{ "strip-trailing-cr",		no_argument,		NULL,	OPT_STRIPCR },
127+	{ "tabsize",			required_argument,	NULL,	OPT_TSIZE },
128+	{ "changed-group-format",	required_argument,	NULL,	OPT_CHANGED_GROUP_FORMAT},
129+	{ "suppress-common-lines",	no_argument,		NULL,	OPT_SUPPRESS_COMMON },
130+	{ "color",			optional_argument,	NULL,	OPT_COLOR },
131+	{ "version",			no_argument,		NULL,	OPT_VERSION},
132+	{ NULL,				0,			0,	'\0'}
133+};
134+
135+static void checked_regcomp(char const *, regex_t *);
136+static void usage(void) __dead2;
137+static void conflicting_format(void) __dead2;
138+static void push_excludes(char *);
139+static void push_ignore_pats(char *);
140+static void read_excludes_file(char *file);
141+static void set_argstr(char **, char **);
142+static char *splice(char *, char *);
143+static bool do_color(void);
144+
145+/* ?man diff: differential file and directory comparator
146+arguments: file1 file2
147+synopsis: [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--rcs] [--show-c-function] [--starting-file] [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [-I pattern] [-F pattern] [-L label] file1 file2
148+synopsis: [-aBbdilpTtw] [-A algo] [-I pattern] [-F pattern] [-L label] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -C number file1 file2
149+synopsis: [-aBbdiltw] [-A algo] [-I pattern] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -D string file1 file2
150+synopsis: [-aBbdilpTtw] [-A algo] [-I pattern] [-F pattern] [-L label] [--brief] [--color=when] [--changed-group-format GFMT] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--show-c-function] [--speed-large-files] [--starting-file] [--strip-trailing-cr] [--tabsize number] [--text] -U number file1 file2
151+synopsis: [-aBbdilNPprsTtw] [-c|-e|-f|-n|-q|-u] [-A algo] [--brief] [--color=when] [--changed-group-format GFMT] [--context] [--ed] [--expand-tabs] [--forward-ed] [--ignore-all-space] [--ignore-case] [--ignore-space-change] [--initial-tab] [--minimal] [--new-file] [--no-dereference] [--no-ignore-file-name-case] [--normal] [--paginate] [--rcs] [--recursive] [--report-identical-files] [--show-c-function] [--speed-large-files] [--strip-trailing-cr] [--tabsize number] [--text] [--unidirectional-new-file] [--unified] [-I pattern] [-F pattern] [-L label] [-S name] [-X file] [-x pattern] dir1 dir2
152+synopsis: [-aBbditwW] [--color=when] [--expand-tabs] [--ignore-all-space] [--ignore-blank-lines] [--ignore-case] [--minimal] [--no-dereference] [--no-ignore-file-name-case] [--strip-trailing-cr] [--suppress-common-lines] [--tabsize number] [--text] -y file1 file2
153+synopsis: diff [--help]
154+synopsis: diff [--version]
155+The diff utility compares the contents of file1 and file2 and writes to the standard output the list of changes necessary to convert one file into the other. No output is produced if the files are identical.
156+## OUTPUT OPTIONS
157+### -C number, --context number
158+Like c but produces a diff with number lines of context.
159+### -c
160+Produces a diff with 3 lines of context.
161+
162+With c the output format is modified slightly: the output begins with identification of the files involved and their creation dates and then each change is separated by a line with fifteen *'s. The lines removed from file1 are marked with '- '; those added to file2 are marked '+ '. Lines which are changed from one file to the other are marked in both files with '! '. Changes which lie within 3 lines of each other are grouped together on output.
163+### -D string, --ifdef string
164+Creates a merged version of file1 and file2 on the standard output, with C preprocessor controls included so that a compilation of the result without defining string is equivalent to compiling file1, while defining string will yield file2.
165+### -e, --ed
166+Produces output in a form suitable as input for the editor utility, ed(1), which can then be used to convert file1 into file2.
167+
168+Extra commands are added to the output when comparing directories with e, so that the result is a sh(1) script for converting text files which are common to the two directories from their state in dir1 to their state in dir2. Note that when comparing directories with e, the resulting file may no longer be interpreted as an ed(1) script. Output is added to indicate which file each set of ed(1) commands applies to. These hunks can be manually extracted to produce an ed(1) script, which can also be applied with patch(1).
169+### -f, --forward-ed
170+Identical output to that of the e flag, but in reverse order. It cannot be digested by ed(1).
171+### --help
172+This option prints a summary to stdout and exits with status 0.
173+### -n
174+Produces a script similar to that of e, but in the opposite order and with a count of changed lines on each insert or delete command. This is the form used by rcsdiff.
175+### -q, --brief
176+Just print a line when the files differ. Does not output a list of changes.
177+### -U number, --unified number
178+Like u but produces a diff with number lines of context.
179+### -u
180+Produces a unified diff with 3 lines of context.
181+
182+A unified diff is similar to the context diff produced by the c option. However, unlike with c, all lines to be changed (added and/or removed) are present in a single section.
183+### --version
184+This option prints a version string to stdout and exits with status 0.
185+### -y, --side-by-side
186+Output in two columns with a marker between them.
187+
188+The marker can be one of the following:
189+
190+### space
191+Corresponding lines are identical.
192+### '|'
193+Corresponding lines are different.
194+### '<'
195+Files differ and only the first file contains the line.
196+### '>'
197+Files differ and only the second file contains the line.
198+## COMPARISON OPTIONS
199+### -A algo, --algorithm algo
200+Configure the algorithm used when comparing files. diff supports 3 algorithms:
201+
202+### myers
203+The Myers diff algorithm finds the shortest edit which transforms one input into the other. It generally runs in O(N+D²) time, requiring O(N) space, where N is the sum of the lengths of the inputs and D is the length of the difference between them, with a theoretical O(N·D) worst case. If it encounters worst-case input, the implementation used by diff falls back to a less optimal but faster algorithm.
204+### patience
205+The Patience variant of the Myers algorithm attempts to create more aesthetically pleasing diff output by logically grouping lines.
206+### stone
207+The Stone algorithm (commonly known as Hunt-McIlroy or Hunt-Szymanski) looks for the longest common subsequence between compared files. Stone encounters worst case performance when there are long common subsequences. In large files this can lead to a significant performance impact. The Stone algorithm is maintained for compatibility.
208+
209+The diff utility defaults to the Myers algorithm, but will fall back to the Stone algorithm if the input or output options are not supported by the Myers implementation.
210+### -a, --text
211+Treat all files as ASCII text.
212+
213+Normally diff will simply print "Binary files ... differ" if files contain binary characters. Use of this option forces diff to produce a diff.
214+### -B, --ignore-blank-lines
215+Causes chunks that include only blank lines to be ignored.
216+### -b, --ignore-space-change
217+Causes trailing blanks (spaces and tabs) to be ignored, and other strings of blanks to compare equal.
218+### --color when
219+Color the additions green, and removals red, or the value in the DIFFCOLORS environment variable.
220+
221+The possible values of when are "never", "always" and "auto". auto will use color if the output is a tty and the COLORTERM environment variable is set to a non-empty string.
222+### -d, --minimal
223+Try very hard to produce a diff as small as possible. This may consume a lot of processing power and memory when processing large files with many changes.
224+### -F pattern, --show-function-line pattern
225+Like p, but display the last line that matches provided pattern.
226+### -I pattern, --ignore-matching-lines pattern
227+Ignores changes, insertions, and deletions whose lines match the extended regular expression pattern. Multiple I patterns may be specified. All lines in the change must match some pattern for the change to be ignored. See re_format(7) for more information on regular expression patterns.
228+### -i, --ignore-case
229+Ignores the case of letters. E.g., "A" will compare equal to "a".
230+### -l, --paginate
231+Pass the output through pr(1) to paginate it.
232+### -L label, --label label
233+Print label instead of the first (and second, if this option is specified twice) file name and time in the context or unified diff header.
234+### -p, --show-c-function
235+With unified and context diffs, show with each change the first 40 characters of the last line before the context beginning with a letter, an underscore or a dollar sign. For C and Objective-C source code following standard layout conventions, this will show the prototype of the function the change applies to.
236+### -T, --initial-tab
237+Print a tab rather than a space before the rest of the line for the normal, context or unified output formats. This makes the alignment of tabs in the line consistent.
238+### -t, --expand-tabs
239+Will expand tabs in output lines.
240+
241+Normal or c output adds character(s) to the front of each line which may screw up the indentation of the original source lines and make the output listing difficult to interpret. This option will preserve the original source's indentation.
242+### -w, --ignore-all-space
243+Is similar to b --ignore-space-change but causes whitespace (blanks and tabs) to be totally ignored. E.g., "if (  a == b  )" will compare equal to "if(a==b)".
244+### -W number, --width number
245+Output at most number columns when using side by side format. The default value is 130. Note that unless t was specified, diff will always align the second column to a tab stop, so values of --width smaller than approximately five times the value of --tabsize may yield surprising results.
246+### --changed-group-format GFMT
247+Format input groups in the provided.
248+
249+the format is a string with special keywords:
250+
251+### %<
252+lines from FILE1
253+### %>
254+lines from FILE2
255+### --ignore-file-name-case
256+ignore case when comparing file names
257+### --no-dereference
258+do not follow symbolic links
259+### --no-ignore-file-name-case
260+do not ignore case when comparing file names (default)
261+### --normal
262+default diff output
263+### --speed-large-files
264+stub option for compatibility with GNU diff
265+### --strip-trailing-cr
266+strip carriage return on input files
267+### --suppress-common-lines
268+Do not output common lines when using the side by side format
269+### --tabsize number
270+Number of spaces representing a tab (default 8)
271+## DIRECTORY COMPARISON OPTIONS
272+### -N, --new-file
273+If a file is found in only one directory, act as if it was found in the other directory too but was of zero size.
274+### -P, --unidirectional-new-file
275+If a file is found only in dir2, act as if it was found in dir1 too but was of zero size.
276+### -r, --recursive
277+Causes application of diff recursively to common subdirectories encountered.
278+### -S name, --starting-file name
279+Re-starts a directory diff in the middle, beginning with file name.
280+### -s, --report-identical-files
281+Causes diff to report files which are the same, which are otherwise not mentioned.
282+### -X file, --exclude-from file
283+Exclude files and subdirectories from comparison whose basenames match lines in file. Multiple X options may be specified.
284+### -x pattern, --exclude pattern
285+Exclude files and subdirectories from comparison whose basenames match pattern. Patterns are matched using shell-style globbing via fnmatch(3). Multiple x options may be specified.
286+
287+If both arguments are directories, diff sorts the contents of the directories by name, and then runs the regular file diff algorithm, producing a change list, on text files which are different. Binary files which differ, common subdirectories, and files which appear in only one directory are described as such. In directory mode only regular files and directories are compared. If a non-regular file such as a device special file or FIFO is encountered, a diagnostic message is printed.
288+
289+If only one of file1 and file2 is a directory, diff is applied to the non-directory file and the file contained in the directory file with a filename that is the same as the last component of the non-directory file.
290+
291+If either file1 or file2 is -, the standard input is used in its place.
292+## OUTPUT STYLE
293+The default (without e, c, or n -rcs options) output contains lines of these forms, where XX, YY, ZZ, QQ are line numbers respective of file order.
294+
295+### XXaYY
296+At (the end of) line XX of file1, append the contents of line YY of file2 to make them equal.
297+### XXaYY,ZZ
298+Same as above, but append the range of lines, YY through ZZ of file2 to line XX of file1.
299+### XXdYY
300+At line XX delete the line. The value YY tells to which line the change would bring file1 in line with file2.
301+### XX,YYdZZ
302+Delete the range of lines XX through YY in file1.
303+### XXcYY
304+Change the line XX in file1 to the line YY in file2.
305+### XX,YYcZZ
306+Replace the range of specified lines with the line ZZ.
307+### XX,YYcZZ,QQ
308+Replace the range XX,YY from file1 with the range ZZ,QQ from file2.
309+
310+These lines resemble ed(1) subcommands to convert file1 into file2. The line numbers before the action letters pertain to file1; those after pertain to file2. Thus, by exchanging a for d and reading the line in reverse order, one can also determine how to convert file2 into file1. As in ed(1), identical pairs (where num1 = num2) are abbreviated as a single number.
311+## ENVIRONMENT
312+### DIFFCOLORS
313+The value of this variable is the form add:rm, where add is the ASCII escape sequence for additions and rm is the ASCII escape sequence for deletions. If this is unset, diff uses green for additions and red for removals.
314+## FILES
315+### /tmp/diff.XXXXXXXX
316+Temporary file used when comparing a device or the standard input. Note that the temporary file is unlinked as soon as it is created so it will not show up in a directory listing.
317+## EXIT STATUS
318+### 0
319+No differences were found.
320+### 1
321+Differences were found.
322+### >1
323+An error occurred.
324+
325+The --help and --version options exit with a status of 0.
326+## EXAMPLES
327+### Compare old_dir and new_dir recursively generating an unified diff and treating files found only in one of those directories as new files
328+$ diff -ruN /path/to/old_dir /path/to/new_dir
329+### Same as above but excluding files matching the expressions "*.h" and "*.c"
330+$ diff -ruN -x '*.h' -x '*.c' /path/to/old_dir /path/to/new_dir
331+### Show a single line indicating if the files differ
332+$ diff -q /boot/loader.conf /boot/defaults/loader.conf
333+Files /boot/loader.conf and /boot/defaults/loader.conf differ
334+### Assuming a file named example.txt with the following contents
335+FreeBSD is an operating system
336+Linux is a kernel
337+OpenBSD is an operating system
338+### Compare stdin with example.txt excluding from the comparison those lines containing either "Linux" or "Open"
339+$ echo "FreeBSD is an operating system" | diff -q -I 'Linux|Open' example.txt -
340+## SEE ALSO
341+cmp(1), comm(1), diff3(1), ed(1), patch(1), pr(1), sdiff(1)
342+## STANDARDS
343+The diff utility is compliant with the POSIX.1-2008 specification.
344+
345+The flags A a D d I i L l N n P p q S s T t w X x y are extensions to that specification.
346+## HISTORY
347+A diff command appeared in AT&T Version 6 UNIX.
348+
349+The diff implementation used in FreeBSD was GNU diff until FreeBSD 11.4. This was replaced in FreeBSD 12.0 by a BSD-licensed implementation written by Todd Miller. Some GNUisms were lost in the process.
350+
351+libdiff was imported from the Game of Trees version control system and default algorithm was changed to Myers for FreeBSD 15 .
352+*/
353+int
354+main(int argc, char **argv)
355+{
356+	const char *errstr = NULL;
357+	char *ep, **oargv;
358+	long  l;
359+	int   ch, dflags, lastch, gotstdin, prevoptind, newarg;
360+
361+	oargv = argv;
362+	gotstdin = 0;
363+	dflags = 0;
364+	lastch = '\0';
365+	prevoptind = 1;
366+	newarg = 1;
367+	diff_context = 3;
368+	diff_format = D_UNSET;
369+	diff_algorithm = D_DIFFMYERS;
370+	diff_algorithm_set = false;
371+#define	FORMAT_MISMATCHED(type)	\
372+	(diff_format != D_UNSET && diff_format != (type))
373+	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
374+		switch (ch) {
375+		case '0': case '1': case '2': case '3': case '4':
376+		case '5': case '6': case '7': case '8': case '9':
377+			if (newarg)
378+				usage();	/* disallow -[0-9]+ */
379+			else if (lastch == 'c' || lastch == 'u')
380+				diff_context = 0;
381+			else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
382+				usage();
383+			diff_context = (diff_context * 10) + (ch - '0');
384+			break;
385+		case 'A':
386+			diff_algorithm = D_DIFFNONE;
387+			for (struct algorithm *a = algorithms; a->name;a++) {
388+				if(strcasecmp(optarg, a->name) == 0) {
389+					diff_algorithm = a->id;
390+					diff_algorithm_set = true;
391+					break;
392+				}
393+			}
394+
395+			if (diff_algorithm == D_DIFFNONE) {
396+				printf("unknown algorithm: %s\n", optarg);
397+				usage();
398+			}
399+			break;
400+		case 'a':
401+			dflags |= D_FORCEASCII;
402+			break;
403+		case 'b':
404+			dflags |= D_FOLDBLANKS;
405+			break;
406+		case 'C':
407+		case 'c':
408+			if (FORMAT_MISMATCHED(D_CONTEXT))
409+				conflicting_format();
410+			cflag = true;
411+			diff_format = D_CONTEXT;
412+			if (optarg != NULL) {
413+				l = strtol(optarg, &ep, 10);
414+				if (*ep != '\0' || l < 0 || l >= INT_MAX)
415+					usage();
416+				diff_context = (int)l;
417+			}
418+			break;
419+		case 'd':
420+			dflags |= D_MINIMAL;
421+			break;
422+		case 'D':
423+			if (FORMAT_MISMATCHED(D_IFDEF))
424+				conflicting_format();
425+			diff_format = D_IFDEF;
426+			ifdefname = optarg;
427+			break;
428+		case 'e':
429+			if (FORMAT_MISMATCHED(D_EDIT))
430+				conflicting_format();
431+			diff_format = D_EDIT;
432+			break;
433+		case 'f':
434+			if (FORMAT_MISMATCHED(D_REVERSE))
435+				conflicting_format();
436+			diff_format = D_REVERSE;
437+			break;
438+		case 'H':
439+			/* ignore but needed for compatibility with GNU diff */
440+			break;
441+		case 'h':
442+			/* silently ignore for backwards compatibility */
443+			break;
444+		case 'B':
445+			dflags |= D_SKIPBLANKLINES;
446+			break;
447+		case 'F':
448+			if (dflags & D_PROTOTYPE)
449+				conflicting_format();
450+			dflags |= D_MATCHLAST;
451+			most_recent_pat = xstrdup(optarg);
452+			break;
453+		case 'I':
454+			push_ignore_pats(optarg);
455+			break;
456+		case 'i':
457+			dflags |= D_IGNORECASE;
458+			break;
459+		case 'L':
460+			if (label[0] == NULL)
461+				label[0] = optarg;
462+			else if (label[1] == NULL)
463+				label[1] = optarg;
464+			else
465+				usage();
466+			break;
467+		case 'l':
468+			lflag = true;
469+			break;
470+		case 'N':
471+			Nflag = true;
472+			break;
473+		case 'n':
474+			if (FORMAT_MISMATCHED(D_NREVERSE))
475+				conflicting_format();
476+			diff_format = D_NREVERSE;
477+			break;
478+		case 'p':
479+			if (dflags & D_MATCHLAST)
480+				conflicting_format();
481+			dflags |= D_PROTOTYPE;
482+			break;
483+		case 'P':
484+			Pflag = true;
485+			break;
486+		case 'r':
487+			rflag = true;
488+			break;
489+		case 'q':
490+			if (FORMAT_MISMATCHED(D_BRIEF))
491+				conflicting_format();
492+			diff_format = D_BRIEF;
493+			break;
494+		case 'S':
495+			start = optarg;
496+			break;
497+		case 's':
498+			sflag = true;
499+			break;
500+		case 'T':
501+			Tflag = true;
502+			break;
503+		case 't':
504+			dflags |= D_EXPANDTABS;
505+			break;
506+		case 'U':
507+		case 'u':
508+			if (FORMAT_MISMATCHED(D_UNIFIED))
509+				conflicting_format();
510+			diff_format = D_UNIFIED;
511+			if (optarg != NULL) {
512+				l = strtol(optarg, &ep, 10);
513+				if (*ep != '\0' || l < 0 || l >= INT_MAX)
514+					usage();
515+				diff_context = (int)l;
516+			}
517+			break;
518+		case 'w':
519+			dflags |= D_IGNOREBLANKS;
520+			break;
521+		case 'W':
522+			width = (int) strtonum(optarg, 1, INT_MAX, &errstr);
523+			if (errstr) {
524+				warnx("Invalid argument for width");
525+				usage();
526+			}
527+			break;
528+		case 'X':
529+			read_excludes_file(optarg);
530+			break;
531+		case 'x':
532+			push_excludes(optarg);
533+			break;
534+		case 'y':
535+			if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
536+				conflicting_format();
537+			diff_format = D_SIDEBYSIDE;
538+			break;
539+		case OPT_CHANGED_GROUP_FORMAT:
540+			if (FORMAT_MISMATCHED(D_GFORMAT))
541+				conflicting_format();
542+			diff_format = D_GFORMAT;
543+			group_format = optarg;
544+			break;
545+		case OPT_HELP:
546+			help = true;
547+			usage();
548+			break;
549+		case OPT_HORIZON_LINES:
550+			break; /* XXX TODO for compatibility with GNU diff3 */
551+		case OPT_IGN_FN_CASE:
552+			ignore_file_case = true;
553+			break;
554+		case OPT_NO_IGN_FN_CASE:
555+			ignore_file_case = false;
556+			break;
557+		case OPT_NORMAL:
558+			if (FORMAT_MISMATCHED(D_NORMAL))
559+				conflicting_format();
560+			diff_format = D_NORMAL;
561+			break;
562+		case OPT_TSIZE:
563+			tabsize = (int) strtonum(optarg, 1, INT_MAX, &errstr);
564+			if (errstr) {
565+				warnx("Invalid argument for tabsize");
566+				usage();
567+			}
568+			break;
569+		case OPT_STRIPCR:
570+			dflags |= D_STRIPCR;
571+			break;
572+		case OPT_SUPPRESS_COMMON:
573+			suppress_common = 1;
574+			break;
575+		case OPT_COLOR:
576+			if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
577+				colorflag = COLORFLAG_AUTO;
578+			else if (strncmp(optarg, "always", 6) == 0)
579+				colorflag = COLORFLAG_ALWAYS;
580+			else if (strncmp(optarg, "never", 5) == 0)
581+				colorflag = COLORFLAG_NEVER;
582+			else
583+				errx(2, "unsupported --color value '%s' (must be always, auto, or never)",
584+					optarg);
585+			break;
586+		case OPT_NO_DEREFERENCE:
587+			noderef = true;
588+			break;
589+		case OPT_VERSION:
590+			printf("%s\n", diff_version);
591+			exit(0);
592+		default:
593+			usage();
594+			break;
595+		}
596+		lastch = ch;
597+		newarg = optind != prevoptind;
598+		prevoptind = optind;
599+	}
600+	if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
601+		diff_format = D_CONTEXT;
602+	if (diff_format == D_UNSET)
603+		diff_format = D_NORMAL;
604+	argc -= optind;
605+	argv += optind;
606+
607+	if (do_color()) {
608+		char *p;
609+		const char *env;
610+
611+		color = true;
612+		add_code = "32";
613+		del_code = "31";
614+		env = getenv("DIFFCOLORS");
615+		if (env != NULL && *env != '\0' && (p = strdup(env))) {
616+			add_code = p;
617+			strsep(&p, ":");
618+			if (p != NULL)
619+				del_code = p;
620+		}
621+	}
622+
623+#ifdef __OpenBSD__
624+	if (pledge("stdio rpath tmppath", NULL) == -1)
625+		err(2, "pledge");
626+#endif
627+
628+	/*
629+	 * Do sanity checks, fill in stb1 and stb2 and call the appropriate
630+	 * driver routine.  Both drivers use the contents of stb1 and stb2.
631+	 */
632+	if (argc != 2)
633+		usage();
634+	checked_regcomp(ignore_pats, &ignore_re);
635+	checked_regcomp(most_recent_pat, &most_recent_re);
636+	if (strcmp(argv[0], "-") == 0) {
637+		fstat(STDIN_FILENO, &stb1);
638+		gotstdin = 1;
639+	} else if (stat(argv[0], &stb1) != 0) {
640+		if (!Nflag || errno != ENOENT)
641+			err(2, "%s", argv[0]);
642+		dflags |= D_EMPTY1;
643+		memset(&stb1, 0, sizeof(struct stat));
644+	}
645+
646+	if (strcmp(argv[1], "-") == 0) {
647+		fstat(STDIN_FILENO, &stb2);
648+		gotstdin = 1;
649+	} else if (stat(argv[1], &stb2) != 0) {
650+		if (!Nflag || errno != ENOENT)
651+			err(2, "%s", argv[1]);
652+		dflags |= D_EMPTY2;
653+		memset(&stb2, 0, sizeof(stb2));
654+		stb2.st_mode = stb1.st_mode;
655+	}
656+
657+	if (dflags & D_EMPTY1 && dflags & D_EMPTY2){
658+		warn("%s", argv[0]);
659+		warn("%s", argv[1]);
660+		exit(2);
661+	}
662+
663+	if (stb1.st_mode == 0)
664+		stb1.st_mode = stb2.st_mode;
665+
666+	if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
667+		errx(2, "can't compare - to a directory");
668+	set_argstr(oargv, argv);
669+	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
670+		if (diff_format == D_IFDEF)
671+			errx(2, "-D option not supported with directories");
672+		diffdir(argv[0], argv[1], dflags);
673+	} else {
674+		if (S_ISDIR(stb1.st_mode)) {
675+			argv[0] = splice(argv[0], argv[1]);
676+			if (stat(argv[0], &stb1) == -1)
677+				err(2, "%s", argv[0]);
678+		}
679+		if (S_ISDIR(stb2.st_mode)) {
680+			argv[1] = splice(argv[1], argv[0]);
681+			if (stat(argv[1], &stb2) == -1)
682+				err(2, "%s", argv[1]);
683+		}
684+		print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0],
685+		    argv[1], "");
686+	}
687+	if (fflush(stdout) != 0)
688+		err(2, "stdout");
689+	exit(status);
690+}
691+
692+static void
693+checked_regcomp(char const *pattern, regex_t *comp)
694+{
695+	char buf[BUFSIZ];
696+	int error;
697+
698+	if (pattern == NULL)
699+		return;
700+
701+	error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
702+	if (error != 0) {
703+		regerror(error, comp, buf, sizeof(buf));
704+		if (*pattern != '\0')
705+			errx(2, "%s: %s", pattern, buf);
706+		else
707+			errx(2, "%s", buf);
708+	}
709+}
710+
711+static void
712+set_argstr(char **av, char **ave)
713+{
714+	size_t argsize;
715+	char **ap;
716+
717+	argsize = 4 + *ave - *av + 1;
718+	diffargs = xmalloc(argsize);
719+	strlcpy(diffargs, "diff", argsize);
720+	for (ap = av + 1; ap < ave; ap++) {
721+		if (strcmp(*ap, "--") != 0) {
722+			strlcat(diffargs, " ", argsize);
723+			strlcat(diffargs, *ap, argsize);
724+		}
725+	}
726+}
727+
728+/*
729+ * Read in an excludes file and push each line.
730+ */
731+static void
732+read_excludes_file(char *file)
733+{
734+	FILE *fp;
735+	char *pattern = NULL;
736+	size_t blen = 0;
737+	ssize_t len;
738+
739+	if (strcmp(file, "-") == 0)
740+		fp = stdin;
741+	else if ((fp = fopen(file, "r")) == NULL)
742+		err(2, "%s", file);
743+	while ((len = getline(&pattern, &blen, fp)) >= 0) {
744+		if ((len > 0) && (pattern[len - 1] == '\n'))
745+			pattern[len - 1] = '\0';
746+		push_excludes(pattern);
747+		/* we allocate a new string per line */
748+		pattern = NULL;
749+		blen = 0;
750+	}
751+	free(pattern);
752+	if (strcmp(file, "-") != 0)
753+		fclose(fp);
754+}
755+
756+/*
757+ * Push a pattern onto the excludes list.
758+ */
759+static void
760+push_excludes(char *pattern)
761+{
762+	struct excludes *entry;
763+
764+	entry = xmalloc(sizeof(*entry));
765+	entry->pattern = pattern;
766+	entry->next = excludes_list;
767+	excludes_list = entry;
768+}
769+
770+static void
771+push_ignore_pats(char *pattern)
772+{
773+	size_t len;
774+
775+	if (ignore_pats == NULL)
776+		ignore_pats = xstrdup(pattern);
777+	else {
778+		/* old + "|" + new + NUL */
779+		len = strlen(ignore_pats) + strlen(pattern) + 2;
780+		ignore_pats = xreallocarray(ignore_pats, 1, len);
781+		strlcat(ignore_pats, "|", len);
782+		strlcat(ignore_pats, pattern, len);
783+	}
784+}
785+
786+void
787+print_status(int val, char *path1, char *path2, const char *entry)
788+{
789+	if (label[0] != NULL)
790+		path1 = label[0];
791+	if (label[1] != NULL)
792+		path2 = label[1];
793+
794+	switch (val) {
795+	case D_BINARY:
796+		printf("Binary files %s%s and %s%s differ\n",
797+		    path1, entry, path2, entry);
798+		break;
799+	case D_DIFFER:
800+		if (diff_format == D_BRIEF)
801+			printf("Files %s%s and %s%s differ\n",
802+			    path1, entry, path2, entry);
803+		break;
804+	case D_SAME:
805+		if (sflag)
806+			printf("Files %s%s and %s%s are identical\n",
807+			    path1, entry, path2, entry);
808+		break;
809+	case D_MISMATCH1:
810+		printf("File %s%s is a directory while file %s%s is a regular file\n",
811+		    path1, entry, path2, entry);
812+		break;
813+	case D_MISMATCH2:
814+		printf("File %s%s is a regular file while file %s%s is a directory\n",
815+		    path1, entry, path2, entry);
816+		break;
817+	case D_SKIPPED1:
818+		printf("File %s%s is not a regular file or directory and was skipped\n",
819+		    path1, entry);
820+		break;
821+	case D_SKIPPED2:
822+		printf("File %s%s is not a regular file or directory and was skipped\n",
823+		    path2, entry);
824+		break;
825+	case D_ERROR:
826+		break;
827+	}
828+}
829+
830+static void
831+usage(void)
832+{
833+	(void)fprintf(help ? stdout : stderr,
834+	    "usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
835+	    "            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
836+	    "            [-I pattern] [-F pattern] [-L label] file1 file2\n"
837+	    "       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
838+	    "            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
839+	    "            [-F pattern] -C number file1 file2\n"
840+	    "       diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
841+	    "            [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
842+	    "       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
843+	    "            [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
844+	    "            [-F pattern] -U number file1 file2\n"
845+	    "       diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
846+	    "            [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
847+	    "            [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2\n"
848+	    "       diff [-aBbditwW] [--expand-tabs] [--ignore-all-space]\n"
849+	    "            [--ignore-blank-lines] [--ignore-case] [--minimal]\n"
850+	    "            [--no-ignore-file-name-case] [--strip-trailing-cr]\n"
851+	    "            [--suppress-common-lines] [--tabsize] [--text] [--width]\n"
852+	    "            -y | --side-by-side file1 file2\n"
853+	    "       diff [--help] [--version]\n");
854+
855+	if (help)
856+		exit(0);
857+	else
858+		exit(2);
859+}
860+
861+static void
862+conflicting_format(void)
863+{
864+
865+	fprintf(stderr, "error: conflicting output format options.\n");
866+	usage();
867+}
868+
869+static bool
870+do_color(void)
871+{
872+	const char *p, *p2;
873+
874+	switch (colorflag) {
875+	case COLORFLAG_AUTO:
876+		p = getenv("CLICOLOR");
877+		p2 = getenv("COLORTERM");
878+		if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0'))
879+			return isatty(STDOUT_FILENO);
880+		break;
881+	case COLORFLAG_ALWAYS:
882+		return (true);
883+	case COLORFLAG_NEVER:
884+		return (false);
885+	}
886+
887+	return (false);
888+}
889+
890+static char *
891+splice(char *dir, char *path)
892+{
893+	char *tail, *buf;
894+	size_t dirlen;
895+
896+	dirlen = strlen(dir);
897+	while (dirlen != 0 && dir[dirlen - 1] == '/')
898+	    dirlen--;
899+	if ((tail = strrchr(path, '/')) == NULL)
900+		tail = path;
901+	else
902+		tail++;
903+	xasprintf(&buf, "%.*s/%s", (int)dirlen, dir, tail);
904+	return (buf);
905+}
+137, -0
  1@@ -0,0 +1,137 @@
  2+/*	$OpenBSD: diff.h,v 1.34 2020/11/01 18:16:08 jcs Exp $	*/
  3+
  4+/*-
  5+ * Copyright (c) 1991, 1993
  6+ *	The Regents of the University of California.  All rights reserved.
  7+ *
  8+ * Redistribution and use in source and binary forms, with or without
  9+ * modification, are permitted provided that the following conditions
 10+ * are met:
 11+ * 1. Redistributions of source code must retain the above copyright
 12+ *    notice, this list of conditions and the following disclaimer.
 13+ * 2. Redistributions in binary form must reproduce the above copyright
 14+ *    notice, this list of conditions and the following disclaimer in the
 15+ *    documentation and/or other materials provided with the distribution.
 16+ * 3. Neither the name of the University nor the names of its contributors
 17+ *    may be used to endorse or promote products derived from this software
 18+ *    without specific prior written permission.
 19+ *
 20+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 21+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 24+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 26+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 27+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 28+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 29+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 30+ * SUCH DAMAGE.
 31+ */
 32+
 33+#include <sys/types.h>
 34+#include <sys/stat.h>
 35+
 36+#include <stdbool.h>
 37+#include <regex.h>
 38+
 39+/*
 40+ * Output format options
 41+ */
 42+#define	D_NORMAL	0	/* Normal output */
 43+#define	D_EDIT		-1	/* Editor script out */
 44+#define	D_REVERSE	1	/* Reverse editor script */
 45+#define	D_CONTEXT	2	/* Diff with context */
 46+#define	D_UNIFIED	3	/* Unified context diff */
 47+#define	D_IFDEF		4	/* Diff with merged #ifdef's */
 48+#define	D_NREVERSE	5	/* Reverse ed script with numbered
 49+				   lines and no trailing . */
 50+#define	D_BRIEF		6	/* Say if the files differ */
 51+#define	D_GFORMAT	7	/* Diff with defined changed group format */
 52+#define D_SIDEBYSIDE    8	/* Side by side */
 53+
 54+#define	D_UNSET		-2
 55+
 56+/*
 57+ * Algorithms
 58+ */
 59+
 60+#define D_DIFFNONE     0
 61+#define D_DIFFSTONE    1       /* Stone or 'old diff' algorithm */
 62+#define D_DIFFMYERS    2       /* Myers diff algorithm */
 63+#define D_DIFFPATIENCE 3       /* Patience diff algorithm */
 64+
 65+/*
 66+ * Output flags
 67+ */
 68+#define	D_HEADER	0x001	/* Print a header/footer between files */
 69+#define	D_EMPTY1	0x002	/* Treat first file as empty (/dev/null) */
 70+#define	D_EMPTY2	0x004	/* Treat second file as empty (/dev/null) */
 71+
 72+/*
 73+ * Command line flags
 74+ */
 75+#define D_FORCEASCII		0x008	/* Treat file as ascii regardless of content */
 76+#define D_FOLDBLANKS		0x010	/* Treat all white space as equal */
 77+#define D_MINIMAL		0x020	/* Make diff as small as possible */
 78+#define D_IGNORECASE		0x040	/* Case-insensitive matching */
 79+#define D_PROTOTYPE		0x080	/* Display C function prototype */
 80+#define D_EXPANDTABS		0x100	/* Expand tabs to spaces */
 81+#define D_IGNOREBLANKS		0x200	/* Ignore white space changes */
 82+#define D_STRIPCR		0x400	/* Strip trailing cr */
 83+#define D_SKIPBLANKLINES	0x800	/* Skip blank lines */
 84+#define D_MATCHLAST		0x1000	/* Display last line matching provided regex */
 85+
 86+/* Features supported by new algorithms */
 87+#define D_NEWALGO_FLAGS                (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS)
 88+
 89+/*
 90+ * Status values for print_status() and diffreg() return values
 91+ */
 92+#define	D_SAME		0	/* Files are the same */
 93+#define	D_DIFFER	1	/* Files are different */
 94+#define	D_BINARY	2	/* Binary files are different */
 95+#define	D_MISMATCH1	3	/* path1 was a dir, path2 a file */
 96+#define	D_MISMATCH2	4	/* path1 was a file, path2 a dir */
 97+#define	D_SKIPPED1	5	/* path1 was a special file */
 98+#define	D_SKIPPED2	6	/* path2 was a special file */
 99+#define	D_ERROR		7	/* A file access error occurred */
100+
101+/*
102+ * Color options
103+ */
104+#define COLORFLAG_NEVER		0
105+#define COLORFLAG_AUTO		1
106+#define COLORFLAG_ALWAYS	2
107+
108+struct excludes {
109+	char *pattern;
110+	struct excludes *next;
111+};
112+
113+extern bool	 lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
114+extern bool	 ignore_file_case, suppress_common, color, noderef, algorithm_set;
115+extern int	 diff_format, diff_context, diff_algorithm, status;
116+extern bool	 diff_algorithm_set;
117+extern int	 tabsize, width;
118+extern char	*start, *ifdefname, *diffargs, *label[2];
119+extern char	*ignore_pats, *most_recent_pat;
120+extern char	*group_format;
121+extern const char	*add_code, *del_code;
122+extern struct stat stb1, stb2;
123+extern struct excludes *excludes_list;
124+extern regex_t	 ignore_re, most_recent_re;
125+
126+int	 diffreg(char *, char *, int, int);
127+int	 diffreg_new(char *, char *, int, int);
128+bool	 can_libdiff(int);
129+void	 diffdir(char *, char *, int);
130+void	 print_status(int, char *, char *, const char *);
131+
132+#ifndef MIN
133+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
134+#endif
135+
136+#ifndef MAX
137+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
138+#endif
+377, -0
  1@@ -0,0 +1,377 @@
  2+/*	$OpenBSD: diffdir.c,v 1.45 2015/10/05 20:15:00 millert Exp $	*/
  3+
  4+/*
  5+ * Copyright (c) 2003, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
  6+ *
  7+ * Permission to use, copy, modify, and distribute this software for any
  8+ * purpose with or without fee is hereby granted, provided that the above
  9+ * copyright notice and this permission notice appear in all copies.
 10+ *
 11+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 12+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 13+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 14+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 15+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 16+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 17+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 18+ *
 19+ * Sponsored in part by the Defense Advanced Research Projects
 20+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
 21+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
 22+ */
 23+
 24+#include <sys/cdefs.h>
 25+#include <sys/stat.h>
 26+
 27+#include <dirent.h>
 28+#include <err.h>
 29+#include <errno.h>
 30+#include <fcntl.h>
 31+#include <fnmatch.h>
 32+#include <limits.h>
 33+#include <stdio.h>
 34+#include <stdlib.h>
 35+#include <string.h>
 36+#include <unistd.h>
 37+
 38+#include "util.h"
 39+
 40+#include "diff.h"
 41+
 42+static int selectfile(const struct dirent *);
 43+static void diffit(struct dirent *, char *, size_t, struct dirent *,
 44+	char *, size_t, int);
 45+static void print_only(const char *, size_t, const char *);
 46+
 47+#define d_status	d_type		/* we need to store status for -l */
 48+
 49+#if 0
 50+struct inode {
 51+	dev_t dev;
 52+	ino_t ino;
 53+	RB_ENTRY(inode) entry;
 54+};
 55+
 56+static int
 57+inodecmp(struct inode *a, struct inode *b)
 58+{
 59+	return (a->dev < b->dev ? -1 : a->dev > b->dev ? 1 :
 60+	    a->ino < b->ino ? -1 : a->ino > b->ino ? 1 : 0);
 61+}
 62+
 63+RB_HEAD(inodetree, inode);
 64+static struct inodetree v1 = RB_INITIALIZER(&v1);
 65+static struct inodetree v2 = RB_INITIALIZER(&v2);
 66+RB_GENERATE_STATIC(inodetree, inode, entry, inodecmp);
 67+
 68+static int
 69+vscandir(struct inodetree *tree, const char *path, struct dirent ***dirp,
 70+    int (*selectf)(const struct dirent *),
 71+    int (*comparf)(const struct dirent **, const struct dirent **))
 72+{
 73+	struct stat sb;
 74+	struct inode *ino = NULL;
 75+	int fd = -1, ret, serrno;
 76+
 77+	if ((fd = open(path, O_DIRECTORY | O_RDONLY)) < 0 ||
 78+	    (ino = calloc(1, sizeof(*ino))) == NULL ||
 79+	    fstat(fd, &sb) != 0)
 80+		goto fail;
 81+	ino->dev = sb.st_dev;
 82+	ino->ino = sb.st_ino;
 83+	if (RB_FIND(inodetree, tree, ino)) {
 84+		free(ino);
 85+		close(fd);
 86+		warnx("%s: Directory loop detected", path);
 87+		*dirp = NULL;
 88+		return (0);
 89+	}
 90+	if ((ret = fdscandir(fd, dirp, selectf, comparf)) < 0)
 91+		goto fail;
 92+	RB_INSERT(inodetree, tree, ino);
 93+	close(fd);
 94+	return (ret);
 95+fail:
 96+	serrno = errno;
 97+	if (ino != NULL)
 98+		free(ino);
 99+	if (fd >= 0)
100+		close(fd);
101+	errno = serrno;
102+	return (-1);
103+}
104+#endif
105+
106+/*
107+ * Diff directory traversal. Will be called recursively if -r was specified.
108+ */
109+void
110+diffdir(char *p1, char *p2, int flags)
111+{
112+	struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
113+	struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
114+	size_t dirlen1, dirlen2;
115+	char path1[PATH_MAX], path2[PATH_MAX];
116+	int pos;
117+
118+	edp1 = edp2 = NULL;
119+
120+	dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
121+	if (dirlen1 >= sizeof(path1) - 1) {
122+		errno = ENAMETOOLONG;
123+		warn("%s", p1);
124+		status |= 2;
125+		return;
126+	}
127+	if (path1[dirlen1 - 1] != '/') {
128+		path1[dirlen1++] = '/';
129+		path1[dirlen1] = '\0';
130+	}
131+	dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
132+	if (dirlen2 >= sizeof(path2) - 1) {
133+		errno = ENAMETOOLONG;
134+		warn("%s", p2);
135+		status |= 2;
136+		return;
137+	}
138+	if (path2[dirlen2 - 1] != '/') {
139+		path2[dirlen2++] = '/';
140+		path2[dirlen2] = '\0';
141+	}
142+
143+	/*
144+	 * Get a list of entries in each directory, skipping "excluded" files
145+	 * and sorting alphabetically.
146+	 */
147+	pos = scandir(path1, &dirp1, selectfile, alphasort);
148+	if (pos == -1) {
149+		if (errno == ENOENT && (Nflag || Pflag)) {
150+			pos = 0;
151+		} else {
152+			warn("%s", path1);
153+			goto closem;
154+		}
155+	}
156+	dp1 = dirp1;
157+	edp1 = dirp1 + pos;
158+
159+	pos = scandir(path2, &dirp2, selectfile, alphasort);
160+	if (pos == -1) {
161+		if (errno == ENOENT && Nflag) {
162+			pos = 0;
163+		} else {
164+			warn("%s", path2);
165+			goto closem;
166+		}
167+	}
168+	dp2 = dirp2;
169+	edp2 = dirp2 + pos;
170+
171+	/*
172+	 * If we were given a starting point, find it.
173+	 */
174+	if (start != NULL) {
175+		while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
176+			dp1++;
177+		while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
178+			dp2++;
179+	}
180+
181+	/*
182+	 * Iterate through the two directory lists, diffing as we go.
183+	 */
184+	while (dp1 != edp1 || dp2 != edp2) {
185+		dent1 = dp1 != edp1 ? *dp1 : NULL;
186+		dent2 = dp2 != edp2 ? *dp2 : NULL;
187+
188+		pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
189+		    ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name) :
190+		    strcmp(dent1->d_name, dent2->d_name) ;
191+		if (pos == 0) {
192+			/* file exists in both dirs, diff it */
193+			diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
194+			dp1++;
195+			dp2++;
196+		} else if (pos < 0) {
197+			/* file only in first dir, only diff if -N */
198+			if (Nflag) {
199+				diffit(dent1, path1, dirlen1, dent2, path2,
200+					dirlen2, flags);
201+			} else {
202+				print_only(path1, dirlen1, dent1->d_name);
203+				status |= 1;
204+			}
205+			dp1++;
206+		} else {
207+			/* file only in second dir, only diff if -N or -P */
208+			if (Nflag || Pflag)
209+				diffit(dent2, path1, dirlen1, dent1, path2,
210+					dirlen2, flags);
211+			else {
212+				print_only(path2, dirlen2, dent2->d_name);
213+				status |= 1;
214+			}
215+			dp2++;
216+		}
217+	}
218+
219+closem:
220+	if (dirp1 != NULL) {
221+		for (dp1 = dirp1; dp1 < edp1; dp1++)
222+			free(*dp1);
223+		free(dirp1);
224+	}
225+	if (dirp2 != NULL) {
226+		for (dp2 = dirp2; dp2 < edp2; dp2++)
227+			free(*dp2);
228+		free(dirp2);
229+	}
230+}
231+
232+/*
233+ * Do the actual diff by calling either diffreg() or diffdir().
234+ */
235+static void
236+diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
237+	char *path2, size_t plen2, int flags)
238+{
239+	flags |= D_HEADER;
240+	strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
241+
242+	/*
243+	 * If we are ignoring file case, use dent2s name here if both names are
244+	 * the same apart from case.
245+	 */
246+	if (ignore_file_case && strcasecmp(dp->d_name, dp2->d_name) == 0)
247+		strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
248+	else
249+		strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
250+
251+	if (noderef) {
252+		if (lstat(path1, &stb1) != 0) {
253+			if (!(Nflag || Pflag) || errno != ENOENT) {
254+				warn("%s", path1);
255+				return;
256+			}
257+			flags |= D_EMPTY1;
258+			memset(&stb1, 0, sizeof(stb1));
259+		}
260+
261+		if (lstat(path2, &stb2) != 0) {
262+			if (!Nflag || errno != ENOENT) {
263+				warn("%s", path2);
264+				return;
265+			}
266+			flags |= D_EMPTY2;
267+			memset(&stb2, 0, sizeof(stb2));
268+			stb2.st_mode = stb1.st_mode;
269+		}
270+		if (stb1.st_mode == 0)
271+			stb1.st_mode = stb2.st_mode;
272+		if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
273+			if  (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
274+				char buf1[PATH_MAX];
275+				char buf2[PATH_MAX];
276+				ssize_t len1 = 0;
277+				ssize_t len2 = 0;
278+
279+				len1 = readlink(path1, buf1, sizeof(buf1));
280+				len2 = readlink(path2, buf2, sizeof(buf2));
281+
282+				if (len1 < 0 || len2 < 0) {
283+					perror("reading links");
284+					return;
285+				}
286+				buf1[len1] = '\0';
287+				buf2[len2] = '\0';
288+
289+				if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
290+					printf("Symbolic links %s and %s differ\n",
291+						path1, path2);
292+					status |= 1;
293+				}
294+
295+				return;
296+			}
297+
298+			printf("File %s is a %s while file %s is a %s\n",
299+				path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
300+					(S_ISDIR(stb1.st_mode) ? "directory" :
301+					(S_ISREG(stb1.st_mode) ? "file" : "error")),
302+				path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
303+					(S_ISDIR(stb2.st_mode) ? "directory" :
304+					(S_ISREG(stb2.st_mode) ? "file" : "error")));
305+			status |= 1;
306+			return;
307+		}
308+	} else {
309+		if (stat(path1, &stb1) != 0) {
310+			if (!(Nflag || Pflag) || errno != ENOENT) {
311+				warn("%s", path1);
312+				return;
313+			}
314+			flags |= D_EMPTY1;
315+			memset(&stb1, 0, sizeof(stb1));
316+		}
317+
318+		if (stat(path2, &stb2) != 0) {
319+			if (!Nflag || errno != ENOENT) {
320+				warn("%s", path2);
321+				return;
322+			}
323+			flags |= D_EMPTY2;
324+			memset(&stb2, 0, sizeof(stb2));
325+			stb2.st_mode = stb1.st_mode;
326+		}
327+		if (stb1.st_mode == 0)
328+			stb1.st_mode = stb2.st_mode;
329+	}
330+	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
331+		if (rflag)
332+			diffdir(path1, path2, flags);
333+		else
334+			printf("Common subdirectories: %s and %s\n",
335+			    path1, path2);
336+		return;
337+	}
338+	if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
339+		dp->d_status = D_SKIPPED1;
340+	else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
341+		dp->d_status = D_SKIPPED2;
342+	else
343+		dp->d_status = diffreg(path1, path2, flags, 0);
344+	print_status(dp->d_status, path1, path2, "");
345+}
346+
347+/*
348+ * Returns 1 if the directory entry should be included in the
349+ * diff, else 0.  Checks the excludes list.
350+ */
351+static int
352+selectfile(const struct dirent *dp)
353+{
354+	struct excludes *excl;
355+
356+	if (dp->d_fileno == 0)
357+		return (0);
358+
359+	/* always skip "." and ".." */
360+	if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
361+	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
362+		return (0);
363+
364+	/* check excludes list */
365+	for (excl = excludes_list; excl != NULL; excl = excl->next)
366+		if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
367+			return (0);
368+
369+	return (1);
370+}
371+
372+void
373+print_only(const char *path, size_t dirlen, const char *entry)
374+{
375+	if (dirlen > 1)
376+		dirlen--;
377+	printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
378+}
+1697, -0
   1@@ -0,0 +1,1697 @@
   2+/*	$OpenBSD: diffreg.c,v 1.93 2019/06/28 13:35:00 deraadt Exp $	*/
   3+
   4+/*-
   5+ * SPDX-License-Identifier: BSD-4-Clause
   6+ *
   7+ * Copyright (C) Caldera International Inc.  2001-2002.
   8+ * All rights reserved.
   9+ *
  10+ * Redistribution and use in source and binary forms, with or without
  11+ * modification, are permitted provided that the following conditions
  12+ * are met:
  13+ * 1. Redistributions of source code and documentation must retain the above
  14+ *    copyright notice, this list of conditions and the following disclaimer.
  15+ * 2. Redistributions in binary form must reproduce the above copyright
  16+ *    notice, this list of conditions and the following disclaimer in the
  17+ *    documentation and/or other materials provided with the distribution.
  18+ * 3. All advertising materials mentioning features or use of this software
  19+ *    must display the following acknowledgement:
  20+ *	This product includes software developed or owned by Caldera
  21+ *	International, Inc.
  22+ * 4. Neither the name of Caldera International, Inc. nor the names of other
  23+ *    contributors may be used to endorse or promote products derived from
  24+ *    this software without specific prior written permission.
  25+ *
  26+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
  27+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
  28+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  29+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  30+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
  31+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  32+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  33+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  34+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  35+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  36+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  37+ * POSSIBILITY OF SUCH DAMAGE.
  38+ */
  39+/*-
  40+ * Copyright (c) 1991, 1993
  41+ *	The Regents of the University of California.  All rights reserved.
  42+ *
  43+ * Redistribution and use in source and binary forms, with or without
  44+ * modification, are permitted provided that the following conditions
  45+ * are met:
  46+ * 1. Redistributions of source code must retain the above copyright
  47+ *    notice, this list of conditions and the following disclaimer.
  48+ * 2. Redistributions in binary form must reproduce the above copyright
  49+ *    notice, this list of conditions and the following disclaimer in the
  50+ *    documentation and/or other materials provided with the distribution.
  51+ * 3. Neither the name of the University nor the names of its contributors
  52+ *    may be used to endorse or promote products derived from this software
  53+ *    without specific prior written permission.
  54+ *
  55+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  56+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  57+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  58+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  59+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  60+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  61+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  62+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  63+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  64+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  65+ * SUCH DAMAGE.
  66+ */
  67+
  68+#include <sys/stat.h>
  69+#include <sys/param.h>
  70+
  71+#include <ctype.h>
  72+#include <err.h>
  73+#include <errno.h>
  74+#include <fcntl.h>
  75+#include <limits.h>
  76+#include <math.h>
  77+#include <paths.h>
  78+#include <regex.h>
  79+#include <stdbool.h>
  80+#include <stddef.h>
  81+#include <stdint.h>
  82+#include <stdio.h>
  83+#include <stdlib.h>
  84+#include <string.h>
  85+#include <time.h>
  86+#include <unistd.h>
  87+#include <limits.h>
  88+
  89+#include "util.h"
  90+
  91+#include "pr.h"
  92+#include "diff.h"
  93+#include "xmalloc.h"
  94+
  95+#ifndef roundup
  96+#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
  97+#endif
  98+
  99+/*
 100+ * diff - compare two files.
 101+ */
 102+
 103+/*
 104+ *	Uses an algorithm due to Harold Stone, which finds a pair of longest
 105+ *	identical subsequences in the two files.
 106+ *
 107+ *	The major goal is to generate the match vector J. J[i] is the index of
 108+ *	the line in file1 corresponding to line i file0. J[i] = 0 if there is no
 109+ *	such line in file1.
 110+ *
 111+ *	Lines are hashed so as to work in core. All potential matches are
 112+ *	located by sorting the lines of each file on the hash (called
 113+ *	``value''). In particular, this collects the equivalence classes in
 114+ *	file1 together. Subroutine equiv replaces the value of each line in
 115+ *	file0 by the index of the first element of its matching equivalence in
 116+ *	(the reordered) file1. To save space equiv squeezes file1 into a single
 117+ *	array member in which the equivalence classes are simply concatenated,
 118+ *	except that their first members are flagged by changing sign.
 119+ *
 120+ *	Next the indices that point into member are unsorted into array class
 121+ *	according to the original order of file0.
 122+ *
 123+ *	The cleverness lies in routine stone. This marches through the lines of
 124+ *	file0, developing a vector klist of "k-candidates". At step i
 125+ *	a k-candidate is a matched pair of lines x,y (x in file0 y in file1)
 126+ *	such that there is a common subsequence of length k between the first
 127+ *	i lines of file0 and the first y lines of file1, but there is no such
 128+ *	subsequence for any smaller y. x is the earliest possible mate to y that
 129+ *	occurs in such a subsequence.
 130+ *
 131+ *	Whenever any of the members of the equivalence class of lines in file1
 132+ *	matable to a line in file0 has serial number less than the y of some
 133+ *	k-candidate, that k-candidate with the smallest such y is replaced. The
 134+ *	new k-candidate is chained (via pred) to the current k-1 candidate so
 135+ *	that the actual subsequence can be recovered. When a member has serial
 136+ *	number greater that the y of all k-candidates, the klist is extended. At
 137+ *	the end, the longest subsequence is pulled out and placed in the array J
 138+ *	by unravel.
 139+ *
 140+ *	With J in hand, the matches there recorded are check'ed against reality
 141+ *	to assure that no spurious matches have crept in due to hashing. If they
 142+ *	have, they are broken, and "jackpot" is recorded -- a harmless matter
 143+ *	except that a true match for a spuriously mated line may now be
 144+ *	unnecessarily reported as a change.
 145+ *
 146+ *	Much of the complexity of the program comes simply from trying to
 147+ *	minimize core utilization and maximize the range of doable problems by
 148+ *	dynamically allocating what is needed and reusing what is not. The core
 149+ *	requirements for problems larger than somewhat are (in words)
 150+ *	2*length(file0) + length(file1) + 3*(number of k-candidates installed),
 151+ *	typically about 6n words for files of length n.
 152+ */
 153+
 154+struct cand {
 155+	int	x;
 156+	int	y;
 157+	int	pred;
 158+};
 159+
 160+static struct line {
 161+	int	serial;
 162+	int	value;
 163+} *file[2];
 164+
 165+/*
 166+ * The following struct is used to record change information when
 167+ * doing a "context" or "unified" diff.  (see routine "change" to
 168+ * understand the highly mnemonic field names)
 169+ */
 170+struct context_vec {
 171+	int	a;		/* start line in old file */
 172+	int	b;		/* end line in old file */
 173+	int	c;		/* start line in new file */
 174+	int	d;		/* end line in new file */
 175+};
 176+
 177+enum readhash { RH_BINARY, RH_OK, RH_EOF };
 178+
 179+static FILE	*opentemp(const char *);
 180+static void	 output(char *, FILE *, char *, FILE *, int);
 181+static void	 check(FILE *, FILE *, int);
 182+static void	 range(int, int, const char *);
 183+static void	 uni_range(int, int);
 184+static void	 dump_context_vec(FILE *, FILE *, int);
 185+static void	 dump_unified_vec(FILE *, FILE *, int);
 186+static bool	 prepare(int, FILE *, size_t, int);
 187+static void	 prune(void);
 188+static void	 equiv(struct line *, int, struct line *, int, int *);
 189+static void	 unravel(int);
 190+static void	 unsort(struct line *, int, int *);
 191+static void	 change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
 192+static void	 sort(struct line *, int);
 193+static void	 print_header(const char *, const char *);
 194+static void	 print_space(int, int, int);
 195+static bool	 ignoreline_pattern(char *);
 196+static bool	 ignoreline(char *, bool);
 197+static int	 asciifile(FILE *);
 198+static int	 fetch(long *, int, int, FILE *, int, int, int);
 199+static int	 newcand(int, int, int);
 200+static int	 search(int *, int, int);
 201+static int	 skipline(FILE *);
 202+static int	 stone(int *, int, int *, int *, int);
 203+static enum readhash readhash(FILE *, int, unsigned *);
 204+static int	 files_differ(FILE *, FILE *, int);
 205+static char	*match_function(const long *, int, FILE *);
 206+static char	*preadline(int, size_t, off_t);
 207+
 208+static int	 *J;			/* will be overlaid on class */
 209+static int	 *class;		/* will be overlaid on file[0] */
 210+static int	 *klist;		/* will be overlaid on file[0] after class */
 211+static int	 *member;		/* will be overlaid on file[1] */
 212+static int	 clen;
 213+static int	 inifdef;		/* whether or not we are in a #ifdef block */
 214+static size_t	 len[2];		/* lengths of files in lines */
 215+static size_t	 pref, suff;		/* lengths of prefix and suffix */
 216+static size_t	 slen[2];		/* lengths of files minus pref / suff */
 217+static int	 anychange;
 218+static int	 hw, lpad,rpad;		/* half width and padding */
 219+static int	 edoffset;
 220+static long	*ixnew;		/* will be overlaid on file[1] */
 221+static long	*ixold;		/* will be overlaid on klist */
 222+static struct cand *clist;	/* merely a free storage pot for candidates */
 223+static int	 clistlen;		/* the length of clist */
 224+static struct line *sfile[2];	/* shortened by pruning common prefix/suffix */
 225+static int	(*chrtran)(int);	/* translation table for case-folding */
 226+static struct context_vec *context_vec_start;
 227+static struct context_vec *context_vec_end;
 228+static struct context_vec *context_vec_ptr;
 229+
 230+#define FUNCTION_CONTEXT_SIZE	55
 231+static char lastbuf[FUNCTION_CONTEXT_SIZE];
 232+static int lastline;
 233+static int lastmatchline;
 234+
 235+static int
 236+clow2low(int c)
 237+{
 238+
 239+	return (c);
 240+}
 241+
 242+static int
 243+cup2low(int c)
 244+{
 245+
 246+	return (tolower(c));
 247+}
 248+
 249+int
 250+diffreg(char *file1, char *file2, int flags, int capsicum)
 251+{
 252+	FILE *f1, *f2;
 253+	int i, rval;
 254+	struct pr *pr = NULL;
 255+
 256+	f1 = f2 = NULL;
 257+	rval = D_SAME;
 258+	anychange = 0;
 259+	lastline = 0;
 260+	lastmatchline = 0;
 261+
 262+	/*
 263+	 * In side-by-side mode, we need to print the left column, a
 264+	 * change marker surrounded by padding, and the right column.
 265+	 *
 266+	 * If expanding tabs, we don't care about alignment, so we simply
 267+	 * subtract 3 from the width and divide by two.
 268+	 *
 269+	 * If not expanding tabs, we need to ensure that the right column
 270+	 * is aligned to a tab stop.  We start with the same formula, then
 271+	 * decrement until we reach a size that lets us tab-align the
 272+	 * right column.  We then adjust the width down if necessary for
 273+	 * the padding calculation to work.
 274+	 *
 275+	 * Left padding is half the space left over, rounded down; right
 276+	 * padding is whatever is needed to match the width.
 277+	 */
 278+	if (diff_format == D_SIDEBYSIDE) {
 279+		if (flags & D_EXPANDTABS) {
 280+			if (width > 3) {
 281+				hw = (width - 3) / 2;
 282+			} else {
 283+				/* not enough space */
 284+				hw = 0;
 285+			}
 286+		} else if (width <= 3 || width <= tabsize) {
 287+			/* not enough space */
 288+			hw = 0;
 289+		} else {
 290+			hw = (width - 3) / 2;
 291+			while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
 292+				hw--;
 293+			if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
 294+				width = roundup(hw + 3, tabsize) + hw;
 295+		}
 296+		lpad = (width - hw * 2 - 1) / 2;
 297+		rpad = (width - hw * 2 - 1) - lpad;
 298+	}
 299+
 300+	if (flags & D_IGNORECASE)
 301+		chrtran = cup2low;
 302+	else
 303+		chrtran = clow2low;
 304+	if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
 305+		return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
 306+	if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
 307+		goto closem;
 308+
 309+	if (flags & D_EMPTY1)
 310+		f1 = fopen(_PATH_DEVNULL, "r");
 311+	else {
 312+		if (!S_ISREG(stb1.st_mode)) {
 313+			if ((f1 = opentemp(file1)) == NULL ||
 314+			    fstat(fileno(f1), &stb1) == -1) {
 315+				warn("%s", file1);
 316+				rval = D_ERROR;
 317+				status |= 2;
 318+				goto closem;
 319+			}
 320+		} else if (strcmp(file1, "-") == 0)
 321+			f1 = stdin;
 322+		else
 323+			f1 = fopen(file1, "r");
 324+	}
 325+	if (f1 == NULL) {
 326+		warn("%s", file1);
 327+		rval = D_ERROR;
 328+		status |= 2;
 329+		goto closem;
 330+	}
 331+
 332+	if (flags & D_EMPTY2)
 333+		f2 = fopen(_PATH_DEVNULL, "r");
 334+	else {
 335+		if (!S_ISREG(stb2.st_mode)) {
 336+			if ((f2 = opentemp(file2)) == NULL ||
 337+			    fstat(fileno(f2), &stb2) == -1) {
 338+				warn("%s", file2);
 339+				rval = D_ERROR;
 340+				status |= 2;
 341+				goto closem;
 342+			}
 343+		} else if (strcmp(file2, "-") == 0)
 344+			f2 = stdin;
 345+		else
 346+			f2 = fopen(file2, "r");
 347+	}
 348+	if (f2 == NULL) {
 349+		warn("%s", file2);
 350+		rval = D_ERROR;
 351+		status |= 2;
 352+		goto closem;
 353+	}
 354+
 355+	if (lflag)
 356+		pr = start_pr(file1, file2);
 357+
 358+	(void)capsicum;
 359+
 360+	switch (files_differ(f1, f2, flags)) {
 361+	case 0:
 362+		goto closem;
 363+	case 1:
 364+		break;
 365+	default:
 366+		/* error */
 367+		rval = D_ERROR;
 368+		status |= 2;
 369+		goto closem;
 370+	}
 371+
 372+	if (diff_format == D_BRIEF && ignore_pats == NULL &&
 373+	    (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|
 374+	    D_SKIPBLANKLINES|D_STRIPCR)) == 0)
 375+	{
 376+		rval = D_DIFFER;
 377+		status |= 1;
 378+		goto closem;
 379+	}
 380+	if ((flags & D_FORCEASCII) != 0) {
 381+		(void)prepare(0, f1, stb1.st_size, flags);
 382+		(void)prepare(1, f2, stb2.st_size, flags);
 383+	} else if (!asciifile(f1) || !asciifile(f2) ||
 384+		    !prepare(0, f1, stb1.st_size, flags) ||
 385+		    !prepare(1, f2, stb2.st_size, flags)) {
 386+		rval = D_BINARY;
 387+		status |= 1;
 388+		goto closem;
 389+	}
 390+	if (len[0] > INT_MAX - 2)
 391+		errno = EFBIG, err(1, "%s", file1);
 392+	if (len[1] > INT_MAX - 2)
 393+		errno = EFBIG, err(1, "%s", file2);
 394+
 395+	prune();
 396+	sort(sfile[0], slen[0]);
 397+	sort(sfile[1], slen[1]);
 398+
 399+	member = (int *)file[1];
 400+	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
 401+	member = xreallocarray(member, slen[1] + 2, sizeof(*member));
 402+
 403+	class = (int *)file[0];
 404+	unsort(sfile[0], slen[0], class);
 405+	class = xreallocarray(class, slen[0] + 2, sizeof(*class));
 406+
 407+	klist = xcalloc(slen[0] + 2, sizeof(*klist));
 408+	clen = 0;
 409+	clistlen = 100;
 410+	clist = xcalloc(clistlen, sizeof(*clist));
 411+	i = stone(class, slen[0], member, klist, flags);
 412+	free(member);
 413+	free(class);
 414+
 415+	J = xreallocarray(J, len[0] + 2, sizeof(*J));
 416+	unravel(klist[i]);
 417+	free(clist);
 418+	free(klist);
 419+
 420+	ixold = xreallocarray(ixold, len[0] + 2, sizeof(*ixold));
 421+	ixnew = xreallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
 422+	check(f1, f2, flags);
 423+	output(file1, f1, file2, f2, flags);
 424+
 425+closem:
 426+	if (pr != NULL)
 427+		stop_pr(pr);
 428+	if (anychange) {
 429+		status |= 1;
 430+		if (rval == D_SAME)
 431+			rval = D_DIFFER;
 432+	}
 433+	if (f1 != NULL)
 434+		fclose(f1);
 435+	if (f2 != NULL)
 436+		fclose(f2);
 437+
 438+	return (rval);
 439+}
 440+
 441+/*
 442+ * Check to see if the given files differ.
 443+ * Returns 0 if they are the same, 1 if different, and -1 on error.
 444+ * XXX - could use code from cmp(1) [faster]
 445+ */
 446+static int
 447+files_differ(FILE *f1, FILE *f2, int flags)
 448+{
 449+	char buf1[BUFSIZ], buf2[BUFSIZ];
 450+	size_t i, j;
 451+
 452+	if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
 453+	    (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
 454+		return (1);
 455+
 456+	if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
 457+		return (0);
 458+
 459+	for (;;) {
 460+		i = fread(buf1, 1, sizeof(buf1), f1);
 461+		j = fread(buf2, 1, sizeof(buf2), f2);
 462+		if ((!i && ferror(f1)) || (!j && ferror(f2)))
 463+			return (-1);
 464+		if (i != j)
 465+			return (1);
 466+		if (i == 0)
 467+			return (0);
 468+		if (memcmp(buf1, buf2, i) != 0)
 469+			return (1);
 470+	}
 471+}
 472+
 473+static FILE *
 474+opentemp(const char *f)
 475+{
 476+	char buf[BUFSIZ], tempfile[PATH_MAX];
 477+	ssize_t nread;
 478+	int ifd, ofd;
 479+
 480+	if (strcmp(f, "-") == 0)
 481+		ifd = STDIN_FILENO;
 482+	else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
 483+		return (NULL);
 484+
 485+	(void)strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
 486+
 487+	if ((ofd = mkstemp(tempfile)) == -1) {
 488+		close(ifd);
 489+		return (NULL);
 490+	}
 491+	unlink(tempfile);
 492+	while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
 493+		if (write(ofd, buf, nread) != nread) {
 494+			close(ifd);
 495+			close(ofd);
 496+			return (NULL);
 497+		}
 498+	}
 499+	close(ifd);
 500+	lseek(ofd, (off_t)0, SEEK_SET);
 501+	return (fdopen(ofd, "r"));
 502+}
 503+
 504+static bool
 505+prepare(int i, FILE *fd, size_t filesize, int flags)
 506+{
 507+	struct line *p;
 508+	unsigned h;
 509+	size_t sz, j = 0;
 510+	enum readhash r;
 511+
 512+	rewind(fd);
 513+
 514+	sz = MIN(filesize, SIZE_MAX) / 25;
 515+	if (sz < 100)
 516+		sz = 100;
 517+
 518+	p = xcalloc(sz + 3, sizeof(*p));
 519+	while ((r = readhash(fd, flags, &h)) != RH_EOF) {
 520+		if (r == RH_BINARY)
 521+			return (false);
 522+		if (j == SIZE_MAX)
 523+			break;
 524+		if (j == sz) {
 525+			sz = sz * 3 / 2;
 526+			p = xreallocarray(p, sz + 3, sizeof(*p));
 527+		}
 528+		p[++j].value = h;
 529+	}
 530+
 531+	len[i] = j;
 532+	file[i] = p;
 533+
 534+	return (true);
 535+}
 536+
 537+static void
 538+prune(void)
 539+{
 540+	size_t i, j;
 541+
 542+	for (pref = 0; pref < len[0] && pref < len[1] &&
 543+	    file[0][pref + 1].value == file[1][pref + 1].value;
 544+	    pref++)
 545+		;
 546+	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
 547+	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
 548+	    suff++)
 549+		;
 550+	for (j = 0; j < 2; j++) {
 551+		sfile[j] = file[j] + pref;
 552+		slen[j] = len[j] - pref - suff;
 553+		for (i = 0; i <= slen[j]; i++)
 554+			sfile[j][i].serial = i;
 555+	}
 556+}
 557+
 558+static void
 559+equiv(struct line *a, int n, struct line *b, int m, int *c)
 560+{
 561+	int i, j;
 562+
 563+	i = j = 1;
 564+	while (i <= n && j <= m) {
 565+		if (a[i].value < b[j].value)
 566+			a[i++].value = 0;
 567+		else if (a[i].value == b[j].value)
 568+			a[i++].value = j;
 569+		else
 570+			j++;
 571+	}
 572+	while (i <= n)
 573+		a[i++].value = 0;
 574+	b[m + 1].value = 0;
 575+	j = 0;
 576+	while (++j <= m) {
 577+		c[j] = -b[j].serial;
 578+		while (b[j + 1].value == b[j].value) {
 579+			j++;
 580+			c[j] = b[j].serial;
 581+		}
 582+	}
 583+	c[j] = -1;
 584+}
 585+
 586+static int
 587+stone(int *a, int n, int *b, int *c, int flags)
 588+{
 589+	int i, k, y, j, l;
 590+	int oldc, tc, oldl, sq;
 591+	unsigned numtries, bound;
 592+
 593+	if (flags & D_MINIMAL)
 594+		bound = UINT_MAX;
 595+	else {
 596+		sq = sqrt(n);
 597+		bound = MAX(256, sq);
 598+	}
 599+
 600+	k = 0;
 601+	c[0] = newcand(0, 0, 0);
 602+	for (i = 1; i <= n; i++) {
 603+		j = a[i];
 604+		if (j == 0)
 605+			continue;
 606+		y = -b[j];
 607+		oldl = 0;
 608+		oldc = c[0];
 609+		numtries = 0;
 610+		do {
 611+			if (y <= clist[oldc].y)
 612+				continue;
 613+			l = search(c, k, y);
 614+			if (l != oldl + 1)
 615+				oldc = c[l - 1];
 616+			if (l <= k) {
 617+				if (clist[c[l]].y <= y)
 618+					continue;
 619+				tc = c[l];
 620+				c[l] = newcand(i, y, oldc);
 621+				oldc = tc;
 622+				oldl = l;
 623+				numtries++;
 624+			} else {
 625+				c[l] = newcand(i, y, oldc);
 626+				k++;
 627+				break;
 628+			}
 629+		} while ((y = b[++j]) > 0 && numtries < bound);
 630+	}
 631+	return (k);
 632+}
 633+
 634+static int
 635+newcand(int x, int y, int pred)
 636+{
 637+	struct cand *q;
 638+
 639+	if (clen == clistlen) {
 640+		clistlen = clistlen * 11 / 10;
 641+		clist = xreallocarray(clist, clistlen, sizeof(*clist));
 642+	}
 643+	q = clist + clen;
 644+	q->x = x;
 645+	q->y = y;
 646+	q->pred = pred;
 647+	return (clen++);
 648+}
 649+
 650+static int
 651+search(int *c, int k, int y)
 652+{
 653+	int i, j, l, t;
 654+
 655+	if (clist[c[k]].y < y)	/* quick look for typical case */
 656+		return (k + 1);
 657+	i = 0;
 658+	j = k + 1;
 659+	for (;;) {
 660+		l = (i + j) / 2;
 661+		if (l <= i)
 662+			break;
 663+		t = clist[c[l]].y;
 664+		if (t > y)
 665+			j = l;
 666+		else if (t < y)
 667+			i = l;
 668+		else
 669+			return (l);
 670+	}
 671+	return (l + 1);
 672+}
 673+
 674+static void
 675+unravel(int p)
 676+{
 677+	struct cand *q;
 678+	size_t i;
 679+
 680+	for (i = 0; i <= len[0]; i++)
 681+		J[i] = i <= pref ? i :
 682+		    i > len[0] - suff ? i + len[1] - len[0] : 0;
 683+	for (q = clist + p; q->y != 0; q = clist + q->pred)
 684+		J[q->x + pref] = q->y + pref;
 685+}
 686+
 687+/*
 688+ * Check does double duty:
 689+ *  1. ferret out any fortuitous correspondences due to confounding by
 690+ *     hashing (which result in "jackpot")
 691+ *  2. collect random access indexes to the two files
 692+ */
 693+static void
 694+check(FILE *f1, FILE *f2, int flags)
 695+{
 696+	int i, j, /* jackpot, */ c, d;
 697+	long ctold, ctnew;
 698+
 699+	rewind(f1);
 700+	rewind(f2);
 701+	j = 1;
 702+	ixold[0] = ixnew[0] = 0;
 703+	/* jackpot = 0; */
 704+	ctold = ctnew = 0;
 705+	for (i = 1; i <= (int)len[0]; i++) {
 706+		if (J[i] == 0) {
 707+			ixold[i] = ctold += skipline(f1);
 708+			continue;
 709+		}
 710+		while (j < J[i]) {
 711+			ixnew[j] = ctnew += skipline(f2);
 712+			j++;
 713+		}
 714+		if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) {
 715+			for (;;) {
 716+				c = getc(f1);
 717+				d = getc(f2);
 718+				/*
 719+				 * GNU diff ignores a missing newline
 720+				 * in one file for -b or -w.
 721+				 */
 722+				if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) {
 723+					if (c == EOF && isspace(d)) {
 724+						ctnew++;
 725+						break;
 726+					} else if (isspace(c) && d == EOF) {
 727+						ctold++;
 728+						break;
 729+					}
 730+				}
 731+				ctold++;
 732+				ctnew++;
 733+				if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
 734+					if (c == '\r') {
 735+						if ((c = getc(f1)) == '\n') {
 736+							ctold++;
 737+						} else {
 738+							ungetc(c, f1);
 739+						}
 740+					}
 741+					if (d == '\r') {
 742+						if ((d = getc(f2)) == '\n') {
 743+							ctnew++;
 744+						} else {
 745+							ungetc(d, f2);
 746+						}
 747+					}
 748+					break;
 749+				}
 750+				if ((flags & D_FOLDBLANKS) && isspace(c) &&
 751+				    isspace(d)) {
 752+					do {
 753+						if (c == '\n')
 754+							break;
 755+						ctold++;
 756+					} while (isspace(c = getc(f1)));
 757+					do {
 758+						if (d == '\n')
 759+							break;
 760+						ctnew++;
 761+					} while (isspace(d = getc(f2)));
 762+				} else if (flags & D_IGNOREBLANKS) {
 763+					while (isspace(c) && c != '\n') {
 764+						c = getc(f1);
 765+						ctold++;
 766+					}
 767+					while (isspace(d) && d != '\n') {
 768+						d = getc(f2);
 769+						ctnew++;
 770+					}
 771+				}
 772+				if (chrtran(c) != chrtran(d)) {
 773+					/* jackpot++; */
 774+					J[i] = 0;
 775+					if (c != '\n' && c != EOF)
 776+						ctold += skipline(f1);
 777+					if (d != '\n' && c != EOF)
 778+						ctnew += skipline(f2);
 779+					break;
 780+				}
 781+				if (c == '\n' || c == EOF)
 782+					break;
 783+			}
 784+		} else {
 785+			for (;;) {
 786+				ctold++;
 787+				ctnew++;
 788+				if ((c = getc(f1)) != (d = getc(f2))) {
 789+					/* jackpot++; */
 790+					J[i] = 0;
 791+					if (c != '\n' && c != EOF)
 792+						ctold += skipline(f1);
 793+					if (d != '\n' && c != EOF)
 794+						ctnew += skipline(f2);
 795+					break;
 796+				}
 797+				if (c == '\n' || c == EOF)
 798+					break;
 799+			}
 800+		}
 801+		ixold[i] = ctold;
 802+		ixnew[j] = ctnew;
 803+		j++;
 804+	}
 805+	for (; j <= (int)len[1]; j++) {
 806+		ixnew[j] = ctnew += skipline(f2);
 807+	}
 808+	/*
 809+	 * if (jackpot)
 810+	 *	fprintf(stderr, "jackpot\n");
 811+	 */
 812+}
 813+
 814+/* shellsort CACM #201 */
 815+static void
 816+sort(struct line *a, int n)
 817+{
 818+	struct line *ai, *aim, w;
 819+	int j, m = 0, k;
 820+
 821+	if (n == 0)
 822+		return;
 823+	for (j = 1; j <= n; j *= 2)
 824+		m = 2 * j - 1;
 825+	for (m /= 2; m != 0; m /= 2) {
 826+		k = n - m;
 827+		for (j = 1; j <= k; j++) {
 828+			for (ai = &a[j]; ai > a; ai -= m) {
 829+				aim = &ai[m];
 830+				if (aim < ai)
 831+					break;	/* wraparound */
 832+				if (aim->value > ai[0].value ||
 833+				    (aim->value == ai[0].value &&
 834+					aim->serial > ai[0].serial))
 835+					break;
 836+				w.value = ai[0].value;
 837+				ai[0].value = aim->value;
 838+				aim->value = w.value;
 839+				w.serial = ai[0].serial;
 840+				ai[0].serial = aim->serial;
 841+				aim->serial = w.serial;
 842+			}
 843+		}
 844+	}
 845+}
 846+
 847+static void
 848+unsort(struct line *f, int l, int *b)
 849+{
 850+	int *a, i;
 851+
 852+	a = xcalloc(l + 1, sizeof(*a));
 853+	for (i = 1; i <= l; i++)
 854+		a[f[i].serial] = f[i].value;
 855+	for (i = 1; i <= l; i++)
 856+		b[i] = a[i];
 857+	free(a);
 858+}
 859+
 860+static int
 861+skipline(FILE *f)
 862+{
 863+	int i, c;
 864+
 865+	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
 866+		continue;
 867+	return (i);
 868+}
 869+
 870+static void
 871+output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
 872+{
 873+	int i, j, m, i0, i1, j0, j1, nc;
 874+
 875+	rewind(f1);
 876+	rewind(f2);
 877+	m = len[0];
 878+	J[0] = 0;
 879+	J[m + 1] = len[1] + 1;
 880+	if (diff_format != D_EDIT) {
 881+		for (i0 = 1; i0 <= m; i0 = i1 + 1) {
 882+			while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
 883+				if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
 884+					nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
 885+					print_space(nc, hw - nc + lpad + 1 + rpad, flags);
 886+					fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
 887+					printf("\n");
 888+				}
 889+				i0++;
 890+			}
 891+			j0 = J[i0 - 1] + 1;
 892+			i1 = i0 - 1;
 893+			while (i1 < m && J[i1 + 1] == 0)
 894+				i1++;
 895+			j1 = J[i1 + 1] - 1;
 896+			J[i1] = j1;
 897+
 898+			/*
 899+			 * When using side-by-side, lines from both of the files are
 900+			 * printed. The algorithm used by diff(1) identifies the ranges
 901+			 * in which two files differ.
 902+			 * See the change() function below.
 903+			 * The for loop below consumes the shorter range, whereas one of
 904+			 * the while loops deals with the longer one.
 905+			 */
 906+			if (diff_format == D_SIDEBYSIDE) {
 907+				for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++)
 908+					change(file1, f1, file2, f2, i, i, j, j, &flags);
 909+
 910+				while (i <= i1) {
 911+					change(file1, f1, file2, f2, i, i, j + 1, j, &flags);
 912+					i++;
 913+				}
 914+
 915+				while (j <= j1) {
 916+					change(file1, f1, file2, f2, i + 1, i, j, j, &flags);
 917+					j++;
 918+				}
 919+			} else
 920+				change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
 921+		}
 922+	} else {
 923+		for (i0 = m; i0 >= 1; i0 = i1 - 1) {
 924+			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
 925+				i0--;
 926+			j0 = J[i0 + 1] - 1;
 927+			i1 = i0 + 1;
 928+			while (i1 > 1 && J[i1 - 1] == 0)
 929+				i1--;
 930+			j1 = J[i1 - 1] + 1;
 931+			J[i1] = j1;
 932+			change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
 933+		}
 934+	}
 935+	if (m == 0)
 936+		change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
 937+	if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
 938+		for (;;) {
 939+#define	c i0
 940+			if ((c = getc(f1)) == EOF)
 941+				return;
 942+			printf("%c", c);
 943+		}
 944+#undef c
 945+	}
 946+	if (anychange != 0) {
 947+		if (diff_format == D_CONTEXT)
 948+			dump_context_vec(f1, f2, flags);
 949+		else if (diff_format == D_UNIFIED)
 950+			dump_unified_vec(f1, f2, flags);
 951+	}
 952+}
 953+
 954+static void
 955+range(int a, int b, const char *separator)
 956+{
 957+	printf("%d", a > b ? b : a);
 958+	if (a < b)
 959+		printf("%s%d", separator, b);
 960+}
 961+
 962+static void
 963+uni_range(int a, int b)
 964+{
 965+	if (a < b)
 966+		printf("%d,%d", a, b - a + 1);
 967+	else if (a == b)
 968+		printf("%d", b);
 969+	else
 970+		printf("%d,0", b);
 971+}
 972+
 973+static char *
 974+preadline(int fd, size_t rlen, off_t off)
 975+{
 976+	char *line;
 977+	ssize_t nr;
 978+
 979+	line = xmalloc(rlen + 1);
 980+	if ((nr = pread(fd, line, rlen, off)) == -1)
 981+		err(2, "preadline");
 982+	if (nr > 0 && line[nr-1] == '\n')
 983+		nr--;
 984+	line[nr] = '\0';
 985+	return (line);
 986+}
 987+
 988+static bool
 989+ignoreline_pattern(char *line)
 990+{
 991+	int ret;
 992+
 993+	ret = regexec(&ignore_re, line, 0, NULL, 0);
 994+	return (ret == 0);	/* if it matched, it should be ignored. */
 995+}
 996+
 997+static bool
 998+ignoreline(char *line, bool skip_blanks)
 999+{
1000+
1001+	if (skip_blanks && *line == '\0')
1002+		return (true);
1003+	if (ignore_pats != NULL && ignoreline_pattern(line))
1004+		return (true);
1005+	return (false);
1006+}
1007+
1008+/*
1009+ * Indicate that there is a difference between lines a and b of the from file
1010+ * to get to lines c to d of the to file.  If a is greater then b then there
1011+ * are no lines in the from file involved and this means that there were
1012+ * lines appended (beginning at b).  If c is greater than d then there are
1013+ * lines missing from the to file.
1014+ */
1015+static void
1016+change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
1017+    int *pflags)
1018+{
1019+	static size_t max_context = 64;
1020+	long curpos;
1021+	int i, nc;
1022+	const char *walk;
1023+	bool skip_blanks, ignore;
1024+
1025+	skip_blanks = (*pflags & D_SKIPBLANKLINES);
1026+restart:
1027+	if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) &&
1028+	    a > b && c > d)
1029+		return;
1030+	if (ignore_pats != NULL || skip_blanks) {
1031+		char *line;
1032+		/*
1033+		 * All lines in the change, insert, or delete must match an ignore
1034+		 * pattern for the change to be ignored.
1035+		 */
1036+		if (a <= b) {		/* Changes and deletes. */
1037+			for (i = a; i <= b; i++) {
1038+				line = preadline(fileno(f1),
1039+				    ixold[i] - ixold[i - 1], ixold[i - 1]);
1040+				ignore = ignoreline(line, skip_blanks);
1041+				free(line);
1042+				if (!ignore)
1043+					goto proceed;
1044+			}
1045+		}
1046+		if (a > b || c <= d) {	/* Changes and inserts. */
1047+			for (i = c; i <= d; i++) {
1048+				line = preadline(fileno(f2),
1049+				    ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1050+				ignore = ignoreline(line, skip_blanks);
1051+				free(line);
1052+				if (!ignore)
1053+					goto proceed;
1054+			}
1055+		}
1056+		return;
1057+	}
1058+proceed:
1059+	if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1060+		printf("%s %s %s\n", diffargs, file1, file2);
1061+		*pflags &= ~D_HEADER;
1062+	}
1063+	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1064+		/*
1065+		 * Allocate change records as needed.
1066+		 */
1067+		if (context_vec_start == NULL ||
1068+		    context_vec_ptr == context_vec_end - 1) {
1069+			ptrdiff_t offset = -1;
1070+
1071+			if (context_vec_start != NULL)
1072+				offset = context_vec_ptr - context_vec_start;
1073+			max_context <<= 1;
1074+			context_vec_start = xreallocarray(context_vec_start,
1075+			    max_context, sizeof(*context_vec_start));
1076+			context_vec_end = context_vec_start + max_context;
1077+			context_vec_ptr = context_vec_start + offset;
1078+		}
1079+		if (anychange == 0) {
1080+			/*
1081+			 * Print the context/unidiff header first time through.
1082+			 */
1083+			print_header(file1, file2);
1084+			anychange = 1;
1085+		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
1086+		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
1087+			/*
1088+			 * If this change is more than 'diff_context' lines from the
1089+			 * previous change, dump the record and reset it.
1090+			 */
1091+			if (diff_format == D_CONTEXT)
1092+				dump_context_vec(f1, f2, *pflags);
1093+			else
1094+				dump_unified_vec(f1, f2, *pflags);
1095+		}
1096+		context_vec_ptr++;
1097+		context_vec_ptr->a = a;
1098+		context_vec_ptr->b = b;
1099+		context_vec_ptr->c = c;
1100+		context_vec_ptr->d = d;
1101+		return;
1102+	}
1103+	if (anychange == 0)
1104+		anychange = 1;
1105+	switch (diff_format) {
1106+	case D_BRIEF:
1107+		return;
1108+	case D_NORMAL:
1109+	case D_EDIT:
1110+		range(a, b, ",");
1111+		printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1112+		if (diff_format == D_NORMAL)
1113+			range(c, d, ",");
1114+		printf("\n");
1115+		break;
1116+	case D_REVERSE:
1117+		printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1118+		range(a, b, " ");
1119+		printf("\n");
1120+		break;
1121+	case D_NREVERSE:
1122+		if (a > b)
1123+			printf("a%d %d\n", b, d - c + 1);
1124+		else {
1125+			printf("d%d %d\n", a, b - a + 1);
1126+			if (!(c > d))
1127+				/* add changed lines */
1128+				printf("a%d %d\n", b, d - c + 1);
1129+		}
1130+		break;
1131+	}
1132+	if (diff_format == D_GFORMAT) {
1133+		curpos = ftell(f1);
1134+		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1135+		nc = ixold[a > b ? b : a - 1] - curpos;
1136+		for (i = 0; i < nc; i++)
1137+			printf("%c", getc(f1));
1138+		for (walk = group_format; *walk != '\0'; walk++) {
1139+			if (*walk == '%') {
1140+				walk++;
1141+				switch (*walk) {
1142+				case '<':
1143+					fetch(ixold, a, b, f1, '<', 1, *pflags);
1144+					break;
1145+				case '>':
1146+					fetch(ixnew, c, d, f2, '>', 0, *pflags);
1147+					break;
1148+				default:
1149+					printf("%%%c", *walk);
1150+					break;
1151+				}
1152+				continue;
1153+			}
1154+			printf("%c", *walk);
1155+		}
1156+	}
1157+	if (diff_format == D_SIDEBYSIDE) {
1158+		if (color && a > b)
1159+			printf("\033[%sm", add_code);
1160+		else if (color && c > d)
1161+			printf("\033[%sm", del_code);
1162+		if (a > b) {
1163+			print_space(0, hw + lpad, *pflags);
1164+		} else {
1165+			nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
1166+			print_space(nc, hw - nc + lpad, *pflags);
1167+		}
1168+		if (color && a > b)
1169+			printf("\033[%sm", add_code);
1170+		else if (color && c > d)
1171+			printf("\033[%sm", del_code);
1172+		printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
1173+		if (color && c > d)
1174+			printf("\033[m");
1175+		print_space(hw + lpad + 1, rpad, *pflags);
1176+		fetch(ixnew, c, d, f2, '\0', 0, *pflags);
1177+		printf("\n");
1178+	}
1179+	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
1180+		fetch(ixold, a, b, f1, '<', 1, *pflags);
1181+		if (a <= b && c <= d && diff_format == D_NORMAL)
1182+			printf("---\n");
1183+	}
1184+	if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
1185+		fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
1186+	if (edoffset != 0 && diff_format == D_EDIT) {
1187+		/*
1188+		 * A non-zero edoffset value for D_EDIT indicates that the last line
1189+		 * printed was a bare dot (".") that has been escaped as ".." to
1190+		 * prevent ed(1) from misinterpreting it.  We have to add a
1191+		 * substitute command to change this back and restart where we left
1192+		 * off.
1193+		 */
1194+		printf(".\n");
1195+		printf("%ds/.//\n", a + edoffset - 1);
1196+		b = a + edoffset - 1;
1197+		a = b + 1;
1198+		c += edoffset;
1199+		goto restart;
1200+	}
1201+	if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
1202+		printf(".\n");
1203+	if (inifdef) {
1204+		printf("#endif /* %s */\n", ifdefname);
1205+		inifdef = 0;
1206+	}
1207+}
1208+
1209+static int
1210+fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
1211+{
1212+	int i, j, c, lastc, col, nc, newcol;
1213+
1214+	edoffset = 0;
1215+	nc = 0;
1216+	col = 0;
1217+	/*
1218+	 * When doing #ifdef's, copy down to current line
1219+	 * if this is the first file, so that stuff makes it to output.
1220+	 */
1221+	if ((diff_format == D_IFDEF) && oldfile) {
1222+		long curpos = ftell(lb);
1223+		/* print through if append (a>b), else to (nb: 0 vs 1 orig) */
1224+		nc = f[a > b ? b : a - 1] - curpos;
1225+		for (i = 0; i < nc; i++)
1226+			printf("%c", getc(lb));
1227+	}
1228+	if (a > b)
1229+		return (0);
1230+	if (diff_format == D_IFDEF) {
1231+		if (inifdef) {
1232+			printf("#else /* %s%s */\n",
1233+			    oldfile == 1 ? "!" : "", ifdefname);
1234+		} else {
1235+			if (oldfile)
1236+				printf("#ifndef %s\n", ifdefname);
1237+			else
1238+				printf("#ifdef %s\n", ifdefname);
1239+		}
1240+		inifdef = 1 + oldfile;
1241+	}
1242+	for (i = a; i <= b; i++) {
1243+		fseek(lb, f[i - 1], SEEK_SET);
1244+		nc = f[i] - f[i - 1];
1245+		if (diff_format == D_SIDEBYSIDE && hw < nc)
1246+			nc = hw;
1247+		if (diff_format != D_IFDEF && diff_format != D_GFORMAT &&
1248+		    ch != '\0') {
1249+			if (color && (ch == '>' || ch == '+'))
1250+				printf("\033[%sm", add_code);
1251+			else if (color && (ch == '<' || ch == '-'))
1252+				printf("\033[%sm", del_code);
1253+			printf("%c", ch);
1254+			if (Tflag && (diff_format == D_NORMAL ||
1255+			    diff_format == D_CONTEXT ||
1256+			    diff_format == D_UNIFIED))
1257+				printf("\t");
1258+			else if (diff_format != D_UNIFIED)
1259+				printf(" ");
1260+		}
1261+		col = j = 0;
1262+		lastc = '\0';
1263+		while (j < nc && (hw == 0 || col < hw)) {
1264+			c = getc(lb);
1265+			if (flags & D_STRIPCR && c == '\r') {
1266+				if ((c = getc(lb)) == '\n')
1267+					j++;
1268+				else {
1269+					ungetc(c, lb);
1270+					c = '\r';
1271+				}
1272+			}
1273+			if (c == EOF) {
1274+				if (diff_format == D_EDIT ||
1275+				    diff_format == D_REVERSE ||
1276+				    diff_format == D_NREVERSE)
1277+					warnx("No newline at end of file");
1278+				else
1279+					printf("\n\\ No newline at end of file\n");
1280+				return (col);
1281+			}
1282+			/*
1283+			 * when using --side-by-side, col needs to be increased
1284+			 * in any case to keep the columns aligned
1285+			 */
1286+			if (c == '\t') {
1287+				/*
1288+				 * Calculate where the tab would bring us.
1289+				 * If it would take us to the end of the
1290+				 * column, either clip it (if expanding
1291+				 * tabs) or return right away (if not).
1292+				 */
1293+				newcol = roundup(col + 1, tabsize);
1294+				if ((flags & D_EXPANDTABS) == 0) {
1295+					if (hw > 0 && newcol >= hw)
1296+						return (col);
1297+					printf("\t");
1298+				} else {
1299+					if (hw > 0 && newcol > hw)
1300+						newcol = hw;
1301+					printf("%*s", newcol - col, "");
1302+				}
1303+				col = newcol;
1304+			} else {
1305+				if (diff_format == D_EDIT && j == 1 && c == '\n' &&
1306+				    lastc == '.') {
1307+					/*
1308+					 * Don't print a bare "." line since that will confuse
1309+					 * ed(1). Print ".." instead and set the, global variable
1310+					 * edoffset to an offset from which to restart. The
1311+					 * caller must check the value of edoffset
1312+					 */
1313+					printf(".\n");
1314+					edoffset = i - a + 1;
1315+					return (edoffset);
1316+				}
1317+				/* when side-by-side, do not print a newline */
1318+				if (diff_format != D_SIDEBYSIDE || c != '\n') {
1319+					if (color && c == '\n')
1320+						printf("\033[m%c", c);
1321+					else
1322+						printf("%c", c);
1323+					col++;
1324+				}
1325+			}
1326+
1327+			j++;
1328+			lastc = c;
1329+		}
1330+	}
1331+	if (color && diff_format == D_SIDEBYSIDE)
1332+		printf("\033[m");
1333+	return (col);
1334+}
1335+
1336+/*
1337+ * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
1338+ */
1339+static enum readhash
1340+readhash(FILE *f, int flags, unsigned *hash)
1341+{
1342+	int i, t, space;
1343+	unsigned sum;
1344+
1345+	sum = 1;
1346+	space = 0;
1347+	for (i = 0;;) {
1348+		switch (t = getc(f)) {
1349+		case '\0':
1350+			if ((flags & D_FORCEASCII) == 0)
1351+				return (RH_BINARY);
1352+			goto hashchar;
1353+		case '\r':
1354+			if (flags & D_STRIPCR) {
1355+				t = getc(f);
1356+				if (t == '\n')
1357+					break;
1358+				ungetc(t, f);
1359+			}
1360+			/* FALLTHROUGH */
1361+		case '\t':
1362+		case '\v':
1363+		case '\f':
1364+		case ' ':
1365+			if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) {
1366+				space++;
1367+				continue;
1368+			}
1369+			/* FALLTHROUGH */
1370+		default:
1371+		hashchar:
1372+			if (space && (flags & D_IGNOREBLANKS) == 0) {
1373+				i++;
1374+				space = 0;
1375+			}
1376+			sum = sum * 127 + chrtran(t);
1377+			i++;
1378+			continue;
1379+		case EOF:
1380+			if (i == 0)
1381+				return (RH_EOF);
1382+			/* FALLTHROUGH */
1383+		case '\n':
1384+			break;
1385+		}
1386+		break;
1387+	}
1388+	*hash = sum;
1389+	return (RH_OK);
1390+}
1391+
1392+static int
1393+asciifile(FILE *f)
1394+{
1395+	unsigned char buf[BUFSIZ];
1396+	size_t cnt;
1397+
1398+	if (f == NULL)
1399+		return (1);
1400+
1401+	rewind(f);
1402+	cnt = fread(buf, 1, sizeof(buf), f);
1403+	return (memchr(buf, '\0', cnt) == NULL);
1404+}
1405+
1406+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0)
1407+
1408+static char *
1409+match_function(const long *f, int pos, FILE *fp)
1410+{
1411+	char buf[FUNCTION_CONTEXT_SIZE];
1412+	size_t nc;
1413+	int last = lastline;
1414+	const char *state = NULL;
1415+
1416+	lastline = pos;
1417+	for (; pos > last; pos--) {
1418+		fseek(fp, f[pos - 1], SEEK_SET);
1419+		nc = f[pos] - f[pos - 1];
1420+		if (nc >= sizeof(buf))
1421+			nc = sizeof(buf) - 1;
1422+		nc = fread(buf, 1, nc, fp);
1423+		if (nc == 0)
1424+			continue;
1425+		buf[nc] = '\0';
1426+		buf[strcspn(buf, "\n")] = '\0';
1427+		if (most_recent_pat != NULL) {
1428+			int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
1429+
1430+			if (ret != 0)
1431+				continue;
1432+			strlcpy(lastbuf, buf, sizeof(lastbuf));
1433+			lastmatchline = pos;
1434+			return (lastbuf);
1435+		} else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$'
1436+			|| buf[0] == '-' || buf[0] == '+') {
1437+			if (begins_with(buf, "private:")) {
1438+				if (!state)
1439+					state = " (private)";
1440+			} else if (begins_with(buf, "protected:")) {
1441+				if (!state)
1442+					state = " (protected)";
1443+			} else if (begins_with(buf, "public:")) {
1444+				if (!state)
1445+					state = " (public)";
1446+			} else {
1447+				strlcpy(lastbuf, buf, sizeof(lastbuf));
1448+				if (state)
1449+					strlcat(lastbuf, state, sizeof(lastbuf));
1450+				lastmatchline = pos;
1451+				return (lastbuf);
1452+			}
1453+		}
1454+	}
1455+	return (lastmatchline > 0 ? lastbuf : NULL);
1456+}
1457+
1458+/* dump accumulated "context" diff changes */
1459+static void
1460+dump_context_vec(FILE *f1, FILE *f2, int flags)
1461+{
1462+	struct context_vec *cvp = context_vec_start;
1463+	int lowa, upb, lowc, upd, do_output;
1464+	int a, b, c, d;
1465+	char ch, *f;
1466+
1467+	if (context_vec_start > context_vec_ptr)
1468+		return;
1469+
1470+	b = d = 0;		/* gcc */
1471+	lowa = MAX(1, cvp->a - diff_context);
1472+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
1473+	lowc = MAX(1, cvp->c - diff_context);
1474+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
1475+
1476+	printf("***************");
1477+	if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
1478+		f = match_function(ixold, cvp->a - 1, f1);
1479+		if (f != NULL)
1480+			printf(" %s", f);
1481+	}
1482+	printf("\n*** ");
1483+	range(lowa, upb, ",");
1484+	printf(" ****\n");
1485+
1486+	/*
1487+	 * Output changes to the "old" file.  The first loop suppresses
1488+	 * output if there were no changes to the "old" file (we'll see
1489+	 * the "old" lines as context in the "new" list).
1490+	 */
1491+	do_output = 0;
1492+	for (; cvp <= context_vec_ptr; cvp++)
1493+		if (cvp->a <= cvp->b) {
1494+			cvp = context_vec_start;
1495+			do_output++;
1496+			break;
1497+		}
1498+	if (do_output) {
1499+		while (cvp <= context_vec_ptr) {
1500+			a = cvp->a;
1501+			b = cvp->b;
1502+			c = cvp->c;
1503+			d = cvp->d;
1504+
1505+			if (a <= b && c <= d)
1506+				ch = 'c';
1507+			else
1508+				ch = (a <= b) ? 'd' : 'a';
1509+
1510+			if (ch == 'a')
1511+				fetch(ixold, lowa, b, f1, ' ', 0, flags);
1512+			else {
1513+				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1514+				fetch(ixold, a, b, f1,
1515+				    ch == 'c' ? '!' : '-', 0, flags);
1516+			}
1517+			lowa = b + 1;
1518+			cvp++;
1519+		}
1520+		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
1521+	}
1522+	/* output changes to the "new" file */
1523+	printf("--- ");
1524+	range(lowc, upd, ",");
1525+	printf(" ----\n");
1526+
1527+	do_output = 0;
1528+	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
1529+		if (cvp->c <= cvp->d) {
1530+			cvp = context_vec_start;
1531+			do_output++;
1532+			break;
1533+		}
1534+	if (do_output) {
1535+		while (cvp <= context_vec_ptr) {
1536+			a = cvp->a;
1537+			b = cvp->b;
1538+			c = cvp->c;
1539+			d = cvp->d;
1540+
1541+			if (a <= b && c <= d)
1542+				ch = 'c';
1543+			else
1544+				ch = (a <= b) ? 'd' : 'a';
1545+
1546+			if (ch == 'd')
1547+				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
1548+			else {
1549+				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1550+				fetch(ixnew, c, d, f2,
1551+				    ch == 'c' ? '!' : '+', 0, flags);
1552+			}
1553+			lowc = d + 1;
1554+			cvp++;
1555+		}
1556+		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1557+	}
1558+	context_vec_ptr = context_vec_start - 1;
1559+}
1560+
1561+/* dump accumulated "unified" diff changes */
1562+static void
1563+dump_unified_vec(FILE *f1, FILE *f2, int flags)
1564+{
1565+	struct context_vec *cvp = context_vec_start;
1566+	int lowa, upb, lowc, upd;
1567+	int a, b, c, d;
1568+	char ch, *f;
1569+
1570+	if (context_vec_start > context_vec_ptr)
1571+		return;
1572+
1573+	b = d = 0;		/* gcc */
1574+	lowa = MAX(1, cvp->a - diff_context);
1575+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
1576+	lowc = MAX(1, cvp->c - diff_context);
1577+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
1578+
1579+	printf("@@ -");
1580+	uni_range(lowa, upb);
1581+	printf(" +");
1582+	uni_range(lowc, upd);
1583+	printf(" @@");
1584+	if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
1585+		f = match_function(ixold, cvp->a - 1, f1);
1586+		if (f != NULL)
1587+			printf(" %s", f);
1588+	}
1589+	printf("\n");
1590+
1591+	/*
1592+	 * Output changes in "unified" diff format--the old and new lines
1593+	 * are printed together.
1594+	 */
1595+	for (; cvp <= context_vec_ptr; cvp++) {
1596+		a = cvp->a;
1597+		b = cvp->b;
1598+		c = cvp->c;
1599+		d = cvp->d;
1600+
1601+		/*
1602+		 * c: both new and old changes
1603+		 * d: only changes in the old file
1604+		 * a: only changes in the new file
1605+		 */
1606+		if (a <= b && c <= d)
1607+			ch = 'c';
1608+		else
1609+			ch = (a <= b) ? 'd' : 'a';
1610+
1611+		switch (ch) {
1612+		case 'c':
1613+			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1614+			fetch(ixold, a, b, f1, '-', 0, flags);
1615+			fetch(ixnew, c, d, f2, '+', 0, flags);
1616+			break;
1617+		case 'd':
1618+			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
1619+			fetch(ixold, a, b, f1, '-', 0, flags);
1620+			break;
1621+		case 'a':
1622+			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
1623+			fetch(ixnew, c, d, f2, '+', 0, flags);
1624+			break;
1625+		}
1626+		lowa = b + 1;
1627+		lowc = d + 1;
1628+	}
1629+	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
1630+
1631+	context_vec_ptr = context_vec_start - 1;
1632+}
1633+
1634+static void
1635+print_header(const char *file1, const char *file2)
1636+{
1637+	const char *time_format;
1638+	char buf[256];
1639+	struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
1640+	int nsec1 = stb1.st_mtim.tv_nsec;
1641+	int nsec2 = stb2.st_mtim.tv_nsec;
1642+
1643+	time_format = "%Y-%m-%d %H:%M:%S";
1644+
1645+	if (cflag)
1646+		time_format = "%c";
1647+	tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
1648+	tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
1649+	if (label[0] != NULL)
1650+		printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---",
1651+		    label[0]);
1652+	else {
1653+		strftime(buf, sizeof(buf), time_format, tm_ptr1);
1654+		printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---",
1655+		    file1, buf);
1656+		if (!cflag) {
1657+			strftime(buf, sizeof(buf), "%z", tm_ptr1);
1658+			printf(".%.9d %s", nsec1, buf);
1659+		}
1660+		printf("\n");
1661+	}
1662+	if (label[1] != NULL)
1663+		printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++",
1664+		    label[1]);
1665+	else {
1666+		strftime(buf, sizeof(buf), time_format, tm_ptr2);
1667+		printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++",
1668+		    file2, buf);
1669+		if (!cflag) {
1670+			strftime(buf, sizeof(buf), "%z", tm_ptr2);
1671+			printf(".%.9d %s", nsec2, buf);
1672+		}
1673+		printf("\n");
1674+	}
1675+}
1676+
1677+/*
1678+ * Prints n number of space characters either by using tab
1679+ * or single space characters.
1680+ * nc is the preceding number of characters
1681+ */
1682+static void
1683+print_space(int nc, int n, int flags)
1684+{
1685+	int col, newcol, tabstop;
1686+
1687+	col = nc;
1688+	newcol = nc + n;
1689+	/* first, use tabs if allowed */
1690+	if ((flags & D_EXPANDTABS) == 0) {
1691+		while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
1692+			printf("\t");
1693+			col = tabstop;
1694+		}
1695+	}
1696+	/* finish with spaces */
1697+	printf("%*s", newcol - col, "");
1698+}
+155, -0
  1@@ -0,0 +1,155 @@
  2+/*-
  3+ * Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
  4+ * All rights reserved.
  5+ *
  6+ * Redistribution and use in source and binary forms, with or without
  7+ * modification, are permitted provided that the following conditions
  8+ * are met:
  9+ * 1. Redistributions of source code must retain the above copyright
 10+ *    notice, this list of conditions and the following disclaimer
 11+ *    in this position and unchanged.
 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+ *
 16+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 17+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 18+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 19+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 20+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 21+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 22+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 23+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 24+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 25+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 26+ */
 27+
 28+#include <sys/cdefs.h>
 29+#include <sys/wait.h>
 30+
 31+#include <err.h>
 32+#include <paths.h>
 33+#include <signal.h>
 34+#include <stdio.h>
 35+#include <stdlib.h>
 36+#include <unistd.h>
 37+#include <fcntl.h>
 38+#include <errno.h>
 39+#include <poll.h>
 40+
 41+#include "util.h"
 42+
 43+#include "pr.h"
 44+#include "diff.h"
 45+#include "xmalloc.h"
 46+
 47+#define _PATH_PR "/usr/bin/pr"
 48+
 49+static int sigpipe[2] = {-1, -1};
 50+static struct pollfd poll_fd;
 51+
 52+static void
 53+handle_sig(int signo)
 54+{
 55+	write(sigpipe[1], &signo, sizeof(signo));
 56+}
 57+
 58+struct pr *
 59+start_pr(char *file1, char *file2)
 60+{
 61+	int pfd[2];
 62+	pid_t pid;
 63+	char *header;
 64+	struct pr *pr;
 65+
 66+	pr = xcalloc(1, sizeof(*pr));
 67+
 68+	xasprintf(&header, "%s %s %s", diffargs, file1, file2);
 69+	signal(SIGPIPE, SIG_IGN);
 70+	fflush(stdout);
 71+	rewind(stdout);
 72+	if (pipe(pfd) == -1)
 73+		err(2, "pipe");
 74+	if (sigpipe[0] < 0) {
 75+		if (pipe(sigpipe) == -1)
 76+			err(2, "pipe");
 77+		if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC) == -1)
 78+			err(2, "fcntl");
 79+		if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC) == -1)
 80+			err(2, "fcntl");
 81+		if (signal(SIGCHLD, handle_sig) == SIG_ERR)
 82+			err(2, "signal");
 83+		poll_fd.fd = sigpipe[0];
 84+		poll_fd.events = POLLIN;
 85+	}
 86+	poll_fd.revents = 0;
 87+	switch ((pid = fork())) {
 88+	case -1:
 89+		status |= 2;
 90+		free(header);
 91+		err(2, "No more processes");
 92+	case 0:
 93+		/* child */
 94+		if (pfd[0] != STDIN_FILENO) {
 95+			dup2(pfd[0], STDIN_FILENO);
 96+			close(pfd[0]);
 97+		}
 98+		close(pfd[1]);
 99+		execl(_PATH_PR, _PATH_PR, "-h", header, (char *)0);
100+		_exit(127);
101+	default:
102+
103+		/* parent */
104+		if (pfd[1] != STDOUT_FILENO) {
105+			pr->ostdout = dup(STDOUT_FILENO);
106+			dup2(pfd[1], STDOUT_FILENO);
107+			close(pfd[1]);
108+		}
109+		close(pfd[0]);
110+		rewind(stdout);
111+		free(header);
112+		pr->cpid = pid;
113+	}
114+	return (pr);
115+}
116+
117+/* close the pipe to pr and restore stdout */
118+void
119+stop_pr(struct pr *pr)
120+{
121+	int wstatus;
122+	int done = 0;
123+
124+	if (pr == NULL)
125+		return;
126+
127+	fflush(stdout);
128+	if (pr->ostdout != STDOUT_FILENO) {
129+		close(STDOUT_FILENO);
130+		dup2(pr->ostdout, STDOUT_FILENO);
131+		close(pr->ostdout);
132+	}
133+	while (!done) {
134+		pid_t wpid;
135+		int npe = poll(&poll_fd, 1, -1);
136+		if (npe == -1) {
137+			if (errno == EINTR) continue;
138+			err(2, "poll");
139+		}
140+		if (poll_fd.revents != POLLIN)
141+			continue;
142+		if (read(poll_fd.fd, &npe, sizeof(npe)) < 0)
143+			err(2, "read");
144+		while ((wpid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
145+			if (wpid != pr->cpid) continue;
146+			if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
147+				errx(2, "pr exited abnormally");
148+			else if (WIFSIGNALED(wstatus))
149+				errx(2, "pr killed by signal %d",
150+				    WTERMSIG(wstatus));
151+			done = 1;
152+			break;
153+		}
154+	}
155+	free(pr);
156+}
+33, -0
 1@@ -0,0 +1,33 @@
 2+/*-
 3+ * Copyright (c) 2017 Baptiste Daroussin <bapt@FreeBSD.org>
 4+ * All rights reserved.
 5+ *
 6+ * Redistribution and use in source and binary forms, with or without
 7+ * modification, are permitted provided that the following conditions
 8+ * are met:
 9+ * 1. Redistributions of source code must retain the above copyright
10+ *    notice, this list of conditions and the following disclaimer
11+ *    in this position and unchanged.
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+ *
16+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+ */
27+
28+struct pr {
29+	int ostdout;
30+	pid_t cpid;
31+};
32+
33+struct pr *start_pr(char *file1, char *file2);
34+void stop_pr(struct pr *);
+96, -0
 1@@ -0,0 +1,96 @@
 2+/* $OpenBSD: xmalloc.c,v 1.10 2019/06/28 05:44:09 deraadt Exp $ */
 3+/*
 4+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
 5+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 6+ *                    All rights reserved
 7+ * Versions of malloc and friends that check their results, and never return
 8+ * failure (they call fatal if they encounter an error).
 9+ *
10+ * As far as I am concerned, the code I have written for this software
11+ * can be used freely for any purpose.  Any derived versions of this
12+ * software must be clearly marked as such, and if the derived work is
13+ * incompatible with the protocol description in the RFC file, it must be
14+ * called by a name other than "ssh" or "Secure Shell".
15+ */
16+
17+#include <err.h>
18+#include <stdarg.h>
19+#include <stdint.h>
20+#include <stdio.h>
21+#include <stdlib.h>
22+#include <string.h>
23+
24+#include "util.h"
25+
26+#include "xmalloc.h"
27+
28+void *
29+xmalloc(size_t size)
30+{
31+	void *ptr;
32+
33+	if (size == 0)
34+		errx(2, "xmalloc: zero size");
35+	ptr = malloc(size);
36+	if (ptr == NULL)
37+		err(2, "xmalloc: allocating %zu bytes", size);
38+	return ptr;
39+}
40+
41+void *
42+xcalloc(size_t nmemb, size_t size)
43+{
44+	void *ptr;
45+
46+	ptr = calloc(nmemb, size);
47+	if (ptr == NULL)
48+		err(2, "xcalloc: allocating %zu * %zu bytes", nmemb, size);
49+	return ptr;
50+}
51+
52+void *
53+xreallocarray(void *ptr, size_t nmemb, size_t size)
54+{
55+	void *new_ptr;
56+
57+	new_ptr = reallocarray(ptr, nmemb, size);
58+	if (new_ptr == NULL)
59+		err(2, "xreallocarray: allocating %zu * %zu bytes",
60+		    nmemb, size);
61+	return new_ptr;
62+}
63+
64+char *
65+xstrdup(const char *str)
66+{
67+	char *cp;
68+
69+	if ((cp = strdup(str)) == NULL)
70+		err(2, "xstrdup");
71+	return cp;
72+}
73+
74+int
75+xasprintf(char **ret, const char *fmt, ...)
76+{
77+	va_list ap;
78+	va_list ap2;
79+	int i;
80+	size_t n;
81+
82+	va_start(ap, fmt);
83+	va_copy(ap2, ap);
84+	i = vsnprintf(NULL, 0, fmt, ap);
85+	if (i < 0)
86+		err(2, "xasprintf");
87+	n = (size_t)i + 1;
88+	*ret = xmalloc(n);
89+	i = vsnprintf(*ret, n, fmt, ap2);
90+	va_end(ap2);
91+	va_end(ap);
92+
93+	if (i == -1)
94+		err(2, "xasprintf");
95+
96+	return i;
97+}
+32, -0
 1@@ -0,0 +1,32 @@
 2+/* $OpenBSD: xmalloc.h,v 1.4 2015/11/12 16:30:30 mmcc Exp $ */
 3+
 4+/*
 5+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
 6+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 7+ *                    All rights reserved
 8+ * Created: Mon Mar 20 22:09:17 1995 ylo
 9+ *
10+ * Versions of malloc and friends that check their results, and never return
11+ * failure (they call fatal if they encounter an error).
12+ *
13+ * As far as I am concerned, the code I have written for this software
14+ * can be used freely for any purpose.  Any derived versions of this
15+ * software must be clearly marked as such, and if the derived work is
16+ * incompatible with the protocol description in the RFC file, it must be
17+ * called by a name other than "ssh" or "Secure Shell".
18+ */
19+
20+#ifndef XMALLOC_H
21+#define XMALLOC_H
22+
23+#include <stddef.h>
24+
25+void	*xmalloc(size_t);
26+void	*xcalloc(size_t, size_t);
27+void	*xreallocarray(void *, size_t, size_t);
28+char	*xstrdup(const char *);
29+int	 xasprintf(char **, const char *, ...)
30+                __attribute__((__format__ (printf, 2, 3)))
31+                __attribute__((__nonnull__ (2)));
32+
33+#endif	/* XMALLOC_H */
+3, -0
 1@@ -29,6 +29,7 @@ BUILD_POSIX_CUT = $(BUILD_POSIX)
 2 BUILD_POSIX_DATE = $(BUILD_POSIX)
 3 BUILD_POSIX_DD = $(BUILD_POSIX)
 4 BUILD_POSIX_DF = $(BUILD_POSIX)
 5+BUILD_POSIX_DIFF = $(BUILD_POSIX)
 6 BUILD_POSIX_DIRNAME = $(BUILD_POSIX)
 7 BUILD_POSIX_DU = $(BUILD_POSIX)
 8 BUILD_POSIX_ECHO = $(BUILD_POSIX)
 9@@ -179,6 +180,8 @@ BUILD_PSEUDO_B3SUM = $(BUILD_PSEUDO)
10 BUILD_PSEUDO_BLKID = $(BUILD_PSEUDO)
11 BUILD_PSEUDO_LSBLK = $(BUILD_PSEUDO)
12 BUILD_PSEUDO_FDISK = $(BUILD_PSEUDO)
13+BUILD_PSEUDO_DIFF3 = $(BUILD_PSEUDO)
14+BUILD_PSEUDO_SYNC = $(BUILD_PSEUDO)
15 BUILD_PSEUDO_DMESG = $(BUILD_PSEUDO)
16 BUILD_PSEUDO_FALLOCATE = $(BUILD_PSEUDO)
17 BUILD_PSEUDO_FREE = $(BUILD_PSEUDO)
+69, -2
  1@@ -47,7 +47,7 @@ mkdir -p "${BDIR}"
  2 OBJS=""
  3 DECL_F="${BDIR}/decls.inc"
  4 : > "${DECL_F}"
  5-for _c in posix linux net pseudo xsi; do
  6+for _c in posix linux net pseudo xsi extra; do
  7 	: > "${BDIR}/entries_${_c}.inc"
  8 done
  9 unset _c
 10@@ -114,6 +114,70 @@ compile_make() {
 11 	printf '\t{ "make", make_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
 12 }
 13 
 14+compile_diff() {
 15+	ddir="${ROOT}/cmd/posix/diff"
 16+	symf="${BDIR}/diff.syms"
 17+	diff_objs=""
 18+
 19+	printf 'mkbox: cc diff (multi)\n'
 20+	for s in diff diffdir diffreg pr xmalloc; do
 21+		out="${BDIR}/diff_${s}.o"
 22+		eval "${CC} ${CPPFLAGS} -I\"\$ddir\" ${CFLAGS} -c \"\$ddir/\$s.c\" -o \"\$out\"" ||
 23+			die "compile failed: diff/${s}.c"
 24+		diff_objs="${diff_objs} ${out}"
 25+	done
 26+
 27+	combined="${BDIR}/diff_combined.o"
 28+	${LD:-ld} -r -o "${combined}" ${diff_objs} ||
 29+		die "ld -r failed: diff"
 30+
 31+	printf 'main diff_main\n' > "${symf}"
 32+	${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
 33+		die "objcopy failed: diff"
 34+	${OBJCOPY} --keep-global-symbol=diff_main "${combined}" ||
 35+		die "objcopy localize failed: diff"
 36+	rm -f "${symf}" ${diff_objs}
 37+
 38+	OBJS="${OBJS} ${combined}"
 39+	LDLIBS="${LDLIBS} -lm"
 40+	printf 'int diff_main(int, char **);\n' >> "${DECL_F}"
 41+	printf '\t{ "diff", diff_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
 42+}
 43+
 44+compile_diff3() {
 45+	ddir="${ROOT}/cmd/extra/diff3"
 46+	pdir="${ROOT}/cmd/posix/diff"
 47+	symf="${BDIR}/diff3.syms"
 48+	diff3_objs=""
 49+
 50+	printf 'mkbox: cc diff3 (multi)\n'
 51+	for s in diff3; do
 52+		out="${BDIR}/diff3_${s}.o"
 53+		eval "${CC} ${CPPFLAGS} -I\"\$pdir\" ${CFLAGS} -c \"\$ddir/\$s.c\" -o \"\$out\"" ||
 54+			die "compile failed: diff3/${s}.c"
 55+		diff3_objs="${diff3_objs} ${out}"
 56+	done
 57+	out="${BDIR}/diff3_xmalloc.o"
 58+	eval "${CC} ${CPPFLAGS} -I\"\$pdir\" ${CFLAGS} -c \"\$pdir/xmalloc.c\" -o \"\$out\"" ||
 59+		die "compile failed: diff/xmalloc.c"
 60+	diff3_objs="${diff3_objs} ${out}"
 61+
 62+	combined="${BDIR}/diff3_combined.o"
 63+	${LD:-ld} -r -o "${combined}" ${diff3_objs} ||
 64+		die "ld -r failed: diff3"
 65+
 66+	printf 'main diff3_main\n' > "${symf}"
 67+	${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
 68+		die "objcopy failed: diff3"
 69+	${OBJCOPY} --keep-global-symbol=diff3_main "${combined}" ||
 70+		die "objcopy localize failed: diff3"
 71+	rm -f "${symf}" ${diff3_objs}
 72+
 73+	OBJS="${OBJS} ${combined}"
 74+	printf 'int diff3_main(int, char **);\n' >> "${DECL_F}"
 75+	printf '\t{ "diff3", diff3_main, "extra" },\n' >> "${BDIR}/entries_extra.inc"
 76+}
 77+
 78 compile_awk() {
 79 	adir="${ROOT}/cmd/posix/awk"
 80 	symf="${BDIR}/awk.syms"
 81@@ -236,6 +300,7 @@ process_dir() {
 82 }
 83 
 84 process_dir "${ROOT}/cmd/posix" "posix"
 85+compile_diff
 86 compile_awk
 87 compile_sh
 88 compile_make
 89@@ -243,6 +308,8 @@ process_dir "${ROOT}/cmd/linux" "linux"
 90 process_dir "${ROOT}/cmd/net" "net"
 91 process_dir "${ROOT}/cmd/pseudo" "pseudo"
 92 process_dir "${ROOT}/cmd/xsi" "xsi"
 93+process_dir "${ROOT}/cmd/extra" "extra"
 94+compile_diff3
 95 
 96 DISP="${BDIR}/aruu-box.c"
 97 printf 'mkbox: generating dispatch\n'
 98@@ -280,7 +347,7 @@ DISPATCH_STRUCT
 99 # sort each category alphabetically by tool name at build time;
100 # entries have the form: \t{ "name", fn_main, "cat" },
101 # splitting on " makes field 2 the tool name
102-for _c in posix linux net pseudo xsi; do
103+for _c in posix linux net pseudo xsi extra; do
104 	sort -t'"' -k2,2 "${BDIR}/entries_${_c}.inc"
105 done >> "${DISP}"
106 unset _c