commit 571dd0f
shrub
·
2026-06-29 15:43:31 +0000 UTC
parent 8c1988e
added posix/diff, extra/diff3, extra/sync diffs are from chimerautils(freebsd) sync is from sbase embedded manpages have been added to both.
13 files changed,
+4779,
-8
M
Makefile
+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)