commit 02a0859

shrub  ·  2026-06-30 15:45:54 +0000 UTC
parent 2182462
patch
16 files changed,  +4635, -1
+23, -1
 1@@ -126,6 +126,7 @@ POSIX_BIN_ALL =\
 2 	cmd/posix/dd\
 3 	cmd/posix/df\
 4 	cmd/posix/diff/diff\
 5+	cmd/posix/patch/patch\
 6 	cmd/posix/dirname\
 7 	cmd/posix/du\
 8 	cmd/posix/echo\
 9@@ -304,6 +305,14 @@ DIFFOBJ =\
10 	cmd/posix/diff/pr.o\
11 	cmd/posix/diff/xmalloc.o
12 
13+PATCHOBJ =\
14+	cmd/posix/patch/patch.o\
15+	cmd/posix/patch/pch.o\
16+	cmd/posix/patch/inp.o\
17+	cmd/posix/patch/util.o\
18+	cmd/posix/patch/backupfile.o\
19+	cmd/posix/patch/mkpath.o
20+
21 BIN_basename_1 = cmd/posix/basename
22 BIN_cal_1 = cmd/posix/cal
23 BIN_cat_1 = cmd/posix/cat
24@@ -319,6 +328,7 @@ BIN_date_1 = cmd/posix/date
25 BIN_dd_1 = cmd/posix/dd
26 BIN_df_1 = cmd/posix/df
27 BIN_diff_1 = cmd/posix/diff/diff
28+BIN_patch_1 = cmd/posix/patch/patch
29 BIN_dirname_1 = cmd/posix/dirname
30 BIN_du_1 = cmd/posix/du
31 BIN_echo_1 = cmd/posix/echo
32@@ -495,6 +505,7 @@ POSIX_BIN = \
33 	$(BIN_dd_$(BUILD_POSIX_DD)) \
34 	$(BIN_df_$(BUILD_POSIX_DF)) \
35 	$(BIN_diff_$(BUILD_POSIX_DIFF)) \
36+	$(BIN_patch_$(BUILD_POSIX_PATCH)) \
37 	$(BIN_dirname_$(BUILD_POSIX_DIRNAME)) \
38 	$(BIN_du_$(BUILD_POSIX_DU)) \
39 	$(BIN_echo_$(BUILD_POSIX_ECHO)) \
40@@ -692,6 +703,16 @@ $(DIFFOBJ): cmd/posix/diff/diff.h cmd/posix/diff/pr.h cmd/posix/diff/xmalloc.h
41 cmd/posix/diff/diff: $(DIFFOBJ) $(LIB)
42 	$(CC) $(LDFLAGS) -o $@ $(DIFFOBJ) $(LIB) $(LDLIBS) -lm
43 
44+$(PATCHOBJ): cmd/posix/patch/common.h cmd/posix/patch/util.h \
45+	cmd/posix/patch/pch.h cmd/posix/patch/inp.h \
46+	cmd/posix/patch/backupfile.h cmd/posix/patch/pathnames.h
47+
48+cmd/posix/patch/%.o: cmd/posix/patch/%.c
49+	$(CC) $(CPPFLAGS) -Icmd/posix/patch $(CFLAGS) -o $@ -c $<
50+
51+cmd/posix/patch/patch: $(PATCHOBJ) $(LIB)
52+	$(CC) $(LDFLAGS) -o $@ $(PATCHOBJ) $(LIB) $(LDLIBS)
53+
54 shared/libutf/libutf.a: $(LIBUTFOBJ)
55 	$(AR) $(ARFLAGS) $@ $?
56 	$(RANLIB) $@
57@@ -739,11 +760,12 @@ man: scripts/mkman/mkman
58 
59 clean:
60 	rm -f shared/libutf/*.o shared/libutil/*.o shared/libredline/*.o
61-	rm -f cmd/posix/*.o cmd/posix/diff/*.o cmd/posix/make/*.o cmd/posix/awk/*.o cmd/posix/sh/*.o
62+	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
63 	rm -f cmd/linux/*.o cmd/net/*.o cmd/xsi/*.o cmd/pseudo/*.o
64 	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
65 	rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL) $(LIB)
66 	rm -f cmd/posix/make/make cmd/posix/getconf.h cmd/posix/bc.c
67+	rm -f cmd/posix/patch/patch
68 	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
69 	rm -f cmd/posix/sh/sh cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
70 	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
+12, -0
 1@@ -303,6 +303,15 @@ build_diff() {
 2 	link_bin "$dir/diff" $objs -- $LIB -lm
 3 }
 4 
 5+build_patch() {
 6+	cfg_enabled BUILD_POSIX_PATCH || return 0
 7+	local dir=cmd/posix/patch
 8+	EXTRA_HDR="$dir/common.h $dir/util.h $dir/pch.h $dir/inp.h $dir/backupfile.h $dir/pathnames.h"
 9+	objs_for "$dir" "" "-I$dir"
10+	EXTRA_HDR=
11+	link_bin "$dir/patch" $_objs -- $LIB
12+}
13+
14 build_diff3() {
15 	cfg_enabled BUILD_PSEUDO_DIFF3 || return 0
16 	local dir=cmd/extra/diff3
17@@ -407,6 +416,7 @@ build_posix() {
18 	}
19 	build_simple_tools posix
20 	build_diff
21+	build_patch
22 	build_awk
23 	build_sh
24 	build_make
25@@ -524,6 +534,7 @@ build_man() {
26 		done
27 	done
28 	build_man_for BUILD_POSIX_DIFF cmd/posix/diff/diff.c
29+	build_man_for BUILD_POSIX_PATCH cmd/posix/patch/patch.c
30 	build_man_for BUILD_PSEUDO_DIFF3 cmd/extra/diff3/diff3.c
31 }
32 
33@@ -549,6 +560,7 @@ do_clean() {
34 	find cmd -type f ! -name '*.*' -perm -100     -exec rm -f {} +
35 	rm -f shared/libaruuelf.so
36 	rm -f cmd/posix/bc.c cmd/posix/getconf.h
37+	rm -f cmd/posix/patch/patch
38 	rm -f cmd/posix/awk/maketab cmd/posix/awk/proctab.c
39 	rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h
40 	rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax
+251, -0
  1@@ -0,0 +1,251 @@
  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+}
+44, -0
 1@@ -0,0 +1,44 @@
 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);
+136, -0
  1@@ -0,0 +1,136 @@
  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+
+519, -0
  1@@ -0,0 +1,519 @@
  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+}
+35, -0
 1@@ -0,0 +1,35 @@
 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);
+81, -0
 1@@ -0,0 +1,81 @@
 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+
+1338, -0
   1@@ -0,0 +1,1338 @@
   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+}
+14, -0
 1@@ -0,0 +1,14 @@
 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"
+1596, -0
   1@@ -0,0 +1,1596 @@
   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+}
+59, -0
 1@@ -0,0 +1,59 @@
 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);
+444, -0
  1@@ -0,0 +1,444 @@
  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+}
+52, -0
 1@@ -0,0 +1,52 @@
 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 *);
+1, -0
1@@ -30,6 +30,7 @@ BUILD_POSIX_DATE = $(BUILD_POSIX)
2 BUILD_POSIX_DD = $(BUILD_POSIX)
3 BUILD_POSIX_DF = $(BUILD_POSIX)
4 BUILD_POSIX_DIFF = $(BUILD_POSIX)
5+BUILD_POSIX_PATCH = $(BUILD_POSIX)
6 BUILD_POSIX_DIRNAME = $(BUILD_POSIX)
7 BUILD_POSIX_DU = $(BUILD_POSIX)
8 BUILD_POSIX_ECHO = $(BUILD_POSIX)
+30, -0
 1@@ -144,6 +144,35 @@ compile_diff() {
 2 	printf '\t{ "diff", diff_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
 3 }
 4 
 5+compile_patch() {
 6+	pdir="${ROOT}/cmd/posix/patch"
 7+	symf="${BDIR}/patch.syms"
 8+	patch_objs=""
 9+
10+	printf 'mkbox: cc patch (multi)\n'
11+	for s in patch pch inp util backupfile mkpath; do
12+		out="${BDIR}/patch_${s}.o"
13+		eval "${CC} ${CPPFLAGS} -I\"\$pdir\" ${CFLAGS} -c \"\$pdir/\$s.c\" -o \"\$out\"" ||
14+			die "compile failed: patch/${s}.c"
15+		patch_objs="${patch_objs} ${out}"
16+	done
17+
18+	combined="${BDIR}/patch_combined.o"
19+	${LD:-ld} -r -o "${combined}" ${patch_objs} ||
20+		die "ld -r failed: patch"
21+
22+	printf 'main patch_main\n' > "${symf}"
23+	${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
24+		die "objcopy failed: patch"
25+	${OBJCOPY} --keep-global-symbol=patch_main "${combined}" ||
26+		die "objcopy localize failed: patch"
27+	rm -f "${symf}" ${patch_objs}
28+
29+	OBJS="${OBJS} ${combined}"
30+	printf 'int patch_main(int, char **);\n' >> "${DECL_F}"
31+	printf '\t{ "patch", patch_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
32+}
33+
34 compile_diff3() {
35 	ddir="${ROOT}/cmd/extra/diff3"
36 	pdir="${ROOT}/cmd/posix/diff"
37@@ -301,6 +330,7 @@ process_dir() {
38 
39 process_dir "${ROOT}/cmd/posix" "posix"
40 compile_diff
41+compile_patch
42 compile_awk
43 compile_sh
44 compile_make