commit fd3a93f

xplshn  ·  2026-06-30 19:56:41 +0000 UTC
parent b2a077b
portable diskutils: blkid, lsblk, fdisk | general cleanup
33 files changed,  +7529, -9837
+12, -37
  1@@ -123,8 +123,9 @@ POSIX_BIN_ALL =\
  2 	cmd/posix/date\
  3 	cmd/posix/dd\
  4 	cmd/posix/df\
  5-	cmd/posix/diff/diff\
  6-	cmd/posix/patch/patch\
  7+	cmd/posix/diff\
  8+	cmd/posix/patch\
  9+	cmd/posix/diff3\
 10 	cmd/posix/dirname\
 11 	cmd/posix/du\
 12 	cmd/posix/echo\
 13@@ -277,7 +278,6 @@ PSEUDO_BIN_ALL =\
 14 	cmd/extra/lsblk\
 15 	cmd/extra/fdisk\
 16 	cmd/extra/sync\
 17-	cmd/extra/diff3/diff3\
 18 	cmd/dev/ar/ar\
 19 	cmd/dev/as/as\
 20 	cmd/dev/ld/ld\
 21@@ -296,20 +296,7 @@ MAKEOBJ =\
 22 	cmd/posix/make/posix.o\
 23 	cmd/posix/make/rules.o
 24 
 25-DIFFOBJ =\
 26-	cmd/posix/diff/diff.o\
 27-	cmd/posix/diff/diffdir.o\
 28-	cmd/posix/diff/diffreg.o\
 29-	cmd/posix/diff/pr.o\
 30-	cmd/posix/diff/xmalloc.o
 31-
 32-PATCHOBJ =\
 33-	cmd/posix/patch/patch.o\
 34-	cmd/posix/patch/pch.o\
 35-	cmd/posix/patch/inp.o\
 36-	cmd/posix/patch/util.o\
 37-	cmd/posix/patch/backupfile.o\
 38-	cmd/posix/patch/mkpath.o
 39+
 40 
 41 BIN_basename_1 = cmd/posix/basename
 42 BIN_cal_1 = cmd/posix/cal
 43@@ -325,8 +312,8 @@ BIN_cut_1 = cmd/posix/cut
 44 BIN_date_1 = cmd/posix/date
 45 BIN_dd_1 = cmd/posix/dd
 46 BIN_df_1 = cmd/posix/df
 47-BIN_diff_1 = cmd/posix/diff/diff
 48-BIN_patch_1 = cmd/posix/patch/patch
 49+BIN_diff_1 = cmd/posix/diff
 50+BIN_patch_1 = cmd/posix/patch
 51 BIN_dirname_1 = cmd/posix/dirname
 52 BIN_du_1 = cmd/posix/du
 53 BIN_echo_1 = cmd/posix/echo
 54@@ -475,7 +462,7 @@ BIN_blkid_1 = cmd/extra/blkid
 55 BIN_lsblk_1 = cmd/extra/lsblk
 56 BIN_fdisk_1 = cmd/extra/fdisk
 57 BIN_sync_1 = cmd/extra/sync
 58-BIN_diff3_1 = cmd/extra/diff3/diff3
 59+BIN_diff3_1 = cmd/posix/diff3
 60 BIN_ar_1 = cmd/dev/ar/ar
 61 BIN_as_1 = cmd/dev/as/as
 62 BIN_ld_1 = cmd/dev/ld/ld
 63@@ -698,20 +685,8 @@ $(MAKEOBJ): cmd/posix/make/make.h
 64 cmd/posix/make/make: $(MAKEOBJ) $(LIB)
 65 	$(CC) $(LDFLAGS) -o $@ $(MAKEOBJ) $(LIB) $(LDLIBS)
 66 
 67-$(DIFFOBJ): cmd/posix/diff/diff.h cmd/posix/diff/pr.h cmd/posix/diff/xmalloc.h
 68-
 69-cmd/posix/diff/diff: $(DIFFOBJ) $(LIB)
 70-	$(CC) $(LDFLAGS) -o $@ $(DIFFOBJ) $(LIB) $(LDLIBS) -lm
 71-
 72-$(PATCHOBJ): cmd/posix/patch/common.h cmd/posix/patch/util.h \
 73-	cmd/posix/patch/pch.h cmd/posix/patch/inp.h \
 74-	cmd/posix/patch/backupfile.h cmd/posix/patch/pathnames.h
 75-
 76-cmd/posix/patch/%.o: cmd/posix/patch/%.c
 77-	$(CC) $(CPPFLAGS) -Icmd/posix/patch $(CFLAGS) -o $@ -c $<
 78-
 79-cmd/posix/patch/patch: $(PATCHOBJ) $(LIB)
 80-	$(CC) $(LDFLAGS) -o $@ $(PATCHOBJ) $(LIB) $(LDLIBS)
 81+cmd/posix/diff: cmd/posix/diff.c $(LIB)
 82+	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ cmd/posix/diff.c $(LIB) $(LDLIBS) -lm
 83 
 84 shared/libutf/libutf.a: $(LIBUTFOBJ)
 85 	$(AR) $(ARFLAGS) $@ $?
 86@@ -760,12 +735,12 @@ man: scripts/mkman/mkman
 87 
 88 clean:
 89 	rm -f shared/libutf/*.o shared/libutil/*.o shared/libredline/*.o
 90-	rm -f cmd/posix/*.o cmd/posix/diff/*.o cmd/posix/patch/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
 91+	rm -f cmd/posix/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
 92 	rm -f cmd/linux/*.o cmd/net/*.o cmd/xsi/*.o cmd/pseudo/*.o
 93-	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
 94+	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
 95 	rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL) $(LIB)
 96 	rm -f cmd/posix/make/make cmd/posix/getconf.h cmd/posix/bc.c
 97-	rm -f cmd/posix/patch/patch
 98+	rm -f
 99 	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
100 	rm -f cmd/posix/sh/sh cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
101 	rm -f cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h cmd/posix/sh/token.h
+6, -47
  1@@ -271,9 +271,6 @@ 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@@ -282,45 +279,14 @@ build_simple_tools() {
 12 	drain
 13 	local bin
 14 	for bin in $staged; do
 15-		link_bin "$bin" "${bin}.o" -- $LIB
 16+		if [ "${bin##*/}" = "diff" ]; then
 17+			link_bin "$bin" "${bin}.o" -- $LIB -lm
 18+		else
 19+			link_bin "$bin" "${bin}.o" -- $LIB
 20+		fi
 21 	done
 22 }
 23 
 24-# generated multi-file tools under cmd/posix
 25-#
 26-build_diff() {
 27-	cfg_enabled BUILD_POSIX_DIFF || return 0
 28-	local dir=cmd/posix/diff
 29-	local objs=""
 30-	EXTRA_HDR="$dir/diff.h $dir/pr.h $dir/xmalloc.h"
 31-	compile_c "$dir/diff.c" "$dir/diff.o"
 32-	compile_c "$dir/diffdir.c" "$dir/diffdir.o"
 33-	compile_c "$dir/diffreg.c" "$dir/diffreg.o"
 34-	compile_c "$dir/pr.c" "$dir/pr.o"
 35-	compile_c "$dir/xmalloc.c" "$dir/xmalloc.o"
 36-	EXTRA_HDR=
 37-	objs="$dir/diff.o $dir/diffdir.o $dir/diffreg.o $dir/pr.o $dir/xmalloc.o"
 38-	link_bin "$dir/diff" $objs -- $LIB -lm
 39-}
 40-
 41-build_patch() {
 42-	cfg_enabled BUILD_POSIX_PATCH || return 0
 43-	local dir=cmd/posix/patch
 44-	EXTRA_HDR="$dir/common.h $dir/util.h $dir/pch.h $dir/inp.h $dir/backupfile.h $dir/pathnames.h"
 45-	objs_for "$dir" "" "-I$dir"
 46-	EXTRA_HDR=
 47-	link_bin "$dir/patch" $_objs -- $LIB
 48-}
 49-
 50-build_diff3() {
 51-	cfg_enabled BUILD_PSEUDO_DIFF3 || return 0
 52-	local dir=cmd/extra/diff3
 53-	EXTRA_HDR="cmd/posix/diff/xmalloc.h"
 54-	compile_c "$dir/diff3.c" "$dir/diff3.o"
 55-	EXTRA_HDR=
 56-	link_bin "$dir/diff3" "$dir/diff3.o" cmd/posix/diff/xmalloc.o -- $LIB
 57-}
 58-
 59 build_awk() {
 60 	cfg_enabled BUILD_POSIX_AWK || return 0
 61 	local dir=cmd/posix/awk
 62@@ -415,8 +381,6 @@ build_posix() {
 63 		scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
 64 	}
 65 	build_simple_tools posix
 66-	build_diff
 67-	build_patch
 68 	build_awk
 69 	build_sh
 70 	build_make
 71@@ -533,9 +497,6 @@ build_man() {
 72 			build_man_for "$(cfgvar "$cat" "$base")" "$src"
 73 		done
 74 	done
 75-	build_man_for BUILD_POSIX_DIFF cmd/posix/diff/diff.c
 76-	build_man_for BUILD_POSIX_PATCH cmd/posix/patch/patch.c
 77-	build_man_for BUILD_PSEUDO_DIFF3 cmd/extra/diff3/diff3.c
 78 }
 79 
 80 # install and clean
 81@@ -560,7 +521,6 @@ do_clean() {
 82 	find cmd -type f ! -name '*.*' -perm -100     -exec rm -f {} +
 83 	rm -f shared/libaruuelf.so
 84 	rm -f cmd/posix/bc.c cmd/posix/getconf.h
 85-	rm -f cmd/posix/patch/patch
 86 	rm -f cmd/posix/awk/maketab cmd/posix/awk/proctab.c
 87 	rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h
 88 	rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
 89@@ -582,14 +542,13 @@ case "$TARGET" in
 90 	build_simple_tools xsi
 91 	build_simple_tools pseudo
 92 	build_simple_tools extra
 93-	build_diff3
 94 		;;
 95 	lib)             build_lib ;;
 96 	posix)           build_lib; build_posix ;;
 97 	dev)             build_lib; build_dev ;;
 98 	make)            build_lib; build_make ;;
 99 	linux|net|xsi|pseudo|extra)
100-	                 build_lib; build_simple_tools "$TARGET"; [ "$TARGET" = extra ] && build_diff3 ;;
101+	                 build_lib; build_simple_tools "$TARGET" ;;
102 	man)             build_man ;;
103 	install)         do_install ;;
104 	clean)           do_clean ;;
+22, -234
  1@@ -8,43 +8,6 @@
  2 #include <unistd.h>
  3 #include <fcntl.h>
  4 
  5-struct FsType {
  6-	const char *name;
  7-	size_t magic_offset;
  8-	size_t magic_len;
  9-	uint64_t magic1;
 10-	uint64_t magic2;
 11-	size_t uuid_offset;
 12-	size_t uuid_len;
 13-	size_t label_offset;
 14-	size_t label_len;
 15-};
 16-
 17-static const struct FsType fstypes[] = {
 18-	/* ext2/3/4 */
 19-	{ "ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16 },
 20-	/* vfat / fat32 */
 21-	{ "vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11 },
 22-	/* vfat / fat12/16 */
 23-	{ "vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11 },
 24-	/* ntfs */
 25-	{ "ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0 },
 26-	/* xfs */
 27-	{ "xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12 },
 28-	/* btrfs */
 29-	{ "btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256 },
 30-	/* f2fs */
 31-	{ "f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512 },
 32-	/* ufs1 */
 33-	{ "ufs1", 9564, 4, 0x011954, 0x54190100, 0, 0, 0, 0 },
 34-	/* ufs2 */
 35-	{ "ufs2", 66908, 4, 0x19540119, 0x19015419, 67056, 16, 67072, 32 },
 36-	/* fossil */
 37-	{ "fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127 },
 38-	/* cwfs */
 39-	{ "cwfs", 0, 4, 0xc1a551f5, 0xf551a5c1, 0, 0, 0, 0 },
 40-};
 41-
 42 static char *oflag = "full";
 43 static char *sflag = NULL;
 44 static int Uflag = 0;
 45@@ -56,35 +19,6 @@ usage(void)
 46 	eprintf("usage: %s [-o format] [-s tag] [-U] [-L] [device ...]\n", argv0);
 47 }
 48 
 49-static void
 50-format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
 51-{
 52-	size_t i;
 53-	char *p = out;
 54-
 55-	if (strcmp(type, "vfat") == 0) {
 56-		snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
 57-		return;
 58-	}
 59-
 60-	if (strcmp(type, "ntfs") == 0) {
 61-		for (i = 8; i > 0; i--) {
 62-			snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
 63-			p += 2;
 64-		}
 65-		return;
 66-	}
 67-
 68-	for (i = 0; i < len; i++) {
 69-		if (i == 4 || i == 6 || i == 8 || i == 10) {
 70-			*p++ = '-';
 71-		}
 72-		snprintf(p, out_len - (p - out), "%02x", buf[i]);
 73-		p += 2;
 74-	}
 75-	*p = '\0';
 76-}
 77-
 78 static void
 79 print_tag(const char *devname, const char *tag, const char *value)
 80 {
 81@@ -100,192 +34,46 @@ print_tag(const char *devname, const char *tag, const char *value)
 82 	}
 83 }
 84 
 85-static int
 86-detect_zfs(const unsigned char *buf, size_t len)
 87-{
 88-	size_t offset;
 89-	uint64_t magic;
 90-
 91-	for (offset = 131072; offset + 8 <= len; offset += 1024) {
 92-		magic = ((uint64_t)buf[offset + 7] << 56) |
 93-		        ((uint64_t)buf[offset + 6] << 48) |
 94-		        ((uint64_t)buf[offset + 5] << 40) |
 95-		        ((uint64_t)buf[offset + 4] << 32) |
 96-		        ((uint64_t)buf[offset + 3] << 24) |
 97-		        ((uint64_t)buf[offset + 2] << 16) |
 98-		        ((uint64_t)buf[offset + 1] << 8)  |
 99-		        (uint64_t)buf[offset];
100-
101-		if (magic == 0x00bab10caULL || magic == 0x0cb1ba0000000000ULL ||
102-		    magic == 0x00bab10cULL || magic == 0x0cb1ba00ULL)
103-			return 1;
104-	}
105-	return 0;
106-}
107-
108-static int
109-detect_plan9_other(const unsigned char *buf, size_t len, const char **name)
110-{
111-	if (len >= 4) {
112-		uint32_t magic = ((uint32_t)buf[3] << 24) | ((uint32_t)buf[2] << 16) | ((uint32_t)buf[1] << 8) | (uint32_t)buf[0];
113-		if (magic == 0x686a6673 || magic == 0x73666a68) {
114-			*name = "hjfs";
115-			return 1;
116-		}
117-		if (magic == 0x67656673 || magic == 0x73666567) {
118-			*name = "gefs";
119-			return 1;
120-		}
121-	}
122-	if (len >= 516) {
123-		uint32_t magic = ((uint32_t)buf[515] << 24) | ((uint32_t)buf[514] << 16) | ((uint32_t)buf[513] << 8) | (uint32_t)buf[512];
124-		if (magic == 0x686a6673 || magic == 0x73666a68) {
125-			*name = "hjfs";
126-			return 1;
127-		}
128-		if (magic == 0x67656673 || magic == 0x73666567) {
129-			*name = "gefs";
130-			return 1;
131-		}
132-	}
133-	return 0;
134-}
135-
136 static int
137 do_blkid(const char *path)
138 {
139 	struct BlockDev dev;
140-	unsigned char *buf;
141-	size_t read_size = 262144; /* 256kb */
142-	size_t i, j;
143-	int found = 0;
144+	char type[64] = {0};
145+	char label[256] = {0};
146+	char uuid[256] = {0};
147+	int res;
148 
149 	if (blockdev_open(&dev, path, 0) < 0)
150 		return -1;
151 
152-	if (dev.size < read_size)
153-		read_size = dev.size;
154-
155-	buf = emalloc(read_size);
156-	/* read sectors */
157-	size_t sectors_to_read = (read_size + dev.sec_size - 1) / dev.sec_size;
158-	if (blockdev_read(&dev, 0, buf, sectors_to_read) < 0) {
159-		free(buf);
160-		blockdev_close(&dev);
161-		return -1;
162-	}
163-
164-	for (i = 0; i < LEN(fstypes); i++) {
165-		const struct FsType *fs = &fstypes[i];
166-		if (fs->magic_offset + fs->magic_len > read_size)
167-			continue;
168-
169-		uint64_t val = 0;
170-		for (j = 0; j < fs->magic_len; j++) {
171-			val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
172-		}
173-
174-		if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
175-			const char *type_name = fs->name;
176-			if (strcmp(fs->name, "ext") == 0) {
177-				if (buf[1116] & 4)
178-					type_name = "ext3";
179-				else if (buf[1120] & 64)
180-					type_name = "ext4";
181-				else
182-					type_name = "ext2";
183-			}
184-
185-			if (Uflag || Lflag) {
186-				char label[256] = {0};
187-				char uuid[256] = {0};
188-				if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
189-					snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
190-				}
191-				if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
192-					format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
193-				}
194-				if (Uflag) {
195-					printf("%s\n", uuid);
196-				} else {
197-					printf("%s\n", label);
198-				}
199-				found = 1;
200-				break;
201+	res = blockdev_detect_fs(&dev, type, sizeof(type), label, sizeof(label), uuid, sizeof(uuid));
202+	if (res == 0) {
203+		if (Uflag || Lflag) {
204+			if (Uflag) {
205+				printf("%s\n", uuid);
206+			} else {
207+				printf("%s\n", label);
208 			}
209-
210+		} else {
211 			if (strcmp(oflag, "export") == 0) {
212 				printf("DEVNAME=%s\n", path);
213 			} else if (strcmp(oflag, "value") != 0) {
214 				printf("%s:", path);
215 			}
216 
217-			if (fs->label_len && fs->label_offset + fs->label_len <= read_size) {
218-				char label[256];
219-				snprintf(label, sizeof(label), "%.*s", (int)fs->label_len, buf + fs->label_offset);
220-				/* strip trailing spaces */
221-				char *end = label + strlen(label) - 1;
222-				while (end >= label && *end == ' ') {
223-					*end = '\0';
224-					end--;
225-				}
226-				if (label[0])
227-					print_tag(path, "LABEL", label);
228-			}
229-
230-			if (fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
231-				char uuid[256];
232-				format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, sizeof(uuid));
233+			if (label[0])
234+				print_tag(path, "LABEL", label);
235+			if (uuid[0])
236 				print_tag(path, "UUID", uuid);
237-			}
238+			print_tag(path, "TYPE", type);
239 
240-			print_tag(path, "TYPE", type_name);
241 			if (strcmp(oflag, "full") == 0)
242 				printf("\n");
243-
244-			found = 1;
245-			break;
246 		}
247 	}
248 
249-	if (!found && detect_zfs(buf, read_size)) {
250-		if (Uflag || Lflag) {
251-			printf("\n");
252-		} else {
253-			if (strcmp(oflag, "export") == 0) {
254-				printf("DEVNAME=%s\n", path);
255-			} else if (strcmp(oflag, "value") != 0) {
256-				printf("%s:", path);
257-			}
258-			print_tag(path, "TYPE", "zfs_member");
259-			if (strcmp(oflag, "full") == 0)
260-				printf("\n");
261-		}
262-		found = 1;
263-	}
264-
265-	if (!found) {
266-		const char *p9_name = NULL;
267-		if (detect_plan9_other(buf, read_size, &p9_name)) {
268-			if (Uflag || Lflag) {
269-				printf("\n");
270-			} else {
271-				if (strcmp(oflag, "export") == 0) {
272-					printf("DEVNAME=%s\n", path);
273-				} else if (strcmp(oflag, "value") != 0) {
274-					printf("%s:", path);
275-				}
276-				print_tag(path, "TYPE", p9_name);
277-				if (strcmp(oflag, "full") == 0)
278-					printf("\n");
279-			}
280-			found = 1;
281-		}
282-	}
283-
284-	free(buf);
285 	blockdev_close(&dev);
286-	return found ? 0 : -2;
287+	return res == 0 ? 0 : -2;
288 }
289 
290 // ?man blkid: print block device attributes
291@@ -330,17 +118,17 @@ main(int argc, char *argv[])
292 			return 1;
293 		}
294 		for (curr = list; curr; curr = curr->next) {
295-			char devpath[128];
296-			snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
297-			do_blkid(devpath);
298+			do_blkid(curr->path);
299 			struct BlockDevInfo *part;
300 			for (part = curr->parts; part; part = part->next) {
301-				snprintf(devpath, sizeof(devpath), "/dev/%s", part->name);
302-				do_blkid(devpath);
303+				do_blkid(part->path);
304 			}
305 		}
306 		blockdev_free_list(list);
307 	}
308 
309+	if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
310+		ret = 2;
311+
312 	return ret;
313 }
+0, -1250
   1@@ -1,1250 +0,0 @@
   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-}
+355, -83
  1@@ -2,11 +2,11 @@
  2 #include "util.h"
  3 #include "diskutil.h"
  4 
  5+#include <fcntl.h>
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 #include <unistd.h>
 10-#include <fcntl.h>
 11 
 12 struct MbrPartition {
 13 	uint8_t boot;
 14@@ -49,6 +49,47 @@ struct GptPartition {
 15 	uint16_t name[36];
 16 } __attribute__((packed));
 17 
 18+struct BsdPartition {
 19+	uint32_t size;
 20+	uint32_t offset;
 21+	uint16_t offset_h;
 22+	uint16_t size_h;
 23+	uint8_t fstype;
 24+	uint8_t fragblock;
 25+	uint16_t cpg;
 26+} __attribute__((packed));
 27+
 28+struct BsdDisklabel {
 29+	uint32_t d_magic;
 30+	uint16_t d_type;
 31+	uint16_t d_subtype;
 32+	char     d_typename[16];
 33+	char     d_packname[16];
 34+	uint32_t d_secsize;
 35+	uint32_t d_nsectors;
 36+	uint32_t d_ntracks;
 37+	uint32_t d_ncylinders;
 38+	uint32_t d_secpercyl;
 39+	uint32_t d_secperunit;
 40+	uint8_t  d_uid[8];
 41+	uint32_t d_acylinders;
 42+	uint16_t d_bstarth;
 43+	uint16_t d_bendh;
 44+	uint32_t d_bstart;
 45+	uint32_t d_bend;
 46+	uint32_t d_flags;
 47+	uint32_t d_spare4[5];
 48+	uint16_t d_secperunith;
 49+	uint16_t d_version;
 50+	uint32_t d_spare[4];
 51+	uint32_t d_magic2;
 52+	uint16_t d_checksum;
 53+	uint16_t d_npartitions;
 54+	uint32_t d_spare2;
 55+	uint32_t d_spare3;
 56+	struct BsdPartition d_partitions[16];
 57+} __attribute__((packed));
 58+
 59 static uint32_t crc32_table[256];
 60 static int crc32_table_initialized = 0;
 61 
 62@@ -84,9 +125,11 @@ static int opt_list = 0;
 63 static int opt_print = 0;
 64 static int opt_init_gpt = 0;
 65 static int opt_init_mbr = 0;
 66+static int opt_init_bsd = 0;
 67 static int opt_add = 0;
 68 static int opt_del = 0;
 69 static int opt_part_idx = -1;
 70+static int opt_part_is_letter = 0;
 71 static uint64_t opt_start = 0;
 72 static uint64_t opt_end = 0;
 73 static const char *opt_type = NULL;
 74@@ -94,11 +137,11 @@ static const char *opt_type = NULL;
 75 static void
 76 usage(void)
 77 {
 78-	eprintf("usage: %s [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]\n", argv0);
 79+	eprintf("usage: %s [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]\n", argv0);
 80 }
 81 
 82 static void
 83-print_mbr(struct MbrHeader *mbr, const char *path)
 84+mbr_print(struct MbrHeader *mbr, const char *path)
 85 {
 86 	int i;
 87 	printf("Disk: %s\n", path);
 88@@ -118,7 +161,7 @@ print_mbr(struct MbrHeader *mbr, const char *path)
 89 }
 90 
 91 static void
 92-print_gpt(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
 93+gpt_print(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
 94 {
 95 	uint32_t i;
 96 	char guid[37];
 97@@ -147,7 +190,7 @@ print_gpt(struct GptHeader *hdr, struct GptPartition *parts, const char *path)
 98 }
 99 
100 static int
101-read_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
102+gpt_read(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
103 {
104 	unsigned char sector[512];
105 	size_t part_sectors;
106@@ -168,31 +211,31 @@ read_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts
107 }
108 
109 static int
110-write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
111+gpt_write(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *parts)
112 {
113 	unsigned char sector[512];
114-	size_t part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
115+	struct MbrHeader pmbr;
116+	struct GptHeader backup_hdr;
117+	uint64_t limit;
118+	size_t part_sectors;
119+
120+	part_sectors = (hdr->num_parts * hdr->part_size + dev->sec_size - 1) / dev->sec_size;
121 
122-	/* update partition entries crc */
123 	hdr->parts_crc = crc32(~0U, parts, hdr->num_parts * hdr->part_size) ^ ~0U;
124 
125-	/* calculate header crc (zeroing the crc field first) */
126 	hdr->crc = 0;
127 	hdr->crc = crc32(~0U, hdr, hdr->size) ^ ~0U;
128 
129-	/* write protective mbr to sector 0 */
130-	struct MbrHeader pmbr;
131 	memset(&pmbr, 0, sizeof(pmbr));
132 	pmbr.parts[0].type = 0xEE;
133 	pmbr.parts[0].start_lba = 1;
134-	uint64_t limit = dev->size / dev->sec_size - 1;
135+	limit = dev->size / dev->sec_size - 1;
136 	pmbr.parts[0].size = limit > 0xFFFFFFFFU ? 0xFFFFFFFFU : (uint32_t)limit;
137 	pmbr.sig[0] = 0x55;
138 	pmbr.sig[1] = 0xAA;
139 	if (blockdev_write(dev, 0, &pmbr, 1) < 0)
140 		return -1;
141 
142-	/* write primary header and partition entries */
143 	memset(sector, 0, sizeof(sector));
144 	memcpy(sector, hdr, sizeof(*hdr));
145 	if (blockdev_write(dev, 1, sector, 1) < 0)
146@@ -200,8 +243,7 @@ write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *part
147 	if (blockdev_write(dev, hdr->partition_lba, parts, part_sectors) < 0)
148 		return -1;
149 
150-	/* write backup partition entries and backup header */
151-	struct GptHeader backup_hdr = *hdr;
152+	backup_hdr = *hdr;
153 	backup_hdr.current_lba = hdr->backup_lba;
154 	backup_hdr.backup_lba = hdr->current_lba;
155 	backup_hdr.partition_lba = backup_hdr.current_lba - part_sectors;
156@@ -220,6 +262,99 @@ write_gpt(struct BlockDev *dev, struct GptHeader *hdr, struct GptPartition *part
157 	return 0;
158 }
159 
160+static uint16_t
161+bsd_dkcksum(void *lp, size_t nparts)
162+{
163+	uint16_t *start;
164+	uint16_t *end;
165+	uint16_t sum = 0;
166+
167+	start = (uint16_t *)lp;
168+	end = (uint16_t *)((char *)lp + 148 + nparts * 16);
169+	while (start < end)
170+		sum ^= *start++;
171+	return sum;
172+}
173+
174+static int
175+bsd_is_partition_type(uint8_t type)
176+{
177+	return type == 0xa5 || type == 0xa6 || type == 0xa9;
178+}
179+
180+static int
181+bsd_read(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
182+{
183+	unsigned char sector[512];
184+	if (blockdev_read(dev, base_sec + 1, sector, 1) < 0)
185+		return -1;
186+	memcpy(lp, sector, sizeof(*lp));
187+	if (lp->d_magic != 0x82564557U || lp->d_magic2 != 0x82564557U)
188+		return -1;
189+	return 0;
190+}
191+
192+static int
193+bsd_write(struct BlockDev *dev, uint64_t base_sec, struct BsdDisklabel *lp)
194+{
195+	unsigned char sector[512];
196+	memset(sector, 0, sizeof(sector));
197+	lp->d_checksum = 0;
198+	lp->d_checksum = bsd_dkcksum(lp, lp->d_npartitions);
199+	memcpy(sector, lp, sizeof(*lp));
200+	if (blockdev_write(dev, base_sec + 1, sector, 1) < 0)
201+		return -1;
202+	return 0;
203+}
204+
205+static void
206+bsd_print(struct BsdDisklabel *lp, const char *path, uint64_t base_sec)
207+{
208+	uint64_t start, size;
209+	int i;
210+
211+	printf("Disk: %s (at sector %llu)\n", path, (unsigned long long)base_sec);
212+	printf("Partition table (BSD disklabel):\n");
213+	printf("%-5s %-12s %-12s %-10s\n", "Index", "Start", "Size", "Type");
214+	for (i = 0; i < lp->d_npartitions; i++) {
215+		struct BsdPartition *p = &lp->d_partitions[i];
216+		start = ((uint64_t)p->offset_h << 32) | p->offset;
217+		size = ((uint64_t)p->size_h << 32) | p->size;
218+		if (size == 0)
219+			continue;
220+		printf("%-5c %-12llu %-12llu 0x%02x\n",
221+		       'a' + i,
222+		       (unsigned long long)start,
223+		       (unsigned long long)size,
224+		       p->fstype);
225+	}
226+}
227+
228+static int
229+bsd_find_label(struct BlockDev *dev, uint64_t *base_sec, struct BsdDisklabel *lp)
230+{
231+	struct MbrHeader mbr;
232+	int i;
233+
234+	if (bsd_read(dev, 0, lp) == 0) {
235+		*base_sec = 0;
236+		return 0;
237+	}
238+
239+	if (blockdev_read(dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
240+		for (i = 0; i < 4; i++) {
241+			if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
242+				if (bsd_read(dev, mbr.parts[i].start_lba, lp) == 0) {
243+					*base_sec = mbr.parts[i].start_lba;
244+					return 0;
245+				}
246+			}
247+		}
248+	}
249+
250+	return -1;
251+}
252+
253 static int
254 do_print(const char *path)
255 {
256@@ -227,17 +362,36 @@ do_print(const char *path)
257 	struct MbrHeader mbr;
258 	struct GptHeader gpt_hdr;
259 	struct GptPartition gpt_parts[128];
260+	struct BsdDisklabel bsd_lp;
261+	int i;
262+	int found_bsd = 0;
263 
264 	if (blockdev_open(&dev, path, 0) < 0) {
265 		weprintf("cannot open %s:", path);
266 		return -1;
267 	}
268 
269-	if (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0) {
270-		print_gpt(&gpt_hdr, gpt_parts, path);
271+	if (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0) {
272+		gpt_print(&gpt_hdr, gpt_parts, path);
273+	} else if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
274+		for (i = 0; i < 4; i++) {
275+			if (mbr.parts[i].size > 0 && bsd_is_partition_type(mbr.parts[i].type)) {
276+				if (bsd_read(&dev, mbr.parts[i].start_lba, &bsd_lp) == 0) {
277+					bsd_print(&bsd_lp, path, mbr.parts[i].start_lba);
278+					found_bsd = 1;
279+				}
280+			}
281+		}
282+		if (!found_bsd) {
283+			if (bsd_read(&dev, 0, &bsd_lp) == 0) {
284+				bsd_print(&bsd_lp, path, 0);
285+			} else {
286+				mbr_print(&mbr, path);
287+			}
288+		}
289 	} else {
290-		if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
291-			print_mbr(&mbr, path);
292+		if (bsd_read(&dev, 0, &bsd_lp) == 0) {
293+			bsd_print(&bsd_lp, path, 0);
294 		} else {
295 			printf("Disk %s: unpartitioned or unknown format\n", path);
296 		}
297@@ -254,14 +408,25 @@ do_fdisk(const char *path)
298 	struct MbrHeader mbr;
299 	struct GptHeader gpt_hdr;
300 	struct GptPartition gpt_parts[128];
301+	struct BsdDisklabel lp;
302+	struct BsdDisklabel bsd_lp;
303+	struct GptPartition *gp;
304+	struct BsdPartition *bp;
305+	struct MbrPartition *mp;
306+	uint64_t base_sec;
307+	uint64_t limit;
308+	uint64_t bsd_base;
309+	uint64_t size;
310+	int idx;
311 	int has_gpt = 0;
312+	int has_bsd = 0;
313 
314 	if (blockdev_open(&dev, path, 1) < 0) {
315 		weprintf("cannot open %s:", path);
316 		return -1;
317 	}
318 
319-	has_gpt = (read_gpt(&dev, &gpt_hdr, gpt_parts) == 0);
320+	has_gpt = (gpt_read(&dev, &gpt_hdr, gpt_parts) == 0);
321 
322 	if (opt_init_mbr) {
323 		memset(&mbr, 0, sizeof(mbr));
324@@ -291,7 +456,7 @@ do_fdisk(const char *path)
325 		gpt_hdr.part_size = 128;
326 		memset(gpt_parts, 0, sizeof(gpt_parts));
327 
328-		if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
329+		if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
330 			weprintf("cannot write GPT to %s:", path);
331 			blockdev_close(&dev);
332 			return -1;
333@@ -301,6 +466,42 @@ do_fdisk(const char *path)
334 		return 0;
335 	}
336 
337+	if (opt_init_bsd) {
338+		base_sec = 0;
339+		limit = dev.size / dev.sec_size;
340+
341+		if (blockdev_read(&dev, 0, &mbr, 1) == 0 && mbr.sig[0] == 0x55 && mbr.sig[1] == 0xAA) {
342+			if (!opt_part_is_letter && opt_part_idx >= 1 && opt_part_idx <= 4) {
343+				mp = &mbr.parts[opt_part_idx - 1];
344+				if (mp->size > 0) {
345+					base_sec = mp->start_lba;
346+					limit = base_sec + mp->size;
347+				}
348+			}
349+		}
350+
351+		memset(&lp, 0, sizeof(lp));
352+		lp.d_magic = 0x82564557U;
353+		lp.d_magic2 = 0x82564557U;
354+		lp.d_secsize = dev.sec_size;
355+		lp.d_npartitions = 8;
356+		lp.d_partitions[2].offset = (uint32_t)base_sec;
357+		lp.d_partitions[2].offset_h = (uint16_t)(base_sec >> 32);
358+		size = limit - base_sec;
359+		lp.d_partitions[2].size = (uint32_t)size;
360+		lp.d_partitions[2].size_h = (uint16_t)(size >> 32);
361+		lp.d_partitions[2].fstype = 0;
362+
363+		if (bsd_write(&dev, base_sec, &lp) < 0) {
364+			weprintf("cannot write BSD disklabel to %s:", path);
365+			blockdev_close(&dev);
366+			return -1;
367+		}
368+		printf("Initialized BSD disklabel on %s (at sector %llu)\n", path, (unsigned long long)base_sec);
369+		blockdev_close(&dev);
370+		return 0;
371+	}
372+
373 	if (opt_add) {
374 		if (opt_part_idx < 1) {
375 			weprintf("add requires partition index (-n)\n");
376@@ -314,46 +515,76 @@ do_fdisk(const char *path)
377 				blockdev_close(&dev);
378 				return -1;
379 			}
380-			struct GptPartition *p = &gpt_parts[opt_part_idx - 1];
381-			p->start_lba = opt_start;
382-			p->end_lba = opt_end;
383-			/* default type guid: linux filesystem data */
384-			p->type_guid[0] = 0xAF; p->type_guid[1] = 0x3D; p->type_guid[2] = 0xC6; p->type_guid[3] = 0x0F;
385-			p->type_guid[4] = 0x83; p->type_guid[5] = 0x84;
386-			p->type_guid[6] = 0x72; p->type_guid[7] = 0x47;
387-			p->type_guid[8] = 0x8E; p->type_guid[9] = 0x79;
388-			p->type_guid[10] = 0x3D; p->type_guid[11] = 0x69; p->type_guid[12] = 0xD8; p->type_guid[13] = 0x47; p->type_guid[14] = 0x7D; p->type_guid[15] = 0xE4;
389-
390-			if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
391+			gp = &gpt_parts[opt_part_idx - 1];
392+			gp->start_lba = opt_start;
393+			gp->end_lba = opt_end;
394+			gp->type_guid[0] = 0xAF; gp->type_guid[1] = 0x3D; gp->type_guid[2] = 0xC6; gp->type_guid[3] = 0x0F;
395+			gp->type_guid[4] = 0x83; gp->type_guid[5] = 0x84;
396+			gp->type_guid[6] = 0x72; gp->type_guid[7] = 0x47;
397+			gp->type_guid[8] = 0x8E; gp->type_guid[9] = 0x79;
398+			gp->type_guid[10] = 0x3D; gp->type_guid[11] = 0x69; gp->type_guid[12] = 0xD8; gp->type_guid[13] = 0x47; gp->type_guid[14] = 0x7D; gp->type_guid[15] = 0xE4;
399+
400+			if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
401 				weprintf("cannot update GPT on %s:", path);
402 				blockdev_close(&dev);
403 				return -1;
404 			}
405 			printf("Added GPT partition %d to %s\n", opt_part_idx, path);
406 		} else {
407-			if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
408-				weprintf("cannot read MBR from %s:", path);
409-				blockdev_close(&dev);
410-				return -1;
411-			}
412-			if (opt_part_idx > 4) {
413-				weprintf("MBR partition index must be 1-4\n");
414-				blockdev_close(&dev);
415-				return -1;
416+			has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
417+			if (opt_part_is_letter || has_bsd) {
418+				if (!has_bsd) {
419+					weprintf("BSD disklabel not found on %s\n", path);
420+					blockdev_close(&dev);
421+					return -1;
422+				}
423+				idx = opt_part_idx - 1;
424+				if (idx >= 16) {
425+					weprintf("BSD partition index must be a-p\n");
426+					blockdev_close(&dev);
427+					return -1;
428+				}
429+				bp = &bsd_lp.d_partitions[idx];
430+				bp->offset = (uint32_t)opt_start;
431+				bp->offset_h = (uint16_t)(opt_start >> 32);
432+				size = opt_end - opt_start + 1;
433+				bp->size = (uint32_t)size;
434+				bp->size_h = (uint16_t)(size >> 32);
435+				bp->fstype = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 7;
436+				if (idx >= bsd_lp.d_npartitions)
437+					bsd_lp.d_npartitions = idx + 1;
438+
439+				if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
440+					weprintf("cannot update BSD disklabel on %s:", path);
441+					blockdev_close(&dev);
442+					return -1;
443+				}
444+				printf("Added BSD partition %c to %s\n", 'a' + idx, path);
445+			} else {
446+				if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
447+					weprintf("cannot read MBR from %s:", path);
448+					blockdev_close(&dev);
449+					return -1;
450+				}
451+				if (opt_part_idx > 4) {
452+					weprintf("MBR partition index must be 1-4\n");
453+					blockdev_close(&dev);
454+					return -1;
455+				}
456+				mp = &mbr.parts[opt_part_idx - 1];
457+				mp->start_lba = (uint32_t)opt_start;
458+				mp->size = (uint32_t)(opt_end - opt_start + 1);
459+				mp->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
460+				mbr.sig[0] = 0x55;
461+				mbr.sig[1] = 0xAA;
462+
463+				if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
464+					weprintf("cannot update MBR on %s:", path);
465+					blockdev_close(&dev);
466+					return -1;
467+				}
468+				printf("Added MBR partition %d to %s\n", opt_part_idx, path);
469 			}
470-			struct MbrPartition *p = &mbr.parts[opt_part_idx - 1];
471-			p->start_lba = (uint32_t)opt_start;
472-			p->size = (uint32_t)(opt_end - opt_start + 1);
473-			p->type = opt_type ? (uint8_t)strtoul(opt_type, NULL, 0) : 0x83;
474-			mbr.sig[0] = 0x55;
475-			mbr.sig[1] = 0xAA;
476-
477-			if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
478-				weprintf("cannot update MBR on %s:", path);
479-				blockdev_close(&dev);
480-				return -1;
481-			}
482-			printf("Added MBR partition %d to %s\n", opt_part_idx, path);
483 		}
484 	}
485 
486@@ -371,30 +602,53 @@ do_fdisk(const char *path)
487 				return -1;
488 			}
489 			memset(&gpt_parts[opt_part_idx - 1], 0, sizeof(struct GptPartition));
490-			if (write_gpt(&dev, &gpt_hdr, gpt_parts) < 0) {
491+			if (gpt_write(&dev, &gpt_hdr, gpt_parts) < 0) {
492 				weprintf("cannot update GPT on %s:", path);
493 				blockdev_close(&dev);
494 				return -1;
495 			}
496 			printf("Deleted GPT partition %d from %s\n", opt_part_idx, path);
497 		} else {
498-			if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
499-				weprintf("cannot read MBR from %s:", path);
500-				blockdev_close(&dev);
501-				return -1;
502-			}
503-			if (opt_part_idx > 4) {
504-				weprintf("MBR partition index must be 1-4\n");
505-				blockdev_close(&dev);
506-				return -1;
507-			}
508-			memset(&mbr.parts[opt_part_idx - 1], 0, sizeof(struct MbrPartition));
509-			if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
510-				weprintf("cannot update MBR on %s:", path);
511-				blockdev_close(&dev);
512-				return -1;
513+			has_bsd = (bsd_find_label(&dev, &bsd_base, &bsd_lp) == 0);
514+			if (opt_part_is_letter || has_bsd) {
515+				if (!has_bsd) {
516+					weprintf("BSD disklabel not found on %s\n", path);
517+					blockdev_close(&dev);
518+					return -1;
519+				}
520+				idx = opt_part_idx - 1;
521+				if (idx >= 16) {
522+					weprintf("BSD partition index must be a-p\n");
523+					blockdev_close(&dev);
524+					return -1;
525+				}
526+				memset(&bsd_lp.d_partitions[idx], 0, sizeof(struct BsdPartition));
527+				if (bsd_write(&dev, bsd_base, &bsd_lp) < 0) {
528+					weprintf("cannot update BSD disklabel on %s:", path);
529+					blockdev_close(&dev);
530+					return -1;
531+				}
532+				printf("Deleted BSD partition %c from %s\n", 'a' + idx, path);
533+			} else {
534+				if (blockdev_read(&dev, 0, &mbr, 1) < 0) {
535+					weprintf("cannot read MBR from %s:", path);
536+					blockdev_close(&dev);
537+					return -1;
538+				}
539+				if (opt_part_idx > 4) {
540+					weprintf("MBR partition index must be 1-4\n");
541+					blockdev_close(&dev);
542+					return -1;
543+				}
544+				mp = &mbr.parts[opt_part_idx - 1];
545+				memset(mp, 0, sizeof(struct MbrPartition));
546+				if (blockdev_write(&dev, 0, &mbr, 1) < 0) {
547+					weprintf("cannot update MBR on %s:", path);
548+					blockdev_close(&dev);
549+					return -1;
550+				}
551+				printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
552 			}
553-			printf("Deleted MBR partition %d from %s\n", opt_part_idx, path);
554 		}
555 	}
556 
557@@ -403,11 +657,13 @@ do_fdisk(const char *path)
558 }
559 
560 // ?man fdisk: partition table manipulator
561-// ?man arguments: [-l] [-p] [-g] [-m] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]
562-// ?man fdisk performs partition table operations for MBR and GPT disks
563+// ?man arguments: [-l] [-p] [-g] [-m] [-s] [-a] [-d] [-n index] [-b start] [-e end] [-t type] [device]
564+// ?man fdisk performs partition table operations for MBR, GPT and BSD disklabels
565 int
566 main(int argc, char *argv[])
567 {
568+	int ret = 0;
569+
570 	ARGBEGIN {
571 	// ?man -l:list partitions of all devices
572 	case 'l':
573@@ -425,6 +681,10 @@ main(int argc, char *argv[])
574 	case 'm':
575 		opt_init_mbr = 1;
576 		break;
577+	// ?man -s:initialize disk with BSD disklabel
578+	case 's':
579+		opt_init_bsd = 1;
580+		break;
581 	// ?man -a:add partition
582 	case 'a':
583 		opt_add = 1;
584@@ -433,10 +693,18 @@ main(int argc, char *argv[])
585 	case 'd':
586 		opt_del = 1;
587 		break;
588-	// ?man -n:partition index
589-	case 'n':
590-		opt_part_idx = (int)estrtol(EARGF(usage()), 10);
591+	// ?man -n:partition index or letter
592+	case 'n': {
593+		char *arg = EARGF(usage());
594+		if (arg[0] >= 'a' && arg[0] <= 'p' && arg[1] == '\0') {
595+			opt_part_idx = arg[0] - 'a' + 1;
596+			opt_part_is_letter = 1;
597+		} else {
598+			opt_part_idx = (int)estrtol(arg, 10);
599+			opt_part_is_letter = 0;
600+		}
601 		break;
602+	}
603 	// ?man -b:starting sector LBA
604 	case 'b':
605 		opt_start = (uint64_t)estrtoul(EARGF(usage()), 10);
606@@ -445,7 +713,7 @@ main(int argc, char *argv[])
607 	case 'e':
608 		opt_end = (uint64_t)estrtoul(EARGF(usage()), 10);
609 		break;
610-	// ?man -t:partition type (MBR hex or GPT string)
611+	// ?man -t:partition type (MBR/BSD hex or GPT string)
612 	case 't':
613 		opt_type = EARGF(usage());
614 		break;
615@@ -459,20 +727,24 @@ main(int argc, char *argv[])
616 		if (!list)
617 			return 1;
618 		for (curr = list; curr; curr = curr->next) {
619-			char devpath[128];
620-			snprintf(devpath, sizeof(devpath), "/dev/%s", curr->name);
621-			do_print(devpath);
622+			do_print(curr->path);
623 		}
624 		blockdev_free_list(list);
625-		return 0;
626+		if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
627+			ret = 2;
628+		return ret;
629 	}
630 
631 	if (argc != 1)
632 		usage();
633 
634 	if (opt_print) {
635-		return do_print(argv[0]) < 0 ? 1 : 0;
636+		ret = do_print(argv[0]) < 0 ? 1 : 0;
637+	} else {
638+		ret = do_fdisk(argv[0]) < 0 ? 1 : 0;
639 	}
640 
641-	return do_fdisk(argv[0]) < 0 ? 1 : 0;
642+	if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
643+		ret = 2;
644+	return ret;
645 }
+3, -0
 1@@ -78,6 +78,9 @@ main(int argc, char *argv[])
 2 	printf("%s\n", hdrbuf);
 3 
 4 	for (curr = list; curr; curr = curr->next) {
 5+		if (strncmp(curr->name, "ram", 3) == 0)
 6+			continue;
 7+
 8 		print_line(max_len,
 9 		           curr->name,
10 		           curr->major, curr->minor,
+2489, -0
   1@@ -0,0 +1,2489 @@
   2+/* See LICENSE file for copyright and license details. */
   3+#include "util.h"
   4+
   5+#include <sys/stat.h>
   6+#include <sys/wait.h>
   7+
   8+#include <ctype.h>
   9+#include <dirent.h>
  10+#include <err.h>
  11+#include <errno.h>
  12+#include <fcntl.h>
  13+#include <fnmatch.h>
  14+#include <getopt.h>
  15+#include <limits.h>
  16+#include <math.h>
  17+#include <paths.h>
  18+#include <poll.h>
  19+#include <regex.h>
  20+#include <signal.h>
  21+#include <stdio.h>
  22+#include <stdlib.h>
  23+#include <string.h>
  24+#include <stdint.h>
  25+#include <time.h>
  26+#include <unistd.h>
  27+
  28+#ifndef __dead
  29+#define __dead __attribute__((__noreturn__))
  30+#endif
  31+
  32+#define d_status d_type
  33+
  34+/* output format options */
  35+#define D_NORMAL     0
  36+#define D_EDIT       -1
  37+#define D_REVERSE    1
  38+#define D_CONTEXT    2
  39+#define D_UNIFIED    3
  40+#define D_IFDEF      4
  41+#define D_NREVERSE   5
  42+#define D_BRIEF      6
  43+#define D_GFORMAT    7
  44+#define D_SIDEBYSIDE 8
  45+
  46+#define D_UNSET      -2
  47+
  48+/* algorithms */
  49+#define D_DIFFNONE     0
  50+#define D_DIFFSTONE    1
  51+#define D_DIFFMYERS    2
  52+#define D_DIFFPATIENCE 3
  53+
  54+/* output flags */
  55+#define D_HEADER 0x001
  56+#define D_EMPTY1 0x002
  57+#define D_EMPTY2 0x004
  58+
  59+/* command line flags */
  60+#define D_FORCEASCII     0x008
  61+#define D_FOLDBLANKS     0x010
  62+#define D_MINIMAL        0x020
  63+#define D_IGNORECASE     0x040
  64+#define D_PROTOTYPE      0x080
  65+#define D_EXPANDTABS     0x100
  66+#define D_IGNOREBLANKS   0x200
  67+#define D_STRIPCR        0x400
  68+#define D_SKIPBLANKLINES 0x800
  69+#define D_MATCHLAST      0x1000
  70+
  71+/* features supported by new algorithms */
  72+#define D_NEWALGO_FLAGS (D_FORCEASCII | D_PROTOTYPE | D_IGNOREBLANKS)
  73+
  74+/* status values for print_status and diffreg return values */
  75+#define D_SAME     0
  76+#define D_DIFFER   1
  77+#define D_BINARY   2
  78+#define D_MISMATCH1 3
  79+#define D_MISMATCH2 4
  80+#define D_SKIPPED1 5
  81+#define D_SKIPPED2 6
  82+#define D_ERROR    7
  83+
  84+/* color options */
  85+#define COLORFLAG_NEVER  0
  86+#define COLORFLAG_AUTO   1
  87+#define COLORFLAG_ALWAYS 2
  88+
  89+#define FUNCTION_CONTEXT_SIZE 55
  90+#define _PATH_PR "/usr/bin/pr"
  91+
  92+#ifndef roundup
  93+#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
  94+#endif
  95+
  96+struct Excludes {
  97+	char *pattern;
  98+	struct Excludes *next;
  99+};
 100+
 101+struct Cand {
 102+	int x;
 103+	int y;
 104+	int pred;
 105+};
 106+
 107+struct Line {
 108+	int serial;
 109+	int value;
 110+};
 111+
 112+struct ContextVec {
 113+	int a;
 114+	int b;
 115+	int c;
 116+	int d;
 117+};
 118+
 119+struct Pr {
 120+	int ostdout;
 121+	pid_t cpid;
 122+};
 123+
 124+struct Algorithm {
 125+	const char *name;
 126+	int id;
 127+};
 128+
 129+static struct Algorithm algorithms[] = {
 130+	{"stone", D_DIFFSTONE},
 131+	{"myers", D_DIFFMYERS},
 132+	{"patience", D_DIFFPATIENCE},
 133+	{NULL, D_DIFFNONE}
 134+};
 135+
 136+/* options */
 137+#define OPTIONS "0123456789A:aBbC:cdD:efF:HhI:iL:lnNPpqrS:sTtU:uwW:X:x:y"
 138+enum {
 139+	OPT_TSIZE = CHAR_MAX + 1,
 140+	OPT_STRIPCR,
 141+	OPT_IGN_FN_CASE,
 142+	OPT_NO_IGN_FN_CASE,
 143+	OPT_NORMAL,
 144+	OPT_HELP,
 145+	OPT_HORIZON_LINES,
 146+	OPT_CHANGED_GROUP_FORMAT,
 147+	OPT_SUPPRESS_COMMON,
 148+	OPT_COLOR,
 149+	OPT_NO_DEREFERENCE,
 150+	OPT_VERSION,
 151+};
 152+
 153+static struct option longopts[] = {
 154+	{ "algorithm",                  required_argument,      0,      'A' },
 155+	{ "text",                       no_argument,            0,      'a' },
 156+	{ "ignore-space-change",        no_argument,            0,      'b' },
 157+	{ "context",                    optional_argument,      0,      'C' },
 158+	{ "ifdef",                      required_argument,      0,      'D' },
 159+	{ "minimal",                    no_argument,            0,      'd' },
 160+	{ "ed",                         no_argument,            0,      'e' },
 161+	{ "forward-ed",                 no_argument,            0,      'f' },
 162+	{ "show-function-line",         required_argument,      0,      'F' },
 163+	{ "speed-large-files",          no_argument,            NULL,   'H' },
 164+	{ "ignore-blank-lines",         no_argument,            0,      'B' },
 165+	{ "ignore-matching-lines",      required_argument,      0,      'I' },
 166+	{ "ignore-case",                no_argument,            0,      'i' },
 167+	{ "paginate",                   no_argument,            NULL,   'l' },
 168+	{ "label",                      required_argument,      0,      'L' },
 169+	{ "new-file",                   no_argument,            0,      'N' },
 170+	{ "rcs",                        no_argument,            0,      'n' },
 171+	{ "unidirectional-new-file",    no_argument,            0,      'P' },
 172+	{ "show-c-function",            no_argument,            0,      'p' },
 173+	{ "brief",                      no_argument,            0,      'q' },
 174+	{ "recursive",                  no_argument,            0,      'r' },
 175+	{ "report-identical-files",     no_argument,            0,      's' },
 176+	{ "starting-file",              required_argument,      0,      'S' },
 177+	{ "expand-tabs",                no_argument,            0,      't' },
 178+	{ "initial-tab",                no_argument,            0,      'T' },
 179+	{ "unified",                    optional_argument,      0,      'U' },
 180+	{ "ignore-all-space",           no_argument,            0,      'w' },
 181+	{ "width",                      required_argument,      0,      'W' },
 182+	{ "exclude",                    required_argument,      0,      'x' },
 183+	{ "exclude-from",               required_argument,      0,      'X' },
 184+	{ "side-by-side",               no_argument,            NULL,   'y' },
 185+	{ "ignore-file-name-case",      no_argument,            NULL,   OPT_IGN_FN_CASE },
 186+	{ "help",                       no_argument,            NULL,   OPT_HELP},
 187+	{ "horizon-lines",              required_argument,      NULL,   OPT_HORIZON_LINES },
 188+	{ "no-dereference",             no_argument,            NULL,   OPT_NO_DEREFERENCE},
 189+	{ "no-ignore-file-name-case",   no_argument,            NULL,   OPT_NO_IGN_FN_CASE },
 190+	{ "normal",                     no_argument,            NULL,   OPT_NORMAL },
 191+	{ "strip-trailing-cr",          no_argument,            NULL,   OPT_STRIPCR },
 192+	{ "tabsize",                    required_argument,      NULL,   OPT_TSIZE },
 193+	{ "changed-group-format",       required_argument,      NULL,   OPT_CHANGED_GROUP_FORMAT},
 194+	{ "suppress-common-lines",      no_argument,            NULL,   OPT_SUPPRESS_COMMON },
 195+	{ "color",                      optional_argument,      NULL,   OPT_COLOR },
 196+	{ "version",                    no_argument,            NULL,   OPT_VERSION},
 197+	{ NULL,                         0,                      0,      '\0'}
 198+};
 199+
 200+static const char diff_version[] = "FreeBSD diff 20240307";
 201+int lflag, Nflag, Pflag, rflag, sflag, Tflag, cflag;
 202+int ignore_file_case, suppress_common, color, noderef;
 203+static int help = 0;
 204+int diff_format, diff_context, diff_algorithm, status;
 205+int diff_algorithm_set;
 206+int tabsize = 8, width = 130;
 207+static int colorflag = COLORFLAG_NEVER;
 208+char *start, *ifdefname, *diffargs, *label[2];
 209+char *ignore_pats, *most_recent_pat;
 210+char *group_format = NULL;
 211+const char *add_code, *del_code;
 212+struct stat stb1, stb2;
 213+struct Excludes *excludes_list;
 214+regex_t ignore_re, most_recent_re;
 215+
 216+enum Readhash {
 217+	RH_BINARY,
 218+	RH_OK,
 219+	RH_EOF
 220+};
 221+
 222+static struct Line *file[2];
 223+
 224+static int *J;
 225+static int *class;
 226+static int *klist;
 227+static int *member;
 228+static int clen;
 229+static int inifdef;
 230+static size_t len[2];
 231+static size_t pref, suff;
 232+static size_t slen[2];
 233+static int anychange;
 234+static int hw, lpad, rpad;
 235+static int edoffset;
 236+static long *ixnew;
 237+static long *ixold;
 238+static struct Cand *clist;
 239+static int clistlen;
 240+static struct Line *sfile[2];
 241+static int (*chrtran)(int);
 242+static struct ContextVec *context_vec_start;
 243+static struct ContextVec *context_vec_end;
 244+static struct ContextVec *context_vec_ptr;
 245+static char lastbuf[FUNCTION_CONTEXT_SIZE];
 246+static int lastline;
 247+static int lastmatchline;
 248+
 249+static int sigpipe[2] = {-1, -1};
 250+static struct pollfd poll_fd;
 251+
 252+static void checked_regcomp(char const *, regex_t *);
 253+static void usage(void);
 254+static void conflicting_format(void);
 255+static void push_excludes(char *);
 256+static void push_ignore_pats(char *);
 257+static void read_excludes_file(char *);
 258+static void set_argstr(char **, char **);
 259+static char *diff_splice(char *, char *);
 260+static int do_color(void);
 261+static int cup2low(int);
 262+static int clow2low(int);
 263+static void xasprintf(char **, const char *, ...);
 264+
 265+int diffreg(char *, char *, int, int);
 266+void diffdir(char *, char *, int);
 267+void print_status(int, char *, char *, const char *);
 268+
 269+static int selectfile(const struct dirent *);
 270+static void diffit(struct dirent *, char *, size_t, struct dirent *, char *, size_t, int);
 271+static void print_only(const char *, size_t, const char *);
 272+
 273+static FILE *opentemp(const char *);
 274+static void output(char *, FILE *, char *, FILE *, int);
 275+static void check(FILE *, FILE *, int);
 276+static void range(int, int, const char *);
 277+static void uni_range(int, int);
 278+static void dump_context_vec(FILE *, FILE *, int);
 279+static void dump_unified_vec(FILE *, FILE *, int);
 280+static int prepare(int, FILE *, size_t, int);
 281+static void prune(void);
 282+static void equiv(struct Line *, int, struct Line *, int, int *);
 283+static void unravel(int);
 284+static void unsort(struct Line *, int, int *);
 285+static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
 286+static void sort(struct Line *, int);
 287+static void print_header(const char *, const char *);
 288+static void print_space(int, int, int);
 289+static int ignoreline_pattern(char *);
 290+static int ignoreline(char *, int);
 291+static int asciifile(FILE *);
 292+static int fetch(long *, int, int, FILE *, int, int, int);
 293+static int newcand(int, int, int);
 294+static int search(int *, int, int);
 295+static int skipline(FILE *);
 296+static int stone(int *, int, int *, int *, int);
 297+static enum Readhash readhash(FILE *, int, unsigned *);
 298+static int files_differ(FILE *, FILE *, int);
 299+static char *match_function(const long *, int, FILE *);
 300+static char *preadline(int, size_t, off_t);
 301+
 302+static void handle_sig(int);
 303+struct Pr *start_pr(char *, char *);
 304+void stop_pr(struct Pr *);
 305+
 306+static void
 307+handle_sig(int signo)
 308+{
 309+	write(sigpipe[1], &signo, sizeof(signo));
 310+}
 311+
 312+struct Pr *
 313+start_pr(char *file1, char *file2)
 314+{
 315+	int pfd[2];
 316+	pid_t pid;
 317+	char *header;
 318+	struct Pr *pr;
 319+
 320+	pr = ecalloc(1, sizeof(*pr));
 321+	xasprintf(&header, "%s %s %s", diffargs, file1, file2);
 322+	signal(SIGPIPE, SIG_IGN);
 323+	fflush(stdout);
 324+	if (pipe(pfd) == -1)
 325+		err(2, "pipe");
 326+	if (sigpipe[0] < 0) {
 327+		if (pipe(sigpipe) == -1)
 328+			err(2, "pipe");
 329+		if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC) == -1)
 330+			err(2, "fcntl");
 331+		if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC) == -1)
 332+			err(2, "fcntl");
 333+		if (signal(SIGCHLD, handle_sig) == SIG_ERR)
 334+			err(2, "signal");
 335+		poll_fd.fd = sigpipe[0];
 336+		poll_fd.events = POLLIN;
 337+	}
 338+	poll_fd.revents = 0;
 339+	switch ((pid = fork())) {
 340+	case -1:
 341+		status |= 2;
 342+		free(header);
 343+		err(2, "no more processes");
 344+	case 0:
 345+		if (pfd[0] != STDIN_FILENO) {
 346+			dup2(pfd[0], STDIN_FILENO);
 347+			close(pfd[0]);
 348+		}
 349+		close(pfd[1]);
 350+		execl(_PATH_PR, _PATH_PR, "-h", header, (char *)0);
 351+		_exit(127);
 352+	default:
 353+		if (pfd[1] != STDOUT_FILENO) {
 354+			pr->ostdout = dup(STDOUT_FILENO);
 355+			dup2(pfd[1], STDOUT_FILENO);
 356+			close(pfd[1]);
 357+		}
 358+		close(pfd[0]);
 359+		free(header);
 360+		pr->cpid = pid;
 361+	}
 362+	return pr;
 363+}
 364+
 365+void
 366+stop_pr(struct Pr *pr)
 367+{
 368+	int wstatus;
 369+	int done = 0;
 370+
 371+	if (!pr)
 372+		return;
 373+
 374+	fflush(stdout);
 375+	if (pr->ostdout != STDOUT_FILENO) {
 376+		close(STDOUT_FILENO);
 377+		dup2(pr->ostdout, STDOUT_FILENO);
 378+		close(pr->ostdout);
 379+	}
 380+	while (!done) {
 381+		pid_t wpid;
 382+		int npe = poll(&poll_fd, 1, -1);
 383+		if (npe == -1) {
 384+			if (errno == EINTR)
 385+				continue;
 386+			err(2, "poll");
 387+		}
 388+		if (poll_fd.revents != POLLIN)
 389+			continue;
 390+		if (read(poll_fd.fd, &npe, sizeof(npe)) < 0)
 391+			err(2, "read");
 392+		while ((wpid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
 393+			if (wpid != pr->cpid)
 394+				continue;
 395+			if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0)
 396+				errx(2, "pr exited abnormally");
 397+			else if (WIFSIGNALED(wstatus))
 398+				errx(2, "pr killed by signal %d", WTERMSIG(wstatus));
 399+			done = 1;
 400+			break;
 401+		}
 402+	}
 403+	free(pr);
 404+}
 405+
 406+// ?man diff: differential file and directory comparator
 407+// ?man arguments: file1 file2
 408+// ?man 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
 409+int
 410+main(int argc, char **argv)
 411+{
 412+	const char *errstr = NULL;
 413+	char *ep, **oargv;
 414+	long l;
 415+	int ch, dflags, lastch, gotstdin, prevoptind, newarg;
 416+
 417+	oargv = argv;
 418+	gotstdin = 0;
 419+	dflags = 0;
 420+	lastch = '\0';
 421+	prevoptind = 1;
 422+	newarg = 1;
 423+	diff_context = 3;
 424+	diff_format = D_UNSET;
 425+	diff_algorithm = D_DIFFMYERS;
 426+	diff_algorithm_set = 0;
 427+#define FORMAT_MISMATCHED(type) \
 428+	(diff_format != D_UNSET && diff_format != (type))
 429+	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
 430+		// ?man -0: context length
 431+		// ?man -1: context length
 432+		// ?man -2: context length
 433+		// ?man -3: context length
 434+		// ?man -4: context length
 435+		// ?man -5: context length
 436+		// ?man -6: context length
 437+		// ?man -7: context length
 438+		// ?man -8: context length
 439+		// ?man -9: context length
 440+		switch (ch) {
 441+		case '0': case '1': case '2': case '3': case '4':
 442+		case '5': case '6': case '7': case '8': case '9':
 443+			if (newarg)
 444+				usage();
 445+			else if (lastch == 'c' || lastch == 'u')
 446+				diff_context = 0;
 447+			else if (!isdigit(lastch) || diff_context > INT_MAX / 10)
 448+				usage();
 449+			diff_context = (diff_context * 10) + (ch - '0');
 450+			break;
 451+		// ?man -A:algo: algorithm (stone, myers, patience)
 452+		case 'A':
 453+			diff_algorithm = D_DIFFNONE;
 454+			for (struct Algorithm *a = algorithms; a->name; a++) {
 455+				if (strcasecmp(optarg, a->name) == 0) {
 456+					diff_algorithm = a->id;
 457+					diff_algorithm_set = 1;
 458+					break;
 459+				}
 460+			}
 461+			if (diff_algorithm == D_DIFFNONE) {
 462+				printf("unknown algorithm: %s\n", optarg);
 463+				usage();
 464+			}
 465+			break;
 466+		// ?man -a: force text mode
 467+		case 'a':
 468+			dflags |= D_FORCEASCII;
 469+			break;
 470+		// ?man -b: ignore space changes
 471+		case 'b':
 472+			dflags |= D_FOLDBLANKS;
 473+			break;
 474+		// ?man -C:lines: context format
 475+		case 'C':
 476+		// ?man -c: context format
 477+		case 'c':
 478+			if (FORMAT_MISMATCHED(D_CONTEXT))
 479+				conflicting_format();
 480+			cflag = 1;
 481+			diff_format = D_CONTEXT;
 482+			if (optarg != NULL) {
 483+				l = strtol(optarg, &ep, 10);
 484+				if (*ep != '\0' || l < 0 || l >= INT_MAX)
 485+					usage();
 486+				diff_context = (int)l;
 487+			}
 488+			break;
 489+		// ?man -d: minimal diff search
 490+		case 'd':
 491+			dflags |= D_MINIMAL;
 492+			break;
 493+		// ?man -D:name: ifdef format
 494+		case 'D':
 495+			if (FORMAT_MISMATCHED(D_IFDEF))
 496+				conflicting_format();
 497+			diff_format = D_IFDEF;
 498+			ifdefname = optarg;
 499+			break;
 500+		// ?man -e: ed script format
 501+		case 'e':
 502+			if (FORMAT_MISMATCHED(D_EDIT))
 503+				conflicting_format();
 504+			diff_format = D_EDIT;
 505+			break;
 506+		// ?man -f: forward ed script format
 507+		case 'f':
 508+			if (FORMAT_MISMATCHED(D_REVERSE))
 509+				conflicting_format();
 510+			diff_format = D_REVERSE;
 511+			break;
 512+		// ?man -H: speed up large files (stub)
 513+		case 'H':
 514+			break;
 515+		// ?man -h: backward compatibility (stub)
 516+		case 'h':
 517+			break;
 518+		// ?man -B: ignore blank lines
 519+		case 'B':
 520+			dflags |= D_SKIPBLANKLINES;
 521+			break;
 522+		// ?man -F:pat: show function matching pattern
 523+		case 'F':
 524+			if (dflags & D_PROTOTYPE)
 525+				conflicting_format();
 526+			dflags |= D_MATCHLAST;
 527+			most_recent_pat = estrdup(optarg);
 528+			break;
 529+		// ?man -I:pat: ignore pattern matching lines
 530+		case 'I':
 531+			push_ignore_pats(optarg);
 532+			break;
 533+		// ?man -i: ignore case
 534+		case 'i':
 535+			dflags |= D_IGNORECASE;
 536+			break;
 537+		// ?man -L:label: custom label
 538+		case 'L':
 539+			if (label[0] == NULL)
 540+				label[0] = optarg;
 541+			else if (label[1] == NULL)
 542+				label[1] = optarg;
 543+			else
 544+				usage();
 545+			break;
 546+		// ?man -l: paginate output using pr
 547+		case 'l':
 548+			lflag = 1;
 549+			break;
 550+		// ?man -N: treat missing files as empty
 551+		case 'N':
 552+			Nflag = 1;
 553+			break;
 554+		// ?man -n: rcs format
 555+		case 'n':
 556+			if (FORMAT_MISMATCHED(D_NREVERSE))
 557+				conflicting_format();
 558+			diff_format = D_NREVERSE;
 559+			break;
 560+		// ?man -p: show C prototype context
 561+		case 'p':
 562+			if (dflags & D_MATCHLAST)
 563+				conflicting_format();
 564+			dflags |= D_PROTOTYPE;
 565+			break;
 566+		// ?man -P: treat missing destination files as empty
 567+		case 'P':
 568+			Pflag = 1;
 569+			break;
 570+		// ?man -r: recursive directory comparison
 571+		case 'r':
 572+			rflag = 1;
 573+			break;
 574+		// ?man -q: brief output (differ / same only)
 575+		case 'q':
 576+			if (FORMAT_MISMATCHED(D_BRIEF))
 577+				conflicting_format();
 578+			diff_format = D_BRIEF;
 579+			break;
 580+		// ?man -S:name: starting file in directory comparison
 581+		case 'S':
 582+			start = optarg;
 583+			break;
 584+		// ?man -s: report identical files
 585+		case 's':
 586+			sflag = 1;
 587+			break;
 588+		// ?man -T: initial tab alignment
 589+		case 'T':
 590+			Tflag = 1;
 591+			break;
 592+		// ?man -t: expand tabs in output
 593+		case 't':
 594+			dflags |= D_EXPANDTABS;
 595+			break;
 596+		// ?man -U:lines: unified context format
 597+		case 'U':
 598+		// ?man -u: unified context format
 599+		case 'u':
 600+			if (FORMAT_MISMATCHED(D_UNIFIED))
 601+				conflicting_format();
 602+			diff_format = D_UNIFIED;
 603+			if (optarg != NULL) {
 604+				l = strtol(optarg, &ep, 10);
 605+				if (*ep != '\0' || l < 0 || l >= INT_MAX)
 606+					usage();
 607+				diff_context = (int)l;
 608+			}
 609+			break;
 610+		// ?man -w: ignore all whitespace
 611+		case 'w':
 612+			dflags |= D_IGNOREBLANKS;
 613+			break;
 614+		// ?man -W:cols: column width for side-by-side
 615+		case 'W':
 616+			width = (int)strtonum(optarg, 1, INT_MAX, &errstr);
 617+			if (errstr) {
 618+				warnx("invalid width argument");
 619+				usage();
 620+			}
 621+			break;
 622+		// ?man -X:file: exclude patterns file
 623+		case 'X':
 624+			read_excludes_file(optarg);
 625+			break;
 626+		// ?man -x:pat: exclude pattern
 627+		case 'x':
 628+			push_excludes(optarg);
 629+			break;
 630+		// ?man -y: side by side format
 631+		case 'y':
 632+			if (FORMAT_MISMATCHED(D_SIDEBYSIDE))
 633+				conflicting_format();
 634+			diff_format = D_SIDEBYSIDE;
 635+			break;
 636+		case OPT_CHANGED_GROUP_FORMAT:
 637+			if (FORMAT_MISMATCHED(D_GFORMAT))
 638+				conflicting_format();
 639+			diff_format = D_GFORMAT;
 640+			group_format = optarg;
 641+			break;
 642+		case OPT_HELP:
 643+			help = 1;
 644+			usage();
 645+			break;
 646+		case OPT_HORIZON_LINES:
 647+			break;
 648+		case OPT_IGN_FN_CASE:
 649+			ignore_file_case = 1;
 650+			break;
 651+		case OPT_NO_IGN_FN_CASE:
 652+			ignore_file_case = 0;
 653+			break;
 654+		case OPT_NORMAL:
 655+			if (FORMAT_MISMATCHED(D_NORMAL))
 656+				conflicting_format();
 657+			diff_format = D_NORMAL;
 658+			break;
 659+		case OPT_TSIZE:
 660+			tabsize = (int)strtonum(optarg, 1, INT_MAX, &errstr);
 661+			if (errstr) {
 662+				warnx("invalid tabsize argument");
 663+				usage();
 664+			}
 665+			break;
 666+		case OPT_STRIPCR:
 667+			dflags |= D_STRIPCR;
 668+			break;
 669+		case OPT_SUPPRESS_COMMON:
 670+			suppress_common = 1;
 671+			break;
 672+		case OPT_COLOR:
 673+			if (optarg == NULL || strncmp(optarg, "auto", 4) == 0)
 674+				colorflag = COLORFLAG_AUTO;
 675+			else if (strncmp(optarg, "always", 6) == 0)
 676+				colorflag = COLORFLAG_ALWAYS;
 677+			else if (strncmp(optarg, "never", 5) == 0)
 678+				colorflag = COLORFLAG_NEVER;
 679+			else
 680+				errx(2, "unsupported color option %s", optarg);
 681+			break;
 682+		case OPT_NO_DEREFERENCE:
 683+			noderef = 1;
 684+			break;
 685+		case OPT_VERSION:
 686+			printf("%s\n", diff_version);
 687+			exit(0);
 688+		default:
 689+			usage();
 690+			break;
 691+		}
 692+		lastch = ch;
 693+		newarg = optind != prevoptind;
 694+		prevoptind = optind;
 695+	}
 696+	if (diff_format == D_UNSET && (dflags & D_PROTOTYPE) != 0)
 697+		diff_format = D_CONTEXT;
 698+	if (diff_format == D_UNSET)
 699+		diff_format = D_NORMAL;
 700+	argc -= optind;
 701+	argv += optind;
 702+
 703+	if (do_color()) {
 704+		char *p;
 705+		const char *env;
 706+
 707+		color = 1;
 708+		add_code = "32";
 709+		del_code = "31";
 710+		env = getenv("DIFFCOLORS");
 711+		if (env != NULL && *env != '\0' && (p = estrdup(env))) {
 712+			add_code = p;
 713+			strsep(&p, ":");
 714+			if (p != NULL)
 715+				del_code = p;
 716+		}
 717+	}
 718+
 719+	if (argc != 2)
 720+		usage();
 721+	checked_regcomp(ignore_pats, &ignore_re);
 722+	checked_regcomp(most_recent_pat, &most_recent_re);
 723+	if (strcmp(argv[0], "-") == 0) {
 724+		fstat(STDIN_FILENO, &stb1);
 725+		gotstdin = 1;
 726+	} else if (stat(argv[0], &stb1) != 0) {
 727+		if (!Nflag || errno != ENOENT)
 728+			err(2, "%s", argv[0]);
 729+		dflags |= D_EMPTY1;
 730+		memset(&stb1, 0, sizeof(struct stat));
 731+	}
 732+
 733+	if (strcmp(argv[1], "-") == 0) {
 734+		fstat(STDIN_FILENO, &stb2);
 735+		gotstdin = 1;
 736+	} else if (stat(argv[1], &stb2) != 0) {
 737+		if (!Nflag || errno != ENOENT)
 738+			err(2, "%s", argv[1]);
 739+		dflags |= D_EMPTY2;
 740+		memset(&stb2, 0, sizeof(stb2));
 741+		stb2.st_mode = stb1.st_mode;
 742+	}
 743+
 744+	if (dflags & D_EMPTY1 && dflags & D_EMPTY2){
 745+		warn("%s", argv[0]);
 746+		warn("%s", argv[1]);
 747+		exit(2);
 748+	}
 749+
 750+	if (stb1.st_mode == 0)
 751+		stb1.st_mode = stb2.st_mode;
 752+
 753+	if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
 754+		errx(2, "cant compare - to directory");
 755+	set_argstr(oargv, argv);
 756+	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
 757+		if (diff_format == D_IFDEF)
 758+			errx(2, "-D option not supported with directories");
 759+		diffdir(argv[0], argv[1], dflags);
 760+	} else {
 761+		if (S_ISDIR(stb1.st_mode)) {
 762+			argv[0] = diff_splice(argv[0], argv[1]);
 763+			if (stat(argv[0], &stb1) == -1)
 764+				err(2, "%s", argv[0]);
 765+		}
 766+		if (S_ISDIR(stb2.st_mode)) {
 767+			argv[1] = diff_splice(argv[1], argv[0]);
 768+			if (stat(argv[1], &stb2) == -1)
 769+				err(2, "%s", argv[1]);
 770+		}
 771+		print_status(diffreg(argv[0], argv[1], dflags, 1), argv[0], argv[1], "");
 772+	}
 773+	if (fflush(stdout) != 0)
 774+		err(2, "stdout");
 775+	exit(status);
 776+}
 777+
 778+static void
 779+checked_regcomp(char const *pattern, regex_t *comp)
 780+{
 781+	char buf[BUFSIZ];
 782+	int error;
 783+
 784+	if (pattern == NULL)
 785+		return;
 786+
 787+	error = regcomp(comp, pattern, REG_NEWLINE | REG_EXTENDED);
 788+	if (error != 0) {
 789+		regerror(error, comp, buf, sizeof(buf));
 790+		if (*pattern != '\0')
 791+			errx(2, "%s: %s", pattern, buf);
 792+		else
 793+			errx(2, "%s", buf);
 794+	}
 795+}
 796+
 797+static void
 798+set_argstr(char **av, char **ave)
 799+{
 800+	size_t argsize;
 801+	char **ap;
 802+
 803+	argsize = strlen("diff") + 1;
 804+	for (ap = av + 1; ap < ave; ap++) {
 805+		if (strcmp(*ap, "--") != 0)
 806+			argsize += 1 + strlen(*ap);
 807+	}
 808+	diffargs = emalloc(argsize);
 809+	strlcpy(diffargs, "diff", argsize);
 810+	for (ap = av + 1; ap < ave; ap++) {
 811+		if (strcmp(*ap, "--") != 0) {
 812+			strlcat(diffargs, " ", argsize);
 813+			strlcat(diffargs, *ap, argsize);
 814+		}
 815+	}
 816+}
 817+
 818+static void
 819+read_excludes_file(char *file)
 820+{
 821+	FILE *fp;
 822+	char *pattern = NULL;
 823+	size_t blen = 0;
 824+	ssize_t len;
 825+
 826+	if (strcmp(file, "-") == 0)
 827+		fp = stdin;
 828+	else if ((fp = fopen(file, "r")) == NULL)
 829+		err(2, "%s", file);
 830+	while ((len = getline(&pattern, &blen, fp)) >= 0) {
 831+		if ((len > 0) && (pattern[len - 1] == '\n'))
 832+			pattern[len - 1] = '\0';
 833+		push_excludes(pattern);
 834+		pattern = NULL;
 835+		blen = 0;
 836+	}
 837+	free(pattern);
 838+	if (strcmp(file, "-") != 0)
 839+		fclose(fp);
 840+}
 841+
 842+static void
 843+push_excludes(char *pattern)
 844+{
 845+	struct Excludes *entry;
 846+
 847+	entry = emalloc(sizeof(*entry));
 848+	entry->pattern = pattern;
 849+	entry->next = excludes_list;
 850+	excludes_list = entry;
 851+}
 852+
 853+static void
 854+push_ignore_pats(char *pattern)
 855+{
 856+	size_t len;
 857+
 858+	if (ignore_pats == NULL)
 859+		ignore_pats = estrdup(pattern);
 860+	else {
 861+		len = strlen(ignore_pats) + strlen(pattern) + 2;
 862+		ignore_pats = ereallocarray(ignore_pats, 1, len);
 863+		strlcat(ignore_pats, "|", len);
 864+		strlcat(ignore_pats, pattern, len);
 865+	}
 866+}
 867+
 868+void
 869+print_status(int val, char *path1, char *path2, const char *entry)
 870+{
 871+	if (label[0] != NULL)
 872+		path1 = label[0];
 873+	if (label[1] != NULL)
 874+		path2 = label[1];
 875+
 876+	switch (val) {
 877+	case D_BINARY:
 878+		printf("Binary files %s%s and %s%s differ\n", path1, entry, path2, entry);
 879+		break;
 880+	case D_DIFFER:
 881+		if (diff_format == D_BRIEF)
 882+			printf("Files %s%s and %s%s differ\n", path1, entry, path2, entry);
 883+		break;
 884+	case D_SAME:
 885+		if (sflag)
 886+			printf("Files %s%s and %s%s are identical\n", path1, entry, path2, entry);
 887+		break;
 888+	case D_MISMATCH1:
 889+		printf("File %s%s is directory, %s%s is file\n", path1, entry, path2, entry);
 890+		break;
 891+	case D_MISMATCH2:
 892+		printf("File %s%s is file, %s%s is directory\n", path1, entry, path2, entry);
 893+		break;
 894+	case D_SKIPPED1:
 895+		printf("File %s%s is not regular file or directory\n", path1, entry);
 896+		break;
 897+	case D_SKIPPED2:
 898+		printf("File %s%s is not regular file or directory\n", path2, entry);
 899+		break;
 900+	}
 901+}
 902+
 903+static void
 904+usage(void)
 905+{
 906+	fprintf(stderr, "usage: diff [-aBbdipTtw] [-c|-e|-f|-n|-q|-u|-y] [-A algo] "
 907+	    "[-I pattern] [-F pattern] [-L label] file1 file2\n");
 908+	exit(2);
 909+}
 910+
 911+static void
 912+conflicting_format(void)
 913+{
 914+	errx(2, "conflicting output format options");
 915+}
 916+
 917+static char *
 918+diff_splice(char *dir, char *file)
 919+{
 920+	char *p, *res;
 921+
 922+	p = strrchr(file, '/');
 923+	p = p ? p + 1 : file;
 924+	xasprintf(&res, "%s/%s", dir, p);
 925+	return res;
 926+}
 927+
 928+static void
 929+xasprintf(char **strp, const char *fmt, ...)
 930+{
 931+	va_list ap;
 932+	int r;
 933+
 934+	va_start(ap, fmt);
 935+	r = vasprintf(strp, fmt, ap);
 936+	va_end(ap);
 937+	if (r == -1)
 938+		err(2, "asprintf");
 939+}
 940+
 941+static int
 942+cup2low(int c)
 943+{
 944+	return tolower(c);
 945+}
 946+
 947+static int
 948+clow2low(int c)
 949+{
 950+	return c;
 951+}
 952+
 953+static int
 954+do_color(void)
 955+{
 956+	if (colorflag == COLORFLAG_ALWAYS)
 957+		return 1;
 958+	if (colorflag == COLORFLAG_NEVER)
 959+		return 0;
 960+	if (isatty(STDOUT_FILENO)) {
 961+		char *term = getenv("COLORTERM");
 962+		if (term && *term != '\0')
 963+			return 1;
 964+	}
 965+	return 0;
 966+}
 967+
 968+void
 969+diffdir(char *p1, char *p2, int flags)
 970+{
 971+	struct dirent *dent1, **dp1, **edp1, **dirp1 = NULL;
 972+	struct dirent *dent2, **dp2, **edp2, **dirp2 = NULL;
 973+	size_t dirlen1, dirlen2;
 974+	char path1[PATH_MAX], path2[PATH_MAX];
 975+	int pos;
 976+
 977+	edp1 = edp2 = NULL;
 978+	dirlen1 = strlcpy(path1, *p1 ? p1 : ".", sizeof(path1));
 979+	if (dirlen1 >= sizeof(path1) - 1) {
 980+		errno = ENAMETOOLONG;
 981+		warn("%s", p1);
 982+		status |= 2;
 983+		return;
 984+	}
 985+	if (path1[dirlen1 - 1] != '/') {
 986+		path1[dirlen1++] = '/';
 987+		path1[dirlen1] = '\0';
 988+	}
 989+	dirlen2 = strlcpy(path2, *p2 ? p2 : ".", sizeof(path2));
 990+	if (dirlen2 >= sizeof(path2) - 1) {
 991+		errno = ENAMETOOLONG;
 992+		warn("%s", p2);
 993+		status |= 2;
 994+		return;
 995+	}
 996+	if (path2[dirlen2 - 1] != '/') {
 997+		path2[dirlen2++] = '/';
 998+		path2[dirlen2] = '\0';
 999+	}
1000+
1001+	pos = scandir(path1, &dirp1, selectfile, alphasort);
1002+	if (pos == -1) {
1003+		if (errno == ENOENT && (Nflag || Pflag))
1004+			pos = 0;
1005+		else {
1006+			warn("%s", path1);
1007+			goto closem;
1008+		}
1009+	}
1010+	dp1 = dirp1;
1011+	edp1 = dirp1 + pos;
1012+
1013+	pos = scandir(path2, &dirp2, selectfile, alphasort);
1014+	if (pos == -1) {
1015+		if (errno == ENOENT && Nflag)
1016+			pos = 0;
1017+		else {
1018+			warn("%s", path2);
1019+			goto closem;
1020+		}
1021+	}
1022+	dp2 = dirp2;
1023+	edp2 = dirp2 + pos;
1024+
1025+	if (start != NULL) {
1026+		while (dp1 != edp1 && strcmp((*dp1)->d_name, start) < 0)
1027+			dp1++;
1028+		while (dp2 != edp2 && strcmp((*dp2)->d_name, start) < 0)
1029+			dp2++;
1030+	}
1031+
1032+	while (dp1 != edp1 || dp2 != edp2) {
1033+		dent1 = dp1 != edp1 ? *dp1 : NULL;
1034+		dent2 = dp2 != edp2 ? *dp2 : NULL;
1035+
1036+		pos = dent1 == NULL ? 1 : dent2 == NULL ? -1 :
1037+		    ignore_file_case ? strcasecmp(dent1->d_name, dent2->d_name) :
1038+		    strcmp(dent1->d_name, dent2->d_name);
1039+		if (pos == 0) {
1040+			diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1041+			dp1++;
1042+			dp2++;
1043+		} else if (pos < 0) {
1044+			if (Nflag)
1045+				diffit(dent1, path1, dirlen1, dent2, path2, dirlen2, flags);
1046+			else {
1047+				print_only(path1, dirlen1, dent1->d_name);
1048+				status |= 1;
1049+			}
1050+			dp1++;
1051+		} else {
1052+			if (Nflag || Pflag)
1053+				diffit(dent2, path1, dirlen1, dent1, path2, dirlen2, flags);
1054+			else {
1055+				print_only(path2, dirlen2, dent2->d_name);
1056+				status |= 1;
1057+			}
1058+			dp2++;
1059+		}
1060+	}
1061+
1062+closem:
1063+	if (dirp1 != NULL) {
1064+		for (dp1 = dirp1; dp1 < edp1; dp1++)
1065+			free(*dp1);
1066+		free(dirp1);
1067+	}
1068+	if (dirp2 != NULL) {
1069+		for (dp2 = dirp2; dp2 < edp2; dp2++)
1070+			free(*dp2);
1071+		free(dirp2);
1072+	}
1073+}
1074+
1075+static void
1076+diffit(struct dirent *dp, char *path1, size_t plen1, struct dirent *dp2,
1077+    char *path2, size_t plen2, int flags)
1078+{
1079+	flags |= D_HEADER;
1080+	strlcpy(path1 + plen1, dp->d_name, PATH_MAX - plen1);
1081+
1082+	if (ignore_file_case && strcasecmp(dp->d_name, dp2->d_name) == 0)
1083+		strlcpy(path2 + plen2, dp2->d_name, PATH_MAX - plen2);
1084+	else
1085+		strlcpy(path2 + plen2, dp->d_name, PATH_MAX - plen2);
1086+
1087+	if (noderef) {
1088+		if (lstat(path1, &stb1) != 0) {
1089+			if (!(Nflag || Pflag) || errno != ENOENT) {
1090+				warn("%s", path1);
1091+				return;
1092+			}
1093+			flags |= D_EMPTY1;
1094+			memset(&stb1, 0, sizeof(stb1));
1095+		}
1096+
1097+		if (lstat(path2, &stb2) != 0) {
1098+			if (!Nflag || errno != ENOENT) {
1099+				warn("%s", path2);
1100+				return;
1101+			}
1102+			flags |= D_EMPTY2;
1103+			memset(&stb2, 0, sizeof(stb2));
1104+			stb2.st_mode = stb1.st_mode;
1105+		}
1106+		if (stb1.st_mode == 0)
1107+			stb1.st_mode = stb2.st_mode;
1108+		if (S_ISLNK(stb1.st_mode) || S_ISLNK(stb2.st_mode)) {
1109+			if (S_ISLNK(stb1.st_mode) && S_ISLNK(stb2.st_mode)) {
1110+				char buf1[PATH_MAX];
1111+				char buf2[PATH_MAX];
1112+				ssize_t len1;
1113+				ssize_t len2;
1114+
1115+				len1 = readlink(path1, buf1, sizeof(buf1));
1116+				len2 = readlink(path2, buf2, sizeof(buf2));
1117+				if (len1 < 0 || len2 < 0) {
1118+					perror("reading links");
1119+					return;
1120+				}
1121+				buf1[len1] = '\0';
1122+				buf2[len2] = '\0';
1123+				if (len1 != len2 || strncmp(buf1, buf2, len1) != 0) {
1124+					printf("Symbolic links %s and %s differ\n", path1, path2);
1125+					status |= 1;
1126+				}
1127+				return;
1128+			}
1129+			printf("File %s is a %s while file %s is a %s\n",
1130+			    path1, S_ISLNK(stb1.st_mode) ? "symbolic link" :
1131+				S_ISDIR(stb1.st_mode) ? "directory" : "file",
1132+			    path2, S_ISLNK(stb2.st_mode) ? "symbolic link" :
1133+				S_ISDIR(stb2.st_mode) ? "directory" : "file");
1134+			status |= 1;
1135+			return;
1136+		}
1137+	} else {
1138+		if (stat(path1, &stb1) != 0) {
1139+			if (!(Nflag || Pflag) || errno != ENOENT) {
1140+				warn("%s", path1);
1141+				return;
1142+			}
1143+			flags |= D_EMPTY1;
1144+			memset(&stb1, 0, sizeof(stb1));
1145+		}
1146+
1147+		if (stat(path2, &stb2) != 0) {
1148+			if (!Nflag || errno != ENOENT) {
1149+				warn("%s", path2);
1150+				return;
1151+			}
1152+			flags |= D_EMPTY2;
1153+			memset(&stb2, 0, sizeof(stb2));
1154+			stb2.st_mode = stb1.st_mode;
1155+		}
1156+		if (stb1.st_mode == 0)
1157+			stb1.st_mode = stb2.st_mode;
1158+	}
1159+	if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1160+		if (rflag)
1161+			diffdir(path1, path2, flags);
1162+		else
1163+			printf("Common subdirectories: %s and %s\n", path1, path2);
1164+		return;
1165+	}
1166+	if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1167+		dp->d_status = D_SKIPPED1;
1168+	else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1169+		dp->d_status = D_SKIPPED2;
1170+	else
1171+		dp->d_status = diffreg(path1, path2, flags, 0);
1172+	print_status(dp->d_status, path1, path2, "");
1173+}
1174+
1175+static int
1176+selectfile(const struct dirent *dp)
1177+{
1178+	struct Excludes *excl;
1179+
1180+	if (dp->d_fileno == 0)
1181+		return 0;
1182+
1183+	if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
1184+	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
1185+		return 0;
1186+
1187+	for (excl = excludes_list; excl != NULL; excl = excl->next)
1188+		if (fnmatch(excl->pattern, dp->d_name, FNM_PATHNAME) == 0)
1189+			return 0;
1190+
1191+	return 1;
1192+}
1193+
1194+void
1195+print_only(const char *path, size_t dirlen, const char *entry)
1196+{
1197+	if (dirlen > 1)
1198+		dirlen--;
1199+	printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
1200+}
1201+
1202+int
1203+diffreg(char *file1, char *file2, int flags, int capsicum)
1204+{
1205+	FILE *f1, *f2;
1206+	int i, rval;
1207+	struct Pr *pr = NULL;
1208+
1209+	f1 = f2 = NULL;
1210+	rval = D_SAME;
1211+	anychange = 0;
1212+	lastline = 0;
1213+	lastmatchline = 0;
1214+
1215+	if (diff_format == D_SIDEBYSIDE) {
1216+		if (flags & D_EXPANDTABS) {
1217+			if (width > 3)
1218+				hw = (width - 3) / 2;
1219+			else
1220+				hw = 0;
1221+		} else if (width <= 3 || width <= tabsize) {
1222+			hw = 0;
1223+		} else {
1224+			hw = (width - 3) / 2;
1225+			while (hw > 0 && roundup(hw + 3, tabsize) + hw > width)
1226+				hw--;
1227+			if (width - (roundup(hw + 3, tabsize) + hw) < tabsize)
1228+				width = roundup(hw + 3, tabsize) + hw;
1229+		}
1230+		lpad = (width - hw * 2 - 1) / 2;
1231+		rpad = (width - hw * 2 - 1) - lpad;
1232+	}
1233+
1234+	if (flags & D_IGNORECASE)
1235+		chrtran = cup2low;
1236+	else
1237+		chrtran = clow2low;
1238+	if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
1239+		return S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2;
1240+	if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
1241+		goto closem;
1242+
1243+	if (flags & D_EMPTY1)
1244+		f1 = fopen(_PATH_DEVNULL, "r");
1245+	else {
1246+		if (!S_ISREG(stb1.st_mode)) {
1247+			if ((f1 = opentemp(file1)) == NULL || fstat(fileno(f1), &stb1) == -1) {
1248+				warn("%s", file1);
1249+				rval = D_ERROR;
1250+				status |= 2;
1251+				goto closem;
1252+			}
1253+		} else if (strcmp(file1, "-") == 0)
1254+			f1 = stdin;
1255+		else
1256+			f1 = fopen(file1, "r");
1257+	}
1258+	if (f1 == NULL) {
1259+		warn("%s", file1);
1260+		rval = D_ERROR;
1261+		status |= 2;
1262+		goto closem;
1263+	}
1264+
1265+	if (flags & D_EMPTY2)
1266+		f2 = fopen(_PATH_DEVNULL, "r");
1267+	else {
1268+		if (!S_ISREG(stb2.st_mode)) {
1269+			if ((f2 = opentemp(file2)) == NULL || fstat(fileno(f2), &stb2) == -1) {
1270+				warn("%s", file2);
1271+				rval = D_ERROR;
1272+				status |= 2;
1273+				goto closem;
1274+			}
1275+		} else if (strcmp(file2, "-") == 0)
1276+			f2 = stdin;
1277+		else
1278+			f2 = fopen(file2, "r");
1279+	}
1280+	if (f2 == NULL) {
1281+		warn("%s", file2);
1282+		rval = D_ERROR;
1283+		status |= 2;
1284+		goto closem;
1285+	}
1286+
1287+	if (lflag)
1288+		pr = start_pr(file1, file2);
1289+
1290+	(void)capsicum;
1291+
1292+	switch (files_differ(f1, f2, flags)) {
1293+	case 0:
1294+		goto closem;
1295+	case 1:
1296+		break;
1297+	default:
1298+		rval = D_ERROR;
1299+		status |= 2;
1300+		goto closem;
1301+	}
1302+
1303+	if (diff_format == D_BRIEF && ignore_pats == NULL &&
1304+	    (flags & (D_FOLDBLANKS|D_IGNOREBLANKS|D_IGNORECASE|D_SKIPBLANKLINES|D_STRIPCR)) == 0)
1305+	{
1306+		rval = D_DIFFER;
1307+		status |= 1;
1308+		goto closem;
1309+	}
1310+	if ((flags & D_FORCEASCII) != 0) {
1311+		prepare(0, f1, stb1.st_size, flags);
1312+		prepare(1, f2, stb2.st_size, flags);
1313+	} else if (!asciifile(f1) || !asciifile(f2) ||
1314+		   !prepare(0, f1, stb1.st_size, flags) ||
1315+		   !prepare(1, f2, stb2.st_size, flags)) {
1316+		rval = D_BINARY;
1317+		status |= 1;
1318+		goto closem;
1319+	}
1320+	if (len[0] > INT_MAX - 2)
1321+		errno = EFBIG, err(1, "%s", file1);
1322+	if (len[1] > INT_MAX - 2)
1323+		errno = EFBIG, err(1, "%s", file2);
1324+
1325+	prune();
1326+	sort(sfile[0], slen[0]);
1327+	sort(sfile[1], slen[1]);
1328+
1329+	member = (int *)file[1];
1330+	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1331+	member = ereallocarray(member, slen[1] + 2, sizeof(*member));
1332+
1333+	class = (int *)file[0];
1334+	unsort(sfile[0], slen[0], class);
1335+	class = ereallocarray(class, slen[0] + 2, sizeof(*class));
1336+
1337+	klist = ecalloc(slen[0] + 2, sizeof(*klist));
1338+	clen = 0;
1339+	clistlen = 100;
1340+	clist = ecalloc(clistlen, sizeof(*clist));
1341+	i = stone(class, slen[0], member, klist, flags);
1342+	free(member);
1343+	free(class);
1344+
1345+	J = ereallocarray(J, len[0] + 2, sizeof(*J));
1346+	unravel(klist[i]);
1347+	free(clist);
1348+	free(klist);
1349+
1350+	ixold = ereallocarray(ixold, len[0] + 2, sizeof(*ixold));
1351+	ixnew = ereallocarray(ixnew, len[1] + 2, sizeof(*ixnew));
1352+	check(f1, f2, flags);
1353+	output(file1, f1, file2, f2, flags);
1354+
1355+closem:
1356+	if (pr != NULL)
1357+		stop_pr(pr);
1358+	if (anychange) {
1359+		status |= 1;
1360+		if (rval == D_SAME)
1361+			rval = D_DIFFER;
1362+	}
1363+	if (f1 != NULL && f1 != stdin)
1364+		fclose(f1);
1365+	if (f2 != NULL && f2 != stdin)
1366+		fclose(f2);
1367+
1368+	return rval;
1369+}
1370+
1371+static int
1372+files_differ(FILE *f1, FILE *f2, int flags)
1373+{
1374+	char buf1[BUFSIZ], buf2[BUFSIZ];
1375+	size_t i, j;
1376+
1377+	if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
1378+	    (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
1379+		return 1;
1380+
1381+	if (stb1.st_dev == stb2.st_dev && stb1.st_ino == stb2.st_ino)
1382+		return 0;
1383+
1384+	for (;;) {
1385+		i = fread(buf1, 1, sizeof(buf1), f1);
1386+		j = fread(buf2, 1, sizeof(buf2), f2);
1387+		if ((!i && ferror(f1)) || (!j && ferror(f2)))
1388+			return -1;
1389+		if (i != j)
1390+			return 1;
1391+		if (i == 0)
1392+			return 0;
1393+		if (memcmp(buf1, buf2, i) != 0)
1394+			return 1;
1395+	}
1396+}
1397+
1398+static FILE *
1399+opentemp(const char *f)
1400+{
1401+	char buf[BUFSIZ], tempfile[PATH_MAX];
1402+	ssize_t nread;
1403+	int ifd, ofd;
1404+
1405+	if (strcmp(f, "-") == 0)
1406+		ifd = STDIN_FILENO;
1407+	else if ((ifd = open(f, O_RDONLY, 0644)) == -1)
1408+		return NULL;
1409+
1410+	strlcpy(tempfile, _PATH_TMP "/diff.XXXXXXXX", sizeof(tempfile));
1411+	if ((ofd = mkstemp(tempfile)) == -1) {
1412+		close(ifd);
1413+		return NULL;
1414+	}
1415+	unlink(tempfile);
1416+	while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
1417+		if (write(ofd, buf, nread) != nread) {
1418+			close(ifd);
1419+			close(ofd);
1420+			return NULL;
1421+		}
1422+	}
1423+	close(ifd);
1424+	lseek(ofd, (off_t)0, SEEK_SET);
1425+	return fdopen(ofd, "r");
1426+}
1427+
1428+static int
1429+prepare(int i, FILE *fd, size_t filesize, int flags)
1430+{
1431+	struct Line *p;
1432+	unsigned h;
1433+	size_t sz, j = 0;
1434+	enum Readhash r;
1435+
1436+	rewind(fd);
1437+	sz = filesize / 25;
1438+	if (sz < 100)
1439+		sz = 100;
1440+
1441+	p = ecalloc(sz + 3, sizeof(*p));
1442+	while ((r = readhash(fd, flags, &h)) != RH_EOF) {
1443+		if (r == RH_BINARY)
1444+			return 0;
1445+		if (j == SIZE_MAX)
1446+			break;
1447+		if (j == sz) {
1448+			sz = sz * 3 / 2;
1449+			p = ereallocarray(p, sz + 3, sizeof(*p));
1450+		}
1451+		p[++j].value = h;
1452+	}
1453+	len[i] = j;
1454+	file[i] = p;
1455+	return 1;
1456+}
1457+
1458+static void
1459+prune(void)
1460+{
1461+	size_t i, j;
1462+
1463+	for (pref = 0; pref < len[0] && pref < len[1] &&
1464+	    file[0][pref + 1].value == file[1][pref + 1].value;
1465+	    pref++)
1466+		;
1467+	for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
1468+	    file[0][len[0] - suff].value == file[1][len[1] - suff].value;
1469+	    suff++)
1470+		;
1471+	for (j = 0; j < 2; j++) {
1472+		sfile[j] = file[j] + pref;
1473+		slen[j] = len[j] - pref - suff;
1474+		for (i = 0; i <= slen[j]; i++)
1475+			sfile[j][i].serial = i;
1476+	}
1477+}
1478+
1479+static void
1480+equiv(struct Line *a, int n, struct Line *b, int m, int *c)
1481+{
1482+	int i, j;
1483+
1484+	i = j = 1;
1485+	while (i <= n && j <= m) {
1486+		if (a[i].value < b[j].value)
1487+			a[i++].value = 0;
1488+		else if (a[i].value == b[j].value)
1489+			a[i++].value = j;
1490+		else
1491+			j++;
1492+	}
1493+	while (i <= n)
1494+		a[i++].value = 0;
1495+	b[m + 1].value = 0;
1496+	j = 0;
1497+	while (++j <= m) {
1498+		c[j] = -b[j].serial;
1499+		while (b[j + 1].value == b[j].value) {
1500+			j++;
1501+			c[j] = b[j].serial;
1502+		}
1503+	}
1504+	c[j] = -1;
1505+}
1506+
1507+static int
1508+stone(int *a, int n, int *b, int *c, int flags)
1509+{
1510+	int i, k, y, j, l;
1511+	int oldc, tc, oldl, sq;
1512+	unsigned numtries, bound;
1513+
1514+	if (flags & D_MINIMAL)
1515+		bound = UINT_MAX;
1516+	else {
1517+		sq = sqrt(n);
1518+		bound = MAX(256, sq);
1519+	}
1520+
1521+	k = 0;
1522+	c[0] = newcand(0, 0, 0);
1523+	for (i = 1; i <= n; i++) {
1524+		j = a[i];
1525+		if (j == 0)
1526+			continue;
1527+		y = -b[j];
1528+		oldl = 0;
1529+		oldc = c[0];
1530+		numtries = 0;
1531+		do {
1532+			if (y <= clist[oldc].y)
1533+				continue;
1534+			l = search(c, k, y);
1535+			if (l != oldl + 1)
1536+				oldc = c[l - 1];
1537+			if (l <= k) {
1538+				if (clist[c[l]].y <= y)
1539+					continue;
1540+				tc = c[l];
1541+				c[l] = newcand(i, y, oldc);
1542+				oldc = tc;
1543+				oldl = l;
1544+				numtries++;
1545+			} else {
1546+				c[l] = newcand(i, y, oldc);
1547+				k++;
1548+				break;
1549+			}
1550+		} while ((y = b[++j]) > 0 && numtries < bound);
1551+	}
1552+	return k;
1553+}
1554+
1555+static int
1556+newcand(int x, int y, int pred)
1557+{
1558+	struct Cand *q;
1559+
1560+	if (clen == clistlen) {
1561+		clistlen = clistlen * 11 / 10;
1562+		clist = ereallocarray(clist, clistlen, sizeof(*clist));
1563+	}
1564+	q = clist + clen;
1565+	q->x = x;
1566+	q->y = y;
1567+	q->pred = pred;
1568+	return clen++;
1569+}
1570+
1571+static int
1572+search(int *c, int k, int y)
1573+{
1574+	int i, j, l, t;
1575+
1576+	if (clist[c[k]].y < y)
1577+		return k + 1;
1578+	i = 0;
1579+	j = k + 1;
1580+	for (;;) {
1581+		l = (i + j) / 2;
1582+		if (l <= i)
1583+			break;
1584+		t = clist[c[l]].y;
1585+		if (t > y)
1586+			j = l;
1587+		else if (t < y)
1588+			i = l;
1589+		else
1590+			return l;
1591+	}
1592+	return l + 1;
1593+}
1594+
1595+static void
1596+unravel(int p)
1597+{
1598+	struct Cand *q;
1599+	size_t i;
1600+
1601+	for (i = 0; i <= len[0]; i++)
1602+		J[i] = i <= pref ? i :
1603+		    i > len[0] - suff ? i + len[1] - len[0] : 0;
1604+	for (q = clist + p; q->y != 0; q = clist + q->pred)
1605+		J[q->x + pref] = q->y + pref;
1606+}
1607+
1608+static void
1609+check(FILE *f1, FILE *f2, int flags)
1610+{
1611+	int i, j, c, d;
1612+	long ctold, ctnew;
1613+
1614+	rewind(f1);
1615+	rewind(f2);
1616+	j = 1;
1617+	ixold[0] = ixnew[0] = 0;
1618+	ctold = ctnew = 0;
1619+	for (i = 1; i <= (int)len[0]; i++) {
1620+		if (J[i] == 0) {
1621+			ixold[i] = ctold += skipline(f1);
1622+			continue;
1623+		}
1624+		while (j < J[i]) {
1625+			ixnew[j] = ctnew += skipline(f2);
1626+			j++;
1627+		}
1628+		if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS | D_IGNORECASE | D_STRIPCR)) {
1629+			for (;;) {
1630+				c = getc(f1);
1631+				d = getc(f2);
1632+				if (flags & (D_FOLDBLANKS | D_IGNOREBLANKS)) {
1633+					if (c == EOF && isspace(d)) {
1634+						ctnew++;
1635+						break;
1636+					} else if (isspace(c) && d == EOF) {
1637+						ctold++;
1638+						break;
1639+					}
1640+				}
1641+				ctold++;
1642+				ctnew++;
1643+				if (flags & D_STRIPCR && (c == '\r' || d == '\r')) {
1644+					if (c == '\r') {
1645+						if ((c = getc(f1)) == '\n')
1646+							ctold++;
1647+						else
1648+							ungetc(c, f1);
1649+					}
1650+					if (d == '\r') {
1651+						if ((d = getc(f2)) == '\n')
1652+							ctnew++;
1653+						else
1654+							ungetc(d, f2);
1655+					}
1656+					break;
1657+				}
1658+				if ((flags & D_FOLDBLANKS) && isspace(c) && isspace(d)) {
1659+					do {
1660+						if (c == '\n')
1661+							break;
1662+						ctold++;
1663+					} while (isspace(c = getc(f1)));
1664+					do {
1665+						if (d == '\n')
1666+							break;
1667+						ctnew++;
1668+					} while (isspace(d = getc(f2)));
1669+				} else if (flags & D_IGNOREBLANKS) {
1670+					while (isspace(c) && c != '\n') {
1671+						c = getc(f1);
1672+						ctold++;
1673+					}
1674+					while (isspace(d) && d != '\n') {
1675+						d = getc(f2);
1676+						ctnew++;
1677+					}
1678+				}
1679+				if (chrtran(c) != chrtran(d)) {
1680+					J[i] = 0;
1681+					if (c != '\n' && c != EOF)
1682+						ctold += skipline(f1);
1683+					if (d != '\n' && c != EOF)
1684+						ctnew += skipline(f2);
1685+					break;
1686+				}
1687+				if (c == '\n' || c == EOF)
1688+					break;
1689+			}
1690+		} else {
1691+			for (;;) {
1692+				ctold++;
1693+				ctnew++;
1694+				if ((c = getc(f1)) != (d = getc(f2))) {
1695+					J[i] = 0;
1696+					if (c != '\n' && c != EOF)
1697+						ctold += skipline(f1);
1698+					if (d != '\n' && c != EOF)
1699+						ctnew += skipline(f2);
1700+					break;
1701+				}
1702+				if (c == '\n' || c == EOF)
1703+					break;
1704+			}
1705+		}
1706+		ixold[i] = ctold;
1707+		ixnew[j] = ctnew;
1708+		j++;
1709+	}
1710+	for (; j <= (int)len[1]; j++) {
1711+		ixnew[j] = ctnew += skipline(f2);
1712+	}
1713+}
1714+
1715+static void
1716+sort(struct Line *a, int n)
1717+{
1718+	struct Line *ai, *aim, w;
1719+	int j, m = 0, k;
1720+
1721+	if (n == 0)
1722+		return;
1723+	for (j = 1; j <= n; j *= 2)
1724+		m = 2 * j - 1;
1725+	for (m /= 2; m != 0; m /= 2) {
1726+		k = n - m;
1727+		for (j = 1; j <= k; j++) {
1728+			for (ai = &a[j]; ai > a; ai -= m) {
1729+				aim = &ai[m];
1730+				if (aim < ai)
1731+					break;
1732+				if (aim->value > ai[0].value ||
1733+				    (aim->value == ai[0].value && aim->serial > ai[0].serial))
1734+					break;
1735+				w.value = ai[0].value;
1736+				ai[0].value = aim->value;
1737+				aim->value = w.value;
1738+				w.serial = ai[0].serial;
1739+				ai[0].serial = aim->serial;
1740+				aim->serial = w.serial;
1741+			}
1742+		}
1743+	}
1744+}
1745+
1746+static void
1747+unsort(struct Line *f, int l, int *b)
1748+{
1749+	int *a, i;
1750+
1751+	a = ecalloc(l + 1, sizeof(*a));
1752+	for (i = 1; i <= l; i++)
1753+		a[f[i].serial] = f[i].value;
1754+	for (i = 1; i <= l; i++)
1755+		b[i] = a[i];
1756+	free(a);
1757+}
1758+
1759+static int
1760+skipline(FILE *f)
1761+{
1762+	int i, c;
1763+
1764+	for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
1765+		continue;
1766+	return i;
1767+}
1768+
1769+static void
1770+output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
1771+{
1772+	int i, j, m, i0, i1, j0, j1, nc;
1773+
1774+	rewind(f1);
1775+	rewind(f2);
1776+	m = len[0];
1777+	J[0] = 0;
1778+	J[m + 1] = len[1] + 1;
1779+	if (diff_format != D_EDIT) {
1780+		for (i0 = 1; i0 <= m; i0 = i1 + 1) {
1781+			while (i0 <= m && J[i0] == J[i0 - 1] + 1) {
1782+				if (diff_format == D_SIDEBYSIDE && suppress_common != 1) {
1783+					nc = fetch(ixold, i0, i0, f1, '\0', 1, flags);
1784+					print_space(nc, hw - nc + lpad + 1 + rpad, flags);
1785+					fetch(ixnew, J[i0], J[i0], f2, '\0', 0, flags);
1786+					printf("\n");
1787+				}
1788+				i0++;
1789+			}
1790+			j0 = J[i0 - 1] + 1;
1791+			i1 = i0 - 1;
1792+			while (i1 < m && J[i1 + 1] == 0)
1793+				i1++;
1794+			j1 = J[i1 + 1] - 1;
1795+			J[i1] = j1;
1796+
1797+			if (diff_format == D_SIDEBYSIDE) {
1798+				for (i = i0, j = j0; i <= i1 && j <= j1; i++, j++)
1799+					change(file1, f1, file2, f2, i, i, j, j, &flags);
1800+				while (i <= i1) {
1801+					change(file1, f1, file2, f2, i, i, j + 1, j, &flags);
1802+					i++;
1803+				}
1804+				while (j <= j1) {
1805+					change(file1, f1, file2, f2, i + 1, i, j, j, &flags);
1806+					j++;
1807+				}
1808+			} else
1809+				change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
1810+		}
1811+	} else {
1812+		for (i0 = m; i0 >= 1; i0 = i1 - 1) {
1813+			while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
1814+				i0--;
1815+			j0 = J[i0 + 1] - 1;
1816+			i1 = i0 + 1;
1817+			while (i1 > 1 && J[i1 - 1] == 0)
1818+				i1--;
1819+			j1 = J[i1 - 1] + 1;
1820+			J[i1] = j1;
1821+			change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
1822+		}
1823+	}
1824+	if (m == 0)
1825+		change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
1826+	if (diff_format == D_IFDEF || diff_format == D_GFORMAT) {
1827+		for (;;) {
1828+#define c i0
1829+			if ((c = getc(f1)) == EOF)
1830+				return;
1831+			printf("%c", c);
1832+		}
1833+#undef c
1834+	}
1835+	if (anychange != 0) {
1836+		if (diff_format == D_CONTEXT)
1837+			dump_context_vec(f1, f2, flags);
1838+		else if (diff_format == D_UNIFIED)
1839+			dump_unified_vec(f1, f2, flags);
1840+	}
1841+}
1842+
1843+static void
1844+range(int a, int b, const char *separator)
1845+{
1846+	printf("%d", a > b ? b : a);
1847+	if (a < b)
1848+		printf("%s%d", separator, b);
1849+}
1850+
1851+static void
1852+uni_range(int a, int b)
1853+{
1854+	if (a < b)
1855+		printf("%d,%d", a, b - a + 1);
1856+	else if (a == b)
1857+		printf("%d", b);
1858+	else
1859+		printf("%d,0", b);
1860+}
1861+
1862+static char *
1863+preadline(int fd, size_t rlen, off_t off)
1864+{
1865+	char *line;
1866+	ssize_t nr;
1867+
1868+	line = emalloc(rlen + 1);
1869+	if ((nr = pread(fd, line, rlen, off)) == -1)
1870+		err(2, "preadline");
1871+	if (nr > 0 && line[nr-1] == '\n')
1872+		nr--;
1873+	line[nr] = '\0';
1874+	return line;
1875+}
1876+
1877+static int
1878+ignoreline_pattern(char *line)
1879+{
1880+	int ret;
1881+
1882+	ret = regexec(&ignore_re, line, 0, NULL, 0);
1883+	return ret == 0;
1884+}
1885+
1886+static int
1887+ignoreline(char *line, int skip_blanks)
1888+{
1889+	if (skip_blanks && *line == '\0')
1890+		return 1;
1891+	if (ignore_pats != NULL && ignoreline_pattern(line))
1892+		return 1;
1893+	return 0;
1894+}
1895+
1896+static void
1897+change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
1898+    int *pflags)
1899+{
1900+	static size_t max_context = 64;
1901+	long curpos;
1902+	int i, nc;
1903+	const char *walk;
1904+	int skip_blanks, ignore;
1905+
1906+	skip_blanks = (*pflags & D_SKIPBLANKLINES);
1907+restart:
1908+	if ((diff_format != D_IFDEF || diff_format == D_GFORMAT) && a > b && c > d)
1909+		return;
1910+	if (ignore_pats != NULL || skip_blanks) {
1911+		char *line;
1912+		if (a <= b) {
1913+			for (i = a; i <= b; i++) {
1914+				line = preadline(fileno(f1), ixold[i] - ixold[i - 1], ixold[i - 1]);
1915+				ignore = ignoreline(line, skip_blanks);
1916+				free(line);
1917+				if (!ignore)
1918+					goto proceed;
1919+			}
1920+		}
1921+		if (a > b || c <= d) {
1922+			for (i = c; i <= d; i++) {
1923+				line = preadline(fileno(f2), ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
1924+				ignore = ignoreline(line, skip_blanks);
1925+				free(line);
1926+				if (!ignore)
1927+					goto proceed;
1928+			}
1929+		}
1930+		return;
1931+	}
1932+proceed:
1933+	if (*pflags & D_HEADER && diff_format != D_BRIEF) {
1934+		printf("%s %s %s\n", diffargs, file1, file2);
1935+		*pflags &= ~D_HEADER;
1936+	}
1937+	if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) {
1938+		if (context_vec_start == NULL || context_vec_ptr == context_vec_end - 1) {
1939+			ptrdiff_t offset = -1;
1940+
1941+			if (context_vec_start != NULL)
1942+				offset = context_vec_ptr - context_vec_start;
1943+			max_context <<= 1;
1944+			context_vec_start = ereallocarray(context_vec_start, max_context, sizeof(*context_vec_start));
1945+			context_vec_end = context_vec_start + max_context;
1946+			context_vec_ptr = context_vec_start + offset;
1947+		}
1948+		if (anychange == 0) {
1949+			print_header(file1, file2);
1950+			anychange = 1;
1951+		} else if (a > context_vec_ptr->b + (2 * diff_context) + 1 &&
1952+		    c > context_vec_ptr->d + (2 * diff_context) + 1) {
1953+			if (diff_format == D_CONTEXT)
1954+				dump_context_vec(f1, f2, *pflags);
1955+			else
1956+				dump_unified_vec(f1, f2, *pflags);
1957+		}
1958+		context_vec_ptr++;
1959+		context_vec_ptr->a = a;
1960+		context_vec_ptr->b = b;
1961+		context_vec_ptr->c = c;
1962+		context_vec_ptr->d = d;
1963+		return;
1964+	}
1965+	if (anychange == 0)
1966+		anychange = 1;
1967+	switch (diff_format) {
1968+	case D_BRIEF:
1969+		return;
1970+	case D_NORMAL:
1971+	case D_EDIT:
1972+		range(a, b, ",");
1973+		printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1974+		if (diff_format == D_NORMAL)
1975+			range(c, d, ",");
1976+		printf("\n");
1977+		break;
1978+	case D_REVERSE:
1979+		printf("%c", a > b ? 'a' : c > d ? 'd' : 'c');
1980+		range(a, b, " ");
1981+		printf("\n");
1982+		break;
1983+	case D_NREVERSE:
1984+		if (a > b)
1985+			printf("a%d %d\n", b, d - c + 1);
1986+		else {
1987+			printf("d%d %d\n", a, b - a + 1);
1988+			if (!(c > d))
1989+				printf("a%d %d\n", b, d - c + 1);
1990+		}
1991+		break;
1992+	}
1993+	if (diff_format == D_GFORMAT) {
1994+		curpos = ftell(f1);
1995+		nc = ixold[a > b ? b : a - 1] - curpos;
1996+		for (i = 0; i < nc; i++)
1997+			printf("%c", getc(f1));
1998+		for (walk = group_format; *walk != '\0'; walk++) {
1999+			if (*walk == '%') {
2000+				walk++;
2001+				switch (*walk) {
2002+				case '<':
2003+					fetch(ixold, a, b, f1, '<', 1, *pflags);
2004+					break;
2005+				case '>':
2006+					fetch(ixnew, c, d, f2, '>', 0, *pflags);
2007+					break;
2008+				default:
2009+					printf("%%%c", *walk);
2010+					break;
2011+				}
2012+				continue;
2013+			}
2014+			printf("%c", *walk);
2015+		}
2016+	}
2017+	if (diff_format == D_SIDEBYSIDE) {
2018+		if (color && a > b)
2019+			printf("\033[%sm", add_code);
2020+		else if (color && c > d)
2021+			printf("\033[%sm", del_code);
2022+		if (a > b) {
2023+			print_space(0, hw + lpad, *pflags);
2024+		} else {
2025+			nc = fetch(ixold, a, b, f1, '\0', 1, *pflags);
2026+			print_space(nc, hw - nc + lpad, *pflags);
2027+		}
2028+		if (color && a > b)
2029+			printf("\033[%sm", add_code);
2030+		else if (color && c > d)
2031+			printf("\033[%sm", del_code);
2032+		printf("%c", (a > b) ? '>' : ((c > d) ? '<' : '|'));
2033+		if (color && c > d)
2034+			printf("\033[m");
2035+		print_space(hw + lpad + 1, rpad, *pflags);
2036+		fetch(ixnew, c, d, f2, '\0', 0, *pflags);
2037+		printf("\n");
2038+	}
2039+	if (diff_format == D_NORMAL || diff_format == D_IFDEF) {
2040+		fetch(ixold, a, b, f1, '<', 1, *pflags);
2041+		if (a <= b && c <= d && diff_format == D_NORMAL)
2042+			printf("---\n");
2043+	}
2044+	if (diff_format != D_GFORMAT && diff_format != D_SIDEBYSIDE)
2045+		fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0, *pflags);
2046+	if (edoffset != 0 && diff_format == D_EDIT) {
2047+		printf(".\n");
2048+		printf("%ds/.//\n", a + edoffset - 1);
2049+		b = a + edoffset - 1;
2050+		a = b + 1;
2051+		c += edoffset;
2052+		goto restart;
2053+	}
2054+	if ((diff_format == D_EDIT || diff_format == D_REVERSE) && c <= d)
2055+		printf(".\n");
2056+	if (inifdef) {
2057+		printf("#endif /* %s */\n", ifdefname);
2058+		inifdef = 0;
2059+	}
2060+}
2061+
2062+static int
2063+fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile, int flags)
2064+{
2065+	int i, j, c, lastc, col, nc, newcol;
2066+
2067+	edoffset = 0;
2068+	nc = 0;
2069+	col = 0;
2070+	if ((diff_format == D_IFDEF) && oldfile) {
2071+		long curpos = ftell(lb);
2072+		nc = f[a > b ? b : a - 1] - curpos;
2073+		for (i = 0; i < nc; i++)
2074+			printf("%c", getc(lb));
2075+	}
2076+	if (a > b)
2077+		return 0;
2078+	if (diff_format == D_IFDEF) {
2079+		if (inifdef) {
2080+			printf("#else /* %s%s */\n",
2081+			    oldfile == 1 ? "!" : "", ifdefname);
2082+		} else {
2083+			if (oldfile)
2084+				printf("#ifndef %s\n", ifdefname);
2085+			else
2086+				printf("#ifdef %s\n", ifdefname);
2087+		}
2088+		inifdef = 1 + oldfile;
2089+	}
2090+	for (i = a; i <= b; i++) {
2091+		fseek(lb, f[i - 1], SEEK_SET);
2092+		nc = f[i] - f[i - 1];
2093+		if (diff_format == D_SIDEBYSIDE && hw < nc)
2094+			nc = hw;
2095+		if (diff_format != D_IFDEF && diff_format != D_GFORMAT && ch != '\0') {
2096+			if (color && (ch == '>' || ch == '+'))
2097+				printf("\033[%sm", add_code);
2098+			else if (color && (ch == '<' || ch == '-'))
2099+				printf("\033[%sm", del_code);
2100+			printf("%c", ch);
2101+			if (Tflag && (diff_format == D_NORMAL ||
2102+			    diff_format == D_CONTEXT ||
2103+			    diff_format == D_UNIFIED))
2104+				printf("\t");
2105+			else if (diff_format != D_UNIFIED)
2106+				printf(" ");
2107+		}
2108+		col = j = 0;
2109+		lastc = '\0';
2110+		while (j < nc && (hw == 0 || col < hw)) {
2111+			c = getc(lb);
2112+			if (flags & D_STRIPCR && c == '\r') {
2113+				if ((c = getc(lb)) == '\n')
2114+					j++;
2115+				else {
2116+					ungetc(c, lb);
2117+					c = '\r';
2118+				}
2119+			}
2120+			if (c == EOF) {
2121+				if (diff_format == D_EDIT ||
2122+				    diff_format == D_REVERSE ||
2123+				    diff_format == D_NREVERSE)
2124+					warnx("No newline at end of file");
2125+				else
2126+					printf("\n\\ No newline at end of file\n");
2127+				return col;
2128+			}
2129+			if (c == '\t') {
2130+				newcol = roundup(col + 1, tabsize);
2131+				if ((flags & D_EXPANDTABS) == 0) {
2132+					if (hw > 0 && newcol >= hw)
2133+						return col;
2134+					printf("\t");
2135+				} else {
2136+					if (hw > 0 && newcol > hw)
2137+						newcol = hw;
2138+					printf("%*s", newcol - col, "");
2139+				}
2140+				col = newcol;
2141+			} else {
2142+				if (diff_format == D_EDIT && j == 1 && c == '\n' && lastc == '.') {
2143+					printf(".\n");
2144+					edoffset = i - a + 1;
2145+					return edoffset;
2146+				}
2147+				if (diff_format != D_SIDEBYSIDE || c != '\n') {
2148+					if (color && c == '\n')
2149+						printf("\033[m%c", c);
2150+					else
2151+						printf("%c", c);
2152+					col++;
2153+				}
2154+			}
2155+			j++;
2156+			lastc = c;
2157+		}
2158+	}
2159+	if (color && diff_format == D_SIDEBYSIDE)
2160+		printf("\033[m");
2161+	return col;
2162+}
2163+
2164+static enum Readhash
2165+readhash(FILE *f, int flags, unsigned *hash)
2166+{
2167+	int i, t, space;
2168+	unsigned sum;
2169+
2170+	sum = 1;
2171+	space = 0;
2172+	for (i = 0;;) {
2173+		switch (t = getc(f)) {
2174+		case '\0':
2175+			if ((flags & D_FORCEASCII) == 0)
2176+				return RH_BINARY;
2177+			goto hashchar;
2178+		case '\r':
2179+			if (flags & D_STRIPCR) {
2180+				t = getc(f);
2181+				if (t == '\n')
2182+					break;
2183+				ungetc(t, f);
2184+			}
2185+			/* FALLTHROUGH */
2186+		case '\t':
2187+		case '\v':
2188+		case '\f':
2189+		case ' ':
2190+			if ((flags & (D_FOLDBLANKS|D_IGNOREBLANKS)) != 0) {
2191+				space++;
2192+				continue;
2193+			}
2194+			/* FALLTHROUGH */
2195+		default:
2196+		hashchar:
2197+			if (space && (flags & D_IGNOREBLANKS) == 0) {
2198+				i++;
2199+				space = 0;
2200+			}
2201+			sum = sum * 127 + chrtran(t);
2202+			i++;
2203+			continue;
2204+		case EOF:
2205+			if (i == 0)
2206+				return RH_EOF;
2207+			/* FALLTHROUGH */
2208+		case '\n':
2209+			break;
2210+		}
2211+		break;
2212+	}
2213+	*hash = sum;
2214+	return RH_OK;
2215+}
2216+
2217+static int
2218+asciifile(FILE *f)
2219+{
2220+	unsigned char buf[BUFSIZ];
2221+	size_t cnt;
2222+
2223+	if (f == NULL)
2224+		return 1;
2225+
2226+	rewind(f);
2227+	cnt = fread(buf, 1, sizeof(buf), f);
2228+	return memchr(buf, '\0', cnt) == NULL;
2229+}
2230+
2231+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre) - 1) == 0)
2232+
2233+static char *
2234+match_function(const long *f, int pos, FILE *fp)
2235+{
2236+	char buf[FUNCTION_CONTEXT_SIZE];
2237+	size_t nc;
2238+	int last = lastline;
2239+	const char *state = NULL;
2240+
2241+	lastline = pos;
2242+	for (; pos > last; pos--) {
2243+		fseek(fp, f[pos - 1], SEEK_SET);
2244+		nc = f[pos] - f[pos - 1];
2245+		if (nc >= sizeof(buf))
2246+			nc = sizeof(buf) - 1;
2247+		nc = fread(buf, 1, nc, fp);
2248+		if (nc == 0)
2249+			continue;
2250+		buf[nc] = '\0';
2251+		buf[strcspn(buf, "\n")] = '\0';
2252+		if (most_recent_pat != NULL) {
2253+			int ret = regexec(&most_recent_re, buf, 0, NULL, 0);
2254+			if (ret != 0)
2255+				continue;
2256+			strlcpy(lastbuf, buf, sizeof(lastbuf));
2257+			lastmatchline = pos;
2258+			return lastbuf;
2259+		} else if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$' || buf[0] == '-' || buf[0] == '+') {
2260+			if (begins_with(buf, "private:")) {
2261+				if (!state)
2262+					state = " (private)";
2263+			} else if (begins_with(buf, "protected:")) {
2264+				if (!state)
2265+					state = " (protected)";
2266+			} else if (begins_with(buf, "public:")) {
2267+				if (!state)
2268+					state = " (public)";
2269+			} else {
2270+				strlcpy(lastbuf, buf, sizeof(lastbuf));
2271+				if (state)
2272+					strlcat(lastbuf, state, sizeof(lastbuf));
2273+				lastmatchline = pos;
2274+				return lastbuf;
2275+			}
2276+		}
2277+	}
2278+	return lastmatchline > 0 ? lastbuf : NULL;
2279+}
2280+
2281+static void
2282+dump_context_vec(FILE *f1, FILE *f2, int flags)
2283+{
2284+	struct ContextVec *cvp = context_vec_start;
2285+	int lowa, upb, lowc, upd, do_output;
2286+	int a, b, c, d;
2287+	char ch, *f;
2288+
2289+	if (context_vec_start > context_vec_ptr)
2290+		return;
2291+
2292+	b = d = 0;
2293+	lowa = MAX(1, cvp->a - diff_context);
2294+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2295+	lowc = MAX(1, cvp->c - diff_context);
2296+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2297+
2298+	printf("***************");
2299+	if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2300+		f = match_function(ixold, cvp->a - 1, f1);
2301+		if (f != NULL)
2302+			printf(" %s", f);
2303+	}
2304+	printf("\n*** ");
2305+	range(lowa, upb, ",");
2306+	printf(" ****\n");
2307+
2308+	do_output = 0;
2309+	for (; cvp <= context_vec_ptr; cvp++)
2310+		if (cvp->a <= cvp->b) {
2311+			cvp = context_vec_start;
2312+			do_output++;
2313+			break;
2314+		}
2315+	if (do_output) {
2316+		while (cvp <= context_vec_ptr) {
2317+			a = cvp->a;
2318+			b = cvp->b;
2319+			c = cvp->c;
2320+			d = cvp->d;
2321+
2322+			if (a <= b && c <= d)
2323+				ch = 'c';
2324+			else
2325+				ch = (a <= b) ? 'd' : 'a';
2326+
2327+			if (ch == 'a')
2328+				fetch(ixold, lowa, b, f1, ' ', 0, flags);
2329+			else {
2330+				fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2331+				fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-', 0, flags);
2332+			}
2333+			lowa = b + 1;
2334+			cvp++;
2335+		}
2336+		fetch(ixold, b + 1, upb, f1, ' ', 0, flags);
2337+	}
2338+
2339+	printf("--- ");
2340+	range(lowc, upd, ",");
2341+	printf(" ----\n");
2342+
2343+	do_output = 0;
2344+	for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
2345+		if (cvp->c <= cvp->d) {
2346+			cvp = context_vec_start;
2347+			do_output++;
2348+			break;
2349+		}
2350+	if (do_output) {
2351+		while (cvp <= context_vec_ptr) {
2352+			a = cvp->a;
2353+			b = cvp->b;
2354+			c = cvp->c;
2355+			d = cvp->d;
2356+
2357+			if (a <= b && c <= d)
2358+				ch = 'c';
2359+			else
2360+				ch = (a <= b) ? 'd' : 'a';
2361+
2362+			if (ch == 'd')
2363+				fetch(ixnew, lowc, d, f2, ' ', 0, flags);
2364+			else {
2365+				fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2366+				fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+', 0, flags);
2367+			}
2368+			lowc = d + 1;
2369+			cvp++;
2370+		}
2371+		fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2372+	}
2373+	context_vec_ptr = context_vec_start - 1;
2374+}
2375+
2376+static void
2377+dump_unified_vec(FILE *f1, FILE *f2, int flags)
2378+{
2379+	struct ContextVec *cvp = context_vec_start;
2380+	int lowa, upb, lowc, upd;
2381+	int a, b, c, d;
2382+	char ch, *f;
2383+
2384+	if (context_vec_start > context_vec_ptr)
2385+		return;
2386+
2387+	b = d = 0;
2388+	lowa = MAX(1, cvp->a - diff_context);
2389+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
2390+	lowc = MAX(1, cvp->c - diff_context);
2391+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
2392+
2393+	printf("@@ -");
2394+	uni_range(lowa, upb);
2395+	printf(" +");
2396+	uni_range(lowc, upd);
2397+	printf(" @@");
2398+	if (flags & (D_PROTOTYPE | D_MATCHLAST)) {
2399+		f = match_function(ixold, cvp->a - 1, f1);
2400+		if (f != NULL)
2401+			printf(" %s", f);
2402+	}
2403+	printf("\n");
2404+
2405+	for (; cvp <= context_vec_ptr; cvp++) {
2406+		a = cvp->a;
2407+		b = cvp->b;
2408+		c = cvp->c;
2409+		d = cvp->d;
2410+
2411+		if (a <= b && c <= d)
2412+			ch = 'c';
2413+		else
2414+			ch = (a <= b) ? 'd' : 'a';
2415+
2416+		switch (ch) {
2417+		case 'c':
2418+			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2419+			fetch(ixold, a, b, f1, '-', 0, flags);
2420+			fetch(ixnew, c, d, f2, '+', 0, flags);
2421+			break;
2422+		case 'd':
2423+			fetch(ixold, lowa, a - 1, f1, ' ', 0, flags);
2424+			fetch(ixold, a, b, f1, '-', 0, flags);
2425+			break;
2426+		case 'a':
2427+			fetch(ixnew, lowc, c - 1, f2, ' ', 0, flags);
2428+			fetch(ixnew, c, d, f2, '+', 0, flags);
2429+			break;
2430+		}
2431+		lowa = b + 1;
2432+		lowc = d + 1;
2433+	}
2434+	fetch(ixnew, d + 1, upd, f2, ' ', 0, flags);
2435+	context_vec_ptr = context_vec_start - 1;
2436+}
2437+
2438+static void
2439+print_header(const char *file1, const char *file2)
2440+{
2441+	const char *time_format;
2442+	char buf[256];
2443+	struct tm tm1, tm2, *tm_ptr1, *tm_ptr2;
2444+	int nsec1 = stb1.st_mtim.tv_nsec;
2445+	int nsec2 = stb2.st_mtim.tv_nsec;
2446+
2447+	time_format = "%Y-%m-%d %H:%M:%S";
2448+	if (cflag)
2449+		time_format = "%c";
2450+	tm_ptr1 = localtime_r(&stb1.st_mtime, &tm1);
2451+	tm_ptr2 = localtime_r(&stb2.st_mtime, &tm2);
2452+	if (label[0] != NULL)
2453+		printf("%s %s\n", diff_format == D_CONTEXT ? "***" : "---", label[0]);
2454+	else {
2455+		strftime(buf, sizeof(buf), time_format, tm_ptr1);
2456+		printf("%s %s\t%s", diff_format == D_CONTEXT ? "***" : "---", file1, buf);
2457+		if (!cflag) {
2458+			strftime(buf, sizeof(buf), "%z", tm_ptr1);
2459+			printf(".%.9d %s", nsec1, buf);
2460+		}
2461+		printf("\n");
2462+	}
2463+	if (label[1] != NULL)
2464+		printf("%s %s\n", diff_format == D_CONTEXT ? "---" : "+++", label[1]);
2465+	else {
2466+		strftime(buf, sizeof(buf), time_format, tm_ptr2);
2467+		printf("%s %s\t%s", diff_format == D_CONTEXT ? "---" : "+++", file2, buf);
2468+		if (!cflag) {
2469+			strftime(buf, sizeof(buf), "%z", tm_ptr2);
2470+			printf(".%.9d %s", nsec2, buf);
2471+		}
2472+		printf("\n");
2473+	}
2474+}
2475+
2476+static void
2477+print_space(int nc, int n, int flags)
2478+{
2479+	int col, newcol, tabstop;
2480+
2481+	col = nc;
2482+	newcol = nc + n;
2483+	if ((flags & D_EXPANDTABS) == 0) {
2484+		while ((tabstop = roundup(col + 1, tabsize)) <= newcol) {
2485+			printf("\t");
2486+			col = tabstop;
2487+		}
2488+	}
2489+	printf("%*s", newcol - col, "");
2490+}
+0, -908
  1@@ -1,908 +0,0 @@
  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 = strlen("diff") + 1;
718-	for (ap = av + 1; ap < ave; ap++) {
719-		if (strcmp(*ap, "--") != 0)
720-			argsize += 1 + strlen(*ap);
721-	}
722-	diffargs = xmalloc(argsize);
723-	strlcpy(diffargs, "diff", argsize);
724-	for (ap = av + 1; ap < ave; ap++) {
725-		if (strcmp(*ap, "--") != 0) {
726-			strlcat(diffargs, " ", argsize);
727-			strlcat(diffargs, *ap, argsize);
728-		}
729-	}
730-}
731-
732-/*
733- * Read in an excludes file and push each line.
734- */
735-static void
736-read_excludes_file(char *file)
737-{
738-	FILE *fp;
739-	char *pattern = NULL;
740-	size_t blen = 0;
741-	ssize_t len;
742-
743-	if (strcmp(file, "-") == 0)
744-		fp = stdin;
745-	else if ((fp = fopen(file, "r")) == NULL)
746-		err(2, "%s", file);
747-	while ((len = getline(&pattern, &blen, fp)) >= 0) {
748-		if ((len > 0) && (pattern[len - 1] == '\n'))
749-			pattern[len - 1] = '\0';
750-		push_excludes(pattern);
751-		/* we allocate a new string per line */
752-		pattern = NULL;
753-		blen = 0;
754-	}
755-	free(pattern);
756-	if (strcmp(file, "-") != 0)
757-		fclose(fp);
758-}
759-
760-/*
761- * Push a pattern onto the excludes list.
762- */
763-static void
764-push_excludes(char *pattern)
765-{
766-	struct excludes *entry;
767-
768-	entry = xmalloc(sizeof(*entry));
769-	entry->pattern = pattern;
770-	entry->next = excludes_list;
771-	excludes_list = entry;
772-}
773-
774-static void
775-push_ignore_pats(char *pattern)
776-{
777-	size_t len;
778-
779-	if (ignore_pats == NULL)
780-		ignore_pats = xstrdup(pattern);
781-	else {
782-		/* old + "|" + new + NUL */
783-		len = strlen(ignore_pats) + strlen(pattern) + 2;
784-		ignore_pats = xreallocarray(ignore_pats, 1, len);
785-		strlcat(ignore_pats, "|", len);
786-		strlcat(ignore_pats, pattern, len);
787-	}
788-}
789-
790-void
791-print_status(int val, char *path1, char *path2, const char *entry)
792-{
793-	if (label[0] != NULL)
794-		path1 = label[0];
795-	if (label[1] != NULL)
796-		path2 = label[1];
797-
798-	switch (val) {
799-	case D_BINARY:
800-		printf("Binary files %s%s and %s%s differ\n",
801-		    path1, entry, path2, entry);
802-		break;
803-	case D_DIFFER:
804-		if (diff_format == D_BRIEF)
805-			printf("Files %s%s and %s%s differ\n",
806-			    path1, entry, path2, entry);
807-		break;
808-	case D_SAME:
809-		if (sflag)
810-			printf("Files %s%s and %s%s are identical\n",
811-			    path1, entry, path2, entry);
812-		break;
813-	case D_MISMATCH1:
814-		printf("File %s%s is a directory while file %s%s is a regular file\n",
815-		    path1, entry, path2, entry);
816-		break;
817-	case D_MISMATCH2:
818-		printf("File %s%s is a regular file while file %s%s is a directory\n",
819-		    path1, entry, path2, entry);
820-		break;
821-	case D_SKIPPED1:
822-		printf("File %s%s is not a regular file or directory and was skipped\n",
823-		    path1, entry);
824-		break;
825-	case D_SKIPPED2:
826-		printf("File %s%s is not a regular file or directory and was skipped\n",
827-		    path2, entry);
828-		break;
829-	case D_ERROR:
830-		break;
831-	}
832-}
833-
834-static void
835-usage(void)
836-{
837-	(void)fprintf(help ? stdout : stderr,
838-	    "usage: diff [-aBbdilpTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
839-	    "            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
840-	    "            [-I pattern] [-F pattern] [-L label] file1 file2\n"
841-	    "       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
842-	    "            [--no-ignore-case] [--normal] [--strip-trailing-cr] [--tabsize]\n"
843-	    "            [-F pattern] -C number file1 file2\n"
844-	    "       diff [-aBbdiltw] [-I pattern] [--ignore-case] [--no-ignore-case]\n"
845-	    "            [--normal] [--strip-trailing-cr] [--tabsize] -D string file1 file2\n"
846-	    "       diff [-aBbdilpTtw] [-I pattern] [-L label] [--ignore-case]\n"
847-	    "            [--no-ignore-case] [--normal] [--tabsize] [--strip-trailing-cr]\n"
848-	    "            [-F pattern] -U number file1 file2\n"
849-	    "       diff [-aBbdilNPprsTtw] [-c | -e | -f | -n | -q | -u] [--ignore-case]\n"
850-	    "            [--no-ignore-case] [--normal] [--tabsize] [-I pattern] [-L label]\n"
851-	    "            [-F pattern] [-S name] [-X file] [-x pattern] dir1 dir2\n"
852-	    "       diff [-aBbditwW] [--expand-tabs] [--ignore-all-space]\n"
853-	    "            [--ignore-blank-lines] [--ignore-case] [--minimal]\n"
854-	    "            [--no-ignore-file-name-case] [--strip-trailing-cr]\n"
855-	    "            [--suppress-common-lines] [--tabsize] [--text] [--width]\n"
856-	    "            -y | --side-by-side file1 file2\n"
857-	    "       diff [--help] [--version]\n");
858-
859-	if (help)
860-		exit(0);
861-	else
862-		exit(2);
863-}
864-
865-static void
866-conflicting_format(void)
867-{
868-
869-	fprintf(stderr, "error: conflicting output format options.\n");
870-	usage();
871-}
872-
873-static bool
874-do_color(void)
875-{
876-	const char *p, *p2;
877-
878-	switch (colorflag) {
879-	case COLORFLAG_AUTO:
880-		p = getenv("CLICOLOR");
881-		p2 = getenv("COLORTERM");
882-		if ((p != NULL && *p != '\0') || (p2 != NULL && *p2 != '\0'))
883-			return isatty(STDOUT_FILENO);
884-		break;
885-	case COLORFLAG_ALWAYS:
886-		return (true);
887-	case COLORFLAG_NEVER:
888-		return (false);
889-	}
890-
891-	return (false);
892-}
893-
894-static char *
895-splice(char *dir, char *path)
896-{
897-	char *tail, *buf;
898-	size_t dirlen;
899-
900-	dirlen = strlen(dir);
901-	while (dirlen != 0 && dir[dirlen - 1] == '/')
902-	    dirlen--;
903-	if ((tail = strrchr(path, '/')) == NULL)
904-		tail = path;
905-	else
906-		tail++;
907-	xasprintf(&buf, "%.*s/%s", (int)dirlen, dir, tail);
908-	return (buf);
909-}
+0, -137
  1@@ -1,137 +0,0 @@
  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
+0, -377
  1@@ -1,377 +0,0 @@
  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-}
+0, -1697
   1@@ -1,1697 +0,0 @@
   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-}
+0, -155
  1@@ -1,155 +0,0 @@
  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-}
+0, -33
 1@@ -1,33 +0,0 @@
 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 *);
+0, -96
 1@@ -1,96 +0,0 @@
 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-}
+0, -32
 1@@ -1,32 +0,0 @@
 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 */
+894, -0
  1@@ -0,0 +1,894 @@
  2+/* See LICENSE file for copyright and license details. */
  3+#include "util.h"
  4+
  5+#include <sys/types.h>
  6+#include <sys/wait.h>
  7+
  8+#include <ctype.h>
  9+#include <err.h>
 10+#include <errno.h>
 11+#include <fcntl.h>
 12+#include <stdarg.h>
 13+#include <getopt.h>
 14+#include <inttypes.h>
 15+#include <limits.h>
 16+#include <poll.h>
 17+#include <stdio.h>
 18+#include <stdlib.h>
 19+#include <string.h>
 20+#include <unistd.h>
 21+
 22+struct Range {
 23+	int from;
 24+	int to;
 25+};
 26+
 27+struct Diff {
 28+#define DIFF_TYPE1 1
 29+#define DIFF_TYPE2 2
 30+#define DIFF_TYPE3 3
 31+	int type;
 32+
 33+	struct Range old;
 34+	struct Range new;
 35+};
 36+
 37+#define EFLAG_NONE     0
 38+#define EFLAG_OVERLAP  1
 39+#define EFLAG_NOOVERLAP 2
 40+#define EFLAG_UNMERGED 3
 41+
 42+static size_t szchanges;
 43+
 44+static struct Diff *d13;
 45+static struct Diff *d23;
 46+static struct Diff *de;
 47+static char *overlap;
 48+static int overlapcnt;
 49+static FILE *fp[3];
 50+static int cline[3];
 51+static int sigpipe[2];
 52+static int last[4];
 53+static int Aflag, eflag, iflag, mflag, Tflag;
 54+static int oflag;
 55+static int strip_cr;
 56+static char *f1mark, *f2mark, *f3mark;
 57+static const char *oldmark = "<<<<<<<";
 58+static const char *orgmark = "|||||||";
 59+static const char *newmark = ">>>>>>>";
 60+static const char *divider = "=======";
 61+
 62+static int duplicate(struct Range *, struct Range *);
 63+static int edit(struct Diff *, int, int, int);
 64+static char *getchange(FILE *);
 65+static char *get_line(FILE *, size_t *);
 66+static int readin(int, struct Diff **);
 67+static int skip(int, int, const char *);
 68+static void change(int, struct Range *, int);
 69+static void keep(int, struct Range *);
 70+static void merge(int, int);
 71+static void prange(struct Range *, int);
 72+static void repos(int);
 73+static void separate(const char *);
 74+static void edscript(int);
 75+static void Ascript(int);
 76+static void mergescript(int);
 77+static void increase(void);
 78+static void usage(void);
 79+static void printrange(FILE *, struct Range *);
 80+static void diffexec(const char *, char **, int *);
 81+static int strtoi(char *, char **);
 82+static void handle_sig(int);
 83+static void xasprintf(char **, const char *, ...);
 84+
 85+static const char diff3_version[] = "FreeBSD diff3 20240925";
 86+
 87+enum {
 88+	DIFFPROG_OPT,
 89+	STRIPCR_OPT,
 90+	HELP_OPT,
 91+	VERSION_OPT
 92+};
 93+
 94+#define DIFF_PATH "/usr/bin/diff"
 95+
 96+#define OPTIONS "3aAeEiL:mTxX"
 97+static struct option longopts[] = {
 98+	{ "ed",                 no_argument,            NULL,   'e' },
 99+	{ "show-overlap",       no_argument,            NULL,   'E' },
100+	{ "overlap-only",       no_argument,            NULL,   'x' },
101+	{ "initial-tab",        no_argument,            NULL,   'T' },
102+	{ "text",               no_argument,            NULL,   'a' },
103+	{ "strip-trailing-cr",  no_argument,            NULL,   STRIPCR_OPT },
104+	{ "show-all",           no_argument,            NULL,   'A' },
105+	{ "easy-only",          no_argument,            NULL,   '3' },
106+	{ "merge",              no_argument,            NULL,   'm' },
107+	{ "label",              required_argument,      NULL,   'L' },
108+	{ "diff-program",       required_argument,      NULL,   DIFFPROG_OPT },
109+	{ "help",               no_argument,            NULL,   HELP_OPT},
110+	{ "version",            no_argument,            NULL,   VERSION_OPT}
111+};
112+
113+static void
114+usage(void)
115+{
116+	fprintf(stderr, "usage: diff3 [-3aAeEimTxX] [-L label1] [-L label2] "
117+	    "[-L label3] file1 file2 file3\n");
118+}
119+
120+static int
121+strtoi(char *str, char **end)
122+{
123+	intmax_t num;
124+
125+	errno = 0;
126+	num = strtoimax(str, end, 10);
127+	if ((end != NULL && *end == str) ||
128+	    num < 0 || num > INT_MAX ||
129+	    errno == EINVAL || errno == ERANGE)
130+		err(1, "error in diff output");
131+	return (int)num;
132+}
133+
134+static int
135+readin(int fd, struct Diff **dd)
136+{
137+	int a, b, c, d;
138+	int i;
139+	char kind, *p;
140+	FILE *f;
141+
142+	f = fdopen(fd, "r");
143+	if (f == NULL)
144+		err(2, "fdopen");
145+	for (i = 0; (p = getchange(f)) != NULL; i++) {
146+		if ((size_t)i >= szchanges - 1)
147+			increase();
148+
149+		a = b = strtoi(p, &p);
150+		if (*p == ',')
151+			b = strtoi(p + 1, &p);
152+		kind = *p++;
153+		c = d = strtoi(p, &p);
154+		if (*p == ',')
155+			d = strtoi(p + 1, &p);
156+		if (*p != '\n')
157+			errx(1, "error in diff output");
158+		if (kind == 'a')
159+			a++;
160+		else if (kind == 'c')
161+			/* nothing */ ;
162+		else if (kind == 'd')
163+			c++;
164+		else
165+			errx(1, "error in diff output");
166+		b++;
167+		d++;
168+		if (b < a || d < c)
169+			errx(1, "error in diff output");
170+		(*dd)[i].old.from = a;
171+		(*dd)[i].old.to = b;
172+		(*dd)[i].new.from = c;
173+		(*dd)[i].new.to = d;
174+		if (i > 0) {
175+			if ((*dd)[i].old.from < (*dd)[i - 1].old.to ||
176+			    (*dd)[i].new.from < (*dd)[i - 1].new.to)
177+				errx(1, "diff output out of order");
178+		}
179+	}
180+	if (i > 0) {
181+		(*dd)[i].old.from = (*dd)[i].old.to = (*dd)[i - 1].old.to;
182+		(*dd)[i].new.from = (*dd)[i].new.to = (*dd)[i - 1].new.to;
183+	}
184+	fclose(f);
185+	return i;
186+}
187+
188+static void
189+diffexec(const char *diffprog, char **diffargv, int fd[])
190+{
191+	switch (fork()) {
192+	case 0:
193+		close(fd[0]);
194+		if (dup2(fd[1], STDOUT_FILENO) == -1)
195+			err(2, "child could not duplicate descriptor");
196+		close(fd[1]);
197+		execvp(diffprog, diffargv);
198+		err(2, "could not execute diff: %s", diffprog);
199+		break;
200+	case -1:
201+		err(2, "could not fork");
202+		break;
203+	}
204+	close(fd[1]);
205+}
206+
207+static char *
208+getchange(FILE *b)
209+{
210+	char *line;
211+
212+	while ((line = get_line(b, NULL)) != NULL) {
213+		if (isdigit((unsigned char)line[0]))
214+			return line;
215+	}
216+	return NULL;
217+}
218+
219+static char *
220+get_line(FILE *b, size_t *n)
221+{
222+	ssize_t len;
223+	static char *buf = NULL;
224+	static size_t bufsize = 0;
225+
226+	if ((len = getline(&buf, &bufsize, b)) < 0)
227+		return NULL;
228+
229+	if (strip_cr && len >= 2 && strcmp("\r\n", &(buf[len - 2])) == 0) {
230+		buf[len - 2] = '\n';
231+		buf[len - 1] = '\0';
232+		len--;
233+	}
234+
235+	if (n != NULL)
236+		*n = len;
237+
238+	return buf;
239+}
240+
241+static void
242+merge(int m1, int m2)
243+{
244+	struct Diff *d1, *d2, *d3;
245+	int j, t1, t2;
246+	int dup = 0;
247+
248+	d1 = d13;
249+	d2 = d23;
250+	j = 0;
251+
252+	for (;;) {
253+		t1 = (d1 < d13 + m1);
254+		t2 = (d2 < d23 + m2);
255+		if (!t1 && !t2)
256+			break;
257+
258+		if (!t2 || (t1 && d1->new.to < d2->new.from)) {
259+			if (eflag == EFLAG_NONE) {
260+				separate("1");
261+				change(1, &d1->old, 0);
262+				keep(2, &d1->new);
263+				change(3, &d1->new, 0);
264+			} else if (eflag == EFLAG_OVERLAP) {
265+				j = edit(d2, dup, j, DIFF_TYPE1);
266+			}
267+			d1++;
268+			continue;
269+		}
270+		if (!t1 || (t2 && d2->new.to < d1->new.from)) {
271+			if (eflag == EFLAG_NONE) {
272+				separate("2");
273+				keep(1, &d2->new);
274+				change(3, &d2->new, 0);
275+				change(2, &d2->old, 0);
276+			} else if (Aflag || mflag) {
277+				if (eflag == EFLAG_UNMERGED)
278+					j = edit(d2, dup, j, DIFF_TYPE2);
279+			}
280+			d2++;
281+			continue;
282+		}
283+		if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
284+			d1[1].old.from = d1->old.from;
285+			d1[1].new.from = d1->new.from;
286+			d1++;
287+			continue;
288+		}
289+		if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
290+			d2[1].old.from = d2->old.from;
291+			d2[1].new.from = d2->new.from;
292+			d2++;
293+			continue;
294+		}
295+		if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
296+			dup = duplicate(&d1->old, &d2->old);
297+			if (eflag == EFLAG_NONE) {
298+				separate(dup ? "3" : "");
299+				change(1, &d1->old, dup);
300+				change(2, &d2->old, 0);
301+				d3 = d1->old.to > d1->old.from ? d1 : d2;
302+				change(3, &d3->new, 0);
303+			} else {
304+				j = edit(d1, dup, j, DIFF_TYPE3);
305+			}
306+			dup = 0;
307+			d1++;
308+			d2++;
309+			continue;
310+		}
311+		if (d1->new.from < d2->new.from) {
312+			d2->old.from -= d2->new.from - d1->new.from;
313+			d2->new.from = d1->new.from;
314+		} else if (d2->new.from < d1->new.from) {
315+			d1->old.from -= d1->new.from - d2->new.from;
316+			d1->new.from = d2->new.from;
317+		}
318+		if (d1->new.to > d2->new.to) {
319+			d2->old.to += d1->new.to - d2->new.to;
320+			d2->new.to = d1->new.to;
321+		} else if (d2->new.to > d1->new.to) {
322+			d1->old.to += d2->new.to - d1->new.to;
323+			d1->new.to = d2->new.to;
324+		}
325+	}
326+
327+	if (mflag)
328+		mergescript(j);
329+	else if (Aflag)
330+		Ascript(j);
331+	else if (eflag)
332+		edscript(j);
333+}
334+
335+static void
336+separate(const char *s)
337+{
338+	printf("====%s\n", s);
339+}
340+
341+static void
342+change(int i, struct Range *rold, int dup)
343+{
344+	printf("%d:", i);
345+	last[i] = rold->to;
346+	prange(rold, 0);
347+	if (dup)
348+		return;
349+	i--;
350+	skip(i, rold->from, NULL);
351+	skip(i, rold->to, "  ");
352+}
353+
354+static void
355+prange(struct Range *rold, int delete)
356+{
357+	if (rold->to <= rold->from)
358+		printf("%da\n", rold->from - 1);
359+	else {
360+		printf("%d", rold->from);
361+		if (rold->to > rold->from + 1)
362+			printf(",%d", rold->to - 1);
363+		if (delete)
364+			printf("d\n");
365+		else
366+			printf("c\n");
367+	}
368+}
369+
370+static void
371+keep(int i, struct Range *rnew)
372+{
373+	int delta;
374+	struct Range trange;
375+
376+	delta = last[3] - last[i];
377+	trange.from = rnew->from - delta;
378+	trange.to = rnew->to - delta;
379+	change(i, &trange, 1);
380+}
381+
382+static int
383+skip(int i, int from, const char *pr)
384+{
385+	size_t j, n;
386+	char *line;
387+
388+	for (n = 0; cline[i] < from - 1; n += j) {
389+		if ((line = get_line(fp[i], &j)) == NULL)
390+			errx(EXIT_FAILURE, "logic error");
391+		if (pr != NULL)
392+			printf("%s%s", Tflag == 1 ? "\t" : pr, line);
393+		cline[i]++;
394+	}
395+	return (int)n;
396+}
397+
398+static int
399+duplicate(struct Range *r1, struct Range *r2)
400+{
401+	int c, d;
402+	int nchar;
403+	int nline;
404+
405+	if (r1->to-r1->from != r2->to-r2->from)
406+		return 0;
407+	skip(0, r1->from, NULL);
408+	skip(1, r2->from, NULL);
409+	nchar = 0;
410+	for (nline = 0; nline < r1->to - r1->from; nline++) {
411+		do {
412+			c = getc(fp[0]);
413+			d = getc(fp[1]);
414+			if (c == -1 && d == -1)
415+				break;
416+			if (c == -1 || d == -1)
417+				errx(EXIT_FAILURE, "logic error");
418+			nchar++;
419+			if (c != d) {
420+				repos(nchar);
421+				return 0;
422+			}
423+		} while (c != '\n');
424+	}
425+	repos(nchar);
426+	return 1;
427+}
428+
429+static void
430+repos(int nchar)
431+{
432+	int i;
433+
434+	for (i = 0; i < 2; i++)
435+		(void)fseek(fp[i], (long)-nchar, SEEK_CUR);
436+}
437+
438+static int
439+edit(struct Diff *diff, int dup, int j, int difftype)
440+{
441+	if (!(eflag == EFLAG_UNMERGED ||
442+	    (!dup && eflag == EFLAG_OVERLAP) ||
443+	    (dup && eflag == EFLAG_NOOVERLAP))) {
444+		return j;
445+	}
446+	j++;
447+	overlap[j] = !dup;
448+	if (!dup)
449+		overlapcnt++;
450+
451+	de[j].type = difftype;
452+	de[j].old.from = diff->old.from;
453+	de[j].old.to = diff->old.to;
454+	de[j].new.from = diff->new.from;
455+	de[j].new.to = diff->new.to;
456+	return j;
457+}
458+
459+static void
460+printrange(FILE *p, struct Range *r)
461+{
462+	char *line = NULL;
463+	size_t len = 0;
464+	int i = 1;
465+
466+	if (r->from == r->to)
467+		return;
468+
469+	if (r->from > r->to)
470+		errx(EXIT_FAILURE, "invalid print range");
471+
472+	fseek(p, 0L, SEEK_SET);
473+	while (getline(&line, &len, p) > 0) {
474+		if (i >= r->from)
475+			printf("%s", line);
476+		if (++i > r->to - 1)
477+			break;
478+	}
479+	free(line);
480+}
481+
482+static void
483+edscript(int n)
484+{
485+	int delete;
486+	struct Range *new, *old;
487+
488+	for (; n > 0; n--) {
489+		new = &de[n].new;
490+		old = &de[n].old;
491+
492+		delete = (new->from == new->to);
493+		if (de[n].type == DIFF_TYPE1) {
494+			if (delete)
495+				printf("%dd\n", new->from - 1);
496+			else if (old->from == new->from && old->to == new->to) {
497+				printf("%dc\n", old->from);
498+				printrange(fp[2], old);
499+				printf(".\n");
500+			}
501+			continue;
502+		} else {
503+			if (!oflag || !overlap[n]) {
504+				prange(old, delete);
505+			} else {
506+				printf("%da\n", old->to - 1);
507+				printf("%s\n", divider);
508+			}
509+			printrange(fp[2], new);
510+			if (!oflag || !overlap[n]) {
511+				if (!delete)
512+					printf(".\n");
513+			} else {
514+				printf("%s %s\n.\n", newmark, f3mark);
515+				printf("%da\n%s %s\n.\n", old->from - 1, oldmark, f1mark);
516+			}
517+		}
518+	}
519+	if (iflag)
520+		printf("w\nq\n");
521+
522+	exit(eflag == EFLAG_NONE ? overlapcnt : 0);
523+}
524+
525+static void
526+Ascript(int n)
527+{
528+	int startmark;
529+	int deletenew;
530+	int deleteold;
531+	struct Range *new, *old;
532+
533+	for (; n > 0; n--) {
534+		new = &de[n].new;
535+		old = &de[n].old;
536+		deletenew = (new->from == new->to);
537+		deleteold = (old->from == old->to);
538+
539+		if (de[n].type == DIFF_TYPE2) {
540+			if (!oflag || !overlap[n]) {
541+				prange(old, deletenew);
542+				printrange(fp[2], new);
543+			} else {
544+				startmark = new->to - 1;
545+				printf("%da\n", startmark);
546+				printf("%s %s\n", newmark, f3mark);
547+				printf(".\n");
548+				printf("%da\n", startmark - (new->to - new->from));
549+				printf("%s %s\n", oldmark, f2mark);
550+				if (!deleteold)
551+					printrange(fp[1], old);
552+				printf("%s\n.\n", divider);
553+			}
554+		} else if (de[n].type == DIFF_TYPE3) {
555+			startmark = old->to - 1;
556+			if (!oflag || !overlap[n]) {
557+				prange(old, deletenew);
558+				printrange(fp[2], new);
559+			} else {
560+				printf("%da\n", startmark);
561+				printf("%s %s\n", orgmark, f2mark);
562+				if (deleteold) {
563+					struct Range r;
564+					r.from = old->from-1;
565+					r.to = new->to;
566+					printrange(fp[1], &r);
567+				} else
568+					printrange(fp[1], old);
569+				printf("%s\n", divider);
570+				printrange(fp[2], new);
571+			}
572+			if (!oflag || !overlap[n]) {
573+				if (!deletenew)
574+					printf(".\n");
575+			} else {
576+				printf("%s %s\n.\n", newmark, f3mark);
577+				printf("%da\n%s %s\n.\n", startmark - (old->to - old->from), oldmark, f1mark);
578+			}
579+		}
580+	}
581+	if (iflag)
582+		printf("w\nq\n");
583+
584+	exit(overlapcnt > 0);
585+}
586+
587+static void
588+mergescript(int i)
589+{
590+	struct Range r, *new, *old;
591+	int n;
592+	int delete = 0;
593+
594+	r.from = 1;
595+	r.to = 1;
596+
597+	for (n = 1; n <= i; n++) {
598+		new = &de[n].new;
599+		old = &de[n].old;
600+
601+		delete = (new->from == new->to);
602+		if (de[n].type == DIFF_TYPE1 && delete)
603+			r.to = new->from - 1;
604+		else if (de[n].type == DIFF_TYPE3 && (old->from == old->to)) {
605+			r.from = old->from - 1;
606+			r.to = new->from;
607+		} else
608+			r.to = old->from;
609+
610+		printrange(fp[0], &r);
611+		switch (de[n].type) {
612+		case DIFF_TYPE1:
613+			if (!delete)
614+				printrange(fp[2], new);
615+			break;
616+		case DIFF_TYPE2:
617+			printf("%s %s\n", oldmark, f2mark);
618+			printrange(fp[1], old);
619+			printf("%s\n", divider);
620+			printrange(fp[2], new);
621+			printf("%s %s\n", newmark, f3mark);
622+			break;
623+		case DIFF_TYPE3:
624+			if (!oflag || !overlap[n]) {
625+				printrange(fp[2], new);
626+			} else {
627+				printf("%s %s\n", oldmark, f1mark);
628+				printrange(fp[0], old);
629+				if (eflag != EFLAG_OVERLAP) {
630+					printf("%s %s\n", orgmark, f2mark);
631+					if (old->from == old->to) {
632+						struct Range or;
633+						or.from = old->from - 1;
634+						or.to = new->to;
635+						printrange(fp[1], &or);
636+					} else {
637+						printrange(fp[1], old);
638+					}
639+				}
640+				printf("%s\n", divider);
641+				printrange(fp[2], new);
642+				printf("%s %s\n", newmark, f3mark);
643+			}
644+			break;
645+		default:
646+			printf("Error: Unhandled diff type - exiting\n");
647+			exit(EXIT_FAILURE);
648+		}
649+
650+		if (old->from == old->to)
651+			r.from = new->to;
652+		else
653+			r.from = old->to;
654+	}
655+
656+	new = &de[n-1].new;
657+	old = &de[n-1].old;
658+
659+	if (old->from == new->from && old->to == new->to)
660+		r.from--;
661+	else if (new->from == new->to)
662+		r.from = old->from;
663+
664+	r.to = INT_MAX;
665+	printrange(fp[2], &r);
666+	exit(overlapcnt > 0);
667+}
668+
669+static void
670+increase(void)
671+{
672+	struct Diff *p;
673+	char *q;
674+	size_t newsz, incr;
675+
676+	newsz = szchanges == 0 ? 64 : 2 * szchanges;
677+	incr = newsz - szchanges;
678+
679+	p = ereallocarray(d13, newsz, sizeof(*p));
680+	memset(p + szchanges, 0, incr * sizeof(*p));
681+	d13 = p;
682+
683+	p = ereallocarray(d23, newsz, sizeof(*p));
684+	memset(p + szchanges, 0, incr * sizeof(*p));
685+	d23 = p;
686+
687+	p = ereallocarray(de, newsz, sizeof(*p));
688+	memset(p + szchanges, 0, incr * sizeof(*p));
689+	de = p;
690+
691+	q = ereallocarray(overlap, newsz, 1);
692+	memset(q + szchanges, 0, incr * 1);
693+	overlap = q;
694+	szchanges = newsz;
695+}
696+
697+static void
698+handle_sig(int signo)
699+{
700+	write(sigpipe[1], &signo, sizeof(signo));
701+}
702+
703+static void
704+xasprintf(char **strp, const char *fmt, ...)
705+{
706+	va_list ap;
707+	int r;
708+
709+	va_start(ap, fmt);
710+	r = vasprintf(strp, fmt, ap);
711+	va_end(ap);
712+	if (r == -1)
713+		err(2, "asprintf");
714+}
715+
716+// ?man diff3: 3-way differential file comparison
717+// ?man arguments: file1 file2 file3
718+// ?man synopsis: [-3aAeEiL:mTxX] [--diff-program program] [--strip-trailing-cr] [-L label1] [-L label2] [-L label3] file1 file2 file3
719+int
720+main(int argc, char **argv)
721+{
722+	int ch, nblabels, status, m, n, npe, nleft;
723+	char *labels[] = { NULL, NULL, NULL };
724+	const char *diffprog = DIFF_PATH;
725+	char *file1, *file2, *file3;
726+	char *diffargv[7];
727+	int diffargc = 0;
728+	int fd13[2], fd23[2], signo;
729+	pid_t wpid;
730+	struct pollfd pfd;
731+
732+	nblabels = 0;
733+	eflag = EFLAG_NONE;
734+	oflag = 0;
735+	diffargv[diffargc++] = (char *)diffprog;
736+	while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
737+		// ?man -3: output changes specific only to file3
738+		switch (ch) {
739+		case '3':
740+			eflag = EFLAG_NOOVERLAP;
741+			break;
742+		// ?man -a: treat files as text
743+		case 'a':
744+			diffargv[diffargc++] = "-a";
745+			break;
746+		// ?man -A: output all changes bracketing conflicts
747+		case 'A':
748+			Aflag = 1;
749+			break;
750+		// ?man -e: output ed script
751+		case 'e':
752+			eflag = EFLAG_UNMERGED;
753+			break;
754+		// ?man -E: show overlapping changes
755+		case 'E':
756+			eflag = EFLAG_OVERLAP;
757+			oflag = 1;
758+			break;
759+		// ?man -i: append w and q ed commands
760+		case 'i':
761+			iflag = 1;
762+			break;
763+		// ?man -L:label: define label
764+		case 'L':
765+			oflag = 1;
766+			if (nblabels >= 3)
767+				errx(2, "too many file label options");
768+			labels[nblabels++] = optarg;
769+			break;
770+		// ?man -m: merge output
771+		case 'm':
772+			Aflag = 1;
773+			oflag = 1;
774+			mflag = 1;
775+			break;
776+		// ?man -T: use tab prefix in normal listing
777+		case 'T':
778+			Tflag = 1;
779+			break;
780+		// ?man -x: output script with changes specific to all three
781+		case 'x':
782+			eflag = EFLAG_OVERLAP;
783+			break;
784+		// ?man -X: show overlapping changes
785+		case 'X':
786+			oflag = 1;
787+			eflag = EFLAG_OVERLAP;
788+			break;
789+		case DIFFPROG_OPT:
790+			diffprog = optarg;
791+			break;
792+		case STRIPCR_OPT:
793+			strip_cr = 1;
794+			diffargv[diffargc++] = "--strip-trailing-cr";
795+			break;
796+		case HELP_OPT:
797+			usage();
798+			exit(0);
799+		case VERSION_OPT:
800+			printf("%s\n", diff3_version);
801+			exit(0);
802+		}
803+	}
804+	argc -= optind;
805+	argv += optind;
806+
807+	if (Aflag) {
808+		if (eflag == EFLAG_NONE)
809+			eflag = EFLAG_UNMERGED;
810+		oflag = 1;
811+	}
812+
813+	if (argc != 3) {
814+		usage();
815+		exit(2);
816+	}
817+
818+	file1 = argv[0];
819+	file2 = argv[1];
820+	file3 = argv[2];
821+
822+	if (oflag) {
823+		xasprintf(&f1mark, "%s", labels[0] != NULL ? labels[0] : file1);
824+		xasprintf(&f2mark, "%s", labels[1] != NULL ? labels[1] : file2);
825+		xasprintf(&f3mark, "%s", labels[2] != NULL ? labels[2] : file3);
826+	}
827+	fp[0] = fopen(file1, "r");
828+	if (fp[0] == NULL)
829+		err(2, "Can't open %s", file1);
830+
831+	fp[1] = fopen(file2, "r");
832+	if (fp[1] == NULL)
833+		err(2, "Can't open %s", file2);
834+
835+	fp[2] = fopen(file3, "r");
836+	if (fp[2] == NULL)
837+		err(2, "Can't open %s", file3);
838+
839+	if (pipe(fd13))
840+		err(2, "pipe");
841+	if (pipe(fd23))
842+		err(2, "pipe");
843+	if (pipe(sigpipe))
844+		err(2, "pipe");
845+	if (fcntl(sigpipe[0], F_SETFD, FD_CLOEXEC))
846+		err(2, "fcntl");
847+	if (fcntl(sigpipe[1], F_SETFD, FD_CLOEXEC))
848+		err(2, "fcntl");
849+
850+	pfd.fd = sigpipe[0];
851+	pfd.events = POLLIN;
852+	pfd.revents = 0;
853+
854+	if (signal(SIGCHLD, handle_sig) == SIG_ERR)
855+		err(2, "signal");
856+
857+	diffargv[diffargc] = file1;
858+	diffargv[diffargc + 1] = file3;
859+	diffargv[diffargc + 2] = NULL;
860+
861+	nleft = 0;
862+	diffexec(diffprog, diffargv, fd13);
863+	nleft++;
864+
865+	diffargv[diffargc] = file2;
866+	diffexec(diffprog, diffargv, fd23);
867+	nleft++;
868+
869+	increase();
870+	m = readin(fd13[0], &d13);
871+	n = readin(fd23[0], &d23);
872+
873+	while (nleft > 0) {
874+		npe = poll(&pfd, 1, -1);
875+		if (npe == -1) {
876+			if (errno == EINTR)
877+				continue;
878+			err(2, "poll");
879+		}
880+		if (pfd.revents != POLLIN)
881+			continue;
882+		if (read(pfd.fd, &signo, sizeof(signo)) < 0)
883+			err(2, "read");
884+		while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {
885+			if (WIFEXITED(status) && WEXITSTATUS(status) >= 2)
886+				errx(2, "diff exited abnormally");
887+			else if (WIFSIGNALED(status))
888+				errx(2, "diff killed by signal %d", WTERMSIG(status));
889+			nleft--;
890+		}
891+	}
892+	merge(m, n);
893+
894+	return EXIT_SUCCESS;
895+}
+3150, -0
   1@@ -0,0 +1,3150 @@
   2+/* See LICENSE file for copyright and license details. */
   3+#include "util.h"
   4+
   5+#include <sys/file.h>
   6+#include <sys/mman.h>
   7+#include <sys/param.h>
   8+#include <sys/stat.h>
   9+#include <sys/wait.h>
  10+
  11+#include <ctype.h>
  12+#include <dirent.h>
  13+#include <err.h>
  14+#include <errno.h>
  15+#include <fcntl.h>
  16+#include <getopt.h>
  17+#include <libgen.h>
  18+#include <limits.h>
  19+#include <paths.h>
  20+#include <stdarg.h>
  21+#include <stddef.h>
  22+#include <stdint.h>
  23+#include <stdio.h>
  24+#include <stdlib.h>
  25+#include <string.h>
  26+#include <unistd.h>
  27+
  28+#define DEBUGGING
  29+
  30+#define MAXHUNKSIZE 100000
  31+#define INITHUNKMAX 125
  32+#define INITLINELEN 8192
  33+#define BUFFERSIZE 1024
  34+#define LINENUM_MAX LONG_MAX
  35+
  36+#define SCCSPREFIX "s."
  37+#define GET "get -e %s"
  38+#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
  39+
  40+#define RCSSUFFIX ",v"
  41+#define CHECKOUT "/usr/bin/co"
  42+#define RCSDIFF "/usr/bin/rcsdiff"
  43+
  44+#define ORIGEXT ".orig"
  45+#define REJEXT ".rej"
  46+
  47+#define strNE(s1,s2) (strcmp(s1, s2))
  48+#define strEQ(s1,s2) (!strcmp(s1, s2))
  49+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
  50+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
  51+
  52+#define OLD_FILE 0
  53+#define NEW_FILE 1
  54+#define INDEX_FILE 2
  55+#define MAX_FILE 3
  56+
  57+#define CONTEXT_DIFF 1
  58+#define NORMAL_DIFF 2
  59+#define ED_DIFF 3
  60+#define NEW_CONTEXT_DIFF 4
  61+#define UNI_DIFF 5
  62+
  63+#define _PATH_ED "/bin/ed"
  64+
  65+#define ISDIGIT(c) (isascii((unsigned char)c) && isdigit((unsigned char)c))
  66+
  67+#ifndef __UNCONST
  68+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
  69+#endif
  70+
  71+#ifndef __dead
  72+#define __dead __attribute__((__noreturn__))
  73+#endif
  74+
  75+typedef long LINENUM;
  76+
  77+enum BackupType {
  78+	BACKUP_UNDEFINED,
  79+	BACKUP_NONE,
  80+	BACKUP_SIMPLE,
  81+	BACKUP_NUMBERED_EXISTING,
  82+	BACKUP_NUMBERED
  83+};
  84+
  85+struct FileName {
  86+	char *path;
  87+	int exists;
  88+};
  89+
  90+mode_t filemode = 0644;
  91+char *buf;
  92+size_t bufsz;
  93+int using_plan_a = 1;
  94+int out_of_mem = 0;
  95+
  96+#define MAXFILEC 2
  97+char *filearg[MAXFILEC];
  98+int ok_to_create_file = 0;
  99+char *outname = NULL;
 100+char *origprae = NULL;
 101+char *TMPOUTNAME;
 102+char *TMPINNAME;
 103+char *TMPREJNAME;
 104+char *TMPPATNAME;
 105+int toutkeep = 0;
 106+int trejkeep = 0;
 107+int warn_on_invalid_line;
 108+int last_line_missing_eol;
 109+
 110+#ifdef DEBUGGING
 111+int debug = 0;
 112+#endif
 113+
 114+int force = 0;
 115+int batch = 0;
 116+int verbose = 1;
 117+int reverse = 0;
 118+int noreverse = 0;
 119+int skip_rest_of_patch = 0;
 120+int strippath = 957;
 121+int canonicalize = 0;
 122+int check_only = 0;
 123+int diff_type = 0;
 124+char *revision = NULL;
 125+LINENUM input_lines = 0;
 126+int posix = 0;
 127+int backup_if_mismatch = -1;
 128+
 129+enum BackupType backup_type = BACKUP_UNDEFINED;
 130+const char *simple_backup_suffix = "~";
 131+
 132+static int remove_empty_files = 0;
 133+static int reverse_flag_specified = 0;
 134+static char rejname[PATH_MAX];
 135+static char serrbuf[BUFSIZ];
 136+static LINENUM last_frozen_line = 0;
 137+static int Argc;
 138+static char **Argv;
 139+static int Argc_last;
 140+static char **Argv_last;
 141+static FILE *ofp = NULL;
 142+static FILE *rejfp = NULL;
 143+static int filec = 0;
 144+static LINENUM last_offset = 0;
 145+static LINENUM maxfuzz = 2;
 146+
 147+static int do_defines = 0;
 148+static char if_defined[128];
 149+static char not_defined[128];
 150+static const char else_defined[] = "#else\n";
 151+static char end_defined[128];
 152+
 153+/* function declarations */
 154+static void reinitialize_almost_everything(void);
 155+static void get_some_switches(void);
 156+static void reset_getopt_state(void);
 157+static LINENUM locate_hunk(LINENUM);
 158+static void abort_context_hunk(void);
 159+static void rej_line(int, LINENUM);
 160+static void abort_hunk(void);
 161+static void apply_hunk(LINENUM);
 162+static void init_output(const char *);
 163+static void init_reject(const char *);
 164+static void copy_till(LINENUM, int);
 165+static int spew_output(void);
 166+static void dump_patch_line(LINENUM, int);
 167+static void dump_input_line(LINENUM, int);
 168+static int patch_match(LINENUM, LINENUM, LINENUM);
 169+static int similar(const char *, const char *, ssize_t);
 170+static void usage(void);
 171+
 172+void re_patch(void);
 173+void open_patch_file(const char *);
 174+void set_hunkmax(void);
 175+int there_is_another_patch(void);
 176+int another_hunk(void);
 177+int pch_swap(void);
 178+char *pfetch(LINENUM);
 179+ssize_t pch_line_len(LINENUM);
 180+LINENUM pch_first(void);
 181+LINENUM pch_ptrn_lines(void);
 182+LINENUM pch_newfirst(void);
 183+LINENUM pch_repl_lines(void);
 184+LINENUM pch_end(void);
 185+LINENUM pch_context(void);
 186+LINENUM pch_hunk_beg(void);
 187+char pch_char(LINENUM);
 188+void do_ed_script(void);
 189+
 190+void re_input(void);
 191+void scan_input(const char *);
 192+char *ifetch(LINENUM, int);
 193+
 194+char *fetchname(const char *, int *, int);
 195+char *checked_in(char *);
 196+LINENUM strtolinenum(char *, char **);
 197+int backup_file(const char *);
 198+int move_file(const char *, const char *);
 199+int copy_file(const char *, const char *);
 200+void say(const char *, ...);
 201+__dead void fatal(const char *, ...);
 202+__dead void pfatal(const char *, ...);
 203+void ask(const char *, ...);
 204+char *savestr(const char *);
 205+void set_signals(int);
 206+void ignore_signals(void);
 207+void makedirs(const char *, int);
 208+__dead void version(void);
 209+__dead void my_exit(int);
 210+static void *pch_realloc(void *, size_t, size_t);
 211+int mkpath(char *);
 212+char *find_backup_file_name(const char *);
 213+enum BackupType get_version(const char *);
 214+
 215+static off_t i_size;
 216+static char *i_womp;
 217+static char **i_ptr;
 218+static char empty_line[] = { '\0' };
 219+
 220+static int tifd = -1;
 221+static char *tibuf[2];
 222+static LINENUM tiline[2] = {-1, -1};
 223+static LINENUM lines_per_buf;
 224+static int tireclen;
 225+
 226+static int rev_in_string(const char *);
 227+static int reallocate_lines(size_t *);
 228+static int plan_a(const char *);
 229+static void plan_b(const char *);
 230+
 231+static long p_filesize;
 232+static LINENUM p_first;
 233+static LINENUM p_newfirst;
 234+static LINENUM p_ptrn_lines;
 235+static LINENUM p_repl_lines;
 236+static LINENUM p_end = -1;
 237+static LINENUM p_max;
 238+static LINENUM p_context = 3;
 239+static LINENUM p_input_line = 0;
 240+static char **p_line = NULL;
 241+static ssize_t *p_len = NULL;
 242+static char *p_char = NULL;
 243+static int hunkmax = INITHUNKMAX;
 244+static int p_indent;
 245+static LINENUM p_base;
 246+static LINENUM p_bline;
 247+static LINENUM p_start;
 248+static LINENUM p_sline;
 249+static LINENUM p_hunk_beg;
 250+static LINENUM p_efake = -1;
 251+static LINENUM p_bfake = -1;
 252+static FILE *pfp = NULL;
 253+static char *bestguess = NULL;
 254+
 255+static void grow_hunkmax(void);
 256+static int intuit_diff_type(void);
 257+static void next_intuit_at(LINENUM, LINENUM);
 258+static void skip_to(LINENUM, LINENUM);
 259+static int pgetline(char **, size_t *, FILE *);
 260+static char *best_name(const struct FileName *, int);
 261+static char *posix_name(const struct FileName *, int);
 262+static size_t num_components(const char *);
 263+
 264+static char *patch_concat(const char *, const char *);
 265+static char *make_version_name(const char *, int);
 266+static int max_backup_version(const char *, const char *);
 267+static int version_number(const char *, const char *, size_t);
 268+static int argmatch(const char *, const char **);
 269+static void invalid_arg(const char *, const char *, int);
 270+
 271+static const char *backup_args[] = {
 272+	"none", "never", "simple", "nil", "existing", "t", "numbered", 0
 273+};
 274+static enum BackupType backup_types[] = {
 275+	BACKUP_NONE, BACKUP_SIMPLE, BACKUP_SIMPLE, BACKUP_NUMBERED_EXISTING,
 276+	BACKUP_NUMBERED_EXISTING, BACKUP_NUMBERED, BACKUP_NUMBERED
 277+};
 278+
 279+/* implementation */
 280+
 281+// ?man patch: apply a diff file to an original
 282+int
 283+main(int argc, char *argv[])
 284+{
 285+	int error, hunk, failed, i, fd;
 286+	LINENUM where, newwhere, fuzz, mymaxfuzz;
 287+	const char *tmpdir;
 288+	char *v;
 289+
 290+	error = 0;
 291+	where = 0;
 292+	bufsz = INITLINELEN;
 293+	if ((buf = malloc(bufsz)) == NULL)
 294+		pfatal("allocating input buffer");
 295+	buf[0] = '\0';
 296+
 297+	setbuf(stderr, serrbuf);
 298+	for (i = 0; i < MAXFILEC; i++)
 299+		filearg[i] = NULL;
 300+
 301+	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
 302+		tmpdir = _PATH_TMP;
 303+	for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
 304+		;
 305+	i++;
 306+	if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
 307+		fatal("cannot allocate memory");
 308+	if ((fd = mkstemp(TMPOUTNAME)) < 0)
 309+		pfatal("cant create %s", TMPOUTNAME);
 310+	close(fd);
 311+
 312+	if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
 313+		fatal("cannot allocate memory");
 314+	if ((fd = mkstemp(TMPINNAME)) < 0)
 315+		pfatal("cant create %s", TMPINNAME);
 316+	close(fd);
 317+
 318+	if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
 319+		fatal("cannot allocate memory");
 320+	if ((fd = mkstemp(TMPREJNAME)) < 0)
 321+		pfatal("cant create %s", TMPREJNAME);
 322+	close(fd);
 323+
 324+	if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
 325+		fatal("cannot allocate memory");
 326+	if ((fd = mkstemp(TMPPATNAME)) < 0)
 327+		pfatal("cant create %s", TMPPATNAME);
 328+	close(fd);
 329+
 330+	v = getenv("SIMPLE_BACKUP_SUFFIX");
 331+	if (v)
 332+		simple_backup_suffix = v;
 333+	else
 334+		simple_backup_suffix = ORIGEXT;
 335+
 336+	if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
 337+		v = getenv("VERSION_CONTROL");
 338+	if (v != NULL)
 339+		backup_type = get_version(v);
 340+
 341+	Argc = argc;
 342+	Argv = argv;
 343+	get_some_switches();
 344+
 345+	if (backup_type == BACKUP_UNDEFINED)
 346+		backup_type = posix ? BACKUP_NONE : BACKUP_NUMBERED_EXISTING;
 347+
 348+	set_signals(0);
 349+
 350+	for (open_patch_file(filearg[1]); there_is_another_patch(); reinitialize_almost_everything()) {
 351+		warn_on_invalid_line = 1;
 352+		if (outname == NULL)
 353+			outname = savestr(filearg[0]);
 354+
 355+		if (diff_type == ED_DIFF) {
 356+			do_ed_script();
 357+			continue;
 358+		}
 359+		if (!skip_rest_of_patch)
 360+			init_output(TMPOUTNAME);
 361+
 362+		init_reject(TMPREJNAME);
 363+
 364+		if (!skip_rest_of_patch)
 365+			scan_input(filearg[0]);
 366+
 367+		hunk = 0;
 368+		failed = 0;
 369+		out_of_mem = 0;
 370+		while (another_hunk()) {
 371+			hunk++;
 372+			fuzz = 0;
 373+			mymaxfuzz = pch_context();
 374+			if (maxfuzz < mymaxfuzz)
 375+				mymaxfuzz = maxfuzz;
 376+			if (!skip_rest_of_patch) {
 377+				do {
 378+					where = locate_hunk(fuzz);
 379+					if (hunk == 1 && where == 0 && !force) {
 380+						if (!pch_swap()) {
 381+							if (fuzz == 0)
 382+								say("Not enough memory to try swapped hunk! Assuming unswapped.\n");
 383+							continue;
 384+						}
 385+						reverse = !reverse;
 386+						where = locate_hunk(fuzz);
 387+						if (where == 0) {
 388+							if (!pch_swap())
 389+								fatal("lost hunk on alloc error!\n");
 390+							reverse = !reverse;
 391+						} else if (noreverse) {
 392+							if (!pch_swap())
 393+								fatal("lost hunk on alloc error!\n");
 394+							reverse = !reverse;
 395+							say("Ignoring previously applied (or reversed) patch.\n");
 396+							skip_rest_of_patch = 1;
 397+						} else if (batch) {
 398+							if (verbose)
 399+								say("%seversed (or %spreviously applied) patch detected! %s -R.",
 400+								    reverse ? "R" : "Unr",
 401+								    reverse ? "" : "not ",
 402+								    reverse ? "Assuming" : "Ignoring");
 403+						} else {
 404+							ask("%seversed (or %spreviously applied) patch detected! %s -R? [y] ",
 405+							    reverse ? "R" : "Unr",
 406+							    reverse ? "" : "not ",
 407+							    reverse ? "Assume" : "Ignore");
 408+							if (*buf == 'n') {
 409+								ask("Apply anyway? [n] ");
 410+								if (*buf != 'y')
 411+									skip_rest_of_patch = 1;
 412+								where = 0;
 413+								reverse = !reverse;
 414+								if (!pch_swap())
 415+									fatal("lost hunk on alloc error!\n");
 416+							}
 417+						}
 418+					}
 419+				} while (!skip_rest_of_patch && where == 0 && ++fuzz <= mymaxfuzz);
 420+
 421+				if (skip_rest_of_patch) {
 422+					if (ferror(ofp) || fclose(ofp)) {
 423+						say("Error writing %s\n", TMPOUTNAME);
 424+						error = 1;
 425+					}
 426+					ofp = NULL;
 427+				}
 428+			}
 429+			newwhere = pch_newfirst() + last_offset;
 430+			if (skip_rest_of_patch) {
 431+				abort_hunk();
 432+				failed++;
 433+				if (verbose)
 434+					say("Hunk #%d ignored at %ld.\n", hunk, newwhere);
 435+			} else if (where == 0) {
 436+				abort_hunk();
 437+				failed++;
 438+				if (verbose)
 439+					say("Hunk #%d failed at %ld.\n", hunk, newwhere);
 440+			} else {
 441+				apply_hunk(where);
 442+				if (verbose) {
 443+					say("Hunk #%d succeeded at %ld", hunk, newwhere);
 444+					if (fuzz != 0)
 445+						say(" with fuzz %ld", fuzz);
 446+					if (last_offset)
 447+						say(" (offset %ld line%s)", last_offset, last_offset == 1L ? "" : "s");
 448+					say(".\n");
 449+				}
 450+			}
 451+		}
 452+
 453+		if (out_of_mem && using_plan_a) {
 454+			Argc = Argc_last;
 455+			Argv = Argv_last;
 456+			say("\n\nRan out of memory using Plan A--trying again...\n\n");
 457+			if (ofp)
 458+				fclose(ofp);
 459+			ofp = NULL;
 460+			if (rejfp)
 461+				fclose(rejfp);
 462+			rejfp = NULL;
 463+			continue;
 464+		}
 465+		if (hunk == 0)
 466+			fatal("internal error: hunk should not be 0\n");
 467+
 468+		if (!skip_rest_of_patch && !spew_output()) {
 469+			say("cant write %s\n", TMPOUTNAME);
 470+			error = 1;
 471+		}
 472+
 473+		ignore_signals();
 474+		if (!skip_rest_of_patch) {
 475+			struct stat statbuf;
 476+			char *realout = outname;
 477+
 478+			if (!check_only) {
 479+				enum BackupType saved = backup_type;
 480+				if (failed > 0 && backup_if_mismatch > 0 && backup_type == BACKUP_NONE)
 481+					backup_type = BACKUP_SIMPLE;
 482+				if (move_file(TMPOUTNAME, outname) < 0) {
 483+					toutkeep = 1;
 484+					realout = TMPOUTNAME;
 485+					chmod(TMPOUTNAME, filemode);
 486+				} else
 487+					chmod(outname, filemode);
 488+				backup_type = saved;
 489+
 490+				if (remove_empty_files && stat(realout, &statbuf) == 0 && statbuf.st_size == 0) {
 491+					if (verbose)
 492+						say("Removing %s (empty after patching).\n", realout);
 493+					unlink(realout);
 494+				}
 495+			}
 496+		}
 497+		if (ferror(rejfp) || fclose(rejfp)) {
 498+			say("Error writing %s\n", rejname);
 499+			error = 1;
 500+		}
 501+		rejfp = NULL;
 502+		if (failed) {
 503+			error = 1;
 504+			if (*rejname == '\0') {
 505+				if (strlcpy(rejname, outname, sizeof(rejname)) >= sizeof(rejname))
 506+					fatal("filename %s is too long\n", outname);
 507+				if (strlcat(rejname, REJEXT, sizeof(rejname)) >= sizeof(rejname))
 508+					fatal("filename %s is too long\n", outname);
 509+			}
 510+			if (skip_rest_of_patch) {
 511+				say("%d out of %d hunks ignored--saving rejects to %s\n", failed, hunk, rejname);
 512+			} else {
 513+				say("%d out of %d hunks FAILED -- saving rejects to %s\n", failed, hunk, rejname);
 514+			}
 515+			if (!check_only && move_file(TMPREJNAME, rejname) < 0)
 516+				trejkeep = 1;
 517+		}
 518+		set_signals(1);
 519+	}
 520+	my_exit(error);
 521+}
 522+
 523+static void
 524+reset_getopt_state(void)
 525+{
 526+#if defined(__GLIBC__) || defined(__linux__)
 527+	optind = 0;
 528+#else
 529+	optreset = 1;
 530+	optind = 1;
 531+#endif
 532+}
 533+
 534+static void
 535+reinitialize_almost_everything(void)
 536+{
 537+	re_patch();
 538+	re_input();
 539+
 540+	input_lines = 0;
 541+	last_frozen_line = 0;
 542+	filec = 0;
 543+	if (!out_of_mem) {
 544+		free(filearg[0]);
 545+		filearg[0] = NULL;
 546+	}
 547+	free(outname);
 548+	outname = NULL;
 549+	last_offset = 0;
 550+	diff_type = 0;
 551+	free(revision);
 552+	revision = NULL;
 553+	reverse = reverse_flag_specified;
 554+	skip_rest_of_patch = 0;
 555+	get_some_switches();
 556+}
 557+
 558+static void
 559+get_some_switches(void)
 560+{
 561+	const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
 562+	static struct option longopts[] = {
 563+		{"backup",              no_argument,            0,      'b'},
 564+		{"backup-if-mismatch",  no_argument,            &backup_if_mismatch, 1},
 565+		{"batch",               no_argument,            0,      't'},
 566+		{"check",               no_argument,            0,      'C'},
 567+		{"context",             no_argument,            0,      'c'},
 568+		{"debug",               required_argument,      0,      'x'},
 569+		{"directory",           required_argument,      0,      'd'},
 570+		{"ed",                  no_argument,            0,      'e'},
 571+		{"force",               no_argument,            0,      'f'},
 572+		{"forward",             no_argument,            0,      'N'},
 573+		{"fuzz",                required_argument,      0,      'F'},
 574+		{"ifdef",               required_argument,      0,      'D'},
 575+		{"input",               required_argument,      0,      'i'},
 576+		{"ignore-whitespace",   no_argument,            0,      'l'},
 577+		{"no-backup-if-mismatch",       no_argument,    &backup_if_mismatch, 0},
 578+		{"normal",              no_argument,            0,      'n'},
 579+		{"output",              required_argument,      0,      'o'},
 580+		{"prefix",              required_argument,      0,      'B'},
 581+		{"quiet",               no_argument,            0,      's'},
 582+		{"reject-file",         required_argument,      0,      'r'},
 583+		{"remove-empty-files",  no_argument,            0,      'E'},
 584+		{"reverse",             no_argument,            0,      'R'},
 585+		{"silent",              no_argument,            0,      's'},
 586+		{"strip",               required_argument,      0,      'p'},
 587+		{"suffix",              required_argument,      0,      'z'},
 588+		{"unified",             no_argument,            0,      'u'},
 589+		{"version",             no_argument,            0,      'v'},
 590+		{"version-control",     required_argument,      0,      'V'},
 591+		{"posix",               no_argument,            &posix, 1},
 592+		{NULL,                  0,                      0,      0}
 593+	};
 594+	int ch;
 595+
 596+	rejname[0] = '\0';
 597+	Argc_last = Argc;
 598+	Argv_last = Argv;
 599+	if (!Argc)
 600+		return;
 601+	reset_getopt_state();
 602+	while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
 603+		switch (ch) {
 604+		case 'b':
 605+			if (backup_type == BACKUP_UNDEFINED)
 606+				backup_type = BACKUP_NUMBERED_EXISTING;
 607+			if (optarg == NULL)
 608+				break;
 609+			if (verbose)
 610+				say("Warning, the -b suffix option has been obsoleted by the -z option\n");
 611+			/* FALLTHROUGH */
 612+		case 'z':
 613+			simple_backup_suffix = savestr(optarg);
 614+			break;
 615+		case 'B':
 616+			origprae = savestr(optarg);
 617+			break;
 618+		case 'c':
 619+			diff_type = CONTEXT_DIFF;
 620+			break;
 621+		case 'C':
 622+			check_only = 1;
 623+			break;
 624+		case 'd':
 625+			if (chdir(optarg) < 0)
 626+				pfatal("cant cd to %s", optarg);
 627+			break;
 628+		case 'D':
 629+			do_defines = 1;
 630+			if (!isalpha((unsigned char)*optarg) && *optarg != '_')
 631+				fatal("argument to -D is not an identifier\n");
 632+			snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", optarg);
 633+			snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", optarg);
 634+			snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", optarg);
 635+			break;
 636+		case 'e':
 637+			diff_type = ED_DIFF;
 638+			break;
 639+		case 'E':
 640+			remove_empty_files = 1;
 641+			break;
 642+		case 'f':
 643+			force = 1;
 644+			break;
 645+		case 'F':
 646+			maxfuzz = strtolinenum(optarg, NULL);
 647+			break;
 648+		case 'i':
 649+			free(filearg[1]);
 650+			filearg[1] = fetchname(optarg, &warn_on_invalid_line, 0);
 651+			break;
 652+		case 'l':
 653+			canonicalize = 1;
 654+			break;
 655+		case 'n':
 656+			diff_type = NORMAL_DIFF;
 657+			break;
 658+		case 'N':
 659+			noreverse = 1;
 660+			break;
 661+		case 'o':
 662+			outname = savestr(optarg);
 663+			break;
 664+		case 'p':
 665+			strippath = (int)strtolinenum(optarg, NULL);
 666+			break;
 667+		case 'r':
 668+			if (strlcpy(rejname, optarg, sizeof(rejname)) >= sizeof(rejname))
 669+				fatal("reject filename %s too long\n", optarg);
 670+			break;
 671+		case 'R':
 672+			reverse = 1;
 673+			reverse_flag_specified = 1;
 674+			break;
 675+		case 's':
 676+			verbose = 0;
 677+			break;
 678+		case 't':
 679+			batch = 1;
 680+			break;
 681+		case 'u':
 682+			diff_type = UNI_DIFF;
 683+			break;
 684+		case 'v':
 685+			version();
 686+			break;
 687+		case 'V':
 688+			backup_type = get_version(optarg);
 689+			break;
 690+		case 'x':
 691+#ifdef DEBUGGING
 692+			debug = (int)strtolinenum(optarg, NULL);
 693+#endif
 694+			break;
 695+		default:
 696+			if (ch == 0)
 697+				break;
 698+			usage();
 699+		}
 700+	}
 701+	Argc -= optind;
 702+	Argv += optind;
 703+
 704+	for (ch = 0; ch < Argc; ch++) {
 705+		if (filec >= MAXFILEC)
 706+			usage();
 707+		filearg[filec++] = savestr(Argv[ch]);
 708+	}
 709+}
 710+
 711+static void
 712+usage(void)
 713+{
 714+	fprintf(stderr, "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] "
 715+	    "[-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] "
 716+	    "[-p strip-count] [-r rej-name] [-V method] [-x number] "
 717+	    "[-z backup-ext] [origfile [patchfile]]\n");
 718+	my_exit(2);
 719+}
 720+
 721+static LINENUM
 722+locate_hunk(LINENUM fuzz)
 723+{
 724+	LINENUM first_hunk_line = pch_first();
 725+	LINENUM count = pch_ptrn_lines();
 726+	LINENUM max_line = input_lines - count + 1;
 727+	LINENUM line;
 728+	LINENUM offset;
 729+
 730+	if (!count)
 731+		return first_hunk_line + last_offset;
 732+
 733+	if (first_hunk_line + last_offset <= 0)
 734+		offset = 1 - first_hunk_line;
 735+	else if (first_hunk_line + last_offset > max_line)
 736+		offset = max_line - first_hunk_line;
 737+	else
 738+		offset = last_offset;
 739+
 740+	for (line = first_hunk_line + offset; line <= max_line; line++) {
 741+		if (patch_match(line, count, fuzz)) {
 742+			last_offset = line - first_hunk_line;
 743+			return line;
 744+		}
 745+	}
 746+	for (line = first_hunk_line + offset - 1; line >= 1; line--) {
 747+		if (patch_match(line, count, fuzz)) {
 748+			last_offset = line - first_hunk_line;
 749+			return line;
 750+		}
 751+	}
 752+	return 0;
 753+}
 754+
 755+static int
 756+patch_match(LINENUM line, LINENUM count, LINENUM fuzz)
 757+{
 758+	LINENUM i;
 759+	LINENUM pat_line;
 760+	char *str;
 761+	char *pat;
 762+	ssize_t len;
 763+
 764+	for (i = 1; i <= count; i++) {
 765+		pat_line = i;
 766+		if (i <= fuzz)
 767+			continue;
 768+		if (i > count - fuzz)
 769+			continue;
 770+		str = ifetch(line + i - 1 - fuzz, 0);
 771+		if (str == NULL)
 772+			return 0;
 773+		pat = pfetch(pat_line);
 774+		len = pch_line_len(pat_line);
 775+		if (canonicalize) {
 776+			if (!similar(str, pat, len))
 777+				return 0;
 778+		} else if (strncmp(str, pat, len) != 0)
 779+			return 0;
 780+	}
 781+	return 1;
 782+}
 783+
 784+static int
 785+similar(const char *a, const char *b, ssize_t len)
 786+{
 787+	while (len > 0) {
 788+		if (isspace((unsigned char)*b)) {
 789+			if (!isspace((unsigned char)*a))
 790+				return 0;
 791+			while (len > 0 && isspace((unsigned char)*b)) {
 792+				b++;
 793+				len--;
 794+			}
 795+			while (isspace((unsigned char)*a) && *a != '\n')
 796+				a++;
 797+		} else {
 798+			if (*a != *b)
 799+				return 0;
 800+			a++;
 801+			b++;
 802+			len--;
 803+		}
 804+	}
 805+	if (*a == '\n' || *a == '\r') {
 806+		while (*a == '\n' || *a == '\r')
 807+			a++;
 808+		return *a == '\0';
 809+	}
 810+	return *a == '\0' || isspace((unsigned char)*a);
 811+}
 812+
 813+static void
 814+abort_hunk(void)
 815+{
 816+	LINENUM i;
 817+	LINENUM pat_line;
 818+	char *str;
 819+	ssize_t len;
 820+
 821+	for (i = 1; i <= pch_end(); i++) {
 822+		pat_line = i;
 823+		str = pfetch(pat_line);
 824+		len = pch_line_len(pat_line);
 825+		rej_line(pat_line, i);
 826+	}
 827+}
 828+
 829+static void
 830+rej_line(int pat_line, LINENUM i)
 831+{
 832+	char *str = pfetch(pat_line);
 833+	char ch = pch_char(pat_line);
 834+	if (rejfp != NULL) {
 835+		if (ch == ' ')
 836+			fprintf(rejfp, " %s", str);
 837+		else
 838+			fprintf(rejfp, "%c%s", ch, str);
 839+	}
 840+}
 841+
 842+static void
 843+abort_context_hunk(void)
 844+{
 845+	LINENUM i;
 846+	for (i = 0; i <= pch_end(); i++) {
 847+		rej_line(i, i);
 848+	}
 849+}
 850+
 851+static void
 852+apply_hunk(LINENUM where)
 853+{
 854+	LINENUM old = 1;
 855+	const LINENUM lastline = pch_ptrn_lines();
 856+	LINENUM new = lastline + 1;
 857+#define OUTSIDE 0
 858+#define IN_IFNDEF 1
 859+#define IN_IFDEF 2
 860+#define IN_ELSE 3
 861+	int def_state = OUTSIDE;
 862+	const LINENUM pat_end = pch_end();
 863+
 864+	where--;
 865+	while (pch_char(new) == '=' || pch_char(new) == '\n')
 866+		new++;
 867+
 868+	while (old <= lastline) {
 869+		if (pch_char(old) == '-') {
 870+			copy_till(where + old - 1, 0);
 871+			if (do_defines) {
 872+				if (def_state == OUTSIDE) {
 873+					fputs(not_defined, ofp);
 874+					def_state = IN_IFNDEF;
 875+				} else if (def_state == IN_IFDEF) {
 876+					fputs(else_defined, ofp);
 877+					def_state = IN_ELSE;
 878+				}
 879+				fputs(pfetch(old), ofp);
 880+			}
 881+			last_frozen_line++;
 882+			old++;
 883+		} else if (new > pat_end) {
 884+			break;
 885+		} else if (pch_char(new) == '+') {
 886+			copy_till(where + old - 1, 0);
 887+			if (do_defines) {
 888+				if (def_state == IN_IFNDEF) {
 889+					fputs(else_defined, ofp);
 890+					def_state = IN_ELSE;
 891+				} else if (def_state == OUTSIDE) {
 892+					fputs(if_defined, ofp);
 893+					def_state = IN_IFDEF;
 894+				}
 895+			}
 896+			fputs(pfetch(new), ofp);
 897+			new++;
 898+		} else if (pch_char(new) != pch_char(old)) {
 899+			say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
 900+			    pch_hunk_beg() + old,
 901+			    pch_hunk_beg() + new);
 902+			my_exit(2);
 903+		} else if (pch_char(new) == '!') {
 904+			copy_till(where + old - 1, 0);
 905+			if (do_defines) {
 906+				fputs(not_defined, ofp);
 907+				def_state = IN_IFNDEF;
 908+			}
 909+			while (pch_char(old) == '!') {
 910+				if (do_defines) {
 911+					fputs(pfetch(old), ofp);
 912+				}
 913+				last_frozen_line++;
 914+				old++;
 915+			}
 916+			if (do_defines) {
 917+				fputs(else_defined, ofp);
 918+				def_state = IN_ELSE;
 919+			}
 920+			while (pch_char(new) == '!') {
 921+				fputs(pfetch(new), ofp);
 922+				new++;
 923+			}
 924+		} else {
 925+			if (pch_char(new) != ' ')
 926+				fatal("Internal error: expected ' '\n");
 927+			old++;
 928+			new++;
 929+			if (do_defines && def_state != OUTSIDE) {
 930+				fputs(end_defined, ofp);
 931+				def_state = OUTSIDE;
 932+			}
 933+		}
 934+	}
 935+	if (new <= pat_end && pch_char(new) == '+') {
 936+		copy_till(where + old - 1, 0);
 937+		if (do_defines) {
 938+			if (def_state == OUTSIDE) {
 939+				fputs(if_defined, ofp);
 940+				def_state = IN_IFDEF;
 941+			} else if (def_state == IN_IFNDEF) {
 942+				fputs(else_defined, ofp);
 943+				def_state = IN_ELSE;
 944+			}
 945+		}
 946+		while (new <= pat_end && pch_char(new) == '+') {
 947+			fputs(pfetch(new), ofp);
 948+			new++;
 949+		}
 950+	}
 951+	if (do_defines && def_state != OUTSIDE) {
 952+		fputs(end_defined, ofp);
 953+	}
 954+#undef OUTSIDE
 955+#undef IN_IFNDEF
 956+#undef IN_IFDEF
 957+#undef IN_ELSE
 958+}
 959+
 960+static void
 961+dump_input_line(LINENUM line, int write_newline)
 962+{
 963+	char *s = ifetch(line, 0);
 964+	if (s == NULL)
 965+		return;
 966+	for (; *s != '\n' && *s != '\0'; s++)
 967+		putc(*s, ofp);
 968+	if (write_newline)
 969+		putc('\n', ofp);
 970+}
 971+
 972+static void
 973+copy_till(LINENUM line, int end)
 974+{
 975+	if (last_frozen_line > line)
 976+		fatal("misordered hunks! output would be garbled\n");
 977+	while (last_frozen_line < line) {
 978+		if (++last_frozen_line == line && end)
 979+			dump_input_line(last_frozen_line, !last_line_missing_eol);
 980+		else
 981+			dump_input_line(last_frozen_line, 1);
 982+	}
 983+}
 984+
 985+static int
 986+spew_output(void)
 987+{
 988+	if (input_lines)
 989+		copy_till(input_lines, 1);
 990+	if (ferror(ofp) || fclose(ofp))
 991+		return 0;
 992+	ofp = NULL;
 993+	return 1;
 994+}
 995+
 996+static void
 997+init_output(const char *name)
 998+{
 999+	ofp = fopen(name, "w");
1000+	if (ofp == NULL)
1001+		pfatal("cant create %s", name);
1002+}
1003+
1004+static void
1005+init_reject(const char *name)
1006+{
1007+	rejfp = fopen(name, "w");
1008+	if (rejfp == NULL)
1009+		pfatal("cant create %s", name);
1010+}
1011+
1012+void
1013+re_input(void)
1014+{
1015+	if (using_plan_a) {
1016+		i_size = 0;
1017+		free(i_ptr);
1018+		i_ptr = NULL;
1019+		if (i_womp != NULL) {
1020+			munmap(i_womp, i_size);
1021+			i_womp = NULL;
1022+		}
1023+	} else {
1024+		using_plan_a = 1;
1025+		close(tifd);
1026+		tifd = -1;
1027+		free(tibuf[0]);
1028+		free(tibuf[1]);
1029+		tibuf[0] = tibuf[1] = NULL;
1030+		tiline[0] = tiline[1] = -1;
1031+		tireclen = 0;
1032+	}
1033+}
1034+
1035+void
1036+scan_input(const char *filename)
1037+{
1038+	if (!plan_a(filename))
1039+		plan_b(filename);
1040+	if (verbose) {
1041+		say("Patching file %s using Plan %s...\n", filename, (using_plan_a ? "A" : "B"));
1042+	}
1043+}
1044+
1045+static int
1046+reallocate_lines(size_t *lines_allocated)
1047+{
1048+	char **p;
1049+	size_t new_size;
1050+
1051+	new_size = *lines_allocated * 3 / 2;
1052+	p = pch_realloc(i_ptr, new_size + 2, sizeof(char *));
1053+	if (p == NULL) {
1054+		munmap(i_womp, i_size);
1055+		i_womp = NULL;
1056+		free(i_ptr);
1057+		i_ptr = NULL;
1058+		*lines_allocated = 0;
1059+		return 0;
1060+	}
1061+	*lines_allocated = new_size;
1062+	i_ptr = p;
1063+	return 1;
1064+}
1065+
1066+static int
1067+plan_a(const char *filename)
1068+{
1069+	int ifd, statfailed, devnull, pstat;
1070+	char *p, *s, *lbuf;
1071+	struct stat filestat;
1072+	off_t i;
1073+	ptrdiff_t sz;
1074+	size_t iline, lines_allocated, lbufsz;
1075+	pid_t pid;
1076+	char *argp[4] = {NULL};
1077+
1078+#ifdef DEBUGGING
1079+	if (debug & 8)
1080+		return 0;
1081+#endif
1082+
1083+	if (filename == NULL || *filename == '\0')
1084+		return 0;
1085+
1086+	statfailed = stat(filename, &filestat);
1087+	if (statfailed && ok_to_create_file) {
1088+		if (verbose)
1089+			say("(Creating file %s...)\n", filename);
1090+		if (check_only)
1091+			return 1;
1092+		makedirs(filename, 1);
1093+		close(creat(filename, 0666));
1094+		statfailed = stat(filename, &filestat);
1095+	}
1096+	if (statfailed && check_only)
1097+		fatal("%s not found, -C mode, cant probe further\n", filename);
1098+
1099+	if (statfailed || (filestat.st_mode & 0222) == 0 ||
1100+	    ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
1101+		char *filebase, *filedir;
1102+		struct stat cstat;
1103+		char *tmp_filename1, *tmp_filename2;
1104+
1105+		tmp_filename1 = strdup(filename);
1106+		tmp_filename2 = strdup(filename);
1107+		if (tmp_filename1 == NULL || tmp_filename2 == NULL)
1108+			fatal("strdupping filename");
1109+		filebase = basename(tmp_filename1);
1110+		filedir = dirname(tmp_filename2);
1111+
1112+		lbufsz = INITLINELEN;
1113+		if ((lbuf = malloc(bufsz)) == NULL)
1114+			pfatal("allocating line buffer");
1115+		lbuf[0] = '\0';
1116+
1117+#define try(f, a1, a2, a3) \
1118+	(snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
1119+
1120+		if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
1121+		    try("%s/RCS/%s%s", filedir, filebase, "") ||
1122+		    try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
1123+			if (!statfailed) {
1124+				if ((filestat.st_mode & 0222) != 0)
1125+					fatal("file %s seems to be locked by somebody else under RCS\n", filename);
1126+				if (verbose)
1127+					say("Comparing file %s to default RCS version...\n", filename);
1128+				switch (pid = fork()) {
1129+				case -1:
1130+					fatal("cant fork: %s\n", strerror(errno));
1131+				case 0:
1132+					devnull = open("/dev/null", O_RDONLY);
1133+					if (devnull == -1)
1134+						fatal("cant open /dev/null: %s", strerror(errno));
1135+					dup2(devnull, STDOUT_FILENO);
1136+					argp[0] = __UNCONST(RCSDIFF);
1137+					argp[1] = __UNCONST(filename);
1138+					execv(RCSDIFF, argp);
1139+					exit(127);
1140+				}
1141+				pid = waitpid(pid, &pstat, 0);
1142+				if (pid == -1 || WEXITSTATUS(pstat) != 0)
1143+					fatal("cant check out file %s: differs from default RCS version\n", filename);
1144+			}
1145+			if (verbose)
1146+				say("Checking out file %s from RCS...\n", filename);
1147+			switch (pid = fork()) {
1148+			case -1:
1149+				fatal("cant fork: %s\n", strerror(errno));
1150+			case 0:
1151+				argp[0] = __UNCONST(CHECKOUT);
1152+				argp[1] = __UNCONST("-l");
1153+				argp[2] = __UNCONST(filename);
1154+				execv(CHECKOUT, argp);
1155+				exit(127);
1156+			}
1157+			pid = waitpid(pid, &pstat, 0);
1158+			if (pid == -1 || WEXITSTATUS(pstat) != 0 || stat(filename, &filestat))
1159+				fatal("cant check out file %s from RCS\n", filename);
1160+		} else if (statfailed) {
1161+			fatal("cant find %s\n", filename);
1162+		}
1163+#undef try
1164+		free(lbuf);
1165+		free(tmp_filename1);
1166+		free(tmp_filename2);
1167+	}
1168+
1169+	filemode = filestat.st_mode;
1170+	if (!S_ISREG(filemode))
1171+		fatal("%s is not a normal file--cant patch\n", filename);
1172+	i_size = filestat.st_size;
1173+	if (out_of_mem) {
1174+		set_hunkmax();
1175+		out_of_mem = 0;
1176+		return 0;
1177+	}
1178+	if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
1179+		say("block too large to mmap\n");
1180+		return 0;
1181+	}
1182+	if ((ifd = open(filename, O_RDONLY)) < 0)
1183+		pfatal("cant open file %s", filename);
1184+
1185+	if (i_size) {
1186+		i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
1187+		if (i_womp == MAP_FAILED) {
1188+			perror("mmap failed");
1189+			i_womp = NULL;
1190+			close(ifd);
1191+			return 0;
1192+		}
1193+	} else
1194+		i_womp = NULL;
1195+
1196+	close(ifd);
1197+	if (i_size)
1198+		madvise(i_womp, i_size, MADV_SEQUENTIAL);
1199+
1200+	lines_allocated = i_size / 25;
1201+	if (lines_allocated < 100)
1202+		lines_allocated = 100;
1203+
1204+	if (!reallocate_lines(&lines_allocated))
1205+		return 0;
1206+
1207+	iline = 1;
1208+	i_ptr[iline] = i_womp;
1209+	for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
1210+		if (*s == '\n') {
1211+			if (iline == lines_allocated) {
1212+				if (!reallocate_lines(&lines_allocated))
1213+					return 0;
1214+			}
1215+			i_ptr[++iline] = s + 1;
1216+		}
1217+	}
1218+	if (i_size > 0 && i_womp[i_size - 1] != '\n') {
1219+		last_line_missing_eol = 1;
1220+		sz = s - i_ptr[iline];
1221+		p = malloc(sz + 1);
1222+		if (p == NULL) {
1223+			free(i_ptr);
1224+			i_ptr = NULL;
1225+			munmap(i_womp, i_size);
1226+			i_womp = NULL;
1227+			return 0;
1228+		}
1229+		memcpy(p, i_ptr[iline], sz);
1230+		p[sz] = '\n';
1231+		i_ptr[iline] = p;
1232+		i_ptr[++iline] = empty_line;
1233+	} else
1234+		last_line_missing_eol = 0;
1235+
1236+	input_lines = iline - 1;
1237+
1238+	if (revision != NULL) {
1239+		if (!rev_in_string(i_womp)) {
1240+			if (force) {
1241+				if (verbose)
1242+					say("Warning: this file doesnt appear to be the %s version--patching anyway\n", revision);
1243+			} else if (batch) {
1244+				fatal("this file doesnt appear to be the %s version--aborting\n", revision);
1245+			} else {
1246+				ask("This file doesnt appear to be the %s version--patch anyway? [n] ", revision);
1247+				if (*buf != 'y')
1248+					fatal("aborted\n");
1249+			}
1250+		} else if (verbose)
1251+			say("Good. This file appears to be the %s version.\n", revision);
1252+	}
1253+	return 1;
1254+}
1255+
1256+static void
1257+plan_b(const char *filename)
1258+{
1259+	FILE *ifp;
1260+	size_t i = 0, j, maxlen = 1;
1261+	char *p;
1262+	int found_revision = (revision == NULL);
1263+
1264+	using_plan_a = 0;
1265+	if ((ifp = fopen(filename, "r")) == NULL)
1266+		pfatal("cant open file %s", filename);
1267+	unlink(TMPINNAME);
1268+	if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
1269+		pfatal("cant open file %s", TMPINNAME);
1270+	while (getline(&buf, &bufsz, ifp) != -1) {
1271+		if (revision != NULL && !found_revision && rev_in_string(buf))
1272+			found_revision = 1;
1273+		if ((i = strlen(buf)) > maxlen)
1274+			maxlen = i;
1275+	}
1276+	last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
1277+	if (last_line_missing_eol && maxlen == i)
1278+		maxlen++;
1279+
1280+	if (revision != NULL) {
1281+		if (!found_revision) {
1282+			if (force) {
1283+				if (verbose)
1284+					say("Warning: this file doesnt appear to be the %s version--patching anyway\n", revision);
1285+			} else if (batch) {
1286+				fatal("this file doesnt appear to be the %s version--aborting\n", revision);
1287+			} else {
1288+				ask("This file doesnt appear to be the %s version--patch anyway? [n] ", revision);
1289+				if (*buf != 'y')
1290+					fatal("aborted\n");
1291+			}
1292+		} else if (verbose)
1293+			say("Good. This file appears to be the %s version.\n", revision);
1294+	}
1295+	fseek(ifp, 0L, SEEK_SET);
1296+	lines_per_buf = BUFFERSIZE / maxlen;
1297+	tireclen = maxlen;
1298+	tibuf[0] = malloc(BUFFERSIZE + 1);
1299+	if (tibuf[0] == NULL)
1300+		fatal("out of memory\n");
1301+	tibuf[1] = malloc(BUFFERSIZE + 1);
1302+	if (tibuf[1] == NULL)
1303+		fatal("out of memory\n");
1304+	for (i = 1;; i++) {
1305+		p = tibuf[0] + maxlen * (i % lines_per_buf);
1306+		if (i % lines_per_buf == 0) {
1307+			if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1308+				pfatal("cant write temp file");
1309+		}
1310+		if (fgets(p, maxlen + 1, ifp) == NULL) {
1311+			input_lines = i - 1;
1312+			if (i % lines_per_buf != 0) {
1313+				if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1314+					pfatal("cant write temp file");
1315+			}
1316+			break;
1317+		}
1318+		j = strlen(p);
1319+		if (j == 0 || p[j - 1] != '\n')
1320+			p[j] = '\n';
1321+	}
1322+	fclose(ifp);
1323+	close(tifd);
1324+	if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
1325+		pfatal("cant reopen file %s", TMPINNAME);
1326+}
1327+
1328+char *
1329+ifetch(LINENUM line, int whichbuf)
1330+{
1331+	if (line < 1 || line > input_lines) {
1332+		if (warn_on_invalid_line) {
1333+			say("No such line %ld in input file, ignoring\n", line);
1334+			warn_on_invalid_line = 0;
1335+		}
1336+		return NULL;
1337+	}
1338+	if (using_plan_a)
1339+		return i_ptr[line];
1340+	else {
1341+		LINENUM offline = line % lines_per_buf;
1342+		LINENUM baseline = line - offline;
1343+
1344+		if (tiline[0] == baseline)
1345+			whichbuf = 0;
1346+		else if (tiline[1] == baseline)
1347+			whichbuf = 1;
1348+		else {
1349+			tiline[whichbuf] = baseline;
1350+			if (lseek(tifd, (off_t)(baseline / lines_per_buf * BUFFERSIZE), SEEK_SET) < 0)
1351+				pfatal("cannot seek in the temporary input file");
1352+			if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
1353+				pfatal("error reading tmp file %s", TMPINNAME);
1354+		}
1355+		return tibuf[whichbuf] + (tireclen * offline);
1356+	}
1357+}
1358+
1359+static int
1360+rev_in_string(const char *string)
1361+{
1362+	const char *s;
1363+	size_t patlen;
1364+
1365+	if (revision == NULL)
1366+		return 1;
1367+	patlen = strlen(revision);
1368+	if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
1369+		return 1;
1370+	for (s = string; *s; s++) {
1371+		if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
1372+		    isspace((unsigned char)s[patlen + 1]))
1373+			return 1;
1374+	}
1375+	return 0;
1376+}
1377+
1378+void
1379+re_patch(void)
1380+{
1381+	p_first = 0;
1382+	p_newfirst = 0;
1383+	p_ptrn_lines = 0;
1384+	p_repl_lines = 0;
1385+	p_end = (LINENUM)-1;
1386+	p_max = 0;
1387+	p_indent = 0;
1388+}
1389+
1390+void
1391+open_patch_file(const char *filename)
1392+{
1393+	struct stat filestat;
1394+
1395+	if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
1396+		pfp = fopen(TMPPATNAME, "w");
1397+		if (pfp == NULL)
1398+			pfatal("cant create %s", TMPPATNAME);
1399+		while (getline(&buf, &bufsz, stdin) != -1)
1400+			fprintf(pfp, "%s", buf);
1401+		if (ferror(pfp) || fclose(pfp))
1402+			pfatal("cant write %s", TMPPATNAME);
1403+		filename = TMPPATNAME;
1404+	}
1405+	pfp = fopen(filename, "r");
1406+	if (pfp == NULL)
1407+		pfatal("patch file %s not found", filename);
1408+	fstat(fileno(pfp), &filestat);
1409+	p_filesize = filestat.st_size;
1410+	next_intuit_at(0L, 1L);
1411+	set_hunkmax();
1412+}
1413+
1414+void
1415+set_hunkmax(void)
1416+{
1417+	if (p_line == NULL)
1418+		p_line = calloc((size_t)hunkmax, sizeof(char *));
1419+	if (p_len == NULL)
1420+		p_len = calloc((size_t)hunkmax, sizeof(ssize_t));
1421+	if (p_char == NULL)
1422+		p_char = calloc((size_t)hunkmax, sizeof(char));
1423+}
1424+
1425+static void
1426+grow_hunkmax(void)
1427+{
1428+	int new_hunkmax;
1429+	char **new_p_line;
1430+	ssize_t *new_p_len;
1431+	char *new_p_char;
1432+
1433+	new_hunkmax = hunkmax * 2;
1434+	if (p_line == NULL || p_len == NULL || p_char == NULL)
1435+		fatal("Internal memory allocation error\n");
1436+
1437+	new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
1438+	if (new_p_line == NULL)
1439+		free(p_line);
1440+	new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
1441+	if (new_p_len == NULL)
1442+		free(p_len);
1443+	new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
1444+	if (new_p_char == NULL)
1445+		free(p_char);
1446+
1447+	p_char = new_p_char;
1448+	p_len = new_p_len;
1449+	p_line = new_p_line;
1450+
1451+	if (p_line != NULL && p_len != NULL && p_char != NULL) {
1452+		hunkmax = new_hunkmax;
1453+		return;
1454+	}
1455+
1456+	if (!using_plan_a)
1457+		fatal("out of memory\n");
1458+	out_of_mem = 1;
1459+}
1460+
1461+int
1462+there_is_another_patch(void)
1463+{
1464+	int exists = 0;
1465+
1466+	if (p_base != 0L && p_base >= p_filesize) {
1467+		if (verbose)
1468+			say("done\n");
1469+		return 0;
1470+	}
1471+	if (verbose)
1472+		say("Hmm...");
1473+	diff_type = intuit_diff_type();
1474+	if (!diff_type) {
1475+		if (p_base != 0L) {
1476+			if (verbose)
1477+				say(" Ignoring the trailing garbage.\ndone\n");
1478+		} else
1479+			say(" I cant seem to find a patch in there anywhere\n");
1480+		return 0;
1481+	}
1482+	if (verbose) {
1483+		say(" %sooks like %s to me...\n",
1484+		    (p_base == 0L ? "L" : "The next patch l"),
1485+		    diff_type == UNI_DIFF ? "a unified diff" :
1486+		    diff_type == CONTEXT_DIFF ? "a context diff" :
1487+		    diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
1488+		    diff_type == NORMAL_DIFF ? "a normal diff" : "an ed script");
1489+	}
1490+	if (p_indent && verbose)
1491+		say("(Patch is indented %d space%s.)\n", p_indent, p_indent == 1 ? "" : "s");
1492+	skip_to(p_start, p_sline);
1493+	while (filearg[0] == NULL) {
1494+		if (force || batch) {
1495+			say("No file to patch. Skipping...\n");
1496+			filearg[0] = savestr(bestguess);
1497+			skip_rest_of_patch = 1;
1498+			return 1;
1499+		}
1500+		ask("File to patch: ");
1501+		if (*buf != '\n') {
1502+			free(bestguess);
1503+			bestguess = savestr(buf);
1504+			filearg[0] = fetchname(buf, &exists, 0);
1505+		}
1506+		if (!exists) {
1507+			ask("No file found--skip this patch? [n] ");
1508+			if (*buf != 'y')
1509+				continue;
1510+			if (verbose)
1511+				say("Skipping patch...\n");
1512+			free(filearg[0]);
1513+			filearg[0] = fetchname(bestguess, &exists, 0);
1514+			skip_rest_of_patch = 1;
1515+			return 1;
1516+		}
1517+	}
1518+	return 1;
1519+}
1520+
1521+static int
1522+intuit_diff_type(void)
1523+{
1524+	long this_line = 0, previous_line;
1525+	long first_command_line = -1;
1526+	LINENUM fcl_line = -1;
1527+	int last_line_was_command = 0, this_is_a_command = 0;
1528+	int stars_last_line = 0, stars_this_line = 0;
1529+	char *s, *t;
1530+	int indent, retval;
1531+	struct FileName names[MAX_FILE];
1532+
1533+	memset(names, 0, sizeof(names));
1534+	ok_to_create_file = 0;
1535+	fseek(pfp, p_base, SEEK_SET);
1536+	p_input_line = p_bline - 1;
1537+	for (;;) {
1538+		previous_line = this_line;
1539+		last_line_was_command = this_is_a_command;
1540+		stars_last_line = stars_this_line;
1541+		this_line = ftell(pfp);
1542+		indent = 0;
1543+		p_input_line++;
1544+		if (getline(&buf, &bufsz, pfp) == -1) {
1545+			if (first_command_line >= 0L) {
1546+				p_start = first_command_line;
1547+				p_sline = fcl_line;
1548+				retval = ED_DIFF;
1549+				goto scan_exit;
1550+			} else {
1551+				p_start = this_line;
1552+				p_sline = p_input_line;
1553+				retval = 0;
1554+				goto scan_exit;
1555+			}
1556+		}
1557+		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
1558+			if (*s == '\t')
1559+				indent += 8 - (indent % 8);
1560+			else
1561+				indent++;
1562+		}
1563+		for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
1564+			;
1565+		this_is_a_command = (isdigit((unsigned char)*s) && (*t == 'd' || *t == 'c' || *t == 'a'));
1566+		if (first_command_line < 0L && this_is_a_command) {
1567+			first_command_line = this_line;
1568+			fcl_line = p_input_line;
1569+			p_indent = indent;
1570+		}
1571+		if (!stars_last_line && strnEQ(s, "*** ", 4)) {
1572+			names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1573+		} else if (strnEQ(s, "--- ", 4)) {
1574+			names[NEW_FILE].path = fetchname(s + 4, &names[NEW_FILE].exists, strippath);
1575+		} else if (strnEQ(s, "+++ ", 4)) {
1576+			names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1577+		} else if (strnEQ(s, "Index:", 6)) {
1578+			names[INDEX_FILE].path = fetchname(s + 6, &names[INDEX_FILE].exists, strippath);
1579+		} else if (strnEQ(s, "Prereq:", 7)) {
1580+			for (t = s + 7; isspace((unsigned char)*t); t++)
1581+				;
1582+			revision = savestr(t);
1583+			for (t = revision; *t && !isspace((unsigned char)*t); t++)
1584+				;
1585+			*t = '\0';
1586+			if (*revision == '\0') {
1587+				free(revision);
1588+				revision = NULL;
1589+			}
1590+		}
1591+		if ((!diff_type || diff_type == ED_DIFF) && first_command_line >= 0L && strEQ(s, ".\n")) {
1592+			p_indent = indent;
1593+			p_start = first_command_line;
1594+			p_sline = fcl_line;
1595+			retval = ED_DIFF;
1596+			goto scan_exit;
1597+		}
1598+		if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
1599+			if (strnEQ(s + 4, "0,0", 3))
1600+				ok_to_create_file = 1;
1601+			p_indent = indent;
1602+			p_start = this_line;
1603+			p_sline = p_input_line;
1604+			retval = UNI_DIFF;
1605+			goto scan_exit;
1606+		}
1607+		stars_this_line = strnEQ(s, "********", 8);
1608+		if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && strnEQ(s, "*** ", 4)) {
1609+			if (atol(s + 4) == 0)
1610+				ok_to_create_file = 1;
1611+			while (*s != '\n')
1612+				s++;
1613+			p_indent = indent;
1614+			p_start = previous_line;
1615+			p_sline = p_input_line - 1;
1616+			retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
1617+			goto scan_exit;
1618+		}
1619+		if ((!diff_type || diff_type == NORMAL_DIFF) && last_line_was_command &&
1620+		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
1621+			p_start = previous_line;
1622+			p_sline = p_input_line - 1;
1623+			p_indent = indent;
1624+			retval = NORMAL_DIFF;
1625+			goto scan_exit;
1626+		}
1627+	}
1628+scan_exit:
1629+	if (retval == UNI_DIFF) {
1630+		struct FileName tmp = names[OLD_FILE];
1631+		names[OLD_FILE] = names[NEW_FILE];
1632+		names[NEW_FILE] = tmp;
1633+	}
1634+	if (filearg[0] == NULL) {
1635+		if (posix)
1636+			filearg[0] = posix_name(names, ok_to_create_file);
1637+		else {
1638+			if (names[OLD_FILE].path != NULL || names[NEW_FILE].path != NULL) {
1639+				free(names[INDEX_FILE].path);
1640+				names[INDEX_FILE].path = NULL;
1641+			}
1642+			filearg[0] = best_name(names, ok_to_create_file);
1643+		}
1644+	}
1645+	free(bestguess);
1646+	bestguess = NULL;
1647+	if (filearg[0] != NULL)
1648+		bestguess = savestr(filearg[0]);
1649+	else if (!ok_to_create_file) {
1650+		if (posix)
1651+			bestguess = posix_name(names, 1);
1652+		else
1653+			bestguess = best_name(names, 1);
1654+	}
1655+	free(names[OLD_FILE].path);
1656+	free(names[NEW_FILE].path);
1657+	free(names[INDEX_FILE].path);
1658+	return retval;
1659+}
1660+
1661+static void
1662+next_intuit_at(LINENUM file_pos, LINENUM file_line)
1663+{
1664+	p_base = file_pos;
1665+	p_bline = file_line;
1666+}
1667+
1668+static void
1669+skip_to(LINENUM file_pos, LINENUM file_line)
1670+{
1671+	int ret;
1672+
1673+	if (p_base > file_pos)
1674+		fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
1675+	if (verbose && p_base < file_pos) {
1676+		fseek(pfp, p_base, SEEK_SET);
1677+		say("The text leading up to this was:\n--------------------------\n");
1678+		while (ftell(pfp) < file_pos) {
1679+			ret = getline(&buf, &bufsz, pfp);
1680+			if (ret == -1)
1681+				fatal("Unexpected end of file\n");
1682+			say("|%s", buf);
1683+		}
1684+		say("--------------------------\n");
1685+	} else
1686+		fseek(pfp, file_pos, SEEK_SET);
1687+	p_input_line = file_line - 1;
1688+}
1689+
1690+__dead static void
1691+malformed(void)
1692+{
1693+	fatal("malformed patch at line %ld: %s", p_input_line, buf);
1694+}
1695+
1696+static LINENUM
1697+getlinenum(const char *s)
1698+{
1699+	LINENUM l = (LINENUM)atol(s);
1700+	if (l < 0) {
1701+		l = 0;
1702+		malformed();
1703+	}
1704+	return l;
1705+}
1706+
1707+static LINENUM
1708+getskiplinenum(char **p)
1709+{
1710+	char *s = *p;
1711+	LINENUM l = getlinenum(s);
1712+	while (isdigit((unsigned char)*s))
1713+		s++;
1714+	*p = s;
1715+	return l;
1716+}
1717+
1718+static int
1719+remove_special_line(void)
1720+{
1721+	int c;
1722+
1723+	c = fgetc(pfp);
1724+	if (c == '\\') {
1725+		do {
1726+			c = fgetc(pfp);
1727+		} while (c != EOF && c != '\n');
1728+		return 1;
1729+	}
1730+	if (c != EOF)
1731+		fseek(pfp, -1L, SEEK_CUR);
1732+	return 0;
1733+}
1734+
1735+int
1736+another_hunk(void)
1737+{
1738+	long line_beginning;
1739+	LINENUM repl_beginning;
1740+	LINENUM fillcnt;
1741+	LINENUM fillsrc;
1742+	LINENUM filldst;
1743+	int ptrn_spaces_eaten;
1744+	int repl_could_be_missing;
1745+	int repl_missing;
1746+	long repl_backtrack_position;
1747+	LINENUM repl_patch_line;
1748+	LINENUM ptrn_copiable;
1749+	char *s;
1750+	int context = 0;
1751+	int ret;
1752+
1753+	while (p_end >= 0) {
1754+		if (p_end == p_efake)
1755+			p_end = p_bfake;
1756+		else
1757+			free(p_line[p_end]);
1758+		p_end--;
1759+	}
1760+	p_efake = -1;
1761+	p_max = hunkmax;
1762+
1763+	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
1764+		line_beginning = ftell(pfp);
1765+		repl_beginning = 0;
1766+		fillcnt = 0;
1767+		fillsrc = 0;
1768+		filldst = 0;
1769+		ptrn_spaces_eaten = 0;
1770+		repl_could_be_missing = 1;
1771+		repl_missing = 0;
1772+		repl_backtrack_position = 0;
1773+		repl_patch_line = 0;
1774+		ptrn_copiable = 0;
1775+
1776+		ret = pgetline(&buf, &bufsz, pfp);
1777+		p_input_line++;
1778+		if (ret == -1 || strnNE(buf, "********", 8)) {
1779+			next_intuit_at(line_beginning, p_input_line);
1780+			return 0;
1781+		}
1782+		p_context = 100;
1783+		p_hunk_beg = p_input_line + 1;
1784+		while (p_end < p_max) {
1785+			ret = pgetline(&buf, &bufsz, pfp);
1786+			p_input_line++;
1787+			if (ret == -1) {
1788+				if (repl_beginning && repl_could_be_missing) {
1789+					repl_missing = 1;
1790+					goto hunk_done;
1791+				}
1792+				fatal("unexpected end of file in patch\n");
1793+			}
1794+			p_end++;
1795+			if (p_end >= hunkmax)
1796+				fatal("Internal error: hunk larger than hunk buffer size");
1797+			p_char[p_end] = *buf;
1798+			p_line[p_end] = NULL;
1799+			switch (*buf) {
1800+			case '*':
1801+				if (strnEQ(buf, "********", 8)) {
1802+					if (repl_beginning && repl_could_be_missing) {
1803+						repl_missing = 1;
1804+						goto hunk_done;
1805+					} else
1806+						fatal("unexpected end of hunk at line %ld\n", p_input_line);
1807+				}
1808+				if (p_end != 0) {
1809+					if (repl_beginning && repl_could_be_missing) {
1810+						repl_missing = 1;
1811+						goto hunk_done;
1812+					}
1813+					fatal("unexpected *** at line %ld: %s", p_input_line, buf);
1814+				}
1815+				context = 0;
1816+				p_line[p_end] = savestr(buf);
1817+				if (out_of_mem) {
1818+					p_end--;
1819+					return 0;
1820+				}
1821+				for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1822+					;
1823+				if (!*s)
1824+					malformed();
1825+				if (strnEQ(s, "0,0", 3))
1826+					memmove(s, s + 2, strlen(s + 2) + 1);
1827+				p_first = getskiplinenum(&s);
1828+				if (*s == ',') {
1829+					for (; *s && !isdigit((unsigned char)*s); s++)
1830+						;
1831+					if (!*s)
1832+						malformed();
1833+					p_ptrn_lines = (getlinenum(s)) - p_first + 1;
1834+					if (p_ptrn_lines < 0)
1835+						malformed();
1836+				} else if (p_first)
1837+					p_ptrn_lines = 1;
1838+				else {
1839+					p_ptrn_lines = 0;
1840+					p_first = 1;
1841+				}
1842+				if (p_first >= LINENUM_MAX - p_ptrn_lines || p_ptrn_lines >= LINENUM_MAX - 6)
1843+					malformed();
1844+				p_max = p_ptrn_lines + 6;
1845+				while (p_max >= hunkmax)
1846+					grow_hunkmax();
1847+				p_max = hunkmax;
1848+				break;
1849+			case '-':
1850+				if (buf[1] == '-') {
1851+					if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_char[p_end - 1] == '\n'))) {
1852+						if (p_end == 1) {
1853+							p_end = p_ptrn_lines + 1;
1854+							fillsrc = p_end + 1;
1855+							filldst = 1;
1856+							fillcnt = p_ptrn_lines;
1857+						} else {
1858+							if (repl_beginning) {
1859+								if (repl_could_be_missing) {
1860+									repl_missing = 1;
1861+									goto hunk_done;
1862+								}
1863+								fatal("duplicate \"---\" at line %ld\n", p_input_line);
1864+							} else {
1865+								fatal("premature/overdue \"---\" at line %ld\n", p_input_line);
1866+							}
1867+						}
1868+					}
1869+					repl_beginning = p_end;
1870+					repl_backtrack_position = ftell(pfp);
1871+					repl_patch_line = p_input_line;
1872+					p_line[p_end] = savestr(buf);
1873+					if (out_of_mem) {
1874+						p_end--;
1875+						return 0;
1876+					}
1877+					p_char[p_end] = '=';
1878+					for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1879+						;
1880+					if (!*s)
1881+						malformed();
1882+					p_newfirst = getskiplinenum(&s);
1883+					if (*s == ',') {
1884+						for (; *s && !isdigit((unsigned char)*s); s++)
1885+							;
1886+						if (!*s)
1887+							malformed();
1888+						p_repl_lines = (getlinenum(s)) - p_newfirst + 1;
1889+						if (p_repl_lines < 0)
1890+							malformed();
1891+					} else if (p_newfirst)
1892+						p_repl_lines = 1;
1893+					else {
1894+						p_repl_lines = 0;
1895+						p_newfirst = 1;
1896+					}
1897+					if (p_newfirst >= LINENUM_MAX - p_repl_lines || p_repl_lines >= LINENUM_MAX - p_end)
1898+						malformed();
1899+					p_max = p_repl_lines + p_end;
1900+					if (p_max > MAXHUNKSIZE)
1901+						fatal("hunk too large at line %ld\n", p_input_line);
1902+					while (p_max >= hunkmax)
1903+						grow_hunkmax();
1904+					if (p_repl_lines != ptrn_copiable && (p_context != 0 || p_repl_lines != 1))
1905+						repl_could_be_missing = 0;
1906+					break;
1907+				}
1908+				goto change_line;
1909+			case '+':
1910+			case '!':
1911+				repl_could_be_missing = 0;
1912+			change_line:
1913+				if (buf[1] == '\n' && canonicalize)
1914+					strlcpy(buf + 1, " \n", bufsz - 1);
1915+				if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' &&
1916+				    repl_beginning && repl_could_be_missing) {
1917+					repl_missing = 1;
1918+					goto hunk_done;
1919+				}
1920+				if (context >= 0) {
1921+					if (context < p_context)
1922+						p_context = context;
1923+					context = -1000;
1924+				}
1925+				p_line[p_end] = savestr(buf + 2);
1926+				if (out_of_mem) {
1927+					p_end--;
1928+					return 0;
1929+				}
1930+				if (p_end == p_ptrn_lines) {
1931+					if (remove_special_line()) {
1932+						int length = strlen(p_line[p_end]) - 1;
1933+						(p_line[p_end])[length] = 0;
1934+					}
1935+				}
1936+				break;
1937+			case '\t':
1938+			case '\n':
1939+				if (repl_beginning && repl_could_be_missing &&
1940+				    (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) {
1941+					repl_missing = 1;
1942+					goto hunk_done;
1943+				}
1944+				p_line[p_end] = savestr(buf);
1945+				if (out_of_mem) {
1946+					p_end--;
1947+					return 0;
1948+				}
1949+				if (p_end != p_ptrn_lines + 1) {
1950+					ptrn_spaces_eaten |= (repl_beginning != 0);
1951+					context++;
1952+					if (!repl_beginning)
1953+						ptrn_copiable++;
1954+					p_char[p_end] = ' ';
1955+				}
1956+				break;
1957+			case ' ':
1958+				if (!isspace((unsigned char)buf[1]) && repl_beginning && repl_could_be_missing) {
1959+					repl_missing = 1;
1960+					goto hunk_done;
1961+				}
1962+				context++;
1963+				if (!repl_beginning)
1964+					ptrn_copiable++;
1965+				p_line[p_end] = savestr(buf + 2);
1966+				if (out_of_mem) {
1967+					p_end--;
1968+					return 0;
1969+				}
1970+				break;
1971+			default:
1972+				if (repl_beginning && repl_could_be_missing) {
1973+					repl_missing = 1;
1974+					goto hunk_done;
1975+				}
1976+				malformed();
1977+			}
1978+			if (p_line[p_end])
1979+				p_len[p_end] = strlen(p_line[p_end]);
1980+			else
1981+				p_len[p_end] = 0;
1982+		}
1983+
1984+hunk_done:
1985+		if (p_end >= 0 && !repl_beginning)
1986+			fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
1987+
1988+		if (repl_missing) {
1989+			p_input_line = repl_patch_line;
1990+			for (p_end--; p_end > repl_beginning; p_end--)
1991+				free(p_line[p_end]);
1992+			fseek(pfp, repl_backtrack_position, SEEK_SET);
1993+
1994+			if (!p_context && p_repl_lines == 1) {
1995+				p_repl_lines = 0;
1996+				p_max--;
1997+			}
1998+			fillsrc = 1;
1999+			filldst = repl_beginning + 1;
2000+			fillcnt = p_repl_lines;
2001+			p_end = p_max;
2002+		} else if (!p_context && fillcnt == 1) {
2003+			while (filldst < p_end) {
2004+				p_line[filldst] = p_line[filldst + 1];
2005+				p_char[filldst] = p_char[filldst + 1];
2006+				p_len[filldst] = p_len[filldst + 1];
2007+				filldst++;
2008+			}
2009+			p_end--;
2010+			p_first++;
2011+			fillcnt = 0;
2012+			p_ptrn_lines = 0;
2013+		}
2014+		if (diff_type == CONTEXT_DIFF && (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
2015+			diff_type = NEW_CONTEXT_DIFF;
2016+		}
2017+		if (fillcnt) {
2018+			p_bfake = filldst;
2019+			p_efake = filldst + fillcnt - 1;
2020+			while (fillcnt-- > 0) {
2021+				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
2022+					fillsrc++;
2023+				if (fillsrc > p_end)
2024+					fatal("replacement text mangled in hunk at line %ld\n", p_hunk_beg);
2025+				p_line[filldst] = p_line[fillsrc];
2026+				p_char[filldst] = p_char[fillsrc];
2027+				p_len[filldst] = p_len[fillsrc];
2028+				fillsrc++;
2029+				filldst++;
2030+			}
2031+			while (fillsrc <= p_end && fillsrc != repl_beginning && p_char[fillsrc] != ' ')
2032+				fillsrc++;
2033+			if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
2034+				malformed();
2035+			if (filldst != p_end + 1 && filldst != repl_beginning)
2036+				malformed();
2037+		}
2038+		if (p_line[p_end] != NULL) {
2039+			if (remove_special_line()) {
2040+				p_len[p_end] -= 1;
2041+				(p_line[p_end])[p_len[p_end]] = 0;
2042+			}
2043+		}
2044+	} else if (diff_type == UNI_DIFF) {
2045+		LINENUM fillold;
2046+		LINENUM fillnew;
2047+		char ch;
2048+
2049+		line_beginning = ftell(pfp);
2050+		ret = pgetline(&buf, &bufsz, pfp);
2051+		p_input_line++;
2052+		if (ret == -1 || strnNE(buf, "@@ -", 4)) {
2053+			next_intuit_at(line_beginning, p_input_line);
2054+			return 0;
2055+		}
2056+		s = buf + 4;
2057+		if (!*s)
2058+			malformed();
2059+		p_first = getskiplinenum(&s);
2060+		if (*s == ',') {
2061+			s++;
2062+			p_ptrn_lines = getskiplinenum(&s);
2063+		} else
2064+			p_ptrn_lines = 1;
2065+		if (p_first >= LINENUM_MAX - p_ptrn_lines)
2066+			malformed();
2067+		if (*s == ' ')
2068+			s++;
2069+		if (*s != '+' || !*++s)
2070+			malformed();
2071+		p_newfirst = getskiplinenum(&s);
2072+		if (*s == ',') {
2073+			s++;
2074+			p_repl_lines = getskiplinenum(&s);
2075+		} else
2076+			p_repl_lines = 1;
2077+		if (*s == ' ')
2078+			s++;
2079+		if (*s != '@')
2080+			malformed();
2081+		if (p_first >= LINENUM_MAX - p_ptrn_lines ||
2082+		    p_newfirst > LINENUM_MAX - p_repl_lines ||
2083+		    p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2084+			malformed();
2085+		if (!p_ptrn_lines)
2086+			p_first++;
2087+		p_max = p_ptrn_lines + p_repl_lines + 1;
2088+		while (p_max >= hunkmax)
2089+			grow_hunkmax();
2090+		fillold = 1;
2091+		fillnew = fillold + p_ptrn_lines;
2092+		p_end = fillnew + p_repl_lines;
2093+		snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first, p_first + p_ptrn_lines - 1);
2094+		p_line[0] = savestr(buf);
2095+		if (out_of_mem) {
2096+			p_end = -1;
2097+			return 0;
2098+		}
2099+		p_char[0] = '*';
2100+		snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst, p_newfirst + p_repl_lines - 1);
2101+		p_line[fillnew] = savestr(buf);
2102+		if (out_of_mem) {
2103+			p_end = 0;
2104+			return 0;
2105+		}
2106+		p_char[fillnew++] = '=';
2107+		p_context = 100;
2108+		context = 0;
2109+		p_hunk_beg = p_input_line + 1;
2110+		while (fillold <= p_ptrn_lines || fillnew <= p_end) {
2111+			ret = pgetline(&buf, &bufsz, pfp);
2112+			p_input_line++;
2113+			if (ret == -1) {
2114+				if (p_max - fillnew < 3)
2115+					strlcpy(buf, " \n", bufsz);
2116+				else
2117+					fatal("unexpected end of file in patch\n");
2118+			}
2119+			if (*buf == '\t' || *buf == '\n') {
2120+				ch = ' ';
2121+				s = savestr(buf);
2122+			} else {
2123+				ch = *buf;
2124+				s = savestr(buf + 1);
2125+			}
2126+			if (out_of_mem) {
2127+				while (--fillnew > p_ptrn_lines)
2128+					free(p_line[fillnew]);
2129+				p_end = fillold - 1;
2130+				return 0;
2131+			}
2132+			switch (ch) {
2133+			case '-':
2134+				if (fillold > p_ptrn_lines) {
2135+					free(s);
2136+					p_end = fillnew - 1;
2137+					malformed();
2138+				}
2139+				p_char[fillold] = ch;
2140+				p_line[fillold] = s;
2141+				p_len[fillold++] = strlen(s);
2142+				if (fillold > p_ptrn_lines) {
2143+					if (remove_special_line()) {
2144+						p_len[fillold - 1] -= 1;
2145+						s[p_len[fillold - 1]] = 0;
2146+					}
2147+				}
2148+				break;
2149+			case '=':
2150+				ch = ' ';
2151+				/* FALL THROUGH */
2152+			case ' ':
2153+				if (fillold > p_ptrn_lines) {
2154+					free(s);
2155+					while (--fillnew > p_ptrn_lines)
2156+						free(p_line[fillnew]);
2157+					p_end = fillold - 1;
2158+					malformed();
2159+				}
2160+				context++;
2161+				p_char[fillold] = ch;
2162+				p_line[fillold] = s;
2163+				p_len[fillold++] = strlen(s);
2164+				s = savestr(s);
2165+				if (out_of_mem) {
2166+					while (--fillnew > p_ptrn_lines)
2167+						free(p_line[fillnew]);
2168+					p_end = fillold - 1;
2169+					return 0;
2170+				}
2171+				if (fillold > p_ptrn_lines) {
2172+					if (remove_special_line()) {
2173+						p_len[fillold - 1] -= 1;
2174+						s[p_len[fillold - 1]] = 0;
2175+					}
2176+				}
2177+				/* FALL THROUGH */
2178+			case '+':
2179+				if (fillnew > p_end) {
2180+					free(s);
2181+					while (--fillnew > p_ptrn_lines)
2182+						free(p_line[fillnew]);
2183+					p_end = fillold - 1;
2184+					malformed();
2185+				}
2186+				p_char[fillnew] = ch;
2187+				p_line[fillnew] = s;
2188+				p_len[fillnew++] = strlen(s);
2189+				if (fillold > p_ptrn_lines) {
2190+					if (remove_special_line()) {
2191+						p_len[fillnew - 1] -= 1;
2192+						s[p_len[fillnew - 1]] = 0;
2193+					}
2194+				}
2195+				break;
2196+			default:
2197+				p_end = fillnew;
2198+				malformed();
2199+			}
2200+			if (ch != ' ' && context > 0) {
2201+				if (context < p_context)
2202+					p_context = context;
2203+				context = -1000;
2204+			}
2205+		}
2206+	} else {
2207+		char hunk_type;
2208+		int i;
2209+		LINENUM min, max;
2210+
2211+		line_beginning = ftell(pfp);
2212+		p_context = 0;
2213+		ret = pgetline(&buf, &bufsz, pfp);
2214+		p_input_line++;
2215+		if (ret == -1 || !isdigit((unsigned char)*buf)) {
2216+			next_intuit_at(line_beginning, p_input_line);
2217+			return 0;
2218+		}
2219+		s = buf;
2220+		p_first = getskiplinenum(&s);
2221+		if (*s == ',') {
2222+			s++;
2223+			p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
2224+		} else
2225+			p_ptrn_lines = (*s != 'a');
2226+		if (p_first >= LINENUM_MAX - p_ptrn_lines)
2227+			malformed();
2228+		hunk_type = *s++;
2229+		if (hunk_type == 'a')
2230+			p_first++;
2231+		min = getskiplinenum(&s);
2232+		if (*s == ',')
2233+			max = getlinenum(++s);
2234+		else
2235+			max = min;
2236+		if (min < 0 || min > max || max - min == LINENUM_MAX)
2237+			malformed();
2238+		if (hunk_type == 'd')
2239+			min++;
2240+		p_end = p_ptrn_lines + p_repl_lines + 1;
2241+		p_newfirst = min;
2242+		p_repl_lines = max - min + 1;
2243+		if (p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2244+			malformed();
2245+		p_end = p_ptrn_lines + p_repl_lines + 1;
2246+		if (p_end > MAXHUNKSIZE)
2247+			fatal("hunk too large at line %ld\n", p_input_line);
2248+		while (p_end >= hunkmax)
2249+			grow_hunkmax();
2250+		snprintf(buf, bufsz, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
2251+		p_line[0] = savestr(buf);
2252+		if (out_of_mem) {
2253+			p_end = -1;
2254+			return 0;
2255+		}
2256+		p_char[0] = '*';
2257+		for (i = 1; i <= p_ptrn_lines; i++) {
2258+			ret = pgetline(&buf, &bufsz, pfp);
2259+			p_input_line++;
2260+			if (ret == -1)
2261+				fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2262+			if (*buf != '<')
2263+				fatal("< expected at line %ld of patch\n", p_input_line);
2264+			p_line[i] = savestr(buf + 2);
2265+			if (out_of_mem) {
2266+				p_end = i - 1;
2267+				return 0;
2268+			}
2269+			p_len[i] = strlen(p_line[i]);
2270+			p_char[i] = '-';
2271+		}
2272+		if (remove_special_line()) {
2273+			p_len[i - 1] -= 1;
2274+			(p_line[i - 1])[p_len[i - 1]] = 0;
2275+		}
2276+		if (hunk_type == 'c') {
2277+			ret = pgetline(&buf, &bufsz, pfp);
2278+			p_input_line++;
2279+			if (ret == -1)
2280+				fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2281+			if (*buf != '-')
2282+				fatal("--- expected at line %ld of patch\n", p_input_line);
2283+		}
2284+		snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
2285+		p_line[i] = savestr(buf);
2286+		if (out_of_mem) {
2287+			p_end = i - 1;
2288+			return 0;
2289+		}
2290+		p_char[i] = '=';
2291+		for (i++; i <= p_end; i++) {
2292+			ret = pgetline(&buf, &bufsz, pfp);
2293+			p_input_line++;
2294+			if (ret == -1)
2295+				fatal("unexpected end of file in patch at line %ld\n", p_input_line);
2296+			if (*buf != '>')
2297+				fatal("> expected at line %ld of patch\n", p_input_line);
2298+			p_line[i] = savestr(buf + 2);
2299+			if (out_of_mem) {
2300+				p_end = i - 1;
2301+				return 0;
2302+			}
2303+			p_len[i] = strlen(p_line[i]);
2304+			p_char[i] = '+';
2305+		}
2306+		if (remove_special_line()) {
2307+			p_len[i - 1] -= 1;
2308+			(p_line[i - 1])[p_len[i - 1]] = 0;
2309+		}
2310+	}
2311+	if (reverse) {
2312+		if (!pch_swap())
2313+			say("Not enough memory to swap next hunk!\n");
2314+	}
2315+	if (p_end + 1 < hunkmax)
2316+		p_char[p_end + 1] = '^';
2317+	return 1;
2318+}
2319+
2320+int
2321+pgetline(char **bf, size_t *sz, FILE *fp)
2322+{
2323+	char *s;
2324+	int indent = 0;
2325+	int ret;
2326+
2327+	ret = getline(bf, sz, fp);
2328+	if (p_indent && ret != -1) {
2329+		for (s = buf; indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
2330+			if (*s == '\t')
2331+				indent += 8 - (indent % 7);
2332+			else
2333+				indent++;
2334+		}
2335+		if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
2336+			fatal("buffer too small in pgetline()\n");
2337+	}
2338+	return ret;
2339+}
2340+
2341+int
2342+pch_swap(void)
2343+{
2344+	char **tp_line;
2345+	ssize_t *tp_len;
2346+	char *tp_char;
2347+	LINENUM i;
2348+	LINENUM n;
2349+	int blankline = 0;
2350+	char *s;
2351+
2352+	i = p_first;
2353+	p_first = p_newfirst;
2354+	p_newfirst = i;
2355+
2356+	tp_line = p_line;
2357+	tp_len = p_len;
2358+	tp_char = p_char;
2359+	p_line = NULL;
2360+	p_len = NULL;
2361+	p_char = NULL;
2362+	set_hunkmax();
2363+	if (p_line == NULL || p_len == NULL || p_char == NULL) {
2364+		free(p_line);
2365+		p_line = tp_line;
2366+		free(p_len);
2367+		p_len = tp_len;
2368+		free(p_char);
2369+		p_char = tp_char;
2370+		return 0;
2371+	}
2372+
2373+	i = p_ptrn_lines + 1;
2374+	if (tp_char[i] == '\n') {
2375+		blankline = 1;
2376+		i++;
2377+	}
2378+	if (p_efake >= 0) {
2379+		if (p_efake <= i)
2380+			n = p_end - i + 1;
2381+		else
2382+			n = -i;
2383+		p_efake += n;
2384+		p_bfake += n;
2385+	}
2386+	for (n = 0; i <= p_end; i++, n++) {
2387+		p_line[n] = tp_line[i];
2388+		p_char[n] = tp_char[i];
2389+		if (p_char[n] == '+')
2390+			p_char[n] = '-';
2391+		p_len[n] = tp_len[i];
2392+	}
2393+	if (blankline) {
2394+		i = p_ptrn_lines + 1;
2395+		p_line[n] = tp_line[i];
2396+		p_char[n] = tp_char[i];
2397+		p_len[n] = tp_len[i];
2398+		n++;
2399+	}
2400+	if (p_char[0] != '=')
2401+		fatal("expected '=' found '%c'\n", p_char[0]);
2402+	p_char[0] = '*';
2403+	for (s = p_line[0]; *s; s++)
2404+		if (*s == '-')
2405+			*s = '*';
2406+
2407+	if (p_char[0] != '*')
2408+		fatal("expected '*' found '%c'\n", p_char[0]);
2409+	tp_char[0] = '=';
2410+	for (s = tp_line[0]; *s; s++)
2411+		if (*s == '*')
2412+			*s = '-';
2413+	for (i = 0; n <= p_end; i++, n++) {
2414+		p_line[n] = tp_line[i];
2415+		p_char[n] = tp_char[i];
2416+		if (p_char[n] == '-')
2417+			p_char[n] = '+';
2418+		p_len[n] = tp_len[i];
2419+	}
2420+	if (i != p_ptrn_lines + 1)
2421+		fatal("expected %ld lines, got %ld\n", p_ptrn_lines + 1, i);
2422+
2423+	i = p_ptrn_lines;
2424+	p_ptrn_lines = p_repl_lines;
2425+	p_repl_lines = i;
2426+
2427+	free(tp_line);
2428+	free(tp_len);
2429+	free(tp_char);
2430+	return 1;
2431+}
2432+
2433+LINENUM
2434+pch_first(void)
2435+{
2436+	return p_first;
2437+}
2438+
2439+LINENUM
2440+pch_ptrn_lines(void)
2441+{
2442+	return p_ptrn_lines;
2443+}
2444+
2445+LINENUM
2446+pch_newfirst(void)
2447+{
2448+	return p_newfirst;
2449+}
2450+
2451+LINENUM
2452+pch_repl_lines(void)
2453+{
2454+	return p_repl_lines;
2455+}
2456+
2457+LINENUM
2458+pch_end(void)
2459+{
2460+	return p_end;
2461+}
2462+
2463+LINENUM
2464+pch_context(void)
2465+{
2466+	return p_context;
2467+}
2468+
2469+ssize_t
2470+pch_line_len(LINENUM line)
2471+{
2472+	return p_len[line];
2473+}
2474+
2475+char
2476+pch_char(LINENUM line)
2477+{
2478+	return p_char[line];
2479+}
2480+
2481+char *
2482+pfetch(LINENUM line)
2483+{
2484+	return p_line[line];
2485+}
2486+
2487+LINENUM
2488+pch_hunk_beg(void)
2489+{
2490+	return p_hunk_beg;
2491+}
2492+
2493+void
2494+do_ed_script(void)
2495+{
2496+	char *t;
2497+	long beginning_of_this_line;
2498+	FILE *pipefp = NULL;
2499+	int continuation;
2500+
2501+	if (!skip_rest_of_patch) {
2502+		if (copy_file(filearg[0], TMPOUTNAME) < 0) {
2503+			unlink(TMPOUTNAME);
2504+			fatal("cant create temp file %s", TMPOUTNAME);
2505+		}
2506+		snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED, verbose ? "" : "s", TMPOUTNAME);
2507+		pipefp = popen(buf, "w");
2508+	}
2509+	for (;;) {
2510+		beginning_of_this_line = ftell(pfp);
2511+		if (pgetline(&buf, &bufsz, pfp) == -1) {
2512+			next_intuit_at(beginning_of_this_line, p_input_line);
2513+			break;
2514+		}
2515+		p_input_line++;
2516+		for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
2517+			;
2518+		if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) {
2519+			if (pipefp != NULL)
2520+				fprintf(pipefp, "%s", buf);
2521+			if (*t == 's') {
2522+				for (;;) {
2523+					continuation = 0;
2524+					t = strchr(buf, '\0') - 1;
2525+					while (--t >= buf && *t == '\\')
2526+						continuation = !continuation;
2527+					if (!continuation || pgetline(&buf, &bufsz, pfp) == -1)
2528+						break;
2529+					if (pipefp != NULL)
2530+						fprintf(pipefp, "%s", buf);
2531+				}
2532+			} else if (*t != 'd') {
2533+				while (pgetline(&buf, &bufsz, pfp) != -1) {
2534+					p_input_line++;
2535+					if (pipefp != NULL)
2536+						fprintf(pipefp, "%s", buf);
2537+					if (strEQ(buf, ".\n"))
2538+						break;
2539+				}
2540+			}
2541+		} else {
2542+			next_intuit_at(beginning_of_this_line, p_input_line);
2543+			break;
2544+		}
2545+	}
2546+	if (pipefp == NULL)
2547+		return;
2548+	fprintf(pipefp, "w\n");
2549+	fprintf(pipefp, "q\n");
2550+	fflush(pipefp);
2551+	pclose(pipefp);
2552+	ignore_signals();
2553+	if (!check_only) {
2554+		if (move_file(TMPOUTNAME, outname) < 0) {
2555+			toutkeep = 1;
2556+			chmod(TMPOUTNAME, filemode);
2557+		} else
2558+			chmod(outname, filemode);
2559+	}
2560+	set_signals(1);
2561+}
2562+
2563+static char *
2564+posix_name(const struct FileName *names, int assume_exists)
2565+{
2566+	char *path = NULL;
2567+	int i;
2568+
2569+	for (i = 0; i < MAX_FILE; i++) {
2570+		if (names[i].path != NULL && names[i].exists) {
2571+			path = names[i].path;
2572+			break;
2573+		}
2574+	}
2575+	if (path == NULL && !assume_exists) {
2576+		for (i = 0; i < MAX_FILE; i++) {
2577+			if (names[i].path != NULL && (path = checked_in(names[i].path)) != NULL)
2578+				break;
2579+		}
2580+		if (path == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2581+			path = names[NEW_FILE].path;
2582+	}
2583+	return path ? savestr(path) : NULL;
2584+}
2585+
2586+static char *
2587+best_name(const struct FileName *names, int assume_exists)
2588+{
2589+	size_t min_components, min_baselen, min_len, tmp;
2590+	char *best = NULL;
2591+	int i;
2592+
2593+	min_components = min_baselen = min_len = SIZE_MAX;
2594+	for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2595+		if (names[i].path == NULL || (!names[i].exists && !assume_exists))
2596+			continue;
2597+		if ((tmp = num_components(names[i].path)) > min_components)
2598+			continue;
2599+		min_components = tmp;
2600+		if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2601+			continue;
2602+		min_baselen = tmp;
2603+		if ((tmp = strlen(names[i].path)) > min_len)
2604+			continue;
2605+		min_len = tmp;
2606+		best = names[i].path;
2607+	}
2608+	if (best == NULL) {
2609+		min_components = min_baselen = min_len = SIZE_MAX;
2610+		for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2611+			if (names[i].path == NULL || checked_in(names[i].path) == NULL)
2612+				continue;
2613+			if ((tmp = num_components(names[i].path)) > min_components)
2614+				continue;
2615+			min_components = tmp;
2616+			if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2617+				continue;
2618+			min_baselen = tmp;
2619+			if ((tmp = strlen(names[i].path)) > min_len)
2620+				continue;
2621+			min_len = tmp;
2622+			best = names[i].path;
2623+		}
2624+		if (best == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2625+			best = names[NEW_FILE].path;
2626+	}
2627+	return best ? savestr(best) : NULL;
2628+}
2629+
2630+static size_t
2631+num_components(const char *path)
2632+{
2633+	size_t n;
2634+	const char *cp;
2635+
2636+	for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
2637+		while (*cp == '/')
2638+			cp++;
2639+	}
2640+	return n;
2641+}
2642+
2643+LINENUM
2644+strtolinenum(char *nptr, char **endptr)
2645+{
2646+	LINENUM rv;
2647+	char c;
2648+	char *p;
2649+	const char *errstr;
2650+
2651+	for (p = nptr; isdigit((unsigned char)*p); p++)
2652+		;
2653+
2654+	if (p == nptr)
2655+		malformed();
2656+
2657+	c = *p;
2658+	*p = '\0';
2659+
2660+	rv = strtonum(nptr, 0, LINENUM_MAX, &errstr);
2661+	if (errstr != NULL)
2662+		fatal("invalid line number at line %ld: `%s' is %s\n",
2663+		    p_input_line, nptr, errstr);
2664+
2665+	*p = c;
2666+	*endptr = p;
2667+
2668+	return rv;
2669+}
2670+
2671+int
2672+move_file(const char *from, const char *to)
2673+{
2674+	int fromfd;
2675+	ssize_t i;
2676+
2677+	if (strEQ(to, "-")) {
2678+		fromfd = open(from, O_RDONLY);
2679+		if (fromfd < 0)
2680+			pfatal("internal error, cant reopen %s", from);
2681+		while ((i = read(fromfd, buf, bufsz)) > 0) {
2682+			if (write(STDOUT_FILENO, buf, i) != i)
2683+				pfatal("write failed");
2684+		}
2685+		close(fromfd);
2686+		return 0;
2687+	}
2688+	if (backup_file(to) < 0) {
2689+		say("Cant backup %s, output is in %s: %s\n", to, from, strerror(errno));
2690+		return -1;
2691+	}
2692+	if (rename(from, to) < 0) {
2693+		if (errno != EXDEV || copy_file(from, to) < 0) {
2694+			say("Cant create %s, output is in %s: %s\n", to, from, strerror(errno));
2695+			return -1;
2696+		}
2697+	}
2698+	return 0;
2699+}
2700+
2701+int
2702+backup_file(const char *orig)
2703+{
2704+	struct stat filestat;
2705+	char bakname[PATH_MAX], *s, *simplename;
2706+	dev_t orig_device;
2707+	ino_t orig_inode;
2708+
2709+	if (backup_type == BACKUP_NONE || stat(orig, &filestat) != 0)
2710+		return 0;
2711+	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
2712+		unlink(orig);
2713+		return 0;
2714+	}
2715+	orig_device = filestat.st_dev;
2716+	orig_inode = filestat.st_ino;
2717+
2718+	if (origprae) {
2719+		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
2720+		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
2721+			fatal("filename too long\n");
2722+	} else {
2723+		if ((s = find_backup_file_name(orig)) == NULL)
2724+			fatal("out of memory\n");
2725+		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
2726+			fatal("filename too long\n");
2727+		free(s);
2728+	}
2729+
2730+	if ((simplename = strrchr(bakname, '/')) != NULL)
2731+		simplename = simplename + 1;
2732+	else
2733+		simplename = bakname;
2734+
2735+	while (stat(bakname, &filestat) == 0 && orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
2736+		for (s = simplename; *s && !islower((unsigned char)*s); s++)
2737+			;
2738+		if (*s)
2739+			*s = toupper((unsigned char)*s);
2740+		else
2741+			memmove(simplename, simplename + 1, strlen(simplename + 1) + 1);
2742+	}
2743+	if (rename(orig, bakname) < 0) {
2744+		if (errno != EXDEV || copy_file(orig, bakname) < 0)
2745+			return -1;
2746+	}
2747+	return 0;
2748+}
2749+
2750+int
2751+copy_file(const char *from, const char *to)
2752+{
2753+	int tofd, fromfd;
2754+	ssize_t i;
2755+
2756+	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
2757+	if (tofd < 0)
2758+		return -1;
2759+	fromfd = open(from, O_RDONLY, 0);
2760+	if (fromfd < 0)
2761+		pfatal("internal error, cant reopen %s", from);
2762+	while ((i = read(fromfd, buf, bufsz)) > 0) {
2763+		if (write(tofd, buf, i) != i)
2764+			pfatal("write to %s failed", to);
2765+	}
2766+	close(fromfd);
2767+	close(tofd);
2768+	return 0;
2769+}
2770+
2771+char *
2772+savestr(const char *s)
2773+{
2774+	char *rv;
2775+
2776+	if (!s)
2777+		s = "Oops";
2778+	rv = strdup(s);
2779+	if (rv == NULL) {
2780+		if (using_plan_a)
2781+			out_of_mem = 1;
2782+		else
2783+			fatal("out of memory\n");
2784+	}
2785+	return rv;
2786+}
2787+
2788+void
2789+say(const char *fmt, ...)
2790+{
2791+	va_list ap;
2792+
2793+	va_start(ap, fmt);
2794+	vfprintf(stderr, fmt, ap);
2795+	va_end(ap);
2796+	fflush(stderr);
2797+}
2798+
2799+__dead void
2800+fatal(const char *fmt, ...)
2801+{
2802+	va_list ap;
2803+
2804+	va_start(ap, fmt);
2805+	fprintf(stderr, "patch: **** ");
2806+	vfprintf(stderr, fmt, ap);
2807+	va_end(ap);
2808+	my_exit(2);
2809+}
2810+
2811+__dead void
2812+pfatal(const char *fmt, ...)
2813+{
2814+	va_list ap;
2815+	int errnum = errno;
2816+
2817+	fprintf(stderr, "patch: **** ");
2818+	va_start(ap, fmt);
2819+	vfprintf(stderr, fmt, ap);
2820+	va_end(ap);
2821+	fprintf(stderr, ": %s\n", strerror(errnum));
2822+	my_exit(2);
2823+}
2824+
2825+void
2826+ask(const char *fmt, ...)
2827+{
2828+	va_list ap;
2829+	ssize_t nr = 0;
2830+	static int ttyfd = -1;
2831+
2832+	va_start(ap, fmt);
2833+	vfprintf(stdout, fmt, ap);
2834+	va_end(ap);
2835+	fflush(stdout);
2836+	if (ttyfd < 0)
2837+		ttyfd = open(_PATH_TTY, O_RDONLY);
2838+	if (ttyfd >= 0) {
2839+		if ((nr = read(ttyfd, buf, bufsz)) > 0 && buf[nr - 1] == '\n')
2840+			buf[nr - 1] = '\0';
2841+	}
2842+	if (ttyfd < 0 || nr <= 0) {
2843+		putchar('\n');
2844+		buf[0] = '\0';
2845+	}
2846+}
2847+
2848+void
2849+set_signals(int reset)
2850+{
2851+	static sig_t hupval, intval;
2852+
2853+	if (!reset) {
2854+		hupval = signal(SIGHUP, SIG_IGN);
2855+		if (hupval != SIG_IGN)
2856+			hupval = (sig_t)my_exit;
2857+		intval = signal(SIGINT, SIG_IGN);
2858+		if (intval != SIG_IGN)
2859+			intval = (sig_t)my_exit;
2860+	}
2861+	signal(SIGHUP, hupval);
2862+	signal(SIGINT, intval);
2863+}
2864+
2865+void
2866+ignore_signals(void)
2867+{
2868+	signal(SIGHUP, SIG_IGN);
2869+	signal(SIGINT, SIG_IGN);
2870+}
2871+
2872+void
2873+makedirs(const char *filename, int striplast)
2874+{
2875+	char *tmpbuf;
2876+
2877+	if ((tmpbuf = strdup(filename)) == NULL)
2878+		fatal("out of memory\n");
2879+
2880+	if (striplast) {
2881+		char *s = strrchr(tmpbuf, '/');
2882+		if (s == NULL) {
2883+			free(tmpbuf);
2884+			return;
2885+		}
2886+		*s = '\0';
2887+	}
2888+	if (mkpath(tmpbuf) != 0)
2889+		pfatal("creation of %s failed", tmpbuf);
2890+	free(tmpbuf);
2891+}
2892+
2893+char *
2894+fetchname(const char *at, int *exists, int strip_leading)
2895+{
2896+	char *fullname, *name, *t;
2897+	int sleading, tab;
2898+	struct stat filestat;
2899+
2900+	if (at == NULL || *at == '\0')
2901+		return NULL;
2902+	while (isspace((unsigned char)*at))
2903+		at++;
2904+	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
2905+		return NULL;
2906+	name = fullname = t = savestr(at);
2907+	tab = strchr(t, '\t') != NULL;
2908+	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || !isspace((unsigned char)*t)); t++) {
2909+		if (t[0] == '/' && t[1] != '/' && t[1] != '\0') {
2910+			if (--sleading >= 0)
2911+				name = t + 1;
2912+		}
2913+	}
2914+	*t = '\0';
2915+
2916+	if (strip_leading == 957 && name != fullname && *fullname != '/') {
2917+		name[-1] = '\0';
2918+		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
2919+			name[-1] = '/';
2920+			name = fullname;
2921+		}
2922+	}
2923+	name = savestr(name);
2924+	free(fullname);
2925+
2926+	*exists = stat(name, &filestat) == 0;
2927+	return name;
2928+}
2929+
2930+char *
2931+checked_in(char *file)
2932+{
2933+	char *filebase, *filedir, tmpbuf[PATH_MAX];
2934+	struct stat filestat;
2935+
2936+	filebase = basename(file);
2937+	filedir = dirname(file);
2938+
2939+#define try(f, a1, a2, a3) \
2940+(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
2941+
2942+	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
2943+	    try("%s/RCS/%s%s", filedir, filebase, "") ||
2944+	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
2945+	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
2946+	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
2947+		return file;
2948+
2949+	return NULL;
2950+}
2951+
2952+__dead void
2953+version(void)
2954+{
2955+	printf("Patch version 2.0-12u9-NetBSD\n");
2956+	my_exit(EXIT_SUCCESS);
2957+}
2958+
2959+__dead void
2960+my_exit(int status)
2961+{
2962+	unlink(TMPINNAME);
2963+	if (!toutkeep)
2964+		unlink(TMPOUTNAME);
2965+	if (!trejkeep)
2966+		unlink(TMPREJNAME);
2967+	unlink(TMPPATNAME);
2968+	exit(status);
2969+}
2970+
2971+static void *
2972+pch_realloc(void *ptr, size_t number, size_t size)
2973+{
2974+	if (number > SIZE_MAX / size) {
2975+		errno = EOVERFLOW;
2976+		return NULL;
2977+	}
2978+	return realloc(ptr, number * size);
2979+}
2980+
2981+int
2982+mkpath(char *path)
2983+{
2984+	struct stat sb;
2985+	char *slash;
2986+	int done = 0;
2987+
2988+	slash = path;
2989+	while (!done) {
2990+		slash += strspn(slash, "/");
2991+		slash += strcspn(slash, "/");
2992+		done = (*slash == '\0');
2993+		*slash = '\0';
2994+		if (stat(path, &sb)) {
2995+			if (errno != ENOENT || (mkdir(path, 0777) && errno != EEXIST)) {
2996+				warn("%s", path);
2997+				return -1;
2998+			}
2999+		} else if (!S_ISDIR(sb.st_mode)) {
3000+			warnx("%s: %s", path, strerror(ENOTDIR));
3001+			return -1;
3002+		}
3003+		*slash = '/';
3004+	}
3005+	return 0;
3006+}
3007+
3008+char *
3009+find_backup_file_name(const char *file)
3010+{
3011+	char *dir, *base_versions, *tmp_file;
3012+	int highest_backup;
3013+
3014+	if (backup_type == BACKUP_SIMPLE)
3015+		return patch_concat(file, simple_backup_suffix);
3016+	tmp_file = strdup(file);
3017+	if (tmp_file == NULL)
3018+		return NULL;
3019+	base_versions = patch_concat(basename(tmp_file), ".~");
3020+	free(tmp_file);
3021+	if (base_versions == NULL)
3022+		return NULL;
3023+	tmp_file = strdup(file);
3024+	if (tmp_file == NULL) {
3025+		free(base_versions);
3026+		return NULL;
3027+	}
3028+	dir = dirname(tmp_file);
3029+	if (dir == NULL) {
3030+		free(base_versions);
3031+		free(tmp_file);
3032+		return NULL;
3033+	}
3034+	highest_backup = max_backup_version(base_versions, dir);
3035+	free(base_versions);
3036+	free(tmp_file);
3037+	if (backup_type == BACKUP_NUMBERED_EXISTING && highest_backup == 0)
3038+		return patch_concat(file, simple_backup_suffix);
3039+	return make_version_name(file, highest_backup + 1);
3040+}
3041+
3042+static int
3043+max_backup_version(const char *file, const char *dir)
3044+{
3045+	DIR *dirp;
3046+	struct dirent *dp;
3047+	int highest_version, this_version;
3048+	size_t file_name_length;
3049+
3050+	dirp = opendir(dir);
3051+	if (dirp == NULL)
3052+		return 0;
3053+
3054+	highest_version = 0;
3055+	file_name_length = strlen(file);
3056+	while ((dp = readdir(dirp)) != NULL) {
3057+		if (strlen(dp->d_name) <= file_name_length)
3058+			continue;
3059+		this_version = version_number(file, dp->d_name, file_name_length);
3060+		if (this_version > highest_version)
3061+			highest_version = this_version;
3062+	}
3063+	closedir(dirp);
3064+	return highest_version;
3065+}
3066+
3067+static char *
3068+make_version_name(const char *file, int version_num)
3069+{
3070+	char *backup_name;
3071+
3072+	if (asprintf(&backup_name, "%s.~%d~", file, version_num) == -1)
3073+		return NULL;
3074+	return backup_name;
3075+}
3076+
3077+static int
3078+version_number(const char *base, const char *backup, size_t base_length)
3079+{
3080+	int version_num;
3081+	const char *p;
3082+
3083+	version_num = 0;
3084+	if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
3085+		for (p = &backup[base_length]; ISDIGIT(*p); ++p)
3086+			version_num = version_num * 10 + *p - '0';
3087+		if (p[0] != '~' || p[1])
3088+			version_num = 0;
3089+	}
3090+	return version_num;
3091+}
3092+
3093+static char *
3094+patch_concat(const char *str1, const char *str2)
3095+{
3096+	char *newstr;
3097+
3098+	if (asprintf(&newstr, "%s%s", str1, str2) == -1)
3099+		return NULL;
3100+	return newstr;
3101+}
3102+
3103+static int
3104+argmatch(const char *arg, const char **optlist)
3105+{
3106+	int i;
3107+	size_t arglen;
3108+	int matchind = -1;
3109+	int ambiguous = 0;
3110+
3111+	arglen = strlen(arg);
3112+	for (i = 0; optlist[i]; i++) {
3113+		if (!strncmp(optlist[i], arg, arglen)) {
3114+			if (strlen(optlist[i]) == arglen)
3115+				return i;
3116+			else if (matchind == -1)
3117+				matchind = i;
3118+			else
3119+				ambiguous = 1;
3120+		}
3121+	}
3122+	if (ambiguous)
3123+		return -2;
3124+	else
3125+		return matchind;
3126+}
3127+
3128+static void
3129+invalid_arg(const char *kind, const char *value, int problem)
3130+{
3131+	fprintf(stderr, "patch: ");
3132+	if (problem == -1)
3133+		fprintf(stderr, "invalid");
3134+	else
3135+		fprintf(stderr, "ambiguous");
3136+	fprintf(stderr, " %s `%s'\n", kind, value);
3137+}
3138+
3139+enum BackupType
3140+get_version(const char *version_str)
3141+{
3142+	int i;
3143+
3144+	if (version_str == NULL || *version_str == '\0')
3145+		return BACKUP_NUMBERED_EXISTING;
3146+	i = argmatch(version_str, backup_args);
3147+	if (i >= 0)
3148+		return backup_types[i];
3149+	invalid_arg("version control type", version_str, i);
3150+	exit(2);
3151+}
+0, -251
  1@@ -1,251 +0,0 @@
  2-/*
  3- * $OpenBSD: backupfile.c,v 1.19 2006/03/11 19:41:30 otto Exp $
  4- * $DragonFly: src/usr.bin/patch/backupfile.c,v 1.5 2008/08/11 00:05:06 joerg Exp $
  5- * $NetBSD: backupfile.c,v 1.16 2021/02/19 17:46:53 nia Exp $
  6- */
  7-
  8-/*
  9- * backupfile.c -- make Emacs style backup file names
 10- *
 11- * Copyright (C) 1990 Free Software Foundation, Inc.
 12- * 
 13- * This program is free software; you can redistribute it and/or modify it
 14- * without restriction.
 15- * 
 16- * This program is distributed in the hope that it will be useful, but WITHOUT
 17- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 18- * FITNESS FOR A PARTICULAR PURPOSE.
 19- */
 20-
 21-/*
 22- * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
 23- */
 24-
 25-#include <ctype.h>
 26-#include <dirent.h>
 27-#include <libgen.h>
 28-#include <stdio.h>
 29-#include <stdlib.h>
 30-#include <string.h>
 31-#include <unistd.h>
 32-
 33-#include "backupfile.h"
 34-
 35-
 36-#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
 37-
 38-/* Which type of backup file names are generated. */
 39-enum backup_type backup_type = undefined;
 40-
 41-/*
 42- * The extension added to file names to produce a simple (as opposed to
 43- * numbered) backup file name.
 44- */
 45-const char	*simple_backup_suffix = "~";
 46-
 47-static char	*concat(const char *, const char *);
 48-static char	*make_version_name(const char *, int);
 49-static int	max_backup_version(const char *, const char *);
 50-static int	version_number(const char *, const char *, size_t);
 51-static int	argmatch(const char *, const char **);
 52-static void	invalid_arg(const char *, const char *, int);
 53-
 54-/*
 55- * Return the name of the new backup file for file FILE, allocated with
 56- * malloc.  Return 0 if out of memory. FILE must not end with a '/' unless it
 57- * is the root directory. Do not call this function if backup_type == none.
 58- */
 59-char *
 60-find_backup_file_name(const char *file)
 61-{
 62-	char	*dir, *base_versions, *tmp_file;
 63-	int	highest_backup;
 64-
 65-	if (backup_type == simple)
 66-		return concat(file, simple_backup_suffix);
 67-	tmp_file = strdup(file);
 68-	if (tmp_file == NULL)
 69-		return NULL;
 70-	base_versions = concat(basename(tmp_file), ".~");
 71-	free(tmp_file);
 72-	if (base_versions == NULL)
 73-		return NULL;
 74-	tmp_file = strdup(file);
 75-	if (tmp_file == NULL) {
 76-		free(base_versions);
 77-		return NULL;
 78-	}
 79-	dir = dirname(tmp_file);
 80-	if (dir == NULL) {
 81-		free(base_versions);
 82-		free(tmp_file);
 83-		return NULL;
 84-	}
 85-	highest_backup = max_backup_version(base_versions, dir);
 86-	free(base_versions);
 87-	free(tmp_file);
 88-	if (backup_type == numbered_existing && highest_backup == 0)
 89-		return concat(file, simple_backup_suffix);
 90-	return make_version_name(file, highest_backup + 1);
 91-}
 92-
 93-/*
 94- * Return the number of the highest-numbered backup file for file FILE in
 95- * directory DIR.  If there are no numbered backups of FILE in DIR, or an
 96- * error occurs reading DIR, return 0. FILE should already have ".~" appended
 97- * to it.
 98- */
 99-static int
100-max_backup_version(const char *file, const char *dir)
101-{
102-	DIR	*dirp;
103-	struct dirent	*dp;
104-	int	highest_version, this_version;
105-	size_t	file_name_length;
106-
107-	dirp = opendir(dir);
108-	if (dirp == NULL)
109-		return 0;
110-
111-	highest_version = 0;
112-	file_name_length = strlen(file);
113-
114-	while ((dp = readdir(dirp)) != NULL) {
115-		if (strlen(dp->d_name) <= file_name_length)
116-			continue;
117-
118-		this_version = version_number(file, dp->d_name, file_name_length);
119-		if (this_version > highest_version)
120-			highest_version = this_version;
121-	}
122-	closedir(dirp);
123-	return highest_version;
124-}
125-
126-/*
127- * Return a string, allocated with malloc, containing "FILE.~VERSION~".
128- * Return 0 if out of memory.
129- */
130-static char *
131-make_version_name(const char *file, int version)
132-{
133-	char	*backup_name;
134-
135-	if (asprintf(&backup_name, "%s.~%d~", file, version) == -1)
136-		return NULL;
137-	return backup_name;
138-}
139-
140-/*
141- * If BACKUP is a numbered backup of BASE, return its version number;
142- * otherwise return 0.  BASE_LENGTH is the length of BASE. BASE should
143- * already have ".~" appended to it.
144- */
145-static int
146-version_number(const char *base, const char *backup, size_t base_length)
147-{
148-	int		version;
149-	const char	*p;
150-
151-	version = 0;
152-	if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
153-		for (p = &backup[base_length]; ISDIGIT(*p); ++p)
154-			version = version * 10 + *p - '0';
155-		if (p[0] != '~' || p[1])
156-			version = 0;
157-	}
158-	return version;
159-}
160-
161-/*
162- * Return the newly-allocated concatenation of STR1 and STR2. If out of
163- * memory, return 0.
164- */
165-static char  *
166-concat(const char *str1, const char *str2)
167-{
168-	char	*newstr;
169-
170-	if (asprintf(&newstr, "%s%s", str1, str2) == -1)
171-		return NULL;
172-	return newstr;
173-}
174-
175-/*
176- * If ARG is an unambiguous match for an element of the null-terminated array
177- * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it
178- * does not match any element or -2 if it is ambiguous (is a prefix of more
179- * than one element).
180- */
181-static int
182-argmatch(const char *arg, const char **optlist)
183-{
184-	int	i;	/* Temporary index in OPTLIST. */
185-	size_t	arglen;	/* Length of ARG. */
186-	int	matchind = -1;	/* Index of first nonexact match. */
187-	int	ambiguous = 0;	/* If nonzero, multiple nonexact match(es). */
188-
189-	arglen = strlen(arg);
190-
191-	/* Test all elements for either exact match or abbreviated matches.  */
192-	for (i = 0; optlist[i]; i++) {
193-		if (!strncmp(optlist[i], arg, arglen)) {
194-			if (strlen(optlist[i]) == arglen)
195-				/* Exact match found.  */
196-				return i;
197-			else if (matchind == -1)
198-				/* First nonexact match found.  */
199-				matchind = i;
200-			else
201-				/* Second nonexact match found.  */
202-				ambiguous = 1;
203-		}
204-	}
205-	if (ambiguous)
206-		return -2;
207-	else
208-		return matchind;
209-}
210-
211-/*
212- * Error reporting for argmatch. KIND is a description of the type of entity
213- * that was being matched. VALUE is the invalid value that was given. PROBLEM
214- * is the return value from argmatch.
215- */
216-static void
217-invalid_arg(const char *kind, const char *value, int problem)
218-{
219-	fprintf(stderr, "patch: ");
220-	if (problem == -1)
221-		fprintf(stderr, "invalid");
222-	else			/* Assume -2. */
223-		fprintf(stderr, "ambiguous");
224-	fprintf(stderr, " %s `%s'\n", kind, value);
225-}
226-
227-static const char *backup_args[] = {
228-	"none", "never", "simple", "nil", "existing", "t", "numbered", 0
229-};
230-
231-static enum backup_type backup_types[] = {
232-	none, simple, simple, numbered_existing,
233-	numbered_existing, numbered, numbered
234-};
235-
236-/*
237- * Return the type of backup indicated by VERSION. Unique abbreviations are
238- * accepted.
239- */
240-enum backup_type
241-get_version(const char *version)
242-{
243-	int	i;
244-
245-	if (version == NULL || *version == '\0')
246-		return numbered_existing;
247-	i = argmatch(version, backup_args);
248-	if (i >= 0)
249-		return backup_types[i];
250-	invalid_arg("version control type", version, i);
251-	exit(2);
252-}
+0, -44
 1@@ -1,44 +0,0 @@
 2-/*
 3- * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $
 4- * $DragonFly: src/usr.bin/patch/backupfile.h,v 1.3 2007/09/29 23:11:10 swildner Exp $
 5- * $NetBSD: backupfile.h,v 1.7 2021/02/19 17:46:53 nia Exp $
 6- */
 7-
 8-/*
 9- * backupfile.h -- declarations for making Emacs style backup file names
10- * Copyright (C) 1990 Free Software Foundation, Inc.
11- * 
12- * This program is free software; you can redistribute it and/or modify it
13- * without restriction.
14- * 
15- * This program is distributed in the hope that it will be useful, but WITHOUT
16- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17- * FITNESS FOR A PARTICULAR PURPOSE.
18- * 
19- */
20-
21-/* When to make backup files. */
22-enum backup_type {
23-	undefined,
24-
25-	/* Never make backups. */
26-	none,
27-
28-	/* Make simple backups of every file. */
29-	simple,
30-
31-	/*
32-	 * Make numbered backups of files that already have numbered backups,
33-	 * and simple backups of the others.
34-	 */
35-	numbered_existing,
36-
37-	/* Make numbered backups of every file. */
38-	numbered
39-};
40-
41-extern enum backup_type backup_type;
42-extern const char	*simple_backup_suffix;
43-
44-char		*find_backup_file_name(const char *file);
45-enum backup_type get_version(const char *version);
+0, -136
  1@@ -1,136 +0,0 @@
  2-/*
  3- * $OpenBSD: common.h,v 1.26 2006/03/11 19:41:30 otto Exp $
  4- * $DragonFly: src/usr.bin/patch/common.h,v 1.5 2008/08/10 23:50:12 joerg Exp $
  5- * $NetBSD: common.h,v 1.22 2021/05/25 11:25:59 cjep Exp $
  6- */
  7-
  8-/*
  9- * patch - a program to apply diffs to original files
 10- * 
 11- * Copyright 1986, Larry Wall
 12- * 
 13- * Redistribution and use in source and binary forms, with or without
 14- * modification, are permitted provided that the following condition is met:
 15- * 1. Redistributions of source code must retain the above copyright notice,
 16- * this condition and the following disclaimer.
 17- * 
 18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 28- * SUCH DAMAGE.
 29- * 
 30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 31- * behaviour
 32- */
 33-
 34-#include <sys/types.h>
 35-
 36-#include <limits.h>
 37-#include <stdbool.h>
 38-#include <stdint.h>
 39-
 40-#include <util.h>
 41-
 42-#ifndef __dead
 43-#define __dead __attribute__((__noreturn__))
 44-#endif
 45-#ifndef __printflike
 46-#define __printflike(a, b) __attribute__((__format__(printf, a, b)))
 47-#endif
 48-#ifndef __UNCONST
 49-#define __UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
 50-#endif
 51-
 52-#define DEBUGGING
 53-
 54-/* constants */
 55-
 56-#define MAXHUNKSIZE 100000	/* is this enough lines? */
 57-#define INITHUNKMAX 125		/* initial dynamic allocation size */
 58-#define INITLINELEN 8192
 59-#define BUFFERSIZE 1024
 60-#define LINENUM_MAX LONG_MAX
 61-
 62-#define SCCSPREFIX "s."
 63-#define GET "get -e %s"
 64-#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
 65-
 66-#define RCSSUFFIX ",v"
 67-#define CHECKOUT "/usr/bin/co"
 68-#define RCSDIFF "/usr/bin/rcsdiff"
 69-
 70-#define ORIGEXT ".orig"
 71-#define REJEXT ".rej"
 72-
 73-/* handy definitions */
 74-
 75-#define strNE(s1,s2) (strcmp(s1, s2))
 76-#define strEQ(s1,s2) (!strcmp(s1, s2))
 77-#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
 78-#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
 79-
 80-/* typedefs */
 81-
 82-typedef long    LINENUM;	/* must be signed */
 83-
 84-/* globals */
 85-
 86-extern mode_t	filemode;
 87-
 88-extern char	*buf;		/* general purpose buffer */
 89-extern size_t	bufsz;		/* general purpose buffer size */
 90-
 91-extern bool	using_plan_a;	/* try to keep everything in memory */
 92-extern bool	out_of_mem;	/* ran out of memory in plan a */
 93-
 94-#define MAXFILEC 2
 95-
 96-extern char	*filearg[MAXFILEC];
 97-extern bool	ok_to_create_file;
 98-extern char	*outname;
 99-extern char	*origprae;
100-
101-extern char	*TMPOUTNAME;
102-extern char	*TMPINNAME;
103-extern char	*TMPREJNAME;
104-extern char	*TMPPATNAME;
105-extern bool	toutkeep;
106-extern bool	trejkeep;
107-
108-#ifdef DEBUGGING
109-extern int	debug;
110-#endif
111-
112-extern bool	force;
113-extern bool	batch;
114-extern bool	verbose;
115-extern bool	reverse;
116-extern bool	noreverse;
117-extern bool	skip_rest_of_patch;
118-extern int	strippath;
119-extern bool	canonicalize;
120-/* TRUE if -C was specified on command line.  */
121-extern bool	check_only;
122-extern bool	warn_on_invalid_line;
123-extern bool	last_line_missing_eol;
124-
125-
126-#define CONTEXT_DIFF 1
127-#define NORMAL_DIFF 2
128-#define ED_DIFF 3
129-#define NEW_CONTEXT_DIFF 4
130-#define UNI_DIFF 5
131-
132-extern int	diff_type;
133-extern char	*revision;	/* prerequisite revision, if any */
134-extern LINENUM	input_lines;	/* how long is input file in lines */
135-
136-extern int	posix;
137-
+0, -519
  1@@ -1,519 +0,0 @@
  2-/*
  3- * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $
  4- * $DragonFly: src/usr.bin/patch/inp.c,v 1.6 2007/09/29 23:11:10 swildner Exp $
  5- * $NetBSD: inp.c,v 1.30 2023/06/16 23:36:26 wiz Exp $
  6- */
  7-
  8-/*
  9- * patch - a program to apply diffs to original files
 10- *
 11- * Copyright 1986, Larry Wall
 12- *
 13- * Redistribution and use in source and binary forms, with or without
 14- * modification, are permitted provided that the following condition is met:
 15- * 1. Redistributions of source code must retain the above copyright notice,
 16- * this condition and the following disclaimer.
 17- *
 18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 28- * SUCH DAMAGE.
 29- *
 30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 31- * behaviour
 32- */
 33-
 34-#include <sys/types.h>
 35-#include <sys/file.h>
 36-#include <sys/stat.h>
 37-#include <sys/mman.h>
 38-#include <sys/wait.h>
 39-
 40-#include <ctype.h>
 41-#include <errno.h>
 42-#include <fcntl.h>
 43-#include <libgen.h>
 44-#include <limits.h>
 45-#include <stddef.h>
 46-#include <stdio.h>
 47-#include <stdlib.h>
 48-#include <string.h>
 49-#include <unistd.h>
 50-
 51-#include "common.h"
 52-#include "util.h"
 53-#include "pch.h"
 54-#include "inp.h"
 55-
 56-
 57-/* Input-file-with-indexable-lines abstract type */
 58-
 59-static off_t	i_size;		/* size of the input file */
 60-static char	*i_womp;	/* plan a buffer for entire file */
 61-static char	**i_ptr;	/* pointers to lines in i_womp */
 62-static char	empty_line[] = { '\0' };
 63-
 64-static int	tifd = -1;	/* plan b virtual string array */
 65-static char	*tibuf[2];	/* plan b buffers */
 66-static LINENUM	tiline[2] = {-1, -1};	/* 1st line in each buffer */
 67-static LINENUM	lines_per_buf;	/* how many lines per buffer */
 68-static int	tireclen;	/* length of records in tmp file */
 69-
 70-static bool	rev_in_string(const char *);
 71-static bool	reallocate_lines(size_t *);
 72-
 73-/* returns false if insufficient memory */
 74-static bool	plan_a(const char *);
 75-
 76-static void	plan_b(const char *);
 77-
 78-/* New patch--prepare to edit another file. */
 79-
 80-void
 81-re_input(void)
 82-{
 83-	if (using_plan_a) {
 84-		i_size = 0;
 85-		free(i_ptr);
 86-		i_ptr = NULL;
 87-		if (i_womp != NULL) {
 88-			munmap(i_womp, i_size);
 89-			i_womp = NULL;
 90-		}
 91-	} else {
 92-		using_plan_a = true;	/* maybe the next one is smaller */
 93-		close(tifd);
 94-		tifd = -1;
 95-		free(tibuf[0]);
 96-		free(tibuf[1]);
 97-		tibuf[0] = tibuf[1] = NULL;
 98-		tiline[0] = tiline[1] = -1;
 99-		tireclen = 0;
100-	}
101-}
102-
103-/* Construct the line index, somehow or other. */
104-
105-void
106-scan_input(const char *filename)
107-{
108-	if (!plan_a(filename))
109-		plan_b(filename);
110-	if (verbose) {
111-		say("Patching file %s using Plan %s...\n", filename,
112-		    (using_plan_a ? "A" : "B"));
113-	}
114-}
115-
116-static bool
117-reallocate_lines(size_t *lines_allocated)
118-{
119-	char	**p;
120-	size_t	new_size;
121-
122-	new_size = *lines_allocated * 3 / 2;
123-	p = pch_realloc(i_ptr, new_size + 2,  sizeof(char *));
124-	if (p == NULL) {	/* shucks, it was a near thing */
125-		munmap(i_womp, i_size);
126-		i_womp = NULL;
127-		free(i_ptr);
128-		i_ptr = NULL;
129-		*lines_allocated = 0;
130-		return false;
131-	}
132-	*lines_allocated = new_size;
133-	i_ptr = p;
134-	return true;
135-}
136-
137-/* Try keeping everything in memory. */
138-
139-static bool
140-plan_a(const char *filename)
141-{
142-	int		ifd, statfailed, devnull, pstat;
143-	char		*p, *s, *lbuf;
144-	struct stat	filestat;
145-	off_t		i;
146-	ptrdiff_t	sz;
147-	size_t		iline, lines_allocated, lbufsz;
148-	pid_t		pid;
149-	char		*argp[4] = {NULL};
150-
151-#ifdef DEBUGGING
152-	if (debug & 8)
153-		return false;
154-#endif
155-
156-	if (filename == NULL || *filename == '\0')
157-		return false;
158-
159-	statfailed = stat(filename, &filestat);
160-	if (statfailed && ok_to_create_file) {
161-		if (verbose)
162-			say("(Creating file %s...)\n", filename);
163-
164-		/*
165-		 * in check_patch case, we still display `Creating file' even
166-		 * though we're not. The rule is that -C should be as similar
167-		 * to normal patch behavior as possible
168-		 */
169-		if (check_only)
170-			return true;
171-		makedirs(filename, true);
172-		close(creat(filename, 0666));
173-		statfailed = stat(filename, &filestat);
174-	}
175-	if (statfailed && check_only)
176-		fatal("%s not found, -C mode, can't probe further\n", filename);
177-	/* For nonexistent or read-only files, look for RCS versions.  */
178-	if (statfailed ||
179-	    /* No one can write to it.  */
180-	    (filestat.st_mode & 0222) == 0 ||
181-	    /* I can't write to it.  */
182-	    ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
183-		char	*filebase, *filedir;
184-		struct stat	cstat;
185-		char *tmp_filename1, *tmp_filename2;
186-
187-		tmp_filename1 = strdup(filename);
188-		tmp_filename2 = strdup(filename);
189-		if (tmp_filename1 == NULL || tmp_filename2 == NULL)
190-			fatal("strdupping filename");
191-
192- 		filebase = basename(tmp_filename1);
193- 		filedir = dirname(tmp_filename2);
194-
195-		lbufsz = INITLINELEN;
196-		if ((lbuf = malloc(bufsz)) == NULL)
197-			pfatal("allocating line buffer");
198-		lbuf[0] = '\0';
199-
200-#define try(f, a1, a2, a3) \
201-	(snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
202-
203-		/*
204-		 * else we can't write to it but it's not under a version
205-		 * control system, so just proceed.
206-		 */
207-		if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
208-		    try("%s/RCS/%s%s", filedir, filebase, "") ||
209-		    try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
210-			if (!statfailed) {
211-				if ((filestat.st_mode & 0222) != 0)
212-					/* The owner can write to it.  */
213-					fatal("file %s seems to be locked "
214-					    "by somebody else under RCS\n",
215-					    filename);
216-				/*
217-				 * It might be checked out unlocked.  See if
218-				 * it's safe to check out the default version
219-				 * locked.
220-				 */
221-				if (verbose)
222-					say("Comparing file %s to default "
223-					    "RCS version...\n", filename);
224-
225-				switch (pid = fork()) {
226-				case -1:
227-					fatal("can't fork: %s\n",
228-					    strerror(errno));
229-				case 0:
230-					devnull = open("/dev/null", O_RDONLY);
231-					if (devnull == -1) {
232-						fatal("can't open /dev/null: %s",
233-						    strerror(errno));
234-					}
235-					(void)dup2(devnull, STDOUT_FILENO);
236-					argp[0] = __UNCONST(RCSDIFF);
237-					argp[1] = __UNCONST(filename);
238-					execv(RCSDIFF, argp);
239-					exit(127);
240-				}
241-				pid = waitpid(pid, &pstat, 0);
242-				if (pid == -1 || WEXITSTATUS(pstat) != 0) {
243-					fatal("can't check out file %s: "
244-					    "differs from default RCS version\n",
245-					    filename);
246-				}
247-			}
248-
249-			if (verbose)
250-				say("Checking out file %s from RCS...\n",
251-				    filename);
252-
253-			switch (pid = fork()) {
254-			case -1:
255-				fatal("can't fork: %s\n", strerror(errno));
256-			case 0:
257-				argp[0] = __UNCONST(CHECKOUT);
258-				argp[1] = __UNCONST("-l");
259-				argp[2] = __UNCONST(filename);
260-				execv(CHECKOUT, argp);
261-				exit(127);
262-			}
263-			pid = waitpid(pid, &pstat, 0);
264-			if (pid == -1 || WEXITSTATUS(pstat) != 0 ||
265-			    stat(filename, &filestat)) {
266-				fatal("can't check out file %s from RCS\n",
267-				    filename);
268-			}
269-		} else if (statfailed) {
270-			fatal("can't find %s\n", filename);
271-		}
272-		free(lbuf);
273-		free(tmp_filename1);
274-		free(tmp_filename2);
275-	}
276-
277-	filemode = filestat.st_mode;
278-	if (!S_ISREG(filemode))
279-		fatal("%s is not a normal file--can't patch\n", filename);
280-	i_size = filestat.st_size;
281-	if (out_of_mem) {
282-		set_hunkmax();	/* make sure dynamic arrays are allocated */
283-		out_of_mem = false;
284-		return false;	/* force plan b because plan a bombed */
285-	}
286-	if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
287-		say("block too large to mmap\n");
288-		return false;
289-	}
290-	if ((ifd = open(filename, O_RDONLY)) < 0)
291-		pfatal("can't open file %s", filename);
292-
293-	if (i_size) {
294-		i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
295-		if (i_womp == MAP_FAILED) {
296-			perror("mmap failed");
297-			i_womp = NULL;
298-			close(ifd);
299-			return false;
300-		}
301-	} else {
302-		i_womp = NULL;
303-	}
304-
305-	close(ifd);
306-	if (i_size)
307-		madvise(i_womp, i_size, MADV_SEQUENTIAL);
308-
309-	/* estimate the number of lines */
310-	lines_allocated = i_size / 25;
311-	if (lines_allocated < 100)
312-		lines_allocated = 100;
313-
314-	if (!reallocate_lines(&lines_allocated))
315-		return false;
316-
317-	/* now scan the buffer and build pointer array */
318-	iline = 1;
319-	i_ptr[iline] = i_womp;
320-	/* test for NUL too, to maintain the behavior of the original code */
321-	for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
322-		if (*s == '\n') {
323-			if (iline == lines_allocated) {
324-				if (!reallocate_lines(&lines_allocated))
325-					return false;
326-			}
327-			/* these are NOT NUL terminated */
328-			i_ptr[++iline] = s + 1;
329-		}
330-	}
331-	/* if the last line contains no EOL, append one */
332-	if (i_size > 0 && i_womp[i_size - 1] != '\n') {
333-		last_line_missing_eol = true;
334-		/* fix last line */
335-		sz = s - i_ptr[iline];
336-		p = malloc(sz + 1);
337-		if (p == NULL) {
338-			free(i_ptr);
339-			i_ptr = NULL;
340-			munmap(i_womp, i_size);
341-			i_womp = NULL;
342-			return false;
343-		}
344-
345-		memcpy(p, i_ptr[iline], sz);
346-		p[sz] = '\n';
347-		i_ptr[iline] = p;
348-		/* count the extra line and make it point to some valid mem */
349-		i_ptr[++iline] = empty_line;
350-	} else
351-		last_line_missing_eol = false;
352-
353-	input_lines = iline - 1;
354-
355-	/* now check for revision, if any */
356-
357-	if (revision != NULL) {
358-		if (!rev_in_string(i_womp)) {
359-			if (force) {
360-				if (verbose)
361-					say("Warning: this file doesn't appear "
362-					    "to be the %s version--patching anyway.\n",
363-					    revision);
364-			} else if (batch) {
365-				fatal("this file doesn't appear to be the "
366-				    "%s version--aborting.\n",
367-				    revision);
368-			} else {
369-				ask("This file doesn't appear to be the "
370-				    "%s version--patch anyway? [n] ",
371-				    revision);
372-				if (*buf != 'y')
373-					fatal("aborted\n");
374-			}
375-		} else if (verbose)
376-			say("Good.  This file appears to be the %s version.\n",
377-			    revision);
378-	}
379-	return true;		/* plan a will work */
380-}
381-
382-/* Keep (virtually) nothing in memory. */
383-
384-static void
385-plan_b(const char *filename)
386-{
387-	FILE	*ifp;
388-	size_t	i = 0, j, maxlen = 1;
389-	char	*p;
390-	bool	found_revision = (revision == NULL);
391-
392-	using_plan_a = false;
393-	if ((ifp = fopen(filename, "r")) == NULL)
394-		pfatal("can't open file %s", filename);
395-	unlink(TMPINNAME);
396-	if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
397-		pfatal("can't open file %s", TMPINNAME);
398-	while (getline(&buf, &bufsz, ifp) != -1) {
399-		if (revision != NULL && !found_revision && rev_in_string(buf))
400-			found_revision = true;
401-		if ((i = strlen(buf)) > maxlen)
402-			maxlen = i;	/* find longest line */
403-	}
404-	last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
405-	if (last_line_missing_eol && maxlen == i)
406-		maxlen++;
407-
408-	if (revision != NULL) {
409-		if (!found_revision) {
410-			if (force) {
411-				if (verbose)
412-					say("Warning: this file doesn't appear "
413-					    "to be the %s version--patching anyway.\n",
414-					    revision);
415-			} else if (batch) {
416-				fatal("this file doesn't appear to be the "
417-				    "%s version--aborting.\n",
418-				    revision);
419-			} else {
420-				ask("This file doesn't appear to be the %s "
421-				    "version--patch anyway? [n] ",
422-				    revision);
423-				if (*buf != 'y')
424-					fatal("aborted\n");
425-			}
426-		} else if (verbose)
427-			say("Good.  This file appears to be the %s version.\n",
428-			    revision);
429-	}
430-	fseek(ifp, 0L, SEEK_SET);	/* rewind file */
431-	lines_per_buf = BUFFERSIZE / maxlen;
432-	tireclen = maxlen;
433-	tibuf[0] = malloc(BUFFERSIZE + 1);
434-	if (tibuf[0] == NULL)
435-		fatal("out of memory\n");
436-	tibuf[1] = malloc(BUFFERSIZE + 1);
437-	if (tibuf[1] == NULL)
438-		fatal("out of memory\n");
439-	for (i = 1;; i++) {
440-		p = tibuf[0] + maxlen * (i % lines_per_buf);
441-		if (i % lines_per_buf == 0)	/* new block */
442-			if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
443-				pfatal("can't write temp file");
444-		if (fgets(p, maxlen + 1, ifp) == NULL) {
445-			input_lines = i - 1;
446-			if (i % lines_per_buf != 0)
447-				if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
448-					pfatal("can't write temp file");
449-			break;
450-		}
451-		j = strlen(p);
452-		/* These are '\n' terminated strings, so no need to add a NUL */
453-		if (j == 0 || p[j - 1] != '\n')
454-			p[j] = '\n';
455-	}
456-	fclose(ifp);
457-	close(tifd);
458-	if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
459-		pfatal("can't reopen file %s", TMPINNAME);
460-}
461-
462-/*
463- * Fetch a line from the input file, \n terminated, not necessarily \0.
464- */
465-char *
466-ifetch(LINENUM line, int whichbuf)
467-{
468-	if (line < 1 || line > input_lines) {
469-		if (warn_on_invalid_line) {
470-			say("No such line %ld in input file, ignoring\n", line);
471-			warn_on_invalid_line = false;
472-		}
473-		return NULL;
474-	}
475-	if (using_plan_a)
476-		return i_ptr[line];
477-	else {
478-		LINENUM	offline = line % lines_per_buf;
479-		LINENUM	baseline = line - offline;
480-
481-		if (tiline[0] == baseline)
482-			whichbuf = 0;
483-		else if (tiline[1] == baseline)
484-			whichbuf = 1;
485-		else {
486-			tiline[whichbuf] = baseline;
487-
488-			if (lseek(tifd, (off_t) (baseline / lines_per_buf *
489-			    BUFFERSIZE), SEEK_SET) < 0)
490-				pfatal("cannot seek in the temporary input file");
491-
492-			if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
493-				pfatal("error reading tmp file %s", TMPINNAME);
494-		}
495-		return tibuf[whichbuf] + (tireclen * offline);
496-	}
497-}
498-
499-/*
500- * True if the string argument contains the revision number we want.
501- */
502-static bool
503-rev_in_string(const char *string)
504-{
505-	const char	*s;
506-	size_t		patlen;
507-
508-	if (revision == NULL)
509-		return true;
510-	patlen = strlen(revision);
511-	if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
512-		return true;
513-	for (s = string; *s; s++) {
514-		if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
515-		    isspace((unsigned char)s[patlen + 1])) {
516-			return true;
517-		}
518-	}
519-	return false;
520-}
+0, -35
 1@@ -1,35 +0,0 @@
 2-/*
 3- * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $
 4- * $DragonFly: src/usr.bin/patch/inp.h,v 1.1 2004/09/24 18:44:28 joerg Exp $
 5- * $NetBSD: inp.h,v 1.10 2008/09/19 18:33:34 joerg Exp $
 6- */
 7-
 8-/*
 9- * patch - a program to apply diffs to original files
10- * 
11- * Copyright 1986, Larry Wall
12- * 
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- * 
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- * 
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-void		re_input(void);
35-void		scan_input(const char *);
36-char		*ifetch(LINENUM, int);
+0, -81
 1@@ -1,81 +0,0 @@
 2-/*
 3- *	$OpenBSD: mkpath.c,v 1.2 2005/06/20 07:14:06 otto Exp $
 4- *	$DragonFly: src/usr.bin/patch/mkpath.c,v 1.1 2007/09/29 23:11:10 swildner Exp $
 5- *	$NetBSD: mkpath.c,v 1.1 2008/09/19 18:33:34 joerg Exp $
 6- */
 7-
 8-/*
 9- * Copyright (c) 1983, 1992, 1993
10- *	The Regents of the University of California.  All rights reserved.
11- *
12- * Redistribution and use in source and binary forms, with or without
13- * modification, are permitted provided that the following conditions
14- * are met:
15- * 1. Redistributions of source code must retain the above copyright
16- *    notice, this list of conditions and the following disclaimer.
17- * 2. Redistributions in binary form must reproduce the above copyright
18- *    notice, this list of conditions and the following disclaimer in the
19- *    documentation and/or other materials provided with the distribution.
20- * 3. Neither the name of the University nor the names of its contributors
21- *    may be used to endorse or promote products derived from this software
22- *    without specific prior written permission.
23- *
24- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34- * SUCH DAMAGE.
35- */
36-
37-#include <sys/types.h>
38-#include <sys/stat.h>
39-#include <err.h>
40-#include <errno.h>
41-#include <string.h>
42-
43-int	mkpath(char *);
44-
45-/* Code taken directly from mkdir(1).
46-
47- * mkpath -- create directories.
48- *	path     - path
49- */
50-int
51-mkpath(char *path)
52-{
53-	struct stat sb;
54-	char *slash;
55-	int done = 0;
56-
57-	slash = path;
58-
59-	while (!done) {
60-		slash += strspn(slash, "/");
61-		slash += strcspn(slash, "/");
62-
63-		done = (*slash == '\0');
64-		*slash = '\0';
65-
66-		if (stat(path, &sb)) {
67-			if (errno != ENOENT || (mkdir(path, 0777) &&
68-			    errno != EEXIST)) {
69-				warn("%s", path);
70-				return (-1);
71-			}
72-		} else if (!S_ISDIR(sb.st_mode)) {
73-			warnx("%s: %s", path, strerror(ENOTDIR));
74-			return (-1);
75-		}
76-
77-		*slash = '/';
78-	}
79-
80-	return (0);
81-}
82-
+0, -1338
   1@@ -1,1338 +0,0 @@
   2-/*
   3- * $OpenBSD: patch.c,v 1.45 2007/04/18 21:52:24 sobrado Exp $
   4- * $DragonFly: src/usr.bin/patch/patch.c,v 1.10 2008/08/10 23:39:56 joerg Exp $
   5- * $NetBSD: patch.c,v 1.35 2024/07/12 15:48:39 manu Exp $
   6- */
   7-
   8-/*
   9- * patch - a program to apply diffs to original files
  10- * 
  11- * Copyright 1986, Larry Wall
  12- * 
  13- * Redistribution and use in source and binary forms, with or without
  14- * modification, are permitted provided that the following condition is met:
  15- * 1. Redistributions of source code must retain the above copyright notice,
  16- * this condition and the following disclaimer.
  17- * 
  18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28- * SUCH DAMAGE.
  29- * 
  30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
  31- * behaviour
  32- */
  33-
  34-/* ?man patch: apply a diff file to an original
  35-synopsis: [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count] [-r rej-name] [-V t | nil | never | none] [-x number] [-z backup-ext] [--backup-if-mismatch] [--no-backup-if-mismatch] [--posix] [origfile [patchfile]]
  36-patch will take a patch file containing any of the four forms of difference listing produced by the `diff(1)` program and apply those differences to an original file, producing a patched version. If `patchfile` is omitted, or is a hyphen, the patch will be read from the standard input.
  37-
  38-patch will attempt to determine the type of the diff listing, unless over-ruled by a `-c`, `-e`, `-n`, or `-u` option. Context diffs (old-style, new-style, and unified) and normal diffs are applied directly by the patch program itself, whereas ed diffs are simply fed to the `ed(1)` editor via a pipe.
  39-
  40-If the `patchfile` contains more than one patch, patch will try to apply each of them as if they came from separate patch files. This means, among other things, that it is assumed that the name of the file to patch must be determined for each diff listing, and that the garbage before each diff listing will be examined for interesting things such as file names and revision level (see the section on Filename Determination below).
  41-
  42-## OPTIONS
  43-### -B backup-prefix, --prefix backup-prefix
  44-Causes the next argument to be interpreted as a prefix to the backup file name. If this argument is specified, any argument to `-z` will be ignored.
  45-
  46-### -b, --backup
  47-Save a backup copy of the file before it is modified. By default the original file is saved with a backup extension of `.orig` unless the file already has a numbered backup, in which case a numbered backup is made. This is equivalent to specifying `-V existing`. This option is currently the default, unless `--posix` is specified.
  48-
  49-### --backup-if-mismatch
  50-Create a backup file if the patch doesn't apply cleanly. This option only makes sense when `--backup` is disabled, i.e. when in `--posix` mode.
  51-
  52-### -C, --check
  53-Checks that the patch would apply cleanly, but does not modify anything.
  54-
  55-### -c, --context
  56-Forces patch to interpret the patch file as a context diff.
  57-
  58-### -D symbol, --ifdef symbol
  59-Causes patch to use the `#ifdef...#endif` construct to mark changes. The argument following will be used as the differentiating symbol. Note that, unlike the C compiler, there must be a space between the `-D` and the argument.
  60-
  61-### -d directory, --directory directory
  62-Causes patch to interpret the next argument as a directory, and change the working directory to it before doing anything else.
  63-
  64-### -E, --remove-empty-files
  65-Causes patch to remove output files that are empty after the patches have been applied. This option is useful when applying patches that create or remove files.
  66-
  67-### -e, --ed
  68-Forces patch to interpret the patch file as an `ed(1)` script.
  69-
  70-### -F max-fuzz, --fuzz max-fuzz
  71-Sets the maximum fuzz factor. This option only applies to context diffs, and causes patch to ignore up to that many lines in looking for places to install a hunk. Note that a larger fuzz factor increases the odds of a faulty patch. The default fuzz factor is 2, and it may not be set to more than the number of lines of context in the context diff, ordinarily 3.
  72-
  73-### -f, --force
  74-Forces patch to assume that the user knows exactly what he or she is doing, and to not ask any questions. It assumes the following: skip patches for which a file to patch can't be found; patch files even though they have the wrong version for the `Prereq:` line in the patch; and assume that patches are not reversed even if they look like they are. This option does not suppress commentary; use `-s` for that.
  75-
  76-### -i patchfile, --input patchfile
  77-Causes the next argument to be interpreted as the input file name (i.e., a patchfile). This option may be specified multiple times.
  78-
  79-### -l, --ignore-whitespace
  80-Causes the pattern matching to be done loosely, in case the tabs and spaces have been munged in your input file. Any sequence of whitespace in the pattern line will match any sequence in the input file. Normal characters must still match exactly. Each line of the context must still match a line in the input file.
  81-
  82-### -N, --forward
  83-Causes patch to ignore patches that it thinks are reversed or already applied. See also `-R`.
  84-
  85-### -n, --normal
  86-Forces patch to interpret the patch file as a normal diff.
  87-
  88-### --no-backup-if-mismatch
  89-Turn off `--backup-if-mismatch`. This option exists mostly for compatibility with GNU patch.
  90-
  91-### -o out-file, --output out-file
  92-Causes the next argument to be interpreted as the output file name.
  93-
  94-### -p strip-count, --strip strip-count
  95-Sets the pathname strip count, which controls how pathnames found in the patch file are treated, in case you keep your files in a different directory than the person who sent out the patch. The strip count specifies how many slashes are to be stripped from the front of the pathname. (Any intervening directory names also go away.) For example, supposing the file name in the patch file was `/u/howard/src/blurfl/blurfl.c`:
  96-
  97-Setting `-p0` gives the entire pathname unmodified.
  98-
  99-`-p1` gives
 100-
 101-`u/howard/src/blurfl/blurfl.c`
 102-
 103-without the leading slash.
 104-
 105-`-p4` gives
 106-
 107-`blurfl/blurfl.c`
 108-
 109-Not specifying `-p` at all just gives you `blurfl.c`, unless all of the directories in the leading path (`u/howard/src/blurfl`) exist and that path is relative, in which case you get the entire pathname unmodified. Whatever you end up with is looked for either in the current directory, or the directory specified by the `-d` option.
 110-
 111-### -R, --reverse
 112-Tells patch that this patch was created with the old and new files swapped. (Yes, I'm afraid that does happen occasionally, human nature being what it is.) patch will attempt to swap each hunk around before applying it. Rejects will come out in the swapped format. The `-R` option will not work with ed diff scripts because there is too little information to reconstruct the reverse operation.
 113-
 114-If the first hunk of a patch fails, patch will reverse the hunk to see if it can be applied that way. If it can, you will be asked if you want to have the `-R` option set. If it can't, the patch will continue to be applied normally. (Note: this method cannot detect a reversed patch if it is a normal diff and if the first command is an append (i.e., it should have been a delete) since appends always succeed, due to the fact that a null context will match anywhere. Luckily, most patches add or change lines rather than delete them, so most reversed normal diffs will begin with a delete, which will fail, triggering the heuristic.)
 115-
 116-### -r rej-name, --reject-file rej-name
 117-Causes the next argument to be interpreted as the reject file name.
 118-
 119-### -s, --quiet, --silent
 120-Makes patch do its work silently, unless an error occurs.
 121-
 122-### -t, --batch
 123-Similar to `-f`, in that it suppresses questions, but makes some different assumptions: skip patches for which a file to patch can't be found (the same as `-f`); skip patches for which the file has the wrong version for the `Prereq:` line in the patch; and assume that patches are reversed if they look like they are.
 124-
 125-### -u, --unified
 126-Forces patch to interpret the patch file as a unified context diff (a unidiff).
 127-
 128-### -V t | nil | never | none, --version-control t | nil | never | none
 129-Causes the next argument to be interpreted as a method for creating backup file names. The type of backups made can also be given in the `PATCH_VERSION_CONTROL` or `VERSION_CONTROL` environment variables, which are overridden by this option. The `-B` option overrides this option, causing the prefix to always be used for making backup file names. The values of the `PATCH_VERSION_CONTROL` and `VERSION_CONTROL` environment variables and the argument to the `-V` option are like the GNU Emacs "version-control" variable; they also recognize synonyms that are more descriptive. The valid values are (unique abbreviations are accepted):
 130-
 131-`t`, numbered
 132-: Always make numbered backups.
 133-
 134-`nil`, existing
 135-: Make numbered backups of files that already have them, simple backups of the others.
 136-
 137-`never`, simple
 138-: Always make simple backups.
 139-
 140-`none`
 141-: No backups are created.
 142-
 143-### -v, --version
 144-Causes patch to print out its revision header and patch level.
 145-
 146-### -x number, --debug number
 147-Sets internal debugging flags, and is of interest only to patchers.
 148-
 149-### -z backup-ext, --suffix backup-ext
 150-Causes the next argument to be interpreted as the backup extension, to be used in place of `.orig`.
 151-
 152-### --posix
 153-Enables strict POSIX.1-2004 conformance, specifically:
 154-
 155-Backup files are not created unless the `-b` option is specified.
 156-
 157-If unspecified, the file name used is the first of the old, new and index files that exists.
 158-
 159-## Patch Application
 160-patch will try to skip any leading garbage, apply the diff, and then skip any trailing garbage. Thus you could feed an article or message containing a diff listing to patch, and it should work. If the entire diff is indented by a consistent amount, this will be taken into account.
 161-
 162-With context diffs, and to a lesser extent with normal diffs, patch can detect when the line numbers mentioned in the patch are incorrect, and will attempt to find the correct place to apply each hunk of the patch. As a first guess, it takes the line number mentioned for the hunk, plus or minus any offset used in applying the previous hunk. If that is not the correct place, patch will scan both forwards and backwards for a set of lines matching the context given in the hunk. First patch looks for a place where all lines of the context match. If no such place is found, and it's a context diff, and the maximum fuzz factor is set to 1 or more, then another scan takes place ignoring the first and last line of context. If that fails, and the maximum fuzz factor is set to 2 or more, the first two and last two lines of context are ignored, and another scan is made. (The default maximum fuzz factor is 2.)
 163-
 164-If patch cannot find a place to install that hunk of the patch, it will put the hunk out to a reject file, which normally is the name of the output file plus `.rej`. (Note that the rejected hunk will come out in context diff form whether the input patch was a context diff or a normal diff. If the input was a normal diff, many of the contexts will simply be null.) The line numbers on the hunks in the reject file may be different than in the patch file: they reflect the approximate location patch thinks the failed hunks belong in the new file rather than the old one.
 165-
 166-As each hunk is completed, you will be told whether the hunk succeeded or failed, and which line (in the new file) patch thought the hunk should go on. If this is different from the line number specified in the diff, you will be told the offset. A single large offset MAY be an indication that a hunk was installed in the wrong place. You will also be told if a fuzz factor was used to make the match, in which case you should also be slightly suspicious.
 167-
 168-## Filename Determination
 169-If no original file is specified on the command line, patch will try to figure out from the leading garbage what the name of the file to edit is. When checking a prospective file name, pathname components are stripped as specified by the `-p` option and the file's existence and writability are checked relative to the current working directory (or the directory specified by the `-d` option).
 170-
 171-If the diff is a context or unified diff, patch is able to determine the old and new file names from the diff header. For context diffs, the "old" file is specified in the line beginning with "***" and the "new" file is specified in the line beginning with "---". For a unified diff, the "old" file is specified in the line beginning with "---" and the "new" file is specified in the line beginning with "+++". If there is an `Index:` line in the leading garbage (regardless of the diff type), patch will use the file name from that line as the "index" file.
 172-
 173-patch will choose the file name by performing the following steps, with the first match used:
 174-
 175-If patch is operating in strict POSIX.1-2004 mode, the first of the "old", "new" and "index" file names that exist is used. Otherwise, patch will examine either the "old" and "new" file names or, for a non-context diff, the "index" file name, and choose the file name with the fewest path components, the shortest basename, and the shortest total file name length (in that order).
 176-
 177-If no file exists, patch checks for the existence of the files in an RCS directory using the criteria specified above. If found, patch will attempt to get or check out the file.
 178-
 179-If no suitable file was found to patch, the patch file is a context or unified diff, and the old file was zero length, the new file name is created and used.
 180-
 181-If the file name still cannot be determined, patch will prompt the user for the file name to use.
 182-
 183-Additionally, if the leading garbage contains a `Prereq:` line, patch will take the first word from the prerequisites line (normally a version number) and check the input file to see if that word can be found. If not, patch will ask for confirmation before proceeding.
 184-
 185-The upshot of all this is that you should be able to say, while in a news interface, the following:
 186-
 187-`| patch -d /usr/src/local/blurfl`
 188-
 189-and patch a file in the blurfl directory directly from the article containing the patch.
 190-
 191-## Backup Files
 192-By default, the patched version is put in place of the original, with the original file backed up to the same name with the extension `.orig`, or as specified by the `-B`, `-V`, or `-z` options. The extension used for making backup files may also be specified in the `SIMPLE_BACKUP_SUFFIX` environment variable, which is overridden by the options above.
 193-
 194-If the backup file is a symbolic or hard link to the original file, patch creates a new backup file name by changing the first lowercase letter in the last component of the file's name into uppercase. If there are no more lowercase letters in the name, it removes the first character from the name. It repeats this process until it comes up with a backup file that does not already exist or is not linked to the original file.
 195-
 196-You may also specify where you want the output to go with the `-o` option; if that file already exists, it is backed up first.
 197-
 198-## Notes For Patch Senders
 199-There are several things you should bear in mind if you are going to be sending out patches:
 200-
 201-First, you can save people a lot of grief by keeping a `patchlevel.h` file which is patched to increment the patch level as the first diff in the patch file you send out. If you put a `Prereq:` line in with the patch, it won't let them apply patches out of order without some warning.
 202-
 203-Second, make sure you've specified the file names right, either in a context diff header, or with an `Index:` line. If you are patching something in a subdirectory, be sure to tell the patch user to specify a `-p` option as needed.
 204-
 205-Third, you can create a file by sending out a diff that compares a null file to the file you want to create. This will only work if the file you want to create doesn't exist already in the target directory.
 206-
 207-Fourth, take care not to send out reversed patches, since it makes people wonder whether they already applied the patch.
 208-
 209-Fifth, while you may be able to get away with putting 582 diff listings into one file, it is probably wiser to group related patches into separate files in case something goes haywire.
 210-
 211-## ENVIRONMENT
 212-`POSIXLY_CORRECT`
 213-: When set, patch behaves as if the `--posix` option has been specified.
 214-
 215-`SIMPLE_BACKUP_SUFFIX`
 216-: Extension to use for backup file names instead of `.orig`.
 217-
 218-`TMPDIR`
 219-: Directory to put temporary files in; default is `/tmp`.
 220-
 221-`PATCH_VERSION_CONTROL`
 222-: Selects when numbered backup files are made.
 223-
 224-`VERSION_CONTROL`
 225-: Same as `PATCH_VERSION_CONTROL`.
 226-
 227-## FILES
 228-`$TMPDIR/patch*`
 229-: patch temporary files
 230-
 231-`/dev/tty`
 232-: used to read input when patch prompts the user
 233-
 234-## DIAGNOSTICS
 235-Too many to list here, but generally indicative that patch couldn't parse your patch file.
 236-
 237-The message "Hmm..." indicates that there is unprocessed text in the patch file and that patch is attempting to intuit whether there is a patch in that text and, if so, what kind of patch it is.
 238-
 239-The patch utility exits with one of the following values:
 240-
 241-`0`
 242-: Successful completion.
 243-
 244-`1`
 245-: One or more lines were written to a reject file.
 246-
 247-`>1`
 248-: An error occurred.
 249-
 250-When applying a set of patches in a loop it behooves you to check this exit status so you don't apply a later patch to a partially patched file.
 251-
 252-## SEE ALSO
 253-`diff(1)`
 254-
 255-## STANDARDS
 256-The patch utility is compliant with the POSIX.1-2004 specification (except as detailed above for the `--posix` option), though the presence of patch itself is optional.
 257-
 258-The flags `-C`, `-E`, `-f`, `-s`, `-t`, `-u`, `-v`, `-B`, `-F`, `-V`, `-x`, and `-z` and `--posix` are extensions to that specification.
 259-
 260-## AUTHORS
 261-Larry Wall with many other contributors.
 262-
 263-## CAVEATS
 264-patch cannot tell if the line numbers are off in an ed script, and can only detect bad line numbers in a normal diff when it finds a "change" or a "delete" command. A context diff using fuzz factor 3 may have the same problem. Until a suitable interactive interface is added, you should probably do a context diff in these cases to see if the changes made sense. Of course, compiling without errors is a pretty good indication that the patch worked, but not always.
 265-
 266-patch usually produces the correct results, even when it has to do a lot of guessing. However, the results are guaranteed to be correct only when the patch is applied to exactly the same version of the file that the patch was generated from.
 267-
 268-## BUGS
 269-Could be smarter about partial matches, excessively deviant offsets and swapped code, but that would take an extra pass.
 270-
 271-Check patch mode (`-C`) will fail if you try to check several patches in succession that build on each other. The entire patch code would have to be restructured to keep temporary files around so that it can handle this situation.
 272-
 273-If code has been duplicated (for instance with `#ifdef OLDCODE ... #else ... #endif`), patch is incapable of patching both versions, and, if it works at all, will likely patch the wrong one, and tell you that it succeeded to boot.
 274-
 275-If you apply a patch you've already applied, patch will think it is a reversed patch, and offer to un-apply the patch. This could be construed as a feature.
 276-*/
 277-
 278-#include <sys/types.h>
 279-#include <sys/stat.h>
 280-
 281-#include <ctype.h>
 282-#include <getopt.h>
 283-#include <limits.h>
 284-#include <stdio.h>
 285-#include <string.h>
 286-#include <stdlib.h>
 287-#include <unistd.h>
 288-
 289-#include "common.h"
 290-#include "util.h"
 291-#include "pch.h"
 292-#include "inp.h"
 293-#include "backupfile.h"
 294-#include "pathnames.h"
 295-
 296-mode_t		filemode = 0644;
 297-
 298-char		*buf;			/* general purpose buffer */
 299-size_t		bufsz;			/* general purpose buffer size */
 300-
 301-bool		using_plan_a = true;	/* try to keep everything in memory */
 302-bool		out_of_mem = false;	/* ran out of memory in plan a */
 303-
 304-#define MAXFILEC 2
 305-
 306-char		*filearg[MAXFILEC];
 307-bool		ok_to_create_file = false;
 308-char		*outname = NULL;
 309-char		*origprae = NULL;
 310-char		*TMPOUTNAME;
 311-char		*TMPINNAME;
 312-char		*TMPREJNAME;
 313-char		*TMPPATNAME;
 314-bool		toutkeep = false;
 315-bool		trejkeep = false;
 316-bool		warn_on_invalid_line;
 317-bool		last_line_missing_eol;
 318-
 319-#ifdef DEBUGGING
 320-int		debug = 0;
 321-#endif
 322-
 323-bool		force = false;
 324-bool		batch = false;
 325-bool		verbose = true;
 326-bool		reverse = false;
 327-bool		noreverse = false;
 328-bool		skip_rest_of_patch = false;
 329-int		strippath = 957;
 330-bool		canonicalize = false;
 331-bool		check_only = false;
 332-int		diff_type = 0;
 333-char		*revision = NULL;	/* prerequisite revision, if any */
 334-LINENUM		input_lines = 0;	/* how long is input file in lines */
 335-int		posix = 0;		/* strict POSIX mode? */
 336-int		backup_if_mismatch = -1;/* create backup file when patch doesn't apply cleanly */
 337-
 338-static void	reinitialize_almost_everything(void);
 339-static void	get_some_switches(void);
 340-static void	reset_getopt_state(void);
 341-static LINENUM	locate_hunk(LINENUM);
 342-static void	abort_context_hunk(void);
 343-static void	rej_line(int, LINENUM);
 344-static void	abort_hunk(void);
 345-static void	apply_hunk(LINENUM);
 346-static void	init_output(const char *);
 347-static void	init_reject(const char *);
 348-static void	copy_till(LINENUM, bool);
 349-static bool	spew_output(void);
 350-static void	dump_line(LINENUM, bool);
 351-static bool	patch_match(LINENUM, LINENUM, LINENUM);
 352-static bool	similar(const char *, const char *, ssize_t);
 353-__dead static void	usage(void);
 354-
 355-/* true if -E was specified on command line.  */
 356-static bool	remove_empty_files = false;
 357-
 358-/* true if -R was specified on command line.  */
 359-static bool	reverse_flag_specified = false;
 360-
 361-/* buffer holding the name of the rejected patch file. */
 362-static char	rejname[PATH_MAX];
 363-
 364-/* buffer for stderr */
 365-static char	serrbuf[BUFSIZ];
 366-
 367-/* how many input lines have been irretractibly output */
 368-static LINENUM	last_frozen_line = 0;
 369-
 370-static int	Argc;		/* guess */
 371-static char	**Argv;
 372-static int	Argc_last;	/* for restarting plan_b */
 373-static char	**Argv_last;
 374-
 375-static FILE	*ofp = NULL;	/* output file pointer */
 376-static FILE	*rejfp = NULL;	/* reject file pointer */
 377-
 378-static int	filec = 0;	/* how many file arguments? */
 379-static LINENUM	last_offset = 0;
 380-static LINENUM	maxfuzz = 2;
 381-
 382-/* patch using ifdef, ifndef, etc. */
 383-static bool		do_defines = false;
 384-/* #ifdef xyzzy */
 385-static char		if_defined[128];
 386-/* #ifndef xyzzy */
 387-static char		not_defined[128];
 388-/* #else */
 389-static const char	else_defined[] = "#else\n";
 390-/* #endif xyzzy */
 391-static char		end_defined[128];
 392-
 393-
 394-/* Apply a set of diffs as appropriate. */
 395-
 396-int
 397-main(int argc, char *argv[])
 398-{
 399-	int	error = 0, hunk, failed, i, fd;
 400-	LINENUM	where = 0, newwhere, fuzz, mymaxfuzz;
 401-	const	char *tmpdir;
 402-	char	*v;
 403-
 404-	bufsz = INITLINELEN;
 405-	if ((buf = malloc(bufsz)) == NULL)
 406-		pfatal("allocating input buffer");
 407-	buf[0] = '\0';
 408-
 409-	setbuf(stderr, serrbuf);
 410-	for (i = 0; i < MAXFILEC; i++)
 411-		filearg[i] = NULL;
 412-
 413-	/* Cons up the names of the temporary files.  */
 414-	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
 415-		tmpdir = _PATH_TMP;
 416-	for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
 417-		;
 418-	i++;
 419-	if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
 420-		fatal("cannot allocate memory");
 421-	if ((fd = mkstemp(TMPOUTNAME)) < 0)
 422-		pfatal("can't create %s", TMPOUTNAME);
 423-	close(fd);
 424-
 425-	if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
 426-		fatal("cannot allocate memory");
 427-	if ((fd = mkstemp(TMPINNAME)) < 0)
 428-		pfatal("can't create %s", TMPINNAME);
 429-	close(fd);
 430-
 431-	if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
 432-		fatal("cannot allocate memory");
 433-	if ((fd = mkstemp(TMPREJNAME)) < 0)
 434-		pfatal("can't create %s", TMPREJNAME);
 435-	close(fd);
 436-
 437-	if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
 438-		fatal("cannot allocate memory");
 439-	if ((fd = mkstemp(TMPPATNAME)) < 0)
 440-		pfatal("can't create %s", TMPPATNAME);
 441-	close(fd);
 442-
 443-	v = getenv("SIMPLE_BACKUP_SUFFIX");
 444-	if (v)
 445-		simple_backup_suffix = v;
 446-	else
 447-		simple_backup_suffix = ORIGEXT;
 448-
 449-	if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
 450-		v = getenv("VERSION_CONTROL");
 451-	if (v != NULL)
 452-		backup_type = get_version(v);
 453-
 454-	/* parse switches */
 455-	Argc = argc;
 456-	Argv = argv;
 457-	get_some_switches();
 458-
 459-	if (backup_type == undefined)
 460-		backup_type = posix ? none : numbered_existing;
 461-
 462-	/* make sure we clean up /tmp in case of disaster */
 463-	set_signals(0);
 464-
 465-	for (open_patch_file(filearg[1]); there_is_another_patch();
 466-	    reinitialize_almost_everything()) {
 467-		/* for each patch in patch file */
 468-
 469-		warn_on_invalid_line = true;
 470-
 471-		if (outname == NULL)
 472-			outname = savestr(filearg[0]);
 473-
 474-		/* for ed script just up and do it and exit */
 475-		if (diff_type == ED_DIFF) {
 476-			do_ed_script();
 477-			continue;
 478-		}
 479-		/* initialize the patched file */
 480-		if (!skip_rest_of_patch)
 481-			init_output(TMPOUTNAME);
 482-
 483-		/* initialize reject file */
 484-		init_reject(TMPREJNAME);
 485-
 486-		/* find out where all the lines are */
 487-		if (!skip_rest_of_patch)
 488-			scan_input(filearg[0]);
 489-
 490-		/* from here on, open no standard i/o files, because malloc */
 491-		/* might misfire and we can't catch it easily */
 492-
 493-		/* apply each hunk of patch */
 494-		hunk = 0;
 495-		failed = 0;
 496-		out_of_mem = false;
 497-		while (another_hunk()) {
 498-			hunk++;
 499-			fuzz = 0;
 500-			mymaxfuzz = pch_context();
 501-			if (maxfuzz < mymaxfuzz)
 502-				mymaxfuzz = maxfuzz;
 503-			if (!skip_rest_of_patch) {
 504-				do {
 505-					where = locate_hunk(fuzz);
 506-					if (hunk == 1 && where == 0 && !force) {
 507-						/* dwim for reversed patch? */
 508-						if (!pch_swap()) {
 509-							if (fuzz == 0)
 510-								say("Not enough memory to try swapped hunk!  Assuming unswapped.\n");
 511-							continue;
 512-						}
 513-						reverse = !reverse;
 514-						/* try again */
 515-						where = locate_hunk(fuzz);
 516-						if (where == 0) {
 517-							/* didn't find it swapped */
 518-							if (!pch_swap())
 519-								/* put it back to normal */
 520-								fatal("lost hunk on alloc error!\n");
 521-							reverse = !reverse;
 522-						} else if (noreverse) {
 523-							if (!pch_swap())
 524-								/* put it back to normal */
 525-								fatal("lost hunk on alloc error!\n");
 526-							reverse = !reverse;
 527-							say("Ignoring previously applied (or reversed) patch.\n");
 528-							skip_rest_of_patch = true;
 529-						} else if (batch) {
 530-							if (verbose)
 531-								say("%seversed (or %spreviously applied) patch detected!  %s -R.",
 532-								    reverse ? "R" : "Unr",
 533-								    reverse ? "" : "not ",
 534-								    reverse ? "Assuming" : "Ignoring");
 535-						} else {
 536-							ask("%seversed (or %spreviously applied) patch detected!  %s -R? [y] ",
 537-							    reverse ? "R" : "Unr",
 538-							    reverse ? "" : "not ",
 539-							    reverse ? "Assume" : "Ignore");
 540-							if (*buf == 'n') {
 541-								ask("Apply anyway? [n] ");
 542-								if (*buf != 'y')
 543-									skip_rest_of_patch = true;
 544-								where = 0;
 545-								reverse = !reverse;
 546-								if (!pch_swap())
 547-									/* put it back to normal */
 548-									fatal("lost hunk on alloc error!\n");
 549-							}
 550-						}
 551-					}
 552-				} while (!skip_rest_of_patch && where == 0 &&
 553-				    ++fuzz <= mymaxfuzz);
 554-
 555-				if (skip_rest_of_patch) {	/* just got decided */
 556-					if (ferror(ofp) || fclose(ofp)) {
 557-						say("Error writing %s\n",
 558-						    TMPOUTNAME);
 559-						error = 1;
 560-					}
 561-					ofp = NULL;
 562-				}
 563-			}
 564-			newwhere = pch_newfirst() + last_offset;
 565-			if (skip_rest_of_patch) {
 566-				abort_hunk();
 567-				failed++;
 568-				if (verbose)
 569-					say("Hunk #%d ignored at %ld.\n",
 570-					    hunk, newwhere);
 571-			} else if (where == 0) {
 572-				abort_hunk();
 573-				failed++;
 574-				if (verbose)
 575-					say("Hunk #%d failed at %ld.\n",
 576-					    hunk, newwhere);
 577-			} else {
 578-				apply_hunk(where);
 579-				if (verbose) {
 580-					say("Hunk #%d succeeded at %ld",
 581-					    hunk, newwhere);
 582-					if (fuzz != 0)
 583-						say(" with fuzz %ld", fuzz);
 584-					if (last_offset)
 585-						say(" (offset %ld line%s)",
 586-						    last_offset,
 587-						    last_offset == 1L ? "" : "s");
 588-					say(".\n");
 589-				}
 590-			}
 591-		}
 592-
 593-		if (out_of_mem && using_plan_a) {
 594-			Argc = Argc_last;
 595-			Argv = Argv_last;
 596-			say("\n\nRan out of memory using Plan A--trying again...\n\n");
 597-			if (ofp)
 598-				fclose(ofp);
 599-			ofp = NULL;
 600-			if (rejfp)
 601-				fclose(rejfp);
 602-			rejfp = NULL;
 603-			continue;
 604-		}
 605-		if (hunk == 0)
 606-			fatal("Internal error: hunk should not be 0\n");
 607-
 608-		/* finish spewing out the new file */
 609-		if (!skip_rest_of_patch && !spew_output()) {
 610-			say("Can't write %s\n", TMPOUTNAME);
 611-			error = 1;
 612-		}
 613-
 614-		/* and put the output where desired */
 615-		ignore_signals();
 616-		if (!skip_rest_of_patch) {
 617-			struct stat	statbuf;
 618-			char	*realout = outname;
 619-
 620-			if (!check_only) {
 621-				/* handle --backup-if-mismatch */
 622-				enum backup_type saved = backup_type;
 623-				if (failed > 0 && backup_if_mismatch > 0 && backup_type == none)
 624-					backup_type = simple;
 625-				if (move_file(TMPOUTNAME, outname) < 0) {
 626-					toutkeep = true;
 627-					realout = TMPOUTNAME;
 628-					chmod(TMPOUTNAME, filemode);
 629-				} else
 630-					chmod(outname, filemode);
 631-				backup_type = saved;
 632-
 633-				if (remove_empty_files &&
 634-				    stat(realout, &statbuf) == 0 &&
 635-				    statbuf.st_size == 0) {
 636-					if (verbose)
 637-						say("Removing %s (empty after patching).\n",
 638-						    realout);
 639-					unlink(realout);
 640-				}
 641-			}
 642-		}
 643-		if (ferror(rejfp) || fclose(rejfp)) {
 644-			say("Error writing %s\n", rejname);
 645-			error = 1;
 646-		}
 647-		rejfp = NULL;
 648-		if (failed) {
 649-			error = 1;
 650-			if (*rejname == '\0') {
 651-				if (strlcpy(rejname, outname,
 652-				    sizeof(rejname)) >= sizeof(rejname))
 653-					fatal("filename %s is too long\n", outname);
 654-				if (strlcat(rejname, REJEXT,
 655-				    sizeof(rejname)) >= sizeof(rejname))
 656-					fatal("filename %s is too long\n", outname);
 657-			}
 658-			if (skip_rest_of_patch) {
 659-				say("%d out of %d hunks ignored--saving rejects to %s\n",
 660-				    failed, hunk, rejname);
 661-			} else {
 662-				say("%d out of %d hunks FAILED -- saving rejects to %s\n",
 663-				    failed, hunk, rejname);
 664-			}
 665-			if (!check_only && move_file(TMPREJNAME, rejname) < 0)
 666-				trejkeep = true;
 667-		}
 668-		set_signals(1);
 669-	}
 670-	my_exit(error);
 671-	/* NOTREACHED */
 672-}
 673-
 674-static void
 675-reset_getopt_state(void)
 676-{
 677-#if defined(__GLIBC__) || defined(__linux__)
 678-	optind = 0;
 679-#else
 680-	optreset = 1;
 681-	optind = 1;
 682-#endif
 683-}
 684-
 685-/* Prepare to find the next patch to do in the patch file. */
 686-
 687-static void
 688-reinitialize_almost_everything(void)
 689-{
 690-	re_patch();
 691-	re_input();
 692-
 693-	input_lines = 0;
 694-	last_frozen_line = 0;
 695-
 696-	filec = 0;
 697-	if (!out_of_mem) {
 698-		free(filearg[0]);
 699-		filearg[0] = NULL;
 700-	}
 701-
 702-	free(outname);
 703-	outname = NULL;
 704-
 705-	last_offset = 0;
 706-	diff_type = 0;
 707-
 708-	free(revision);
 709-	revision = NULL;
 710-
 711-	reverse = reverse_flag_specified;
 712-	skip_rest_of_patch = false;
 713-
 714-	get_some_switches();
 715-}
 716-
 717-/* Process switches and filenames. */
 718-
 719-static void
 720-get_some_switches(void)
 721-{
 722-	const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
 723-	static struct option longopts[] = {
 724-		{"backup",		no_argument,		0,	'b'},
 725-		{"backup-if-mismatch",	no_argument,		&backup_if_mismatch, 1},
 726-		{"batch",		no_argument,		0,	't'},
 727-		{"check",		no_argument,		0,	'C'},
 728-		{"context",		no_argument,		0,	'c'},
 729-		{"debug",		required_argument,	0,	'x'},
 730-		{"directory",		required_argument,	0,	'd'},
 731-		{"ed",			no_argument,		0,	'e'},
 732-		{"force",		no_argument,		0,	'f'},
 733-		{"forward",		no_argument,		0,	'N'},
 734-		{"fuzz",		required_argument,	0,	'F'},
 735-		{"ifdef",		required_argument,	0,	'D'},
 736-		{"input",		required_argument,	0,	'i'},
 737-		{"ignore-whitespace",	no_argument,		0,	'l'},
 738-		{"no-backup-if-mismatch",	no_argument,	&backup_if_mismatch, 0},
 739-		{"normal",		no_argument,		0,	'n'},
 740-		{"output",		required_argument,	0,	'o'},
 741-		{"prefix",		required_argument,	0,	'B'},
 742-		{"quiet",		no_argument,		0,	's'},
 743-		{"reject-file",		required_argument,	0,	'r'},
 744-		{"remove-empty-files",	no_argument,		0,	'E'},
 745-		{"reverse",		no_argument,		0,	'R'},
 746-		{"silent",		no_argument,		0,	's'},
 747-		{"strip",		required_argument,	0,	'p'},
 748-		{"suffix",		required_argument,	0,	'z'},
 749-		{"unified",		no_argument,		0,	'u'},
 750-		{"version",		no_argument,		0,	'v'},
 751-		{"version-control",	required_argument,	0,	'V'},
 752-		{"posix",		no_argument,		&posix,	1},
 753-		{NULL,			0,			0,	0}
 754-	};
 755-	int ch;
 756-
 757-	rejname[0] = '\0';
 758-	Argc_last = Argc;
 759-	Argv_last = Argv;
 760-	if (!Argc)
 761-		return;
 762-	reset_getopt_state();
 763-	while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
 764-		switch (ch) {
 765-		case 'b':
 766-			if (backup_type == undefined)
 767-				backup_type = numbered_existing;
 768-			if (optarg == NULL)
 769-				break;
 770-			if (verbose)
 771-				say("Warning, the ``-b suffix'' option has been"
 772-				    " obsoleted by the -z option.\n");
 773-			/* FALLTHROUGH */
 774-		case 'z':
 775-			/* must directly follow 'b' case for backwards compat */
 776-			simple_backup_suffix = savestr(optarg);
 777-			break;
 778-		case 'B':
 779-			origprae = savestr(optarg);
 780-			break;
 781-		case 'c':
 782-			diff_type = CONTEXT_DIFF;
 783-			break;
 784-		case 'C':
 785-			check_only = true;
 786-			break;
 787-		case 'd':
 788-			if (chdir(optarg) < 0)
 789-				pfatal("can't cd to %s", optarg);
 790-			break;
 791-		case 'D':
 792-			do_defines = true;
 793-			if (!isalpha((unsigned char)*optarg) && *optarg != '_')
 794-				fatal("argument to -D is not an identifier\n");
 795-			snprintf(if_defined, sizeof if_defined,
 796-			    "#ifdef %s\n", optarg);
 797-			snprintf(not_defined, sizeof not_defined,
 798-			    "#ifndef %s\n", optarg);
 799-			snprintf(end_defined, sizeof end_defined,
 800-			    "#endif /* %s */\n", optarg);
 801-			break;
 802-		case 'e':
 803-			diff_type = ED_DIFF;
 804-			break;
 805-		case 'E':
 806-			remove_empty_files = true;
 807-			break;
 808-		case 'f':
 809-			force = true;
 810-			break;
 811-		case 'F':
 812-			maxfuzz = atoi(optarg);
 813-			break;
 814-		case 'i':
 815-			if (++filec == MAXFILEC)
 816-				fatal("too many file arguments\n");
 817-			filearg[filec] = savestr(optarg);
 818-			break;
 819-		case 'l':
 820-			canonicalize = true;
 821-			break;
 822-		case 'n':
 823-			diff_type = NORMAL_DIFF;
 824-			break;
 825-		case 'N':
 826-			noreverse = true;
 827-			break;
 828-		case 'o':
 829-			outname = savestr(optarg);
 830-			break;
 831-		case 'p':
 832-			strippath = atoi(optarg);
 833-			break;
 834-		case 'r':
 835-			if (strlcpy(rejname, optarg,
 836-			    sizeof(rejname)) >= sizeof(rejname))
 837-				fatal("argument for -r is too long\n");
 838-			break;
 839-		case 'R':
 840-			reverse = true;
 841-			reverse_flag_specified = true;
 842-			break;
 843-		case 's':
 844-			verbose = false;
 845-			break;
 846-		case 't':
 847-			batch = true;
 848-			break;
 849-		case 'u':
 850-			diff_type = UNI_DIFF;
 851-			break;
 852-		case 'v':
 853-			version();
 854-			break;
 855-		case 'V':
 856-			backup_type = get_version(optarg);
 857-			break;
 858-#ifdef DEBUGGING
 859-		case 'x':
 860-			debug = atoi(optarg);
 861-			break;
 862-#endif
 863-		default:
 864-			if (ch != '\0')
 865-				usage();
 866-			break;
 867-		}
 868-	}
 869-	Argc -= optind;
 870-	Argv += optind;
 871-
 872-	if (Argc > 0) {
 873-		filearg[0] = savestr(*Argv++);
 874-		Argc--;
 875-		while (Argc > 0) {
 876-			if (++filec == MAXFILEC)
 877-				fatal("too many file arguments\n");
 878-			filearg[filec] = savestr(*Argv++);
 879-			Argc--;
 880-		}
 881-	}
 882-
 883-	if (getenv("POSIXLY_CORRECT") != NULL)
 884-		posix = 1;
 885-
 886-	if (backup_if_mismatch == -1) {
 887-		backup_if_mismatch = posix ? 0 : 1;
 888-	}
 889-}
 890-
 891-static void
 892-usage(void)
 893-{
 894-	fprintf(stderr,
 895-"usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n"
 896-"             [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
 897-"             [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n"
 898-"             [--backup-if-mismatch] [--no-backup-if-mismatch] [--posix]\n"
 899-"             [origfile [patchfile]]\n"
 900-"       patch <patchfile\n");
 901-	my_exit(EXIT_FAILURE);
 902-}
 903-
 904-/*
 905- * Attempt to find the right place to apply this hunk of patch.
 906- */
 907-static LINENUM
 908-locate_hunk(LINENUM fuzz)
 909-{
 910-	LINENUM	first_guess = pch_first() + last_offset;
 911-	LINENUM	offset;
 912-	LINENUM	pat_lines = pch_ptrn_lines();
 913-	LINENUM	max_pos_offset = input_lines - first_guess - pat_lines + 1;
 914-	LINENUM	max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
 915-
 916-	if (pat_lines == 0) {		/* null range matches always */
 917-		if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
 918-		    || diff_type == NEW_CONTEXT_DIFF
 919-		    || diff_type == UNI_DIFF)) {
 920-			say("Empty context always matches.\n");
 921-		}
 922-		return (first_guess);
 923-	}
 924-	if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
 925-		max_neg_offset = first_guess - 1;
 926-	if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
 927-		return first_guess;
 928-	for (offset = 1; ; offset++) {
 929-		bool	check_after = (offset <= max_pos_offset);
 930-		bool	check_before = (offset <= max_neg_offset);
 931-
 932-		if (check_after && patch_match(first_guess, offset, fuzz)) {
 933-#ifdef DEBUGGING
 934-			if (debug & 1)
 935-				say("Offset changing from %ld to %ld\n",
 936-				    last_offset, offset);
 937-#endif
 938-			last_offset = offset;
 939-			return first_guess + offset;
 940-		} else if (check_before && patch_match(first_guess, -offset, fuzz)) {
 941-#ifdef DEBUGGING
 942-			if (debug & 1)
 943-				say("Offset changing from %ld to %ld\n",
 944-				    last_offset, -offset);
 945-#endif
 946-			last_offset = -offset;
 947-			return first_guess - offset;
 948-		} else if (!check_before && !check_after)
 949-			return 0;
 950-	}
 951-}
 952-
 953-/* We did not find the pattern, dump out the hunk so they can handle it. */
 954-
 955-static void
 956-abort_context_hunk(void)
 957-{
 958-	LINENUM	i;
 959-	const LINENUM	pat_end = pch_end();
 960-	/*
 961-	 * add in last_offset to guess the same as the previous successful
 962-	 * hunk
 963-	 */
 964-	const LINENUM	oldfirst = pch_first() + last_offset;
 965-	const LINENUM	newfirst = pch_newfirst() + last_offset;
 966-	const LINENUM	oldlast = oldfirst + pch_ptrn_lines() - 1;
 967-	const LINENUM	newlast = newfirst + pch_repl_lines() - 1;
 968-	const char	*stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
 969-	const char	*minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
 970-
 971-	fprintf(rejfp, "***************\n");
 972-	for (i = 0; i <= pat_end; i++) {
 973-		switch (pch_char(i)) {
 974-		case '*':
 975-			if (oldlast < oldfirst)
 976-				fprintf(rejfp, "*** 0%s\n", stars);
 977-			else if (oldlast == oldfirst)
 978-				fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
 979-			else
 980-				fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
 981-				    oldlast, stars);
 982-			break;
 983-		case '=':
 984-			if (newlast < newfirst)
 985-				fprintf(rejfp, "--- 0%s\n", minuses);
 986-			else if (newlast == newfirst)
 987-				fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
 988-			else
 989-				fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
 990-				    newlast, minuses);
 991-			break;
 992-		case '\n':
 993-			fprintf(rejfp, "%s", pfetch(i));
 994-			break;
 995-		case ' ':
 996-		case '-':
 997-		case '+':
 998-		case '!':
 999-			fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
1000-			break;
1001-		default:
1002-			fatal("fatal internal error in abort_context_hunk\n");
1003-		}
1004-	}
1005-}
1006-
1007-static void
1008-rej_line(int ch, LINENUM i)
1009-{
1010-	size_t len;
1011-	const char *line = pfetch(i);
1012-
1013-	len = strlen(line);
1014-
1015-	fprintf(rejfp, "%c%s", ch, line);
1016-	if (len == 0 || line[len-1] != '\n')
1017-		fprintf(rejfp, "\n\\ No newline at end of file\n");
1018-}
1019-
1020-static void
1021-abort_hunk(void)
1022-{
1023-	LINENUM		i, j, split;
1024-	int		ch1, ch2;
1025-	const LINENUM	pat_end = pch_end();
1026-	const LINENUM	oldfirst = pch_first() + last_offset;
1027-	const LINENUM	newfirst = pch_newfirst() + last_offset;
1028-
1029-	if (diff_type != UNI_DIFF) {
1030-		abort_context_hunk();
1031-		return;
1032-	}
1033-	split = -1;
1034-	for (i = 0; i <= pat_end; i++) {
1035-		if (pch_char(i) == '=') {
1036-			split = i;
1037-			break;
1038-		}
1039-	}
1040-	if (split == -1) {
1041-		fprintf(rejfp, "malformed hunk: no split found\n");
1042-		return;
1043-	}
1044-	i = 0;
1045-	j = split + 1;
1046-	fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
1047-	    pch_ptrn_lines() ? oldfirst : 0,
1048-	    pch_ptrn_lines(), newfirst, pch_repl_lines());
1049-	while (i < split || j <= pat_end) {
1050-		ch1 = i < split ? pch_char(i) : -1;
1051-		ch2 = j <= pat_end ? pch_char(j) : -1;
1052-		if (ch1 == '-') {
1053-			rej_line('-', i);
1054-			i++;
1055-		} else if (ch1 == ' ' && ch2 == ' ') {
1056-			rej_line(' ', i);
1057-			i++;
1058-			j++;
1059-		} else if (ch1 == '!' && ch2 == '!') {
1060-			while (i < split && ch1 == '!') {
1061-				rej_line('-', i);
1062-				i++;
1063-				ch1 = i < split ? pch_char(i) : -1;
1064-			}
1065-			while (j <= pat_end && ch2 == '!') {
1066-				rej_line('+', j);
1067-				j++;
1068-				ch2 = j <= pat_end ? pch_char(j) : -1;
1069-			}
1070-		} else if (ch1 == '*') {
1071-			i++;
1072-		} else if (ch2 == '+' || ch2 == ' ') {
1073-			rej_line(ch2, j);
1074-			j++;
1075-		} else {
1076-			fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
1077-			    i, split, j);
1078-			rej_line(ch1, i);
1079-			rej_line(ch2, j);
1080-			return;
1081-		}
1082-	}
1083-}
1084-
1085-/* We found where to apply it (we hope), so do it. */
1086-
1087-static void
1088-apply_hunk(LINENUM where)
1089-{
1090-	LINENUM		old = 1;
1091-	const LINENUM	lastline = pch_ptrn_lines();
1092-	LINENUM		new = lastline + 1;
1093-#define OUTSIDE 0
1094-#define IN_IFNDEF 1
1095-#define IN_IFDEF 2
1096-#define IN_ELSE 3
1097-	int		def_state = OUTSIDE;
1098-	const LINENUM	pat_end = pch_end();
1099-
1100-	where--;
1101-	while (pch_char(new) == '=' || pch_char(new) == '\n')
1102-		new++;
1103-
1104-	while (old <= lastline) {
1105-		if (pch_char(old) == '-') {
1106-			copy_till(where + old - 1, false);
1107-			if (do_defines) {
1108-				if (def_state == OUTSIDE) {
1109-					fputs(not_defined, ofp);
1110-					def_state = IN_IFNDEF;
1111-				} else if (def_state == IN_IFDEF) {
1112-					fputs(else_defined, ofp);
1113-					def_state = IN_ELSE;
1114-				}
1115-				fputs(pfetch(old), ofp);
1116-			}
1117-			last_frozen_line++;
1118-			old++;
1119-		} else if (new > pat_end) {
1120-			break;
1121-		} else if (pch_char(new) == '+') {
1122-			copy_till(where + old - 1, false);
1123-			if (do_defines) {
1124-				if (def_state == IN_IFNDEF) {
1125-					fputs(else_defined, ofp);
1126-					def_state = IN_ELSE;
1127-				} else if (def_state == OUTSIDE) {
1128-					fputs(if_defined, ofp);
1129-					def_state = IN_IFDEF;
1130-				}
1131-			}
1132-			fputs(pfetch(new), ofp);
1133-			new++;
1134-		} else if (pch_char(new) != pch_char(old)) {
1135-			say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
1136-			    pch_hunk_beg() + old,
1137-			    pch_hunk_beg() + new);
1138-#ifdef DEBUGGING
1139-			say("oldchar = '%c', newchar = '%c'\n",
1140-			    pch_char(old), pch_char(new));
1141-#endif
1142-			my_exit(2);
1143-		} else if (pch_char(new) == '!') {
1144-			copy_till(where + old - 1, false);
1145-			if (do_defines) {
1146-				fputs(not_defined, ofp);
1147-				def_state = IN_IFNDEF;
1148-			}
1149-			while (pch_char(old) == '!') {
1150-				if (do_defines) {
1151-					fputs(pfetch(old), ofp);
1152-				}
1153-				last_frozen_line++;
1154-				old++;
1155-			}
1156-			if (do_defines) {
1157-				fputs(else_defined, ofp);
1158-				def_state = IN_ELSE;
1159-			}
1160-			while (pch_char(new) == '!') {
1161-				fputs(pfetch(new), ofp);
1162-				new++;
1163-			}
1164-		} else {
1165-			if (pch_char(new) != ' ')
1166-				fatal("Internal error: expected ' '\n");
1167-			old++;
1168-			new++;
1169-			if (do_defines && def_state != OUTSIDE) {
1170-				fputs(end_defined, ofp);
1171-				def_state = OUTSIDE;
1172-			}
1173-		}
1174-	}
1175-	if (new <= pat_end && pch_char(new) == '+') {
1176-		copy_till(where + old - 1, false);
1177-		if (do_defines) {
1178-			if (def_state == OUTSIDE) {
1179-				fputs(if_defined, ofp);
1180-				def_state = IN_IFDEF;
1181-			} else if (def_state == IN_IFNDEF) {
1182-				fputs(else_defined, ofp);
1183-				def_state = IN_ELSE;
1184-			}
1185-		}
1186-		while (new <= pat_end && pch_char(new) == '+') {
1187-			fputs(pfetch(new), ofp);
1188-			new++;
1189-		}
1190-	}
1191-	if (do_defines && def_state != OUTSIDE) {
1192-		fputs(end_defined, ofp);
1193-	}
1194-}
1195-
1196-/*
1197- * Open the new file.
1198- */
1199-static void
1200-init_output(const char *name)
1201-{
1202-	ofp = fopen(name, "w");
1203-	if (ofp == NULL)
1204-		pfatal("can't create %s", name);
1205-}
1206-
1207-/*
1208- * Open a file to put hunks we can't locate.
1209- */
1210-static void
1211-init_reject(const char *name)
1212-{
1213-	rejfp = fopen(name, "w");
1214-	if (rejfp == NULL)
1215-		pfatal("can't create %s", name);
1216-}
1217-
1218-/*
1219- * Copy input file to output, up to wherever hunk is to be applied.
1220- * If endoffile is true, treat the last line specially since it may
1221- * lack a newline.
1222- */
1223-static void
1224-copy_till(LINENUM lastline, bool endoffile)
1225-{
1226-	if (last_frozen_line > lastline)
1227-		fatal("misordered hunks! output would be garbled\n");
1228-	while (last_frozen_line < lastline) {
1229-		if (++last_frozen_line == lastline && endoffile)
1230-			dump_line(last_frozen_line, !last_line_missing_eol);
1231-		else
1232-			dump_line(last_frozen_line, true);
1233-	}
1234-}
1235-
1236-/*
1237- * Finish copying the input file to the output file.
1238- */
1239-static bool
1240-spew_output(void)
1241-{
1242-	int rv;
1243-
1244-#ifdef DEBUGGING
1245-	if (debug & 256)
1246-		say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1247-#endif
1248-	if (input_lines)
1249-		copy_till(input_lines, true);	/* dump remainder of file */
1250-	rv = ferror(ofp) == 0 && fclose(ofp) == 0;
1251-	ofp = NULL;
1252-	return rv;
1253-}
1254-
1255-/*
1256- * Copy one line from input to output.
1257- */
1258-static void
1259-dump_line(LINENUM line, bool write_newline)
1260-{
1261-	char	*s;
1262-
1263-	s = ifetch(line, 0);
1264-	if (s == NULL)
1265-		return;
1266-	/* Note: string is not NUL terminated. */
1267-	for (; *s != '\n'; s++)
1268-		putc(*s, ofp);
1269-	if (write_newline)
1270-		putc('\n', ofp);
1271-}
1272-
1273-/*
1274- * Does the patch pattern match at line base+offset?
1275- */
1276-static bool
1277-patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
1278-{
1279-	LINENUM		pline = 1 + fuzz;
1280-	LINENUM		iline;
1281-	LINENUM		pat_lines = pch_ptrn_lines() - fuzz;
1282-	const char	*ilineptr;
1283-	const char	*plineptr;
1284-	ssize_t		plinelen;
1285-
1286-	for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
1287-		ilineptr = ifetch(iline, offset >= 0);
1288-		if (ilineptr == NULL)
1289-			return false;
1290-		plineptr = pfetch(pline);
1291-		plinelen = pch_line_len(pline);
1292-		if (canonicalize) {
1293-			if (!similar(ilineptr, plineptr, plinelen))
1294-				return false;
1295-		} else if (strnNE(ilineptr, plineptr, plinelen))
1296-			return false;
1297-		if (iline == input_lines) {
1298-			/*
1299-			 * We are looking at the last line of the file.
1300-			 * If the file has no eol, the patch line should
1301-			 * not have one either and vice-versa. Note that
1302-			 * plinelen > 0.
1303-			 */
1304-			if (last_line_missing_eol) {
1305-				if (plineptr[plinelen - 1] == '\n')
1306-					return false;
1307-			} else {
1308-				if (plineptr[plinelen - 1] != '\n')
1309-					return false;
1310-			}
1311-		}
1312-	}
1313-	return true;
1314-}
1315-
1316-/*
1317- * Do two lines match with canonicalized white space?
1318- */
1319-static bool
1320-similar(const char *a, const char *b, ssize_t len)
1321-{
1322-	while (len) {
1323-		if (isspace((unsigned char)*b)) {	/* whitespace (or \n) to match? */
1324-			if (!isspace((unsigned char)*a))	/* no corresponding whitespace? */
1325-				return false;
1326-			while (len && isspace((unsigned char)*b) && *b != '\n')
1327-				b++, len--;	/* skip pattern whitespace */
1328-			while (isspace((unsigned char)*a) && *a != '\n')
1329-				a++;	/* skip target whitespace */
1330-			if (*a == '\n' || *b == '\n')
1331-				return (*a == *b);	/* should end in sync */
1332-		} else if (*a++ != *b++)	/* match non-whitespace chars */
1333-			return false;
1334-		else
1335-			len--;	/* probably not necessary */
1336-	}
1337-	return true;		/* actually, this is not reached */
1338-	/* since there is always a \n */
1339-}
+0, -14
 1@@ -1,14 +0,0 @@
 2-/*
 3- * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
 4- * $DragonFly: src/usr.bin/patch/pathnames.h,v 1.2 2008/08/11 00:04:12 joerg Exp $
 5- * $NetBSD: pathnames.h,v 1.1 2008/09/19 18:33:34 joerg Exp $
 6- */
 7-
 8-/*
 9- * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
10- * on July 29, 2003.
11- */
12-
13-#include <paths.h>
14-
15-#define	_PATH_ED		"/bin/ed"
+0, -1596
   1@@ -1,1596 +0,0 @@
   2-/*
   3- * $OpenBSD: pch.c,v 1.37 2007/09/02 15:19:33 deraadt Exp $
   4- * $DragonFly: src/usr.bin/patch/pch.c,v 1.6 2008/08/10 23:35:40 joerg Exp $
   5- * $NetBSD: pch.c,v 1.34 2024/07/12 15:48:39 manu Exp $
   6- */
   7-
   8-/*
   9- * patch - a program to apply diffs to original files
  10- *
  11- * Copyright 1986, Larry Wall
  12- *
  13- * Redistribution and use in source and binary forms, with or without
  14- * modification, are permitted provided that the following condition is met:
  15- * 1. Redistributions of source code must retain the above copyright notice,
  16- * this condition and the following disclaimer.
  17- *
  18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28- * SUCH DAMAGE.
  29- *
  30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
  31- * behaviour
  32- */
  33-
  34-#include <sys/types.h>
  35-#include <sys/stat.h>
  36-
  37-#include <ctype.h>
  38-#include <libgen.h>
  39-#include <limits.h>
  40-#include <stdio.h>
  41-#include <stdlib.h>
  42-#include <string.h>
  43-#include <unistd.h>
  44-
  45-#include "common.h"
  46-#include "util.h"
  47-#include "pch.h"
  48-#include "pathnames.h"
  49-
  50-/* Patch (diff listing) abstract type. */
  51-
  52-static long	p_filesize;	/* size of the patch file */
  53-static LINENUM	p_first;	/* 1st line number */
  54-static LINENUM	p_newfirst;	/* 1st line number of replacement */
  55-static LINENUM	p_ptrn_lines;	/* # lines in pattern */
  56-static LINENUM	p_repl_lines;	/* # lines in replacement text */
  57-static LINENUM	p_end = -1;	/* last line in hunk */
  58-static LINENUM	p_max;		/* max allowed value of p_end */
  59-static LINENUM	p_context = 3;	/* # of context lines */
  60-static LINENUM	p_input_line = 0;	/* current line # from patch file */
  61-static char	**p_line = NULL;/* the text of the hunk */
  62-static ssize_t	*p_len = NULL;	/* length of each line */
  63-static char	*p_char = NULL;	/* +, -, and ! */
  64-static int	hunkmax = INITHUNKMAX;	/* size of above arrays to begin with */
  65-static int	p_indent;	/* indent to patch */
  66-static LINENUM	p_base;		/* where to intuit this time */
  67-static LINENUM	p_bline;	/* line # of p_base */
  68-static LINENUM	p_start;	/* where intuit found a patch */
  69-static LINENUM	p_sline;	/* and the line number for it */
  70-static LINENUM	p_hunk_beg;	/* line number of current hunk */
  71-static LINENUM	p_efake = -1;	/* end of faked up lines--don't free */
  72-static LINENUM	p_bfake = -1;	/* beg of faked up lines */
  73-static FILE	*pfp = NULL;	/* patch file pointer */
  74-static char	*bestguess = NULL;	/* guess at correct filename */
  75-
  76-static void	grow_hunkmax(void);
  77-static int	intuit_diff_type(void);
  78-static void	next_intuit_at(LINENUM, LINENUM);
  79-static void	skip_to(LINENUM, LINENUM);
  80-static int	pgetline(char **, size_t *, FILE *);
  81-static char	*best_name(const struct file_name *, bool);
  82-static char	*posix_name(const struct file_name *, bool);
  83-static size_t	num_components(const char *);
  84-
  85-/*
  86- * Prepare to look for the next patch in the patch file.
  87- */
  88-void
  89-re_patch(void)
  90-{
  91-	p_first = 0;
  92-	p_newfirst = 0;
  93-	p_ptrn_lines = 0;
  94-	p_repl_lines = 0;
  95-	p_end = (LINENUM) - 1;
  96-	p_max = 0;
  97-	p_indent = 0;
  98-}
  99-
 100-/*
 101- * Open the patch file at the beginning of time.
 102- */
 103-void
 104-open_patch_file(const char *filename)
 105-{
 106-	struct stat filestat;
 107-
 108-	if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
 109-		pfp = fopen(TMPPATNAME, "w");
 110-		if (pfp == NULL)
 111-			pfatal("can't create %s", TMPPATNAME);
 112-		while (getline(&buf, &bufsz, stdin) != -1)
 113-			fprintf(pfp, "%s", buf);
 114-		if (ferror(pfp) || fclose(pfp))
 115-			pfatal("can't write %s", TMPPATNAME);
 116-		filename = TMPPATNAME;
 117-	}
 118-	pfp = fopen(filename, "r");
 119-	if (pfp == NULL)
 120-		pfatal("patch file %s not found", filename);
 121-	fstat(fileno(pfp), &filestat);
 122-	p_filesize = filestat.st_size;
 123-	next_intuit_at(0L, 1L);	/* start at the beginning */
 124-	set_hunkmax();
 125-}
 126-
 127-/*
 128- * Make sure our dynamically realloced tables are malloced to begin with.
 129- */
 130-void
 131-set_hunkmax(void)
 132-{
 133-	if (p_line == NULL)
 134-		p_line = calloc((size_t) hunkmax, sizeof(char *));
 135-	if (p_len == NULL)
 136-		p_len = calloc((size_t) hunkmax, sizeof(ssize_t));
 137-	if (p_char == NULL)
 138-		p_char = calloc((size_t) hunkmax, sizeof(char));
 139-}
 140-
 141-/*
 142- * Enlarge the arrays containing the current hunk of patch.
 143- */
 144-static void
 145-grow_hunkmax(void)
 146-{
 147-	int		new_hunkmax;
 148-	char		**new_p_line;
 149-	ssize_t		*new_p_len;
 150-	char		*new_p_char;
 151-
 152-	new_hunkmax = hunkmax * 2;
 153-
 154-	if (p_line == NULL || p_len == NULL || p_char == NULL)
 155-		fatal("Internal memory allocation error\n");
 156-
 157-	new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
 158-	if (new_p_line == NULL)
 159-		free(p_line);
 160-
 161-	new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
 162-	if (new_p_len == NULL)
 163-		free(p_len);
 164-
 165-	new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
 166-	if (new_p_char == NULL)
 167-		free(p_char);
 168-
 169-	p_char = new_p_char;
 170-	p_len = new_p_len;
 171-	p_line = new_p_line;
 172-
 173-	if (p_line != NULL && p_len != NULL && p_char != NULL) {
 174-		hunkmax = new_hunkmax;
 175-		return;
 176-	}
 177-
 178-	if (!using_plan_a)
 179-		fatal("out of memory\n");
 180-	out_of_mem = true;	/* whatever is null will be allocated again */
 181-				/* from within plan_a(), of all places */
 182-}
 183-
 184-/* True if the remainder of the patch file contains a diff of some sort. */
 185-
 186-bool
 187-there_is_another_patch(void)
 188-{
 189-	bool exists = false;
 190-
 191-	if (p_base != 0L && p_base >= p_filesize) {
 192-		if (verbose)
 193-			say("done\n");
 194-		return false;
 195-	}
 196-	if (verbose)
 197-		say("Hmm...");
 198-	diff_type = intuit_diff_type();
 199-	if (!diff_type) {
 200-		if (p_base != 0L) {
 201-			if (verbose)
 202-				say("  Ignoring the trailing garbage.\ndone\n");
 203-		} else
 204-			say("  I can't seem to find a patch in there anywhere.\n");
 205-		return false;
 206-	}
 207-	if (verbose)
 208-		say("  %sooks like %s to me...\n",
 209-		    (p_base == 0L ? "L" : "The next patch l"),
 210-		    diff_type == UNI_DIFF ? "a unified diff" :
 211-		    diff_type == CONTEXT_DIFF ? "a context diff" :
 212-		diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
 213-		    diff_type == NORMAL_DIFF ? "a normal diff" :
 214-		    "an ed script");
 215-	if (p_indent && verbose)
 216-		say("(Patch is indented %d space%s.)\n", p_indent,
 217-		    p_indent == 1 ? "" : "s");
 218-	skip_to(p_start, p_sline);
 219-	while (filearg[0] == NULL) {
 220-		if (force || batch) {
 221-			say("No file to patch.  Skipping...\n");
 222-			filearg[0] = savestr(bestguess);
 223-			skip_rest_of_patch = true;
 224-			return true;
 225-		}
 226-		ask("File to patch: ");
 227-		if (*buf != '\n') {
 228-			free(bestguess);
 229-			bestguess = savestr(buf);
 230-			filearg[0] = fetchname(buf, &exists, 0);
 231-		}
 232-		if (!exists) {
 233-			ask("No file found--skip this patch? [n] ");
 234-			if (*buf != 'y')
 235-				continue;
 236-			if (verbose)
 237-				say("Skipping patch...\n");
 238-			free(filearg[0]);
 239-			filearg[0] = fetchname(bestguess, &exists, 0);
 240-			skip_rest_of_patch = true;
 241-			return true;
 242-		}
 243-	}
 244-	return true;
 245-}
 246-
 247-/* Determine what kind of diff is in the remaining part of the patch file. */
 248-
 249-static int
 250-intuit_diff_type(void)
 251-{
 252-	long	this_line = 0, previous_line;
 253-	long	first_command_line = -1;
 254-	LINENUM	fcl_line = -1;
 255-	bool	last_line_was_command = false, this_is_a_command = false;
 256-	bool	stars_last_line = false, stars_this_line = false;
 257-	char	*s, *t;
 258-	int	indent, retval;
 259-	struct file_name names[MAX_FILE];
 260-
 261-	memset(names, 0, sizeof(names));
 262-	ok_to_create_file = false;
 263-	fseek(pfp, p_base, SEEK_SET);
 264-	p_input_line = p_bline - 1;
 265-	for (;;) {
 266-		previous_line = this_line;
 267-		last_line_was_command = this_is_a_command;
 268-		stars_last_line = stars_this_line;
 269-		this_line = ftell(pfp);
 270-		indent = 0;
 271-		p_input_line++;
 272-		if (getline(&buf, &bufsz, pfp) == -1) {
 273-			if (first_command_line >= 0L) {
 274-				/* nothing but deletes!? */
 275-				p_start = first_command_line;
 276-				p_sline = fcl_line;
 277-				retval = ED_DIFF;
 278-				goto scan_exit;
 279-			} else {
 280-				p_start = this_line;
 281-				p_sline = p_input_line;
 282-				retval = 0;
 283-				goto scan_exit;
 284-			}
 285-		}
 286-		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
 287-			if (*s == '\t')
 288-				indent += 8 - (indent % 8);
 289-			else
 290-				indent++;
 291-		}
 292-		for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
 293-			;
 294-		this_is_a_command = (isdigit((unsigned char)*s) &&
 295-		    (*t == 'd' || *t == 'c' || *t == 'a'));
 296-		if (first_command_line < 0L && this_is_a_command) {
 297-			first_command_line = this_line;
 298-			fcl_line = p_input_line;
 299-			p_indent = indent;	/* assume this for now */
 300-		}
 301-		if (!stars_last_line && strnEQ(s, "*** ", 4))
 302-			names[OLD_FILE].path = fetchname(s + 4,
 303-			    &names[OLD_FILE].exists, strippath);
 304-		else if (strnEQ(s, "--- ", 4))
 305-			names[NEW_FILE].path = fetchname(s + 4,
 306-			    &names[NEW_FILE].exists, strippath);
 307-		else if (strnEQ(s, "+++ ", 4))
 308-			/* pretend it is the old name */
 309-			names[OLD_FILE].path = fetchname(s + 4,
 310-			    &names[OLD_FILE].exists, strippath);
 311-		else if (strnEQ(s, "Index:", 6))
 312-			names[INDEX_FILE].path = fetchname(s + 6,
 313-			    &names[INDEX_FILE].exists, strippath);
 314-		else if (strnEQ(s, "Prereq:", 7)) {
 315-			for (t = s + 7; isspace((unsigned char)*t); t++)
 316-				;
 317-			revision = savestr(t);
 318-			for (t = revision; *t && !isspace((unsigned char)*t); t++)
 319-				;
 320-			*t = '\0';
 321-			if (*revision == '\0') {
 322-				free(revision);
 323-				revision = NULL;
 324-			}
 325-		}
 326-		if ((!diff_type || diff_type == ED_DIFF) &&
 327-		    first_command_line >= 0L &&
 328-		    strEQ(s, ".\n")) {
 329-			p_indent = indent;
 330-			p_start = first_command_line;
 331-			p_sline = fcl_line;
 332-			retval = ED_DIFF;
 333-			goto scan_exit;
 334-		}
 335-		if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
 336-			if (strnEQ(s + 4, "0,0", 3))
 337-				ok_to_create_file = true;
 338-			p_indent = indent;
 339-			p_start = this_line;
 340-			p_sline = p_input_line;
 341-			retval = UNI_DIFF;
 342-			goto scan_exit;
 343-		}
 344-		stars_this_line = strnEQ(s, "********", 8);
 345-		if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
 346-		    strnEQ(s, "*** ", 4)) {
 347-			if (atol(s + 4) == 0)
 348-				ok_to_create_file = true;
 349-			/*
 350-			 * If this is a new context diff the character just
 351-			 * before the newline is a '*'.
 352-			 */
 353-			while (*s != '\n')
 354-				s++;
 355-			p_indent = indent;
 356-			p_start = previous_line;
 357-			p_sline = p_input_line - 1;
 358-			retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
 359-			goto scan_exit;
 360-		}
 361-		if ((!diff_type || diff_type == NORMAL_DIFF) &&
 362-		    last_line_was_command &&
 363-		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
 364-			p_start = previous_line;
 365-			p_sline = p_input_line - 1;
 366-			p_indent = indent;
 367-			retval = NORMAL_DIFF;
 368-			goto scan_exit;
 369-		}
 370-	}
 371-scan_exit:
 372-	if (retval == UNI_DIFF) {
 373-		/* unswap old and new */
 374-		struct file_name tmp = names[OLD_FILE];
 375-		names[OLD_FILE] = names[NEW_FILE];
 376-		names[NEW_FILE] = tmp;
 377-	}
 378-	if (filearg[0] == NULL) {
 379-		if (posix)
 380-			filearg[0] = posix_name(names, ok_to_create_file);
 381-		else {
 382-			/* Ignore the Index: name for context diffs, like GNU */
 383-			if (names[OLD_FILE].path != NULL ||
 384-			    names[NEW_FILE].path != NULL) {
 385-				free(names[INDEX_FILE].path);
 386-				names[INDEX_FILE].path = NULL;
 387-			}
 388-			filearg[0] = best_name(names, ok_to_create_file);
 389-		}
 390-	}
 391-
 392-	free(bestguess);
 393-	bestguess = NULL;
 394-	if (filearg[0] != NULL)
 395-		bestguess = savestr(filearg[0]);
 396-	else if (!ok_to_create_file) {
 397-		/*
 398-		 * We don't want to create a new file but we need a
 399-		 * filename to set bestguess.  Avoid setting filearg[0]
 400-		 * so the file is not created automatically.
 401-		 */
 402-		if (posix)
 403-			bestguess = posix_name(names, true);
 404-		else
 405-			bestguess = best_name(names, true);
 406-	}
 407-	free(names[OLD_FILE].path);
 408-	free(names[NEW_FILE].path);
 409-	free(names[INDEX_FILE].path);
 410-	return retval;
 411-}
 412-
 413-/*
 414- * Remember where this patch ends so we know where to start up again.
 415- */
 416-static void
 417-next_intuit_at(LINENUM file_pos, LINENUM file_line)
 418-{
 419-	p_base = file_pos;
 420-	p_bline = file_line;
 421-}
 422-
 423-/*
 424- * Basically a verbose fseek() to the actual diff listing.
 425- */
 426-static void
 427-skip_to(LINENUM file_pos, LINENUM file_line)
 428-{
 429-	int	ret;
 430-
 431-	if (p_base > file_pos)
 432-		fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
 433-	if (verbose && p_base < file_pos) {
 434-		fseek(pfp, p_base, SEEK_SET);
 435-		say("The text leading up to this was:\n--------------------------\n");
 436-		while (ftell(pfp) < file_pos) {
 437-			ret = getline(&buf, &bufsz, pfp);
 438-			if (ret == -1)
 439-				fatal("Unexpected end of file\n");
 440-			say("|%s", buf);
 441-		}
 442-		say("--------------------------\n");
 443-	} else
 444-		fseek(pfp, file_pos, SEEK_SET);
 445-	p_input_line = file_line - 1;
 446-}
 447-
 448-/* Make this a function for better debugging.  */
 449-__dead static void
 450-malformed(void)
 451-{
 452-	fatal("malformed patch at line %ld: %s", p_input_line, buf);
 453-	/* about as informative as "Syntax error" in C */
 454-}
 455-
 456-static LINENUM
 457-getlinenum(const char *s)
 458-{
 459-	LINENUM l = (LINENUM)atol(s);
 460-	if (l < 0) {
 461-		l = 0;
 462-		malformed();
 463-	}
 464-	return l;
 465-}
 466-
 467-static LINENUM
 468-getskiplinenum(char **p)
 469-{
 470-	char *s = *p;
 471-	LINENUM l = getlinenum(s);
 472-	while (isdigit((unsigned char)*s))
 473-		s++;
 474-	*p = s;
 475-	return l;
 476-}
 477-
 478-/*
 479- * True if the line has been discarded (i.e., it is a line saying
 480- *  "\ No newline at end of file".)
 481- */
 482-static bool
 483-remove_special_line(void)
 484-{
 485-	int	c;
 486-
 487-	c = fgetc(pfp);
 488-	if (c == '\\') {
 489-		do {
 490-			c = fgetc(pfp);
 491-		} while (c != EOF && c != '\n');
 492-
 493-		return true;
 494-	}
 495-	if (c != EOF)
 496-		fseek(pfp, -1L, SEEK_CUR);
 497-
 498-	return false;
 499-}
 500-
 501-/*
 502- * True if there is more of the current diff listing to process.
 503- */
 504-bool
 505-another_hunk(void)
 506-{
 507-	long	line_beginning;			/* file pos of the current line */
 508-	LINENUM	repl_beginning;			/* index of --- line */
 509-	LINENUM	fillcnt;			/* #lines of missing ptrn or repl */
 510-	LINENUM	fillsrc;			/* index of first line to copy */
 511-	LINENUM	filldst;			/* index of first missing line */
 512-	bool	ptrn_spaces_eaten;		/* ptrn was slightly misformed */
 513-	bool	repl_could_be_missing;		/* no + or ! lines in this hunk */
 514-	bool	repl_missing;			/* we are now backtracking */
 515-	long	repl_backtrack_position;	/* file pos of first repl line */
 516-	LINENUM	repl_patch_line;		/* input line number for same */
 517-	LINENUM	ptrn_copiable;			/* # of copiable lines in ptrn */
 518-	char	*s;
 519-	int	context = 0;
 520-	int	ret;
 521-
 522-	while (p_end >= 0) {
 523-		if (p_end == p_efake)
 524-			p_end = p_bfake;	/* don't free twice */
 525-		else
 526-			free(p_line[p_end]);
 527-		p_end--;
 528-	}
 529-	p_efake = -1;
 530-
 531-	p_max = hunkmax;	/* gets reduced when --- found */
 532-	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
 533-		line_beginning = ftell(pfp);
 534-		repl_beginning = 0;
 535-		fillcnt = 0;
 536-		fillsrc = 0;
 537-		filldst = 0;
 538-		ptrn_spaces_eaten = false;
 539-		repl_could_be_missing = true;
 540-		repl_missing = false;
 541-		repl_backtrack_position = 0;
 542-		repl_patch_line = 0;
 543-		ptrn_copiable = 0;
 544-
 545-		ret = pgetline(&buf, &bufsz, pfp);
 546-		p_input_line++;
 547-		if (ret == -1 || strnNE(buf, "********", 8)) {
 548-			next_intuit_at(line_beginning, p_input_line);
 549-			return false;
 550-		}
 551-		p_context = 100;
 552-		p_hunk_beg = p_input_line + 1;
 553-		while (p_end < p_max) {
 554-			// line_beginning = ftell(pfp);
 555-			ret = pgetline(&buf, &bufsz, pfp);
 556-			p_input_line++;
 557-			if (ret == -1) {
 558-				if (repl_beginning && repl_could_be_missing) {
 559-					repl_missing = true;
 560-					goto hunk_done;
 561-				}
 562-				fatal("unexpected end of file in patch\n");
 563-			}
 564-			p_end++;
 565-			if (p_end >= hunkmax)
 566-				fatal("Internal error: hunk larger than hunk "
 567-				    "buffer size");
 568-			p_char[p_end] = *buf;
 569-			p_line[p_end] = NULL;
 570-			switch (*buf) {
 571-			case '*':
 572-				if (strnEQ(buf, "********", 8)) {
 573-					if (repl_beginning && repl_could_be_missing) {
 574-						repl_missing = true;
 575-						goto hunk_done;
 576-					} else
 577-						fatal("unexpected end of hunk "
 578-						    "at line %ld\n",
 579-						    p_input_line);
 580-				}
 581-				if (p_end != 0) {
 582-					if (repl_beginning && repl_could_be_missing) {
 583-						repl_missing = true;
 584-						goto hunk_done;
 585-					}
 586-					fatal("unexpected *** at line %ld: %s",
 587-					    p_input_line, buf);
 588-				}
 589-				context = 0;
 590-				p_line[p_end] = savestr(buf);
 591-				if (out_of_mem) {
 592-					p_end--;
 593-					return false;
 594-				}
 595-				for (s = buf; *s && !isdigit((unsigned char)*s); s++)
 596-					;
 597-				if (!*s)
 598-					malformed();
 599-				if (strnEQ(s, "0,0", 3))
 600-					memmove(s, s + 2, strlen(s + 2) + 1);
 601-				p_first = getskiplinenum(&s);
 602-				if (*s == ',') {
 603-					for (; *s && !isdigit((unsigned char)*s); s++)
 604-						;
 605-					if (!*s)
 606-						malformed();
 607-					p_ptrn_lines = (getlinenum(s)) - p_first + 1;
 608-					if (p_ptrn_lines < 0)
 609-						malformed();
 610-				} else if (p_first)
 611-					p_ptrn_lines = 1;
 612-				else {
 613-					p_ptrn_lines = 0;
 614-					p_first = 1;
 615-				}
 616-				if (p_first >= LINENUM_MAX - p_ptrn_lines ||
 617-				    p_ptrn_lines >= LINENUM_MAX - 6)
 618-					malformed();
 619-
 620-				/* we need this much at least */
 621-				p_max = p_ptrn_lines + 6;
 622-				while (p_max >= hunkmax)
 623-					grow_hunkmax();
 624-				p_max = hunkmax;
 625-				break;
 626-			case '-':
 627-				if (buf[1] == '-') {
 628-					if (repl_beginning ||
 629-					    (p_end != p_ptrn_lines + 1 +
 630-					    (p_char[p_end - 1] == '\n'))) {
 631-						if (p_end == 1) {
 632-							/*
 633-							 * `old' lines were omitted;
 634-							 * set up to fill them in
 635-							 * from 'new' context lines.
 636-							 */
 637-							p_end = p_ptrn_lines + 1;
 638-							fillsrc = p_end + 1;
 639-							filldst = 1;
 640-							fillcnt = p_ptrn_lines;
 641-						} else {
 642-							if (repl_beginning) {
 643-								if (repl_could_be_missing) {
 644-									repl_missing = true;
 645-									goto hunk_done;
 646-								}
 647-								fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
 648-								    p_input_line, p_hunk_beg + repl_beginning);
 649-							} else {
 650-								fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
 651-								    (p_end <= p_ptrn_lines
 652-								    ? "Premature"
 653-								    : "Overdue"),
 654-								    p_input_line, p_hunk_beg);
 655-							}
 656-						}
 657-					}
 658-					repl_beginning = p_end;
 659-					repl_backtrack_position = ftell(pfp);
 660-					repl_patch_line = p_input_line;
 661-					p_line[p_end] = savestr(buf);
 662-					if (out_of_mem) {
 663-						p_end--;
 664-						return false;
 665-					}
 666-					p_char[p_end] = '=';
 667-					for (s = buf; *s && !isdigit((unsigned char)*s); s++)
 668-						;
 669-					if (!*s)
 670-						malformed();
 671-					p_newfirst = getskiplinenum(&s);
 672-					if (*s == ',') {
 673-						for (; *s && !isdigit((unsigned char)*s); s++)
 674-							;
 675-						if (!*s)
 676-							malformed();
 677-						p_repl_lines = (getlinenum(s)) -
 678-						    p_newfirst + 1;
 679-						if (p_repl_lines < 0)
 680-							malformed();
 681-					} else if (p_newfirst)
 682-						p_repl_lines = 1;
 683-					else {
 684-						p_repl_lines = 0;
 685-						p_newfirst = 1;
 686-					}
 687-					if (p_newfirst >= LINENUM_MAX - p_repl_lines ||
 688-					    p_repl_lines >= LINENUM_MAX - p_end)
 689-						malformed();
 690-					p_max = p_repl_lines + p_end;
 691-					if (p_max > MAXHUNKSIZE)
 692-						fatal("hunk too large (%ld lines) at line %ld: %s",
 693-						    p_max, p_input_line, buf);
 694-					while (p_max >= hunkmax)
 695-						grow_hunkmax();
 696-					if (p_repl_lines != ptrn_copiable &&
 697-					    (p_context != 0 || p_repl_lines != 1))
 698-						repl_could_be_missing = false;
 699-					break;
 700-				}
 701-				goto change_line;
 702-			case '+':
 703-			case '!':
 704-				repl_could_be_missing = false;
 705-		change_line:
 706-				if (buf[1] == '\n' && canonicalize)
 707-					strlcpy(buf + 1, " \n", bufsz - 1);
 708-				if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
 709-				    buf[1] != '<' &&
 710-				    repl_beginning && repl_could_be_missing) {
 711-					repl_missing = true;
 712-					goto hunk_done;
 713-				}
 714-				if (context >= 0) {
 715-					if (context < p_context)
 716-						p_context = context;
 717-					context = -1000;
 718-				}
 719-				p_line[p_end] = savestr(buf + 2);
 720-				if (out_of_mem) {
 721-					p_end--;
 722-					return false;
 723-				}
 724-				if (p_end == p_ptrn_lines) {
 725-					if (remove_special_line()) {
 726-						int	len;
 727-
 728-						len = strlen(p_line[p_end]) - 1;
 729-						(p_line[p_end])[len] = 0;
 730-					}
 731-				}
 732-				break;
 733-			case '\t':
 734-			case '\n':	/* assume the 2 spaces got eaten */
 735-				if (repl_beginning && repl_could_be_missing &&
 736-				    (!ptrn_spaces_eaten ||
 737-				    diff_type == NEW_CONTEXT_DIFF)) {
 738-					repl_missing = true;
 739-					goto hunk_done;
 740-				}
 741-				p_line[p_end] = savestr(buf);
 742-				if (out_of_mem) {
 743-					p_end--;
 744-					return false;
 745-				}
 746-				if (p_end != p_ptrn_lines + 1) {
 747-					ptrn_spaces_eaten |= (repl_beginning != 0);
 748-					context++;
 749-					if (!repl_beginning)
 750-						ptrn_copiable++;
 751-					p_char[p_end] = ' ';
 752-				}
 753-				break;
 754-			case ' ':
 755-				if (!isspace((unsigned char)buf[1]) &&
 756-				    repl_beginning && repl_could_be_missing) {
 757-					repl_missing = true;
 758-					goto hunk_done;
 759-				}
 760-				context++;
 761-				if (!repl_beginning)
 762-					ptrn_copiable++;
 763-				p_line[p_end] = savestr(buf + 2);
 764-				if (out_of_mem) {
 765-					p_end--;
 766-					return false;
 767-				}
 768-				break;
 769-			default:
 770-				if (repl_beginning && repl_could_be_missing) {
 771-					repl_missing = true;
 772-					goto hunk_done;
 773-				}
 774-				malformed();
 775-			}
 776-			/* set up p_len for strncmp() so we don't have to */
 777-			/* assume null termination */
 778-			if (p_line[p_end])
 779-				p_len[p_end] = strlen(p_line[p_end]);
 780-			else
 781-				p_len[p_end] = 0;
 782-		}
 783-
 784-hunk_done:
 785-		if (p_end >= 0 && !repl_beginning)
 786-			fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
 787-
 788-		if (repl_missing) {
 789-
 790-			/* reset state back to just after --- */
 791-			p_input_line = repl_patch_line;
 792-			for (p_end--; p_end > repl_beginning; p_end--)
 793-				free(p_line[p_end]);
 794-			fseek(pfp, repl_backtrack_position, SEEK_SET);
 795-
 796-			/* redundant 'new' context lines were omitted - set */
 797-			/* up to fill them in from the old file context */
 798-			if (!p_context && p_repl_lines == 1) {
 799-				p_repl_lines = 0;
 800-				p_max--;
 801-			}
 802-			fillsrc = 1;
 803-			filldst = repl_beginning + 1;
 804-			fillcnt = p_repl_lines;
 805-			p_end = p_max;
 806-		} else if (!p_context && fillcnt == 1) {
 807-			/* the first hunk was a null hunk with no context */
 808-			/* and we were expecting one line -- fix it up. */
 809-			while (filldst < p_end) {
 810-				p_line[filldst] = p_line[filldst + 1];
 811-				p_char[filldst] = p_char[filldst + 1];
 812-				p_len[filldst] = p_len[filldst + 1];
 813-				filldst++;
 814-			}
 815-#if 0
 816-			repl_beginning--;	/* this doesn't need to be fixed */
 817-#endif
 818-			p_end--;
 819-			p_first++;	/* do append rather than insert */
 820-			fillcnt = 0;
 821-			p_ptrn_lines = 0;
 822-		}
 823-		if (diff_type == CONTEXT_DIFF &&
 824-		    (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
 825-			if (verbose)
 826-				say("%s\n%s\n%s\n",
 827-				    "(Fascinating--this is really a new-style context diff but without",
 828-				    "the telltale extra asterisks on the *** line that usually indicate",
 829-				    "the new style...)");
 830-			diff_type = NEW_CONTEXT_DIFF;
 831-		}
 832-		/* if there were omitted context lines, fill them in now */
 833-		if (fillcnt) {
 834-			p_bfake = filldst;	/* remember where not to free() */
 835-			p_efake = filldst + fillcnt - 1;
 836-			while (fillcnt-- > 0) {
 837-				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
 838-					fillsrc++;
 839-				if (fillsrc > p_end)
 840-					fatal("replacement text or line numbers mangled in hunk at line %ld\n",
 841-					    p_hunk_beg);
 842-				p_line[filldst] = p_line[fillsrc];
 843-				p_char[filldst] = p_char[fillsrc];
 844-				p_len[filldst] = p_len[fillsrc];
 845-				fillsrc++;
 846-				filldst++;
 847-			}
 848-			while (fillsrc <= p_end && fillsrc != repl_beginning &&
 849-			    p_char[fillsrc] != ' ')
 850-				fillsrc++;
 851-#ifdef DEBUGGING
 852-			if (debug & 64)
 853-				printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
 854-				fillsrc, filldst, repl_beginning, p_end + 1);
 855-#endif
 856-			if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
 857-				malformed();
 858-			if (filldst != p_end + 1 && filldst != repl_beginning)
 859-				malformed();
 860-		}
 861-		if (p_line[p_end] != NULL) {
 862-			if (remove_special_line()) {
 863-				p_len[p_end] -= 1;
 864-				(p_line[p_end])[p_len[p_end]] = 0;
 865-			}
 866-		}
 867-	} else if (diff_type == UNI_DIFF) {
 868-		LINENUM	fillold;	/* index of old lines */
 869-		LINENUM	fillnew;	/* index of new lines */
 870-		char	ch;
 871-
 872-		line_beginning = ftell(pfp); /* file pos of the current line */
 873-		ret = pgetline(&buf, &bufsz, pfp);
 874-		p_input_line++;
 875-		if (ret == -1 || strnNE(buf, "@@ -", 4)) {
 876-			next_intuit_at(line_beginning, p_input_line);
 877-			return false;
 878-		}
 879-		s = buf + 4;
 880-		if (!*s)
 881-			malformed();
 882-		p_first = getskiplinenum(&s);
 883-		if (*s == ',') {
 884-			s++;
 885-			p_ptrn_lines = getskiplinenum(&s);
 886-		} else
 887-			p_ptrn_lines = 1;
 888-		if (p_first >= LINENUM_MAX - p_ptrn_lines)
 889-			malformed();
 890-		if (*s == ' ')
 891-			s++;
 892-		if (*s != '+' || !*++s)
 893-			malformed();
 894-		p_newfirst = getskiplinenum(&s);
 895-		if (*s == ',') {
 896-			s++;
 897-			p_repl_lines = getskiplinenum(&s);
 898-		} else
 899-			p_repl_lines = 1;
 900-		if (*s == ' ')
 901-			s++;
 902-		if (*s != '@')
 903-			malformed();
 904-		if (p_first >= LINENUM_MAX - p_ptrn_lines ||
 905-		    p_newfirst > LINENUM_MAX - p_repl_lines ||
 906-		    p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
 907-			malformed();
 908-		if (!p_ptrn_lines)
 909-			p_first++;	/* do append rather than insert */
 910-		p_max = p_ptrn_lines + p_repl_lines + 1;
 911-		while (p_max >= hunkmax)
 912-			grow_hunkmax();
 913-		fillold = 1;
 914-		fillnew = fillold + p_ptrn_lines;
 915-		p_end = fillnew + p_repl_lines;
 916-		snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first,
 917-		    p_first + p_ptrn_lines - 1);
 918-		p_line[0] = savestr(buf);
 919-		if (out_of_mem) {
 920-			p_end = -1;
 921-			return false;
 922-		}
 923-		p_char[0] = '*';
 924-		snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst,
 925-		    p_newfirst + p_repl_lines - 1);
 926-		p_line[fillnew] = savestr(buf);
 927-		if (out_of_mem) {
 928-			p_end = 0;
 929-			return false;
 930-		}
 931-		p_char[fillnew++] = '=';
 932-		p_context = 100;
 933-		context = 0;
 934-		p_hunk_beg = p_input_line + 1;
 935-		while (fillold <= p_ptrn_lines || fillnew <= p_end) {
 936-			// line_beginning = ftell(pfp);
 937-			ret = pgetline(&buf, &bufsz, pfp);
 938-			p_input_line++;
 939-			if (ret == -1) {
 940-				if (p_max - fillnew < 3) {
 941-					/* assume blank lines got chopped */
 942-					strlcpy(buf, " \n", bufsz);
 943-				} else {
 944-					fatal("unexpected end of file in patch\n");
 945-				}
 946-			}
 947-			if (*buf == '\t' || *buf == '\n') {
 948-				ch = ' ';	/* assume the space got eaten */
 949-				s = savestr(buf);
 950-			} else {
 951-				ch = *buf;
 952-				s = savestr(buf + 1);
 953-			}
 954-			if (out_of_mem) {
 955-				while (--fillnew > p_ptrn_lines)
 956-					free(p_line[fillnew]);
 957-				p_end = fillold - 1;
 958-				return false;
 959-			}
 960-			switch (ch) {
 961-			case '-':
 962-				if (fillold > p_ptrn_lines) {
 963-					free(s);
 964-					p_end = fillnew - 1;
 965-					malformed();
 966-				}
 967-				p_char[fillold] = ch;
 968-				p_line[fillold] = s;
 969-				p_len[fillold++] = strlen(s);
 970-				if (fillold > p_ptrn_lines) {
 971-					if (remove_special_line()) {
 972-						p_len[fillold - 1] -= 1;
 973-						s[p_len[fillold - 1]] = 0;
 974-					}
 975-				}
 976-				break;
 977-			case '=':
 978-				ch = ' ';
 979-				/* FALL THROUGH */
 980-			case ' ':
 981-				if (fillold > p_ptrn_lines) {
 982-					free(s);
 983-					while (--fillnew > p_ptrn_lines)
 984-						free(p_line[fillnew]);
 985-					p_end = fillold - 1;
 986-					malformed();
 987-				}
 988-				context++;
 989-				p_char[fillold] = ch;
 990-				p_line[fillold] = s;
 991-				p_len[fillold++] = strlen(s);
 992-				s = savestr(s);
 993-				if (out_of_mem) {
 994-					while (--fillnew > p_ptrn_lines)
 995-						free(p_line[fillnew]);
 996-					p_end = fillold - 1;
 997-					return false;
 998-				}
 999-				if (fillold > p_ptrn_lines) {
1000-					if (remove_special_line()) {
1001-						p_len[fillold - 1] -= 1;
1002-						s[p_len[fillold - 1]] = 0;
1003-					}
1004-				}
1005-				/* FALL THROUGH */
1006-			case '+':
1007-				if (fillnew > p_end) {
1008-					free(s);
1009-					while (--fillnew > p_ptrn_lines)
1010-						free(p_line[fillnew]);
1011-					p_end = fillold - 1;
1012-					malformed();
1013-				}
1014-				p_char[fillnew] = ch;
1015-				p_line[fillnew] = s;
1016-				p_len[fillnew++] = strlen(s);
1017-				if (fillold > p_ptrn_lines) {
1018-					if (remove_special_line()) {
1019-						p_len[fillnew - 1] -= 1;
1020-						s[p_len[fillnew - 1]] = 0;
1021-					}
1022-				}
1023-				break;
1024-			default:
1025-				p_end = fillnew;
1026-				malformed();
1027-			}
1028-			if (ch != ' ' && context > 0) {
1029-				if (context < p_context)
1030-					p_context = context;
1031-				context = -1000;
1032-			}
1033-		}		/* while */
1034-	} else {		/* normal diff--fake it up */
1035-		char	hunk_type;
1036-		int	i;
1037-		LINENUM	min, max;
1038-
1039-		line_beginning = ftell(pfp);
1040-		p_context = 0;
1041-		ret = pgetline(&buf, &bufsz, pfp);
1042-		p_input_line++;
1043-		if (ret == -1 || !isdigit((unsigned char)*buf)) {
1044-			next_intuit_at(line_beginning, p_input_line);
1045-			return false;
1046-		}
1047-		s = buf;
1048-		p_first = getskiplinenum(&s);
1049-		if (*s == ',') {
1050-			s++;
1051-			p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
1052-		} else
1053-			p_ptrn_lines = (*s != 'a');
1054-		if (p_first >= LINENUM_MAX - p_ptrn_lines)
1055-			malformed();
1056-		hunk_type = *s++;
1057-		if (hunk_type == 'a')
1058-			p_first++;	/* do append rather than insert */
1059-		min = getskiplinenum(&s);
1060-		if (*s == ',')
1061-			max = getlinenum(++s);
1062-		else
1063-			max = min;
1064-		if (min < 0 || min > max || max - min == LINENUM_MAX)
1065-			malformed();
1066-		if (hunk_type == 'd')
1067-			min++;
1068-		p_end = p_ptrn_lines + 1 + max - min + 1;
1069-		p_newfirst = min;
1070-		p_repl_lines = max - min + 1;
1071-		if (p_newfirst > LINENUM_MAX - p_repl_lines ||
1072-		    p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
1073-			malformed();
1074-		p_end = p_ptrn_lines + p_repl_lines + 1;
1075-		if (p_end > MAXHUNKSIZE)
1076-			fatal("hunk too large (%ld lines) at line %ld: %s",
1077-			    p_end, p_input_line, buf);
1078-		while (p_end >= hunkmax)
1079-			grow_hunkmax();
1080-		snprintf(buf, bufsz, "*** %ld,%ld\n", p_first,
1081-		    p_first + p_ptrn_lines - 1);
1082-		p_line[0] = savestr(buf);
1083-		if (out_of_mem) {
1084-			p_end = -1;
1085-			return false;
1086-		}
1087-		p_char[0] = '*';
1088-		for (i = 1; i <= p_ptrn_lines; i++) {
1089-			ret = pgetline(&buf, &bufsz, pfp);
1090-			p_input_line++;
1091-			if (ret == -1)
1092-				fatal("unexpected end of file in patch at line %ld\n",
1093-				    p_input_line);
1094-			if (*buf != '<')
1095-				fatal("< expected at line %ld of patch\n",
1096-				    p_input_line);
1097-			p_line[i] = savestr(buf + 2);
1098-			if (out_of_mem) {
1099-				p_end = i - 1;
1100-				return false;
1101-			}
1102-			p_len[i] = strlen(p_line[i]);
1103-			p_char[i] = '-';
1104-		}
1105-
1106-		if (remove_special_line()) {
1107-			p_len[i - 1] -= 1;
1108-			(p_line[i - 1])[p_len[i - 1]] = 0;
1109-		}
1110-		if (hunk_type == 'c') {
1111-			ret = pgetline(&buf, &bufsz, pfp);
1112-			p_input_line++;
1113-			if (ret == -1)
1114-				fatal("unexpected end of file in patch at line %ld\n",
1115-				    p_input_line);
1116-			if (*buf != '-')
1117-				fatal("--- expected at line %ld of patch\n",
1118-				    p_input_line);
1119-		}
1120-		snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
1121-		p_line[i] = savestr(buf);
1122-		if (out_of_mem) {
1123-			p_end = i - 1;
1124-			return false;
1125-		}
1126-		p_char[i] = '=';
1127-		for (i++; i <= p_end; i++) {
1128-			ret = pgetline(&buf, &bufsz, pfp);
1129-			p_input_line++;
1130-			if (ret == -1)
1131-				fatal("unexpected end of file in patch at line %ld\n",
1132-				    p_input_line);
1133-			if (*buf != '>')
1134-				fatal("> expected at line %ld of patch\n",
1135-				    p_input_line);
1136-			p_line[i] = savestr(buf + 2);
1137-			if (out_of_mem) {
1138-				p_end = i - 1;
1139-				return false;
1140-			}
1141-			p_len[i] = strlen(p_line[i]);
1142-			p_char[i] = '+';
1143-		}
1144-
1145-		if (remove_special_line()) {
1146-			p_len[i - 1] -= 1;
1147-			(p_line[i - 1])[p_len[i - 1]] = 0;
1148-		}
1149-	}
1150-	if (reverse)		/* backwards patch? */
1151-		if (!pch_swap())
1152-			say("Not enough memory to swap next hunk!\n");
1153-#ifdef DEBUGGING
1154-	if (debug & 2) {
1155-		int	i;
1156-		char	special;
1157-
1158-		for (i = 0; i <= p_end; i++) {
1159-			if (i == p_ptrn_lines)
1160-				special = '^';
1161-			else
1162-				special = ' ';
1163-			fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1164-			    special, p_line[i]);
1165-			fflush(stderr);
1166-		}
1167-	}
1168-#endif
1169-	if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1170-		p_char[p_end + 1] = '^';	/* add a stopper for apply_hunk */
1171-	return true;
1172-}
1173-
1174-/*
1175- * Input a line from the patch file, worrying about indentation.
1176- */
1177-int
1178-pgetline(char **bf, size_t *sz, FILE *fp)
1179-{
1180-	char	*s;
1181-	int	indent = 0;
1182-	int	ret;
1183-
1184-	ret = getline(bf, sz, fp);
1185-
1186-	if (p_indent && ret != -1) {
1187-		for (s = buf;
1188-		    indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1189-		    s++) {
1190-			if (*s == '\t')
1191-				indent += 8 - (indent % 7);
1192-			else
1193-				indent++;
1194-		}
1195-		if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
1196-			fatal("buffer too small in pgetline()\n");
1197-	}
1198-	return ret;
1199-}
1200-
1201-/*
1202- * Reverse the old and new portions of the current hunk.
1203- */
1204-bool
1205-pch_swap(void)
1206-{
1207-	char	**tp_line;	/* the text of the hunk */
1208-	ssize_t	*tp_len;	/* length of each line */
1209-	char	*tp_char;	/* +, -, and ! */
1210-	LINENUM	i;
1211-	LINENUM	n;
1212-	bool	blankline = false;
1213-	char	*s;
1214-
1215-	i = p_first;
1216-	p_first = p_newfirst;
1217-	p_newfirst = i;
1218-
1219-	/* make a scratch copy */
1220-
1221-	tp_line = p_line;
1222-	tp_len = p_len;
1223-	tp_char = p_char;
1224-	p_line = NULL;	/* force set_hunkmax to allocate again */
1225-	p_len = NULL;
1226-	p_char = NULL;
1227-	set_hunkmax();
1228-	if (p_line == NULL || p_len == NULL || p_char == NULL) {
1229-
1230-		free(p_line);
1231-		p_line = tp_line;
1232-		free(p_len);
1233-		p_len = tp_len;
1234-		free(p_char);
1235-		p_char = tp_char;
1236-		return false;	/* not enough memory to swap hunk! */
1237-	}
1238-	/* now turn the new into the old */
1239-
1240-	i = p_ptrn_lines + 1;
1241-	if (tp_char[i] == '\n') {	/* account for possible blank line */
1242-		blankline = true;
1243-		i++;
1244-	}
1245-	if (p_efake >= 0) {	/* fix non-freeable ptr range */
1246-		if (p_efake <= i)
1247-			n = p_end - i + 1;
1248-		else
1249-			n = -i;
1250-		p_efake += n;
1251-		p_bfake += n;
1252-	}
1253-	for (n = 0; i <= p_end; i++, n++) {
1254-		p_line[n] = tp_line[i];
1255-		p_char[n] = tp_char[i];
1256-		if (p_char[n] == '+')
1257-			p_char[n] = '-';
1258-		p_len[n] = tp_len[i];
1259-	}
1260-	if (blankline) {
1261-		i = p_ptrn_lines + 1;
1262-		p_line[n] = tp_line[i];
1263-		p_char[n] = tp_char[i];
1264-		p_len[n] = tp_len[i];
1265-		n++;
1266-	}
1267-	if (p_char[0] != '=')
1268-		fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1269-		    p_input_line, p_char[0]);
1270-	p_char[0] = '*';
1271-	for (s = p_line[0]; *s; s++)
1272-		if (*s == '-')
1273-			*s = '*';
1274-
1275-	/* now turn the old into the new */
1276-
1277-	if (p_char[0] != '*')
1278-		fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1279-		    p_input_line, p_char[0]);
1280-	tp_char[0] = '=';
1281-	for (s = tp_line[0]; *s; s++)
1282-		if (*s == '*')
1283-			*s = '-';
1284-	for (i = 0; n <= p_end; i++, n++) {
1285-		p_line[n] = tp_line[i];
1286-		p_char[n] = tp_char[i];
1287-		if (p_char[n] == '-')
1288-			p_char[n] = '+';
1289-		p_len[n] = tp_len[i];
1290-	}
1291-
1292-	if (i != p_ptrn_lines + 1)
1293-		fatal("Malformed patch at line %ld: expected %ld lines, "
1294-		    "got %ld\n",
1295-		    p_input_line, p_ptrn_lines + 1, i);
1296-
1297-	i = p_ptrn_lines;
1298-	p_ptrn_lines = p_repl_lines;
1299-	p_repl_lines = i;
1300-
1301-	free(tp_line);
1302-	free(tp_len);
1303-	free(tp_char);
1304-
1305-	return true;
1306-}
1307-
1308-/*
1309- * Return the specified line position in the old file of the old context.
1310- */
1311-LINENUM
1312-pch_first(void)
1313-{
1314-	return p_first;
1315-}
1316-
1317-/*
1318- * Return the number of lines of old context.
1319- */
1320-LINENUM
1321-pch_ptrn_lines(void)
1322-{
1323-	return p_ptrn_lines;
1324-}
1325-
1326-/*
1327- * Return the probable line position in the new file of the first line.
1328- */
1329-LINENUM
1330-pch_newfirst(void)
1331-{
1332-	return p_newfirst;
1333-}
1334-
1335-/*
1336- * Return the number of lines in the replacement text including context.
1337- */
1338-LINENUM
1339-pch_repl_lines(void)
1340-{
1341-	return p_repl_lines;
1342-}
1343-
1344-/*
1345- * Return the number of lines in the whole hunk.
1346- */
1347-LINENUM
1348-pch_end(void)
1349-{
1350-	return p_end;
1351-}
1352-
1353-/*
1354- * Return the number of context lines before the first changed line.
1355- */
1356-LINENUM
1357-pch_context(void)
1358-{
1359-	return p_context;
1360-}
1361-
1362-/*
1363- * Return the length of a particular patch line.
1364- */
1365-ssize_t
1366-pch_line_len(LINENUM line)
1367-{
1368-	return p_len[line];
1369-}
1370-
1371-/*
1372- * Return the control character (+, -, *, !, etc) for a patch line.
1373- */
1374-char
1375-pch_char(LINENUM line)
1376-{
1377-	return p_char[line];
1378-}
1379-
1380-/*
1381- * Return a pointer to a particular patch line.
1382- */
1383-char *
1384-pfetch(LINENUM line)
1385-{
1386-	return p_line[line];
1387-}
1388-
1389-/*
1390- * Return where in the patch file this hunk began, for error messages.
1391- */
1392-LINENUM
1393-pch_hunk_beg(void)
1394-{
1395-	return p_hunk_beg;
1396-}
1397-
1398-/*
1399- * Apply an ed script by feeding ed itself.
1400- */
1401-void
1402-do_ed_script(void)
1403-{
1404-	char	*t;
1405-	long	beginning_of_this_line;
1406-	FILE	*pipefp = NULL;
1407-	int	continuation;
1408-
1409-	if (!skip_rest_of_patch) {
1410-		if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1411-			unlink(TMPOUTNAME);
1412-			fatal("can't create temp file %s", TMPOUTNAME);
1413-		}
1414-		snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED,
1415-		    verbose ? "" : "s", TMPOUTNAME);
1416-		pipefp = popen(buf, "w");
1417-	}
1418-	for (;;) {
1419-		beginning_of_this_line = ftell(pfp);
1420-		if (pgetline(&buf, &bufsz, pfp) == -1) {
1421-			next_intuit_at(beginning_of_this_line, p_input_line);
1422-			break;
1423-		}
1424-		p_input_line++;
1425-		for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1426-			;
1427-		/* POSIX defines allowed commands as {a,c,d,i,s} */
1428-		if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
1429-		    *t == 'd' || *t == 'i' || *t == 's')) {
1430-			if (pipefp != NULL)
1431-				fprintf(pipefp, "%s", buf);
1432-			if (*t == 's') {
1433-				for (;;) {
1434-					continuation = 0;
1435-					t = strchr(buf, '\0') - 1;
1436-					while (--t >= buf && *t == '\\')
1437-						continuation = !continuation;
1438-					if (!continuation ||
1439-					    pgetline(&buf, &bufsz, pfp) == -1)
1440-						break;
1441-					if (pipefp != NULL)
1442-						fprintf(pipefp, "%s", buf);
1443-				}
1444-			} else if (*t != 'd') {
1445-				while (pgetline(&buf, &bufsz, pfp) != -1) {
1446-					p_input_line++;
1447-					if (pipefp != NULL)
1448-						fprintf(pipefp, "%s", buf);
1449-					if (strEQ(buf, ".\n"))
1450-						break;
1451-				}
1452-			}
1453-		} else {
1454-			next_intuit_at(beginning_of_this_line, p_input_line);
1455-			break;
1456-		}
1457-	}
1458-	if (pipefp == NULL)
1459-		return;
1460-	fprintf(pipefp, "w\n");
1461-	fprintf(pipefp, "q\n");
1462-	fflush(pipefp);
1463-	pclose(pipefp);
1464-	ignore_signals();
1465-	if (!check_only) {
1466-		if (move_file(TMPOUTNAME, outname) < 0) {
1467-			toutkeep = true;
1468-			chmod(TMPOUTNAME, filemode);
1469-		} else
1470-			chmod(outname, filemode);
1471-	}
1472-	set_signals(1);
1473-}
1474-
1475-/*
1476- * Choose the name of the file to be patched based on POSIX rules.
1477- * NOTE: the POSIX rules are amazingly stupid and we only follow them
1478- *       if the user specified --posix or set POSIXLY_CORRECT.
1479- */
1480-static char *
1481-posix_name(const struct file_name *names, bool assume_exists)
1482-{
1483-	char *path = NULL;
1484-	int i;
1485-
1486-	/*
1487-	 * POSIX states that the filename will be chosen from one
1488-	 * of the old, new and index names (in that order) if
1489-	 * the file exists relative to CWD after -p stripping.
1490-	 */
1491-	for (i = 0; i < MAX_FILE; i++) {
1492-		if (names[i].path != NULL && names[i].exists) {
1493-			path = names[i].path;
1494-			break;
1495-		}
1496-	}
1497-	if (path == NULL && !assume_exists) {
1498-		/*
1499-		 * No files found, look for something we can checkout from
1500-		 * RCS/SCCS dirs.  Same order as above.
1501-		 */
1502-		for (i = 0; i < MAX_FILE; i++) {
1503-			if (names[i].path != NULL &&
1504-			    (path = checked_in(names[i].path)) != NULL)
1505-				break;
1506-		}
1507-		/*
1508-		 * Still no match?  Check to see if the diff could be creating
1509-		 * a new file.
1510-		 */
1511-		if (path == NULL && ok_to_create_file &&
1512-		    names[NEW_FILE].path != NULL)
1513-			path = names[NEW_FILE].path;
1514-	}
1515-
1516-	return path ? savestr(path) : NULL;
1517-}
1518-
1519-/*
1520- * Choose the name of the file to be patched based the "best" one
1521- * available.
1522- */
1523-static char *
1524-best_name(const struct file_name *names, bool assume_exists)
1525-{
1526-	size_t min_components, min_baselen, min_len, tmp;
1527-	char *best = NULL;
1528-	int i;
1529-
1530-	/*
1531-	 * The "best" name is the one with the fewest number of path
1532-	 * components, the shortest basename length, and the shortest
1533-	 * overall length (in that order).  We only use the Index: file
1534-	 * if neither of the old or new files could be intuited from
1535-	 * the diff header.
1536-	 */
1537-	min_components = min_baselen = min_len = SIZE_MAX;
1538-	for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1539-		if (names[i].path == NULL ||
1540-		    (!names[i].exists && !assume_exists))
1541-			continue;
1542-		if ((tmp = num_components(names[i].path)) > min_components)
1543-			continue;
1544-		min_components = tmp;
1545-		if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1546-			continue;
1547-		min_baselen = tmp;
1548-		if ((tmp = strlen(names[i].path)) > min_len)
1549-			continue;
1550-		min_len = tmp;
1551-		best = names[i].path;
1552-	}
1553-	if (best == NULL) {
1554-		/*
1555-		 * No files found, look for something we can checkout from
1556-		 * RCS/SCCS dirs.  Logic is identical to that above...
1557-		 */
1558-		min_components = min_baselen = min_len = SIZE_MAX;
1559-		for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1560-			if (names[i].path == NULL ||
1561-			    checked_in(names[i].path) == NULL)
1562-				continue;
1563-			if ((tmp = num_components(names[i].path)) > min_components)
1564-				continue;
1565-			min_components = tmp;
1566-			if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1567-				continue;
1568-			min_baselen = tmp;
1569-			if ((tmp = strlen(names[i].path)) > min_len)
1570-				continue;
1571-			min_len = tmp;
1572-			best = names[i].path;
1573-		}
1574-		/*
1575-		 * Still no match?  Check to see if the diff could be creating
1576-		 * a new file.
1577-		 */
1578-		if (best == NULL && ok_to_create_file &&
1579-		    names[NEW_FILE].path != NULL)
1580-			best = names[NEW_FILE].path;
1581-	}
1582-
1583-	return best ? savestr(best) : NULL;
1584-}
1585-
1586-static size_t
1587-num_components(const char *path)
1588-{
1589-	size_t n;
1590-	const char *cp;
1591-
1592-	for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1593-		while (*cp == '/')
1594-			cp++;		/* skip consecutive slashes */
1595-	}
1596-	return n;
1597-}
+0, -59
 1@@ -1,59 +0,0 @@
 2-/*
 3- * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
 4- * $DragonFly: src/usr.bin/patch/pch.h,v 1.1 2004/09/24 18:44:28 joerg Exp $
 5- * $NetBSD: pch.h,v 1.11 2024/07/12 15:48:39 manu Exp $
 6- */
 7-
 8-/*
 9- * patch - a program to apply diffs to original files
10- * 
11- * Copyright 1986, Larry Wall
12- * 
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- * 
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- * 
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-#define OLD_FILE	0
35-#define NEW_FILE	1
36-#define INDEX_FILE	2
37-#define MAX_FILE	3
38-
39-struct file_name {
40-	char *path;
41-	bool exists;
42-};
43-
44-void		re_patch(void);
45-void		open_patch_file(const char *);
46-void		set_hunkmax(void);
47-bool		there_is_another_patch(void);
48-bool		another_hunk(void);
49-bool		pch_swap(void);
50-char		*pfetch(LINENUM);
51-ssize_t		pch_line_len(LINENUM);
52-LINENUM		pch_first(void);
53-LINENUM		pch_ptrn_lines(void);
54-LINENUM		pch_newfirst(void);
55-LINENUM		pch_repl_lines(void);
56-LINENUM		pch_end(void);
57-LINENUM		pch_context(void);
58-LINENUM		pch_hunk_beg(void);
59-char		pch_char(LINENUM);
60-void		do_ed_script(void);
+0, -444
  1@@ -1,444 +0,0 @@
  2-/*
  3- * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
  4- * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
  5- * $NetBSD: util.c,v 1.30 2021/05/25 11:25:59 cjep Exp $
  6- */
  7-
  8-/*
  9- * patch - a program to apply diffs to original files
 10- * 
 11- * Copyright 1986, Larry Wall
 12- * 
 13- * Redistribution and use in source and binary forms, with or without
 14- * modification, are permitted provided that the following condition is met:
 15- * 1. Redistributions of source code must retain the above copyright notice,
 16- * this condition and the following disclaimer.
 17- * 
 18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
 19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 28- * SUCH DAMAGE.
 29- * 
 30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 31- * behaviour
 32- */
 33-
 34-#include <sys/param.h>
 35-#include <sys/stat.h>
 36-
 37-#include <ctype.h>
 38-#include <errno.h>
 39-#include <fcntl.h>
 40-#include <libgen.h>
 41-#include <signal.h>
 42-#include <stdarg.h>
 43-#include <stdlib.h>
 44-#include <stdio.h>
 45-#include <string.h>
 46-#include <unistd.h>
 47-
 48-#include "common.h"
 49-#include "util.h"
 50-#include "backupfile.h"
 51-#include "pathnames.h"
 52-
 53-/* Rename a file, copying it if necessary. */
 54-
 55-int
 56-move_file(const char *from, const char *to)
 57-{
 58-	int	fromfd;
 59-	ssize_t	i;
 60-
 61-	/* to stdout? */
 62-
 63-	if (strEQ(to, "-")) {
 64-#ifdef DEBUGGING
 65-		if (debug & 4)
 66-			say("Moving %s to stdout.\n", from);
 67-#endif
 68-		fromfd = open(from, O_RDONLY);
 69-		if (fromfd < 0)
 70-			pfatal("internal error, can't reopen %s", from);
 71-		while ((i = read(fromfd, buf, bufsz)) > 0)
 72-			if (write(STDOUT_FILENO, buf, i) != i)
 73-				pfatal("write failed");
 74-		close(fromfd);
 75-		return 0;
 76-	}
 77-	if (backup_file(to) < 0) {
 78-		say("Can't backup %s, output is in %s: %s\n", to, from,
 79-		    strerror(errno));
 80-		return -1;
 81-	}
 82-#ifdef DEBUGGING
 83-	if (debug & 4)
 84-		say("Moving %s to %s.\n", from, to);
 85-#endif
 86-	if (rename(from, to) < 0) {
 87-		if (errno != EXDEV || copy_file(from, to) < 0) {
 88-			say("Can't create %s, output is in %s: %s\n",
 89-			    to, from, strerror(errno));
 90-			return -1;
 91-		}
 92-	}
 93-	return 0;
 94-}
 95-
 96-/* Backup the original file.  */
 97-
 98-int
 99-backup_file(const char *orig)
100-{
101-	struct stat	filestat;
102-	char		bakname[PATH_MAX], *s, *simplename;
103-	dev_t		orig_device;
104-	ino_t		orig_inode;
105-
106-	if (backup_type == none || stat(orig, &filestat) != 0)
107-		return 0;			/* nothing to do */
108-	/*
109-	 * If the user used zero prefixes or suffixes, then
110-	 * he doesn't want backups.  Yet we have to remove
111-	 * orig to break possible hardlinks.
112-	 */
113-	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
114-		unlink(orig);
115-		return 0;
116-	}
117-	orig_device = filestat.st_dev;
118-	orig_inode = filestat.st_ino;
119-
120-	if (origprae) {
121-		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
122-		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
123-			fatal("filename %s too long for buffer\n", origprae);
124-	} else {
125-		if ((s = find_backup_file_name(orig)) == NULL)
126-			fatal("out of memory\n");
127-		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
128-			fatal("filename %s too long for buffer\n", s);
129-		free(s);
130-	}
131-
132-	if ((simplename = strrchr(bakname, '/')) != NULL)
133-		simplename = simplename + 1;
134-	else
135-		simplename = bakname;
136-
137-	/*
138-	 * Find a backup name that is not the same file. Change the
139-	 * first lowercase char into uppercase; if that isn't
140-	 * sufficient, chop off the first char and try again.
141-	 */
142-	while (stat(bakname, &filestat) == 0 &&
143-	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
144-		/* Skip initial non-lowercase chars.  */
145-		for (s = simplename; *s && !islower((unsigned char)*s); s++)
146-			;
147-		if (*s)
148-			*s = toupper((unsigned char)*s);
149-		else
150-			memmove(simplename, simplename + 1,
151-			    strlen(simplename + 1) + 1);
152-	}
153-#ifdef DEBUGGING
154-	if (debug & 4)
155-		say("Moving %s to %s.\n", orig, bakname);
156-#endif
157-	if (rename(orig, bakname) < 0) {
158-		if (errno != EXDEV || copy_file(orig, bakname) < 0)
159-			return -1;
160-	}
161-	return 0;
162-}
163-
164-/*
165- * Copy a file.
166- */
167-int
168-copy_file(const char *from, const char *to)
169-{
170-	int	tofd, fromfd;
171-	ssize_t	i;
172-
173-	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
174-	if (tofd < 0)
175-		return -1;
176-	fromfd = open(from, O_RDONLY, 0);
177-	if (fromfd < 0)
178-		pfatal("internal error, can't reopen %s", from);
179-	while ((i = read(fromfd, buf, bufsz)) > 0)
180-		if (write(tofd, buf, i) != i)
181-			pfatal("write to %s failed", to);
182-	close(fromfd);
183-	close(tofd);
184-	return 0;
185-}
186-
187-/*
188- * Allocate a unique area for a string.
189- */
190-char *
191-savestr(const char *s)
192-{
193-	char	*rv;
194-
195-	if (!s)
196-		s = "Oops";
197-	rv = strdup(s);
198-	if (rv == NULL) {
199-		if (using_plan_a)
200-			out_of_mem = true;
201-		else
202-			fatal("out of memory\n");
203-	}
204-	return rv;
205-}
206-
207-/*
208- * Vanilla terminal output (buffered).
209- */
210-void
211-say(const char *fmt, ...)
212-{
213-	va_list	ap;
214-
215-	va_start(ap, fmt);
216-	vfprintf(stderr, fmt, ap);
217-	va_end(ap);
218-	fflush(stderr);
219-}
220-
221-/*
222- * Terminal output, pun intended.
223- */
224-void
225-fatal(const char *fmt, ...)
226-{
227-	va_list	ap;
228-
229-	va_start(ap, fmt);
230-	fprintf(stderr, "patch: **** ");
231-	vfprintf(stderr, fmt, ap);
232-	va_end(ap);
233-	my_exit(2);
234-}
235-
236-/*
237- * Say something from patch, something from the system, then silence . . .
238- */
239-void
240-pfatal(const char *fmt, ...)
241-{
242-	va_list	ap;
243-	int	errnum = errno;
244-
245-	fprintf(stderr, "patch: **** ");
246-	va_start(ap, fmt);
247-	vfprintf(stderr, fmt, ap);
248-	va_end(ap);
249-	fprintf(stderr, ": %s\n", strerror(errnum));
250-	my_exit(2);
251-}
252-
253-/*
254- * Get a response from the user via /dev/tty
255- */
256-void
257-ask(const char *fmt, ...)
258-{
259-	va_list	ap;
260-	ssize_t	nr = 0;
261-	static	int ttyfd = -1;
262-
263-	va_start(ap, fmt);
264-	vfprintf(stdout, fmt, ap);
265-	va_end(ap);
266-	fflush(stdout);
267-	if (ttyfd < 0)
268-		ttyfd = open(_PATH_TTY, O_RDONLY);
269-	if (ttyfd >= 0) {
270-		if ((nr = read(ttyfd, buf, bufsz)) > 0 &&
271-		    buf[nr - 1] == '\n')
272-			buf[nr - 1] = '\0';
273-	}
274-	if (ttyfd < 0 || nr <= 0) {
275-		/* no tty or error reading, pretend user entered 'return' */
276-		putchar('\n');
277-		buf[0] = '\0';
278-	}
279-}
280-
281-/*
282- * How to handle certain events when not in a critical region.
283- */
284-void
285-set_signals(int reset)
286-{
287-	static sig_t	hupval, intval;
288-
289-	if (!reset) {
290-		hupval = signal(SIGHUP, SIG_IGN);
291-		if (hupval != SIG_IGN)
292-			hupval = my_exit;
293-		intval = signal(SIGINT, SIG_IGN);
294-		if (intval != SIG_IGN)
295-			intval = my_exit;
296-	}
297-	signal(SIGHUP, hupval);
298-	signal(SIGINT, intval);
299-}
300-
301-/*
302- * How to handle certain events when in a critical region.
303- */
304-void
305-ignore_signals(void)
306-{
307-	signal(SIGHUP, SIG_IGN);
308-	signal(SIGINT, SIG_IGN);
309-}
310-
311-/*
312- * Make sure we'll have the directories to create a file. If `striplast' is
313- * true, ignore the last element of `filename'.
314- */
315-
316-void
317-makedirs(const char *filename, bool striplast)
318-{
319-	char	*tmpbuf;
320-
321-	if ((tmpbuf = strdup(filename)) == NULL)
322-		fatal("out of memory\n");
323-
324-	if (striplast) {
325-		char	*s = strrchr(tmpbuf, '/');
326-		if (s == NULL) {
327-			free(tmpbuf);
328-			return;	/* nothing to be done */
329-		}
330-		*s = '\0';
331-	}
332-	if (mkpath(tmpbuf) != 0)
333-		pfatal("creation of %s failed", tmpbuf);
334-	free(tmpbuf);
335-}
336-
337-/*
338- * Make filenames more reasonable.
339- */
340-char *
341-fetchname(const char *at, bool *exists, int strip_leading)
342-{
343-	char		*fullname, *name, *t;
344-	int		sleading, tab;
345-	struct stat	filestat;
346-
347-	if (at == NULL || *at == '\0')
348-		return NULL;
349-	while (isspace((unsigned char)*at))
350-		at++;
351-#ifdef DEBUGGING
352-	if (debug & 128)
353-		say("fetchname %s %d\n", at, strip_leading);
354-#endif
355-	/* So files can be created by diffing against /dev/null.  */
356-	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
357-		return NULL;
358-	name = fullname = t = savestr(at);
359-
360-	tab = strchr(t, '\t') != NULL;
361-	/* Strip off up to `strip_leading' path components and NUL terminate. */
362-	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
363-	    !isspace((unsigned char)*t)); t++) {
364-		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
365-			if (--sleading >= 0)
366-				name = t + 1;
367-	}
368-	*t = '\0';
369-
370-	/*
371-	 * If no -p option was given (957 is the default value!), we were
372-	 * given a relative pathname, and the leading directories that we
373-	 * just stripped off all exist, put them back on.
374-	 */
375-	if (strip_leading == 957 && name != fullname && *fullname != '/') {
376-		name[-1] = '\0';
377-		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
378-			name[-1] = '/';
379-			name = fullname;
380-		}
381-	}
382-	name = savestr(name);
383-	free(fullname);
384-
385-	*exists = stat(name, &filestat) == 0;
386-	return name;
387-}
388-
389-/*
390- * Takes the name returned by fetchname and looks in RCS/SCCS directories
391- * for a checked in version.
392- */
393-char *
394-checked_in(char *file)
395-{
396-	char		*filebase, *filedir, tmpbuf[PATH_MAX];
397-	struct stat	filestat;
398-
399-	filebase = basename(file);
400-	filedir = dirname(file);
401-
402-#define try(f, a1, a2, a3) \
403-(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
404-
405-	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
406-	    try("%s/RCS/%s%s", filedir, filebase, "") ||
407-	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
408-	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
409-	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
410-		return file;
411-
412-	return NULL;
413-}
414-
415-void
416-version(void)
417-{
418-	printf("Patch version 2.0-12u9-NetBSD\n");
419-	my_exit(EXIT_SUCCESS);
420-}
421-
422-/*
423- * Exit with cleanup.
424- */
425-void
426-my_exit(int status)
427-{
428-	unlink(TMPINNAME);
429-	if (!toutkeep)
430-		unlink(TMPOUTNAME);
431-	if (!trejkeep)
432-		unlink(TMPREJNAME);
433-	unlink(TMPPATNAME);
434-	exit(status);
435-}
436-
437-void *
438-pch_realloc(void *ptr, size_t number, size_t size)
439-{
440-	if (number > SIZE_MAX / size) {
441-		errno = EOVERFLOW;
442-		return NULL;
443-	}
444-	return realloc(ptr, number * size);
445-}
+0, -52
 1@@ -1,52 +0,0 @@
 2-/*
 3- * $OpenBSD: util.h,v 1.15 2005/06/20 07:14:06 otto Exp $
 4- * $DragonFly: src/usr.bin/patch/util.h,v 1.2 2007/09/29 23:11:10 swildner Exp $
 5- * $NetBSD: util.h,v 1.13 2018/06/18 18:33:31 christos Exp $
 6- */
 7-
 8-/*
 9- * patch - a program to apply diffs to original files
10- * 
11- * Copyright 1986, Larry Wall
12- * 
13- * Redistribution and use in source and binary forms, with or without
14- * modification, are permitted provided that the following condition is met:
15- * 1. Redistributions of source code must retain the above copyright notice,
16- * this condition and the following disclaimer.
17- * 
18- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21- * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28- * SUCH DAMAGE.
29- * 
30- * -C option added in 1998, original code by Marc Espie, based on FreeBSD
31- * behaviour
32- */
33-
34-char		*fetchname(const char *, bool *, int);
35-char		*checked_in(char *);
36-LINENUM		strtolinenum(char *, char **);
37-int		backup_file(const char *);
38-int		move_file(const char *, const char *);
39-int		copy_file(const char *, const char *);
40-void		say(const char *, ...) __printflike(1, 2);
41-void		fatal(const char *, ...) __printflike(1, 2) __dead;
42-void		pfatal(const char *, ...) __printflike(1, 2) __dead;
43-void		ask(const char *, ...) __printflike(1, 2);
44-char		*savestr(const char *);
45-void		set_signals(int);
46-void		ignore_signals(void);
47-void		makedirs(const char *, bool);
48-void		version(void) __dead;
49-void		my_exit(int) __dead;
50-void		*pch_realloc(void *, size_t, size_t);
51-
52-/* in mkpath.c */
53-extern int mkpath(char *);
+164, -0
  1@@ -0,0 +1,164 @@
  2+/* See LICENSE file for copyright and license details. */
  3+#include "util.h"
  4+
  5+#include <ctype.h>
  6+#include <fcntl.h>
  7+#include <stdio.h>
  8+#include <stdlib.h>
  9+#include <string.h>
 10+#include <unistd.h>
 11+
 12+static void
 13+usage(void)
 14+{
 15+	eprintf("usage: %s [-P fd] [-m type] [-S salt] [password] [salt]\n", argv0);
 16+}
 17+
 18+// ?man mkpasswd: encrypt the given password using salt
 19+// ?man arguments: [password] [salt]
 20+// ?man encrypt password using crypt(3) with random or provided salt
 21+int
 22+main(int argc, char *argv[])
 23+{
 24+	char *mflag;
 25+	char *sflag;
 26+	char *parg;
 27+	char *password;
 28+	char *cryptpass;
 29+	int pfd;
 30+	int fd;
 31+	int r;
 32+	int i;
 33+	int len;
 34+	int id;
 35+	char salt[128];
 36+	char rand_bytes[32];
 37+	char passbuf[1024];
 38+	static const char saltchars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 39+
 40+	mflag = NULL;
 41+	sflag = NULL;
 42+	parg = NULL;
 43+	password = NULL;
 44+	cryptpass = NULL;
 45+	pfd = -1;
 46+
 47+	ARGBEGIN {
 48+	// ?man -P:fd: read password from file descriptor
 49+	case 'P':
 50+		parg = EARGF(usage());
 51+		pfd = estrtonum(parg, 0, INT_MAX);
 52+		break;
 53+	// ?man -m:type: encryption method (des, md5, sha256, sha512)
 54+	case 'm':
 55+		mflag = EARGF(usage());
 56+		break;
 57+	// ?man -S:salt: salt to use
 58+	case 'S':
 59+		sflag = EARGF(usage());
 60+		break;
 61+	default:
 62+		usage();
 63+	} ARGEND
 64+
 65+	if (argc > 2)
 66+		usage();
 67+
 68+	if (argc == 2) {
 69+		if (sflag)
 70+			eprintf("duplicate salt\n");
 71+		sflag = argv[1];
 72+	}
 73+
 74+	if (argc >= 1)
 75+		password = argv[0];
 76+
 77+	/* default type is des */
 78+	if (!mflag)
 79+		mflag = "des";
 80+
 81+	if (strcasecmp(mflag, "des") == 0) {
 82+		id = 0;
 83+		len = 2;
 84+	} else if (strcasecmp(mflag, "md5") == 0) {
 85+		id = 1;
 86+		len = 8;
 87+	} else if (strcasecmp(mflag, "sha256") == 0) {
 88+		id = 5;
 89+		len = 16;
 90+	} else if (strcasecmp(mflag, "sha512") == 0) {
 91+		id = 6;
 92+		len = 16;
 93+	} else {
 94+		eprintf("bad method: %s\n", mflag);
 95+	}
 96+
 97+	if (sflag) {
 98+		/* validate provided salt */
 99+		char *s = sflag;
100+		while (*s) {
101+			if (!isalnum((unsigned char)*s) && *s != '.' && *s != '/')
102+				eprintf("bad SALT (need [a-zA-Z0-9./])\n");
103+			s++;
104+		}
105+		if (id > 0)
106+			snprintf(salt, sizeof(salt), "$%d$%s", id, sflag);
107+		else
108+			estrlcpy(salt, sflag, sizeof(salt));
109+	} else {
110+		/* generate random salt */
111+		fd = open("/dev/urandom", O_RDONLY);
112+		if (fd < 0)
113+			eprintf("open /dev/urandom:");
114+		r = read(fd, rand_bytes, len);
115+		if (r < len)
116+			eprintf("read /dev/urandom:");
117+		close(fd);
118+
119+		if (id > 0) {
120+			snprintf(salt, sizeof(salt), "$%d$", id);
121+			for (i = 0; i < len; i++)
122+				salt[3 + i] = saltchars[(unsigned char)rand_bytes[i] % 64];
123+			salt[3 + len] = '\0';
124+		} else {
125+			for (i = 0; i < len; i++)
126+				salt[i] = saltchars[(unsigned char)rand_bytes[i] % 64];
127+			salt[len] = '\0';
128+		}
129+	}
130+
131+	if (pfd >= 0) {
132+		if (dup2(pfd, 0) == -1)
133+			eprintf("dup2:");
134+		close(pfd);
135+	}
136+
137+	if (!password) {
138+		if (isatty(0)) {
139+			char *p = getpass("Password: ");
140+			if (!p)
141+				eprintf("getpass failed\n");
142+			estrlcpy(passbuf, p, sizeof(passbuf));
143+			password = passbuf;
144+		} else {
145+			/* read from stdin */
146+			int bytes = read(0, passbuf, sizeof(passbuf) - 1);
147+			if (bytes < 0)
148+				eprintf("read stdin:");
149+			/* strip trailing newline or cr */
150+			while (bytes > 0 && (passbuf[bytes - 1] == '\n' || passbuf[bytes - 1] == '\r'))
151+				bytes--;
152+			passbuf[bytes] = '\0';
153+			password = passbuf;
154+		}
155+	}
156+
157+	cryptpass = crypt(password, salt);
158+	if (!cryptpass)
159+		eprintf("crypt:");
160+	printf("%s\n", cryptpass);
161+
162+	if (fshut(stdout, "<stdout>"))
163+		return 2;
164+	return 0;
165+}
+4, -72
 1@@ -20,11 +20,6 @@ cmd/extra/lsblk.o: shared/util.h
 2 cmd/extra/lsblk.o: shared/arg.h
 3 cmd/extra/lsblk.o: shared/compat.h
 4 cmd/extra/lsblk.o: shared/diskutil.h
 5-cmd/extra/diff3/diff3.o: cmd/extra/diff3/diff3.c
 6-cmd/extra/diff3/diff3.o: shared/util.h
 7-cmd/extra/diff3/diff3.o: shared/arg.h
 8-cmd/extra/diff3/diff3.o: shared/compat.h
 9-cmd/extra/diff3/diff3.o: cmd/extra/diff3/../../posix/diff/xmalloc.h
10 cmd/extra/sync.o: cmd/extra/sync.c
11 cmd/extra/sync.o: shared/arg.h
12 cmd/extra/sync.o: shared/util.h
13@@ -858,36 +853,7 @@ cmd/posix/date.o: cmd/posix/date.c
14 cmd/posix/date.o: shared/util.h
15 cmd/posix/date.o: shared/arg.h
16 cmd/posix/date.o: shared/compat.h
17-cmd/posix/diff/diff.o: cmd/posix/diff/diff.c
18-cmd/posix/diff/diff.o: shared/util.h
19-cmd/posix/diff/diff.o: shared/arg.h
20-cmd/posix/diff/diff.o: shared/compat.h
21-cmd/posix/diff/diff.o: cmd/posix/diff/diff.h
22-cmd/posix/diff/diff.o: cmd/posix/diff/xmalloc.h
23-cmd/posix/diff/diffdir.o: cmd/posix/diff/diffdir.c
24-cmd/posix/diff/diffdir.o: shared/util.h
25-cmd/posix/diff/diffdir.o: shared/arg.h
26-cmd/posix/diff/diffdir.o: shared/compat.h
27-cmd/posix/diff/diffdir.o: cmd/posix/diff/diff.h
28-cmd/posix/diff/diffreg.o: cmd/posix/diff/diffreg.c
29-cmd/posix/diff/diffreg.o: shared/util.h
30-cmd/posix/diff/diffreg.o: shared/arg.h
31-cmd/posix/diff/diffreg.o: shared/compat.h
32-cmd/posix/diff/diffreg.o: cmd/posix/diff/pr.h
33-cmd/posix/diff/diffreg.o: cmd/posix/diff/diff.h
34-cmd/posix/diff/diffreg.o: cmd/posix/diff/xmalloc.h
35-cmd/posix/diff/pr.o: cmd/posix/diff/pr.c
36-cmd/posix/diff/pr.o: shared/util.h
37-cmd/posix/diff/pr.o: shared/arg.h
38-cmd/posix/diff/pr.o: shared/compat.h
39-cmd/posix/diff/pr.o: cmd/posix/diff/pr.h
40-cmd/posix/diff/pr.o: cmd/posix/diff/diff.h
41-cmd/posix/diff/pr.o: cmd/posix/diff/xmalloc.h
42-cmd/posix/diff/xmalloc.o: cmd/posix/diff/xmalloc.c
43-cmd/posix/diff/xmalloc.o: shared/util.h
44-cmd/posix/diff/xmalloc.o: shared/arg.h
45-cmd/posix/diff/xmalloc.o: shared/compat.h
46-cmd/posix/diff/xmalloc.o: cmd/posix/diff/xmalloc.h
47+cmd/posix/diff.o: cmd/posix/diff.c shared/util.h shared/arg.h shared/compat.h
48 cmd/posix/env.o: cmd/posix/env.c
49 cmd/posix/env.o: shared/util.h
50 cmd/posix/env.o: shared/arg.h
51@@ -949,43 +915,9 @@ cmd/posix/uuencode.o: cmd/posix/uuencode.c
52 cmd/posix/uuencode.o: shared/util.h
53 cmd/posix/uuencode.o: shared/arg.h
54 cmd/posix/uuencode.o: shared/compat.h
55-cmd/posix/patch/backupfile.o: cmd/posix/patch/backupfile.c
56-cmd/posix/patch/backupfile.o: cmd/posix/patch/backupfile.h
57-cmd/posix/patch/inp.o: cmd/posix/patch/inp.c
58-cmd/posix/patch/inp.o: cmd/posix/patch/common.h
59-cmd/posix/patch/inp.o: shared/util.h
60-cmd/posix/patch/inp.o: shared/arg.h
61-cmd/posix/patch/inp.o: shared/compat.h
62-cmd/posix/patch/inp.o: cmd/posix/patch/util.h
63-cmd/posix/patch/inp.o: cmd/posix/patch/pch.h
64-cmd/posix/patch/inp.o: cmd/posix/patch/inp.h
65-cmd/posix/patch/mkpath.o: cmd/posix/patch/mkpath.c
66-cmd/posix/patch/patch.o: cmd/posix/patch/patch.c
67-cmd/posix/patch/patch.o: cmd/posix/patch/common.h
68-cmd/posix/patch/patch.o: shared/util.h
69-cmd/posix/patch/patch.o: shared/arg.h
70-cmd/posix/patch/patch.o: shared/compat.h
71-cmd/posix/patch/patch.o: cmd/posix/patch/util.h
72-cmd/posix/patch/patch.o: cmd/posix/patch/pch.h
73-cmd/posix/patch/patch.o: cmd/posix/patch/inp.h
74-cmd/posix/patch/patch.o: cmd/posix/patch/backupfile.h
75-cmd/posix/patch/patch.o: cmd/posix/patch/pathnames.h
76-cmd/posix/patch/pch.o: cmd/posix/patch/pch.c
77-cmd/posix/patch/pch.o: cmd/posix/patch/common.h
78-cmd/posix/patch/pch.o: shared/util.h
79-cmd/posix/patch/pch.o: shared/arg.h
80-cmd/posix/patch/pch.o: shared/compat.h
81-cmd/posix/patch/pch.o: cmd/posix/patch/util.h
82-cmd/posix/patch/pch.o: cmd/posix/patch/pch.h
83-cmd/posix/patch/pch.o: cmd/posix/patch/pathnames.h
84-cmd/posix/patch/util.o: cmd/posix/patch/util.c
85-cmd/posix/patch/util.o: cmd/posix/patch/common.h
86-cmd/posix/patch/util.o: shared/util.h
87-cmd/posix/patch/util.o: shared/arg.h
88-cmd/posix/patch/util.o: shared/compat.h
89-cmd/posix/patch/util.o: cmd/posix/patch/util.h
90-cmd/posix/patch/util.o: cmd/posix/patch/backupfile.h
91-cmd/posix/patch/util.o: cmd/posix/patch/pathnames.h
92+cmd/posix/patch.o: cmd/posix/patch.c shared/util.h shared/arg.h shared/compat.h
93+cmd/posix/diff3.o: cmd/posix/diff3.c shared/util.h shared/arg.h shared/compat.h
94+cmd/pseudo/mkpasswd.o: cmd/pseudo/mkpasswd.c shared/util.h shared/arg.h shared/compat.h
95 cmd/pseudo/base64.o: cmd/pseudo/base64.c
96 cmd/pseudo/base64.o: shared/util.h
97 cmd/pseudo/base64.o: shared/arg.h
+23, -5
 1@@ -2,18 +2,30 @@
 2 #ifndef ARUU_DISKUTIL_H
 3 #define ARUU_DISKUTIL_H
 4 
 5-#include <stdint.h>
 6 #include <stddef.h>
 7+#include <stdint.h>
 8+
 9+#ifndef PATH_DEV
10+#define PATH_DEV "/dev"
11+#endif
12+
13+#ifndef PATH_PROC
14+#define PATH_PROC "/proc"
15+#endif
16+
17+#define PATH_PARTITIONS PATH_PROC "/partitions"
18+#define PATH_MOUNTS PATH_PROC "/mounts"
19 
20 struct BlockDev {
21 	int fd;
22-	uint64_t size; /* size in bytes */
23+	uint64_t size;	 /* size in bytes */
24 	size_t sec_size; /* sector size */
25 };
26 
27 struct BlockDevInfo {
28 	char name[32];
29-	char type[16]; /* disk, part, loop */
30+	char path[128]; /* absolute path to device node */
31+	char type[16];	/* disk, part, loop */
32 	uint64_t size;
33 	int major;
34 	int minor;
35@@ -24,10 +36,16 @@ struct BlockDevInfo {
36 };
37 
38 int blockdev_open(struct BlockDev *dev, const char *path, int writable);
39-int blockdev_read(struct BlockDev *dev, uint64_t sector, void *buf, size_t sector_count);
40-int blockdev_write(struct BlockDev *dev, uint64_t sector, const void *buf, size_t sector_count);
41+int blockdev_read(struct BlockDev *dev, uint64_t sector, void *buf,
42+		  size_t sector_count);
43+int blockdev_write(struct BlockDev *dev, uint64_t sector, const void *buf,
44+		   size_t sector_count);
45 void blockdev_close(struct BlockDev *dev);
46 
47+int blockdev_detect_fs(struct BlockDev *dev, char *type, size_t type_sz,
48+		       char *label, size_t label_sz, char *uuid,
49+		       size_t uuid_sz);
50+
51 struct BlockDevInfo *blockdev_get_list(void);
52 void blockdev_free_list(struct BlockDevInfo *list);
53 
+407, -105
  1@@ -2,14 +2,14 @@
  2 #include "../diskutil.h"
  3 #include "../util.h"
  4 
  5-#include <sys/types.h>
  6-#include <sys/stat.h>
  7+#include <errno.h>
  8 #include <fcntl.h>
  9-#include <unistd.h>
 10 #include <stdio.h>
 11 #include <stdlib.h>
 12 #include <string.h>
 13-#include <errno.h>
 14+#include <sys/stat.h>
 15+#include <sys/types.h>
 16+#include <unistd.h>
 17 
 18 #if defined(__linux__)
 19 #include <sys/ioctl.h>
 20@@ -123,13 +123,22 @@ blockdev_get_list(void)
 21 {
 22 #if defined(__linux__)
 23 	FILE *fp;
 24+	FILE *mfile;
 25 	char line[256];
 26-	struct BlockDevInfo *head = NULL, *tail = NULL;
 27+	char mline[512];
 28+	char devpath[128];
 29+	char mnt[256];
 30+	char fs[64];
 31+	struct BlockDevInfo *head = NULL;
 32+	struct BlockDevInfo *tail = NULL;
 33+	struct BlockDevInfo *info;
 34+	struct BlockDevInfo *curr;
 35 	unsigned int major, minor;
 36 	unsigned long long blocks;
 37 	char name[64];
 38+	int is_part;
 39 
 40-	fp = fopen("/proc/partitions", "r");
 41+	fp = fopen(PATH_PARTITIONS, "r");
 42 	if (!fp)
 43 		return NULL;
 44 
 45@@ -137,24 +146,28 @@ blockdev_get_list(void)
 46 		if (sscanf(line, " %u %u %llu %63s", &major, &minor, &blocks, name) != 4)
 47 			continue;
 48 
 49-		struct BlockDevInfo *info = calloc(1, sizeof(*info));
 50-		if (!info)
 51-			break;
 52+		info = ecalloc(1, sizeof(*info));
 53 
 54 		strlcpy(info->name, name, sizeof(info->name));
 55+		snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, name);
 56 		info->size = blocks * 1024;
 57 		info->major = major;
 58 		info->minor = minor;
 59 
 60-		/* check if this is a partition of an existing disk */
 61-		struct BlockDevInfo *curr;
 62-		int is_part = 0;
 63+		/* search tree topology to link partition node under proper target parent disk */
 64+		is_part = 0;
 65 		for (curr = head; curr; curr = curr->next) {
 66 			size_t len = strlen(curr->name);
 67 			if (strncmp(name, curr->name, len) == 0) {
 68 				char c = name[len];
 69-				if ((c >= '0' && c <= '9') || (c == 'p' && name[len+1] >= '0' && name[len+1] <= '9')) {
 70-					is_part = 1;
 71+				if (curr->name[len - 1] >= '0' && curr->name[len - 1] <= '9') {
 72+					if (c == 'p' && name[len + 1] >= '0' && name[len + 1] <= '9')
 73+						is_part = 1;
 74+				} else {
 75+					if (c >= '0' && c <= '9')
 76+						is_part = 1;
 77+				}
 78+				if (is_part) {
 79 					strlcpy(info->type, "part", sizeof(info->type));
 80 					struct BlockDevInfo *p = curr->parts;
 81 					if (!p) {
 82@@ -181,16 +194,14 @@ blockdev_get_list(void)
 83 	}
 84 	fclose(fp);
 85 
 86-	/* parse mounts to populate mountpoint and fstype */
 87-	FILE *mfile = fopen("/proc/mounts", "r");
 88+	mfile = fopen(PATH_MOUNTS, "r");
 89 	if (mfile) {
 90-		char mline[512];
 91-		char devpath[128], mnt[256], fs[64];
 92 		while (fgets(mline, sizeof(mline), mfile)) {
 93 			if (sscanf(mline, "%127s %255s %63s", devpath, mnt, fs) == 3) {
 94 				char *devname = devpath;
 95-				if (strncmp(devpath, "/dev/", 5) == 0)
 96-					devname = devpath + 5;
 97+				size_t dev_len = strlen(PATH_DEV);
 98+				if (strncmp(devpath, PATH_DEV, dev_len) == 0 && devpath[dev_len] == '/')
 99+					devname = devpath + dev_len + 1;
100 				struct BlockDevInfo *curr;
101 				for (curr = head; curr; curr = curr->next) {
102 					if (strcmp(curr->name, devname) == 0) {
103@@ -214,57 +225,67 @@ blockdev_get_list(void)
104 
105 #elif defined(__FreeBSD__)
106 	char disks[1024];
107-	size_t len = sizeof(disks);
108-	struct BlockDevInfo *head = NULL, *tail = NULL;
109+	char devpath[128];
110+	char *tok;
111+	char *ptr;
112+	struct BlockDevInfo *head = NULL;
113+	struct BlockDevInfo *tail = NULL;
114+	struct BlockDevInfo *info;
115+	struct BlockDevInfo *part;
116+	struct BlockDevInfo *p;
117+	struct BlockDev dev;
118+	DIR *dir;
119+	struct dirent *de;
120+	size_t disk_len;
121+	size_t len;
122+	struct statfs *mntbuf;
123+	int mntsize;
124+	int i;
125 
126+	len = sizeof(disks);
127 	if (sysctlbyname("kern.disks", disks, &len, NULL, 0) < 0)
128 		return NULL;
129 
130-	char *tok, *ptr = disks;
131+	ptr = disks;
132 	while ((tok = strsep(&ptr, " \t")) != NULL) {
133 		if (!*tok)
134 			continue;
135 
136-		struct BlockDevInfo *info = calloc(1, sizeof(*info));
137-		if (!info)
138-			break;
139+		info = ecalloc(1, sizeof(*info));
140 
141 		strlcpy(info->name, tok, sizeof(info->name));
142+		snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, tok);
143 		strlcpy(info->type, "disk", sizeof(info->type));
144 
145-		char devpath[64];
146-		snprintf(devpath, sizeof(devpath), "/dev/%s", tok);
147-		struct BlockDev dev;
148+		snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, tok);
149 		if (blockdev_open(&dev, devpath, 0) == 0) {
150 			info->size = dev.size;
151 			blockdev_close(&dev);
152 		}
153 
154-		DIR *dir = opendir("/dev");
155+		dir = opendir(PATH_DEV);
156 		if (dir) {
157-			struct dirent *de;
158-			size_t disk_len = strlen(tok);
159+			disk_len = strlen(tok);
160 			while ((de = readdir(dir)) != NULL) {
161 				if (strncmp(de->d_name, tok, disk_len) == 0) {
162 					char c = de->d_name[disk_len];
163 					if (c == 's' || c == 'p') {
164-						struct BlockDevInfo *part = calloc(1, sizeof(*part));
165-						if (part) {
166-							strlcpy(part->name, de->d_name, sizeof(part->name));
167-							strlcpy(part->type, "part", sizeof(part->type));
168-							snprintf(devpath, sizeof(devpath), "/dev/%s", de->d_name);
169-							if (blockdev_open(&dev, devpath, 0) == 0) {
170-								part->size = dev.size;
171-								blockdev_close(&dev);
172-							}
173-							struct BlockDevInfo *p = info->parts;
174-							if (!p) {
175-								info->parts = part;
176-							} else {
177-								while (p->next)
178-									p = p->next;
179-								p->next = part;
180-							}
181+						part = ecalloc(1, sizeof(*part));
182+						strlcpy(part->name, de->d_name, sizeof(part->name));
183+						snprintf(part->path, sizeof(part->path), "%s/%s", PATH_DEV, de->d_name);
184+						strlcpy(part->type, "part", sizeof(part->type));
185+						snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, de->d_name);
186+						if (blockdev_open(&dev, devpath, 0) == 0) {
187+							part->size = dev.size;
188+							blockdev_close(&dev);
189+						}
190+						p = info->parts;
191+						if (!p) {
192+							info->parts = part;
193+						} else {
194+							while (p->next)
195+								p = p->next;
196+							p->next = part;
197 						}
198 					}
199 				}
200@@ -280,13 +301,13 @@ blockdev_get_list(void)
201 		tail = info;
202 	}
203 
204-	struct statfs *mntbuf;
205-	int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
206-	for (int i = 0; i < mntsize; i++) {
207-		char *devpath = mntbuf[i].f_mntfromname;
208-		char *devname = devpath;
209-		if (strncmp(devpath, "/dev/", 5) == 0)
210-			devname = devpath + 5;
211+	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
212+	for (i = 0; i < mntsize; i++) {
213+		char *frompath = mntbuf[i].f_mntfromname;
214+		char *devname = frompath;
215+		size_t dev_len = strlen(PATH_DEV);
216+		if (strncmp(frompath, PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
217+			devname = frompath + dev_len + 1;
218 		struct BlockDevInfo *curr;
219 		for (curr = head; curr; curr = curr->next) {
220 			if (strcmp(curr->name, devname) == 0) {
221@@ -307,54 +328,66 @@ blockdev_get_list(void)
222 
223 #elif defined(__OpenBSD__)
224 	char disknames[1024];
225-	size_t len = sizeof(disknames);
226-	int mib[2] = { CTL_HW, HW_DISKNAMES };
227-	struct BlockDevInfo *head = NULL, *tail = NULL;
228+	char devpath[128];
229+	char *tok;
230+	char *ptr;
231+	char *colon;
232+	struct BlockDevInfo *head = NULL;
233+	struct BlockDevInfo *tail = NULL;
234+	struct BlockDevInfo *info;
235+	struct BlockDevInfo *part;
236+	struct BlockDevInfo *p;
237+	struct BlockDev dev;
238+	size_t len;
239+	int mib[2];
240+	char c;
241+	struct statfs *mntbuf;
242+	int mntsize;
243+	int i;
244 
245+	mib[0] = CTL_HW;
246+	mib[1] = HW_DISKNAMES;
247+	len = sizeof(disknames);
248 	if (sysctl(mib, 2, disknames, &len, NULL, 0) < 0)
249 		return NULL;
250 
251-	char *tok, *ptr = disknames;
252+	ptr = disknames;
253 	while ((tok = strsep(&ptr, ",")) != NULL) {
254-		char *colon = strchr(tok, ':');
255+		colon = strchr(tok, ':');
256 		if (colon)
257 			*colon = '\0';
258 		if (!*tok)
259 			continue;
260 
261-		struct BlockDevInfo *info = calloc(1, sizeof(*info));
262-		if (!info)
263-			break;
264+		info = ecalloc(1, sizeof(*info));
265 
266 		strlcpy(info->name, tok, sizeof(info->name));
267+		snprintf(info->path, sizeof(info->path), "%s/r%sc", PATH_DEV, tok);
268 		strlcpy(info->type, "disk", sizeof(info->type));
269 
270-		char devpath[64];
271-		snprintf(devpath, sizeof(devpath), "/dev/r%sc", tok);
272-		struct BlockDev dev;
273+		snprintf(devpath, sizeof(devpath), "%s/r%sc", PATH_DEV, tok);
274 		if (blockdev_open(&dev, devpath, 0) == 0) {
275 			info->size = dev.size;
276 			blockdev_close(&dev);
277 		}
278 
279-		for (char c = 'a'; c <= 'p'; c++) {
280+		for (c = 'a'; c <= 'p'; c++) {
281 			if (c == 'c')
282 				continue;
283-			snprintf(devpath, sizeof(devpath), "/dev/r%s%c", tok, c);
284+			snprintf(devpath, sizeof(devpath), "%s/r%s%c", PATH_DEV, tok, c);
285 			if (blockdev_open(&dev, devpath, 0) == 0) {
286-				struct BlockDevInfo *part = calloc(1, sizeof(*part));
287-				if (part) {
288-					snprintf(part->name, sizeof(part->name), "%s%c", tok, c);
289-					strlcpy(part->type, "part", sizeof(part->type));
290-					part->size = dev.size;
291-					struct BlockDevInfo *p = info->parts;
292-					if (!p) {
293-						info->parts = part;
294-					} else {
295-						while (p->next)
296-							p = p->next;
297-						p->next = part;
298-					}
299+				part = ecalloc(1, sizeof(*part));
300+				snprintf(part->name, sizeof(part->name), "%s%c", tok, c);
301+				snprintf(part->path, sizeof(part->path), "%s/r%s%c", PATH_DEV, tok, c);
302+				strlcpy(part->type, "part", sizeof(part->type));
303+				part->size = dev.size;
304+				p = info->parts;
305+				if (!p) {
306+					info->parts = part;
307+				} else {
308+					while (p->next)
309+						p = p->next;
310+					p->next = part;
311 				}
312 				blockdev_close(&dev);
313 			}
314@@ -368,13 +401,13 @@ blockdev_get_list(void)
315 		tail = info;
316 	}
317 
318-	struct statfs *mntbuf;
319-	int mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
320-	for (int i = 0; i < mntsize; i++) {
321-		char *devpath = mntbuf[i].f_mntfromname;
322-		char *devname = devpath;
323-		if (strncmp(devpath, "/dev/", 5) == 0)
324-			devname = devpath + 5;
325+	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
326+	for (i = 0; i < mntsize; i++) {
327+		char *frompath = mntbuf[i].f_mntfromname;
328+		char *devname = frompath;
329+		size_t dev_len = strlen(PATH_DEV);
330+		if (strncmp(frompath, PATH_DEV, dev_len) == 0 && frompath[dev_len] == '/')
331+			devname = frompath + dev_len + 1;
332 		struct BlockDevInfo *curr;
333 		for (curr = head; curr; curr = curr->next) {
334 			if (strcmp(curr->name, devname) == 0) {
335@@ -394,26 +427,29 @@ blockdev_get_list(void)
336 	return head;
337 
338 #else
339-	struct BlockDevInfo *head = NULL, *tail = NULL;
340+	struct BlockDevInfo *head = NULL;
341+	struct BlockDevInfo *tail = NULL;
342+	struct BlockDevInfo *info;
343+	struct BlockDev dev;
344 	char devname[16];
345-	for (char c = 'a'; c <= 'z'; c++) {
346+	char devpath[128];
347+	char c;
348+
349+	for (c = 'a'; c <= 'z'; c++) {
350 		snprintf(devname, sizeof(devname), "sd%c", c);
351-		char devpath[32];
352-		snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
353-		struct BlockDev dev;
354+		snprintf(devpath, sizeof(devpath), "%s/%s", PATH_DEV, devname);
355 		if (blockdev_open(&dev, devpath, 0) == 0) {
356-			struct BlockDevInfo *info = calloc(1, sizeof(*info));
357-			if (info) {
358-				strlcpy(info->name, devname, sizeof(info->name));
359-				strlcpy(info->type, "disk", sizeof(info->type));
360-				info->size = dev.size;
361-				if (!head) {
362-					head = info;
363-				} else {
364-					tail->next = info;
365-				}
366-				tail = info;
367+			info = ecalloc(1, sizeof(*info));
368+			strlcpy(info->name, devname, sizeof(info->name));
369+			snprintf(info->path, sizeof(info->path), "%s/%s", PATH_DEV, devname);
370+			strlcpy(info->type, "disk", sizeof(info->type));
371+			info->size = dev.size;
372+			if (!head) {
373+				head = info;
374+			} else {
375+				tail->next = info;
376 			}
377+			tail = info;
378 			blockdev_close(&dev);
379 		}
380 	}
381@@ -437,3 +473,269 @@ blockdev_free_list(struct BlockDevInfo *list)
382 		curr = next;
383 	}
384 }
385+
386+struct FsType {
387+	const char *name;
388+	size_t magic_offset;
389+	size_t magic_len;
390+	uint64_t magic1;
391+	uint64_t magic2;
392+	size_t uuid_offset;
393+	size_t uuid_len;
394+	size_t label_offset;
395+	size_t label_len;
396+};
397+
398+static const struct FsType fstypes[] = {
399+	/* ext2/3/4 */
400+	{ "ext", 1080, 2, 0xEF53, 0x53EF, 1128, 16, 1144, 16 },
401+	/* vfat / fat32 */
402+	{ "vfat", 82, 5, 0x3233544146ULL, 0, 67, 4, 71, 11 },
403+	/* vfat / fat12/16 */
404+	{ "vfat", 54, 4, 0x31544146, 0, 39, 4, 43, 11 },
405+	/* ntfs */
406+	{ "ntfs", 3, 4, 0x5346544e, 0, 72, 8, 0, 0 },
407+	/* xfs */
408+	{ "xfs", 0, 4, 0x42534658, 0x58465342, 32, 16, 108, 12 },
409+	/* btrfs */
410+	{ "btrfs", 65600, 8, 0x4D5F53665248425FULL, 0, 65803, 16, 65819, 256 },
411+	/* f2fs */
412+	{ "f2fs", 1024, 4, 0xF2F52010, 0x1020F5F2, 1132, 16, 1148, 512 },
413+	/* ufs1 */
414+	{ "ufs1", 9564, 4, 0x011954, 0x54190100, 8428, 8, 0, 0 },
415+	/* ufs2 */
416+	{ "ufs2", 66908, 4, 0x19540119, 0x19015419, 65772, 8, 66216, 32 },
417+	/* fossil */
418+	{ "fossil", 131200, 4, 0x2340A3B1, 0xB1A34023, 0, 0, 131072, 127 },
419+};
420+
421+static void
422+format_uuid(const unsigned char *buf, size_t len, const char *type, char *out, size_t out_len)
423+{
424+	size_t i;
425+	char *p;
426+
427+	p = out;
428+	if (strcmp(type, "vfat") == 0) {
429+		snprintf(out, out_len, "%02X%02X-%02X%02X", buf[3], buf[2], buf[1], buf[0]);
430+		return;
431+	}
432+
433+	if (strcmp(type, "ntfs") == 0) {
434+		for (i = 8; i > 0; i--) {
435+			snprintf(p, out_len - (p - out), "%02X", buf[i - 1]);
436+			p += 2;
437+		}
438+		return;
439+	}
440+
441+	for (i = 0; i < len; i++) {
442+		if (i == 4 || i == 6 || i == 8 || i == 10) {
443+			*p++ = '-';
444+		}
445+		snprintf(p, out_len - (p - out), "%02x", buf[i]);
446+		p += 2;
447+	}
448+	*p = '\0';
449+}
450+
451+static int
452+zfs_detect(const unsigned char *buf, size_t len)
453+{
454+	size_t offset;
455+	uint64_t magic;
456+
457+	for (offset = 131072; offset + 8 <= len; offset += 1024) {
458+		magic = ((uint64_t)buf[offset + 7] << 56) |
459+		        ((uint64_t)buf[offset + 6] << 48) |
460+		        ((uint64_t)buf[offset + 5] << 40) |
461+		        ((uint64_t)buf[offset + 4] << 32) |
462+		        ((uint64_t)buf[offset + 3] << 24) |
463+		        ((uint64_t)buf[offset + 2] << 16) |
464+		        ((uint64_t)buf[offset + 1] << 8)  |
465+		        (uint64_t)buf[offset];
466+
467+		if (magic == 0x00bab10c00000000ULL || magic == 0x0cb1ba0000000000ULL ||
468+		    magic == 0x00bab10cULL || magic == 0x0cb1ba00ULL)
469+			return 1;
470+	}
471+	return 0;
472+}
473+
474+struct Plan9FsDef {
475+	const char *name;
476+	int blocksize;
477+	int namelen;
478+	int dirblks;
479+	int indirblks;
480+	int daddrbits;
481+};
482+
483+static const struct Plan9FsDef p9_fs_defs[] = {
484+	{ "cwfs64x", 16384, 144, 6, 4, 64 },
485+	{ "cwfs64",  16384,  56, 6, 4, 64 },
486+	{ "cwfs",    16384,  28, 6, 2, 32 },
487+	{ "fs64",     8192,  56, 6, 4, 64 },
488+	{ "fs",       4096,  28, 6, 2, 32 },
489+};
490+
491+static int
492+plan9_magic_check(const unsigned char *p, const char **name)
493+{
494+	if (memcmp(p, "\x01\x1c\xe5\x0d", 4) == 0 || memcmp(p, "\x0d\xe5\x1c\x01", 4) == 0) {
495+		*name = "hjfs";
496+		return 1;
497+	}
498+	if (memcmp(p, "gefs", 4) == 0 || memcmp(p, "sfeg", 4) == 0) {
499+		*name = "gefs";
500+		return 1;
501+	}
502+	return 0;
503+}
504+
505+static int
506+plan9_detect_other(const unsigned char *buf, size_t len, const char **name)
507+{
508+	if (len >= 4 && plan9_magic_check(buf, name))
509+		return 1;
510+	if (len >= 516 && plan9_magic_check(buf + 512, name))
511+		return 1;
512+	return 0;
513+}
514+
515+static int
516+plan9_detect_text(const unsigned char *buf, size_t len, const char **name)
517+{
518+	int blocksize = 0, namelen = 0, dirblks = 0, indirblks = 0, daddrbits = 0;
519+	char line[128];
520+	char key[64];
521+	int val;
522+	size_t i = 0, j;
523+
524+	if (len >= 256 + 15) {
525+		if (memcmp(buf + 256, "kfs wren device", 15) == 0) {
526+			*name = "kfs";
527+			return 1;
528+		}
529+	}
530+
531+	if (len < 512)
532+		return 0;
533+	i = 512;
534+	if (len > 1536)
535+		len = 1536;
536+
537+	while (i < len) {
538+		size_t k = 0;
539+		while (i < len && buf[i] != '\n' && k < sizeof(line) - 1) {
540+			line[k++] = buf[i++];
541+		}
542+		if (i < len && buf[i] == '\n')
543+			i++;
544+		line[k] = '\0';
545+
546+		if (sscanf(line, "%63s %d", key, &val) == 2) {
547+			if (strcmp(key, "blocksize") == 0) blocksize = val;
548+			else if (strcmp(key, "namelen") == 0) namelen = val;
549+			else if (strcmp(key, "dirblks") == 0) dirblks = val;
550+			else if (strcmp(key, "indirblks") == 0) indirblks = val;
551+			else if (strcmp(key, "daddrbits") == 0) daddrbits = val;
552+		}
553+	}
554+
555+	for (j = 0; j < LEN(p9_fs_defs); j++) {
556+		if (blocksize == p9_fs_defs[j].blocksize &&
557+		    namelen == p9_fs_defs[j].namelen &&
558+		    dirblks == p9_fs_defs[j].dirblks &&
559+		    indirblks == p9_fs_defs[j].indirblks &&
560+		    daddrbits == p9_fs_defs[j].daddrbits) {
561+			*name = p9_fs_defs[j].name;
562+			return 1;
563+		}
564+	}
565+
566+	return 0;
567+}
568+
569+int
570+blockdev_detect_fs(struct BlockDev *dev, char *type, size_t type_sz,
571+                   char *label, size_t label_sz, char *uuid, size_t uuid_sz)
572+{
573+	unsigned char *buf;
574+	size_t read_size = 262144; /* 256kb */
575+	size_t sectors_to_read;
576+	size_t i, j;
577+	int found = 0;
578+
579+	if (dev->size < read_size)
580+		read_size = dev->size;
581+
582+	buf = emalloc(read_size);
583+	sectors_to_read = (read_size + dev->sec_size - 1) / dev->sec_size;
584+	if (blockdev_read(dev, 0, buf, sectors_to_read) < 0) {
585+		free(buf);
586+		return -2;
587+	}
588+
589+	for (i = 0; i < LEN(fstypes); i++) {
590+		const struct FsType *fs = &fstypes[i];
591+		if (fs->magic_offset + fs->magic_len > read_size)
592+			continue;
593+
594+		uint64_t val = 0;
595+		for (j = 0; j < fs->magic_len; j++) {
596+			val |= ((uint64_t)buf[fs->magic_offset + j]) << (8 * j);
597+		}
598+
599+		if (val == fs->magic1 || (fs->magic2 && val == fs->magic2)) {
600+			const char *type_name = fs->name;
601+			if (strcmp(fs->name, "ext") == 0) {
602+				if (buf[1116] & 4)
603+					type_name = "ext3";
604+				else if (buf[1120] & 64)
605+					type_name = "ext4";
606+				else
607+					type_name = "ext2";
608+			}
609+
610+			if (type && type_sz)
611+				strlcpy(type, type_name, type_sz);
612+
613+			if (label && label_sz && fs->label_len && fs->label_offset + fs->label_len <= read_size) {
614+				snprintf(label, label_sz, "%.*s", (int)fs->label_len, buf + fs->label_offset);
615+				/* strip trailing spaces */
616+				char *end = label + strlen(label) - 1;
617+				while (end >= label && *end == ' ') {
618+					*end = '\0';
619+					end--;
620+				}
621+			}
622+
623+			if (uuid && uuid_sz && fs->uuid_len && fs->uuid_offset + fs->uuid_len <= read_size) {
624+				format_uuid(buf + fs->uuid_offset, fs->uuid_len, fs->name, uuid, uuid_sz);
625+			}
626+
627+			found = 1;
628+			break;
629+		}
630+	}
631+
632+	if (!found && zfs_detect(buf, read_size)) {
633+		if (type && type_sz)
634+			strlcpy(type, "zfs_member", type_sz);
635+		found = 1;
636+	}
637+
638+	if (!found) {
639+		const char *p9_name = NULL;
640+		if (plan9_detect_other(buf, read_size, &p9_name) ||
641+		    plan9_detect_text(buf, read_size, &p9_name)) {
642+			if (type && type_sz)
643+				strlcpy(type, p9_name, type_sz);
644+			found = 1;
645+		}
646+	}
647+
648+	free(buf);
649+	return found ? 0 : -1;
650+}