Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Jun 2006 11:17:18 +0200
From:      "Andreas Kohn" <andreas@syndrom23.de>
To:        "FreeBSD gnats submit" <FreeBSD-gnats-submit@FreeBSD.org>
Subject:   gnu/99173: [PATCH] replace gnu patch with a bsd-licensed one.
Message-ID:  <1150708638.5990@klamath.syndrom23.de>
Resent-Message-ID: <200606190920.k5J9KDbN056519@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         99173
>Category:       gnu
>Synopsis:       [PATCH] replace gnu patch with a bsd-licensed one.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jun 19 09:20:13 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Andreas Kohn
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
>Environment:


System: FreeBSD 7.0-CURRENT #0: Thu Jun 15 22:54:30 CEST 2006
    root@klamath.syndrom23.de:/usr/obj/usr/src/sys/KLAMATH



>Description:


FreeBSD still uses GNU's version of patch, while other *BSD have alrerady switched to using larry walls version that is available under a BSD-license. 

The attached patch uses the version of lwpatch that dragonfly uses, as there were no code changes necessary I did not update or add $FreeBSD$ lines. (Wasn't sure if there are needed in this case)

GNU patch still gets installed as gpatch, with the man page renamed accordingly.

The second patch attached changes how usr.bin/awk is calling patch. The chosen solution works for both GNU and lwpatch.
Note that I think patch should be made a build tool, as you cannot build a world with !GNU patch currently. Another, possibly better solution would be to remove the patching in usr.bin/awk/Makefile. 



>How-To-Repeat:





>Fix:


--- freebsd-lw-patch.diff begins here ---
Index: usr.bin/Makefile
===================================================================
RCS file: /storage/freebsd/cvs/src/usr.bin/Makefile,v
retrieving revision 1.294
diff -u -r1.294 Makefile
--- usr.bin/Makefile	4 May 2006 08:44:44 -0000	1.294
+++ usr.bin/Makefile	2 Jun 2006 19:11:32 -0000
@@ -146,6 +146,7 @@
 	pagesize \
 	passwd \
 	paste \
+	patch \
 	pathchk \
 	pkill \
 	pr \
Index: gnu/usr.bin/patch/Makefile
===================================================================
RCS file: /storage/freebsd/cvs/src/gnu/usr.bin/patch/Makefile,v
retrieving revision 1.8
diff -u -r1.8 Makefile
--- gnu/usr.bin/patch/Makefile	17 Feb 2004 01:24:45 -0000	1.8
+++ gnu/usr.bin/patch/Makefile	2 Jun 2006 19:16:12 -0000
@@ -1,7 +1,12 @@
 # $FreeBSD: src/gnu/usr.bin/patch/Makefile,v 1.8 2004/02/17 01:24:45 ache Exp $
 
-PROG= 	patch
+PROG= 	gpatch
 SRCS=   backupfile.c inp.c patch.c pch.c util.c version.c
 CFLAGS+=-DHAVE_CONFIG_H
 
+# "Rename" patch.1 to gpatch.1
+CLEANFILES= gpatch.1
+gpatch.1: patch.1
+	cat ${.CURDIR}/patch.1 > ${.TARGET}
+
 .include <bsd.prog.mk>
Index: usr.bin/patch/Makefile
===================================================================
RCS file: usr.bin/patch/Makefile
diff -N usr.bin/patch/Makefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/Makefile	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,12 @@
+#	$OpenBSD: Makefile,v 1.3 2003/07/18 02:00:09 deraadt Exp $
+#	$DragonFly$
+
+PROG=	patch
+SRCS=	patch.c pch.c inp.c util.c backupfile.c
+WARNS?=	6
+
+#.if defined(BOOTSTRAPPING)
+CFLAGS+= -DBOOTSTRAPPING
+#.endif
+
+.include <bsd.prog.mk>
Index: usr.bin/patch/README
===================================================================
RCS file: usr.bin/patch/README
diff -N usr.bin/patch/README
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/README	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,126 @@
+$DragonFly$
+#######################################################################
+
+this version modified to fit in with the 386bsd release.
+this isn't gnu software, so we're not obligated to give
+you the original sources -- if you want them, get them
+from prep.ai.mit.edu:pub/gnu/patch-2.0.12u8.tar.z
+
+					-- cgd
+
+#######################################################################
+
+
+This version of patch contains modifications made by the Free Software
+Foundation, summarized in the file ChangeLog.  Primarily they are to
+support the unified context diff format that GNU diff can produce, and
+to support making GNU Emacs-style backup files.  They also include
+fixes for some bugs.
+
+There are two GNU variants of patch: this one, which retains Larry
+Wall's interactive Configure script and has patchlevels starting with
+`12u'; and another one that has a GNU-style non-interactive configure
+script and accepts long-named options, and has patchlevels starting
+with `12g'.  Unlike the 12g variant, the 12u variant contains no
+copylefted code, for the paranoid.  The two variants are otherwise the
+same.  They should be available from the same places.
+
+The FSF is distributing this version of patch independently because as
+of this writing, Larry Wall has not released a new version of patch
+since mid-1988.  I have heard that he has been too busy working on
+other things, like Perl.
+
+Here is a wish list of some projects to improve patch:
+
+1.  Correctly handle files and patchfiles that contain NUL characters.
+This is hard to do straightforwardly; it would be less work to
+adopt a kind of escape encoding internally.
+Let ESC be a "control prefix".  ESC @ stands for NUL.  ESC [ stands for ESC.
+You need to crunch this when reading input (replace fgets),
+and when writing the output file (replace fputs),
+but otherwise everything can go along as it does now.
+Be careful to handle reject files correctly;
+I think they are currently created using `write', not `fputs'.
+
+2.  Correctly handle patches produced by GNU diff for files that do
+not end with a newline.
+
+Please send bug reports for this version of patch to
+bug-gnu-utils@prep.ai.mit.edu as well as to Larry Wall (lwall@netlabs.com).
+ --djm@gnu.ai.mit.edu (David MacKenzie)
+
+			Patch Kit, Version 2.0
+
+		    Copyright (c) 1988, Larry Wall
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following condition
+is met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this condition and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+--------------------------------------------------------------------------
+
+Please read all the directions below before you proceed any further, and
+then follow them carefully.  Failure to do so may void your warranty. :-)
+
+After you have unpacked your kit, you should have all the files listed
+in MANIFEST.
+
+Installation
+
+1)  Run Configure.  This will figure out various things about your system.
+    Some things Configure will figure out for itself, other things it will
+    ask you about.  It will then proceed to make config.h, config.sh, and
+    Makefile.
+
+    You might possibly have to trim # comments from the front of Configure
+    if your sh doesn't handle them, but all other # comments will be taken
+    care of.
+
+    If you don't have sh, you'll have to rip the prototype of config.h out
+    of Configure and generate the defines by hand.
+
+2)  Glance through config.h to make sure system dependencies are correct.
+    Most of them should have been taken care of by running the
+    Configure script. 
+
+    If you have any additional changes to make to the C definitions, they
+    can be done in the Makefile, or in config.h.  Bear in mind that they may
+    get undone next time you run Configure.
+
+3)  make
+
+    This will attempt to make patch in the current directory.
+
+4)  make install
+
+    This will put patch into a public directory (normally /usr/local/bin).
+    It will also try to put the man pages in a reasonable place.  It will not
+    nroff the man page, however.
+
+5)  Read the manual entry before running patch.
+
+6)  IMPORTANT!  Help save the world!  Communicate any problems and
+    suggested patches to me, lwall@netlabs.com (Larry Wall),
+    so we can keep the world in sync.  If you have a problem, there's
+    someone else out there who either has had or will have the same problem.
+
+    If possible, send in patches such that the patch program will apply them.
+    Context diffs are the best, then normal diffs.  Don't send ed scripts--
+    I've probably changed my copy since the version you have.
+
+    Watch for patch patches in comp.sources.bugs.  Patches will generally be
+    in a form usable by the patch program.  Your current patch level
+    is shown in patchlevel.h.
Index: usr.bin/patch/backupfile.c
===================================================================
RCS file: usr.bin/patch/backupfile.c
diff -N usr.bin/patch/backupfile.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/backupfile.c	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,248 @@
+/*
+ * $OpenBSD: backupfile.c,v 1.18 2004/08/05 21:47:24 deraadt Exp $
+ * $DragonFly$
+ */
+
+/*
+ * backupfile.c -- make Emacs style backup file names Copyright (C) 1990 Free
+ * Software Foundation, Inc.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * without restriction.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "backupfile.h"
+
+
+#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
+
+/* Which type of backup file names are generated. */
+enum backup_type backup_type = none;
+
+/*
+ * The extension added to file names to produce a simple (as opposed to
+ * numbered) backup file name.
+ */
+const char	*simple_backup_suffix = "~";
+
+static char	*concat(const char *, const char *);
+static char	*make_version_name(const char *, int);
+static int	max_backup_version(const char *, const char *);
+static int	version_number(const char *, const char *, int);
+static int	argmatch(const char *, const char **);
+static void	invalid_arg(const char *, const char *, int);
+
+/*
+ * Return the name of the new backup file for file FILE, allocated with
+ * malloc.  Return 0 if out of memory. FILE must not end with a '/' unless it
+ * is the root directory. Do not call this function if backup_type == none.
+ */
+char *
+find_backup_file_name(const char *file)
+{
+	char	*dir, *base_versions, *tmp_file;
+	int	highest_backup;
+
+	if (backup_type == simple)
+		return concat(file, simple_backup_suffix);
+	tmp_file = strdup(file);
+	if (tmp_file == NULL)
+		return NULL;
+	base_versions = concat(basename(tmp_file), ".~");
+	free(tmp_file);
+	if (base_versions == NULL)
+		return NULL;
+	tmp_file = strdup(file);
+	if (tmp_file == NULL) {
+		free(base_versions);
+		return NULL;
+	}
+	dir = dirname(tmp_file);
+	if (dir == NULL) {
+		free(base_versions);
+		free(tmp_file);
+		return NULL;
+	}
+	highest_backup = max_backup_version(base_versions, dir);
+	free(base_versions);
+	free(tmp_file);
+	if (backup_type == numbered_existing && highest_backup == 0)
+		return concat(file, simple_backup_suffix);
+	return make_version_name(file, highest_backup + 1);
+}
+
+/*
+ * Return the number of the highest-numbered backup file for file FILE in
+ * directory DIR.  If there are no numbered backups of FILE in DIR, or an
+ * error occurs reading DIR, return 0. FILE should already have ".~" appended
+ * to it.
+ */
+static int
+max_backup_version(const char *file, const char *dir)
+{
+	DIR	*dirp;
+	struct dirent	*dp;
+	int	highest_version, this_version, file_name_length;
+
+	dirp = opendir(dir);
+	if (dirp == NULL)
+		return 0;
+
+	highest_version = 0;
+	file_name_length = strlen(file);
+
+	while ((dp = readdir(dirp)) != NULL) {
+		if (dp->d_namlen <= file_name_length)
+			continue;
+
+		this_version = version_number(file, dp->d_name, file_name_length);
+		if (this_version > highest_version)
+			highest_version = this_version;
+	}
+	closedir(dirp);
+	return highest_version;
+}
+
+/*
+ * Return a string, allocated with malloc, containing "FILE.~VERSION~".
+ * Return 0 if out of memory.
+ */
+static char *
+make_version_name(const char *file, int version)
+{
+	char	*backup_name;
+
+	if (asprintf(&backup_name, "%s.~%d~", file, version) == -1)
+		return NULL;
+	return backup_name;
+}
+
+/*
+ * If BACKUP is a numbered backup of BASE, return its version number;
+ * otherwise return 0.  BASE_LENGTH is the length of BASE. BASE should
+ * already have ".~" appended to it.
+ */
+static int
+version_number(const char *base, const char *backup, int base_length)
+{
+	int		version;
+	const char	*p;
+
+	version = 0;
+	if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
+		for (p = &backup[base_length]; ISDIGIT(*p); ++p)
+			version = version * 10 + *p - '0';
+		if (p[0] != '~' || p[1])
+			version = 0;
+	}
+	return version;
+}
+
+/*
+ * Return the newly-allocated concatenation of STR1 and STR2. If out of
+ * memory, return 0.
+ */
+static char  *
+concat(const char *str1, const char *str2)
+{
+	char	*newstr;
+
+	if (asprintf(&newstr, "%s%s", str1, str2) == -1)
+		return NULL;
+	return newstr;
+}
+
+/*
+ * If ARG is an unambiguous match for an element of the null-terminated array
+ * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it
+ * does not match any element or -2 if it is ambiguous (is a prefix of more
+ * than one element).
+ */
+static int
+argmatch(const char *arg, const char **optlist)
+{
+	int	i;	/* Temporary index in OPTLIST. */
+	size_t	arglen;	/* Length of ARG. */
+	int	matchind = -1;	/* Index of first nonexact match. */
+	int	ambiguous = 0;	/* If nonzero, multiple nonexact match(es). */
+
+	arglen = strlen(arg);
+
+	/* Test all elements for either exact match or abbreviated matches.  */
+	for (i = 0; optlist[i]; i++) {
+		if (!strncmp(optlist[i], arg, arglen)) {
+			if (strlen(optlist[i]) == arglen)
+				/* Exact match found.  */
+				return i;
+			else if (matchind == -1)
+				/* First nonexact match found.  */
+				matchind = i;
+			else
+				/* Second nonexact match found.  */
+				ambiguous = 1;
+		}
+	}
+	if (ambiguous)
+		return -2;
+	else
+		return matchind;
+}
+
+/*
+ * Error reporting for argmatch. KIND is a description of the type of entity
+ * that was being matched. VALUE is the invalid value that was given. PROBLEM
+ * is the return value from argmatch.
+ */
+static void
+invalid_arg(const char *kind, const char *value, int problem)
+{
+	fprintf(stderr, "patch: ");
+	if (problem == -1)
+		fprintf(stderr, "invalid");
+	else			/* Assume -2. */
+		fprintf(stderr, "ambiguous");
+	fprintf(stderr, " %s `%s'\n", kind, value);
+}
+
+static const char *backup_args[] = {
+	"never", "simple", "nil", "existing", "t", "numbered", 0
+};
+
+static enum backup_type backup_types[] = {
+	simple, simple, numbered_existing,
+	numbered_existing, numbered, numbered
+};
+
+/*
+ * Return the type of backup indicated by VERSION. Unique abbreviations are
+ * accepted.
+ */
+enum backup_type
+get_version(const char *version)
+{
+	int	i;
+
+	if (version == NULL || *version == '\0')
+		return numbered_existing;
+	i = argmatch(version, backup_args);
+	if (i >= 0)
+		return backup_types[i];
+	invalid_arg("version control type", version, i);
+	exit(2);
+}
Index: usr.bin/patch/backupfile.h
===================================================================
RCS file: usr.bin/patch/backupfile.h
diff -N usr.bin/patch/backupfile.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/backupfile.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,41 @@
+/*
+ * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $
+ * $DragonFly$
+ */
+
+/*
+ * backupfile.h -- declarations for making Emacs style backup file names
+ * Copyright (C) 1990 Free Software Foundation, Inc.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * without restriction.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ */
+
+/* When to make backup files. */
+enum backup_type {
+	/* Never make backups. */
+	none,
+
+	/* Make simple backups of every file. */
+	simple,
+
+	/*
+	 * Make numbered backups of files that already have numbered backups,
+	 * and simple backups of the others.
+	 */
+	numbered_existing,
+
+	/* Make numbered backups of every file. */
+	numbered
+};
+
+extern enum backup_type backup_type;
+extern const char	*simple_backup_suffix;
+
+char		*find_backup_file_name(const char *file);
+enum backup_type get_version(const char *vers);
Index: usr.bin/patch/common.h
===================================================================
RCS file: usr.bin/patch/common.h
diff -N usr.bin/patch/common.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/common.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,125 @@
+/*
+ * $OpenBSD: common.h,v 1.25 2003/10/31 20:20:45 millert Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#include <stdbool.h>
+
+/*
+ * SIZE_MAX: older BSD compatibility for buildworld bootstrap mode only
+ */
+#if defined(BOOTSTRAPPING) && !defined(SIZE_MAX)
+#define SIZE_MAX	0x7fffffff
+#endif
+
+#define DEBUGGING
+
+/* constants */
+
+#define MAXHUNKSIZE 100000	/* is this enough lines? */
+#define INITHUNKMAX 125		/* initial dynamic allocation size */
+#define MAXLINELEN 8192
+#define BUFFERSIZE 1024
+
+#define SCCSPREFIX "s."
+#define GET "get -e %s"
+#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
+
+#define RCSSUFFIX ",v"
+#define CHECKOUT "co -l %s"
+#define RCSDIFF "rcsdiff %s > /dev/null"
+
+#define ORIGEXT ".orig"
+#define REJEXT ".rej"
+
+/* handy definitions */
+
+#define strNE(s1,s2) (strcmp(s1, s2))
+#define strEQ(s1,s2) (!strcmp(s1, s2))
+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
+
+/* typedefs */
+
+typedef long    LINENUM;	/* must be signed */
+
+/* globals */
+
+extern int	filemode;
+
+extern char	buf[MAXLINELEN];/* general purpose buffer */
+extern size_t	buf_len;
+
+extern bool	using_plan_a;	/* try to keep everything in memory */
+extern bool	out_of_mem;	/* ran out of memory in plan a */
+
+#define MAXFILEC 2
+
+extern char	*filearg[MAXFILEC];
+extern bool	ok_to_create_file;
+extern char	*outname;
+extern char	*origprae;
+
+extern char	*TMPOUTNAME;
+extern char	*TMPINNAME;
+extern char	*TMPREJNAME;
+extern char	*TMPPATNAME;
+extern bool	toutkeep;
+extern bool	trejkeep;
+
+#ifdef DEBUGGING
+extern int	debug;
+#endif
+
+extern bool	force;
+extern bool	batch;
+extern bool	verbose;
+extern bool	reverse;
+extern bool	noreverse;
+extern bool	skip_rest_of_patch;
+extern int	strippath;
+extern bool	canonicalize;
+/* TRUE if -C was specified on command line.  */
+extern bool	check_only;
+extern bool	warn_on_invalid_line;
+extern bool	last_line_missing_eol;
+
+
+#define CONTEXT_DIFF 1
+#define NORMAL_DIFF 2
+#define ED_DIFF 3
+#define NEW_CONTEXT_DIFF 4
+#define UNI_DIFF 5
+
+extern int	diff_type;
+extern char	*revision;	/* prerequisite revision, if any */
+extern LINENUM	input_lines;	/* how long is input file in lines */
+
+extern int	posix;
+
Index: usr.bin/patch/inp.c
===================================================================
RCS file: usr.bin/patch/inp.c
diff -N usr.bin/patch/inp.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/inp.c	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,482 @@
+/*
+ * $OpenBSD: inp.c,v 1.32 2004/08/05 21:47:24 deraadt Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+
+
+/* Input-file-with-indexable-lines abstract type */
+
+static off_t	i_size;		/* size of the input file */
+static char	*i_womp;	/* plan a buffer for entire file */
+static char	**i_ptr;	/* pointers to lines in i_womp */
+static char	empty_line[] = { '\0' };
+
+static int	tifd = -1;	/* plan b virtual string array */
+static char	*tibuf[2];	/* plan b buffers */
+static LINENUM	tiline[2] = {-1, -1};	/* 1st line in each buffer */
+static LINENUM	lines_per_buf;	/* how many lines per buffer */
+static int	tireclen;	/* length of records in tmp file */
+
+static bool	rev_in_string(const char *);
+static bool	reallocate_lines(size_t *);
+
+/* returns false if insufficient memory */
+static bool	plan_a(const char *);
+
+static void	plan_b(const char *);
+
+/* New patch--prepare to edit another file. */
+
+void
+re_input(void)
+{
+	if (using_plan_a) {
+		i_size = 0;
+		free(i_ptr);
+		i_ptr = NULL;
+		if (i_womp != NULL) {
+			munmap(i_womp, i_size);
+			i_womp = NULL;
+		}
+	} else {
+		using_plan_a = true;	/* maybe the next one is smaller */
+		close(tifd);
+		tifd = -1;
+		free(tibuf[0]);
+		free(tibuf[1]);
+		tibuf[0] = tibuf[1] = NULL;
+		tiline[0] = tiline[1] = -1;
+		tireclen = 0;
+	}
+}
+
+/* Constuct the line index, somehow or other. */
+
+void
+scan_input(const char *filename)
+{
+	if (!plan_a(filename))
+		plan_b(filename);
+	if (verbose) {
+		say("Patching file %s using Plan %s...\n", filename,
+		    (using_plan_a ? "A" : "B"));
+	}
+}
+
+static bool
+reallocate_lines(size_t *lines_allocated)
+{
+	char	**p;
+	size_t	new_size;
+
+	new_size = *lines_allocated * 3 / 2;
+	p = realloc(i_ptr, (new_size + 2) * sizeof(char *));
+	if (p == NULL) {	/* shucks, it was a near thing */
+		munmap(i_womp, i_size);
+		i_womp = NULL;
+		free(i_ptr);
+		i_ptr = NULL;
+		*lines_allocated = 0;
+		return false;
+	}
+	*lines_allocated = new_size;
+	i_ptr = p;
+	return true;
+}
+
+/* Try keeping everything in memory. */
+
+static bool
+plan_a(const char *filename)
+{
+	int		ifd, statfailed;
+	char		*p, *s, lbuf[MAXLINELEN];
+	LINENUM		iline;
+	struct stat	filestat;
+	off_t		i;
+	ptrdiff_t	sz;
+	size_t		lines_allocated;
+
+#ifdef DEBUGGING
+	if (debug & 8)
+		return false;
+#endif
+
+	if (filename == NULL || *filename == '\0')
+		return false;
+
+	statfailed = stat(filename, &filestat);
+	if (statfailed && ok_to_create_file) {
+		if (verbose)
+			say("(Creating file %s...)\n", filename);
+
+		/*
+		 * in check_patch case, we still display `Creating file' even
+		 * though we're not. The rule is that -C should be as similar
+		 * to normal patch behavior as possible
+		 */
+		if (check_only)
+			return true;
+		makedirs(filename, true);
+		close(creat(filename, 0666));
+		statfailed = stat(filename, &filestat);
+	}
+	if (statfailed && check_only)
+		fatal("%s not found, -C mode, can't probe further\n", filename);
+	/* For nonexistent or read-only files, look for RCS or SCCS versions.  */
+	if (statfailed ||
+	    /* No one can write to it.  */
+	    (filestat.st_mode & 0222) == 0 ||
+	    /* I can't write to it.  */
+	    ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
+		const char	*cs = NULL, *filebase, *filedir;
+		struct stat	cstat;
+		char *tmp_filename1, *tmp_filename2;
+
+		tmp_filename1 = strdup(filename);
+		tmp_filename2 = strdup(filename);
+		if (tmp_filename1 == NULL || tmp_filename2 == NULL)
+			fatal("strdupping filename");
+		filebase = basename(tmp_filename1);
+		filedir = dirname(tmp_filename2);
+
+		/* Leave room in lbuf for the diff command.  */
+		s = lbuf + 20;
+
+#define try(f, a1, a2, a3) \
+	(snprintf(s, sizeof lbuf - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
+
+		if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
+		    try("%s/RCS/%s%s", filedir, filebase, "") ||
+		    try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
+			snprintf(buf, buf_len, CHECKOUT, filename);
+			snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
+			cs = "RCS";
+		} else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
+		    try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
+			snprintf(buf, buf_len, GET, s);
+			snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
+			cs = "SCCS";
+		} else if (statfailed)
+			fatal("can't find %s\n", filename);
+
+		free(tmp_filename1);
+		free(tmp_filename2);
+
+		/*
+		 * else we can't write to it but it's not under a version
+		 * control system, so just proceed.
+		 */
+		if (cs) {
+			if (!statfailed) {
+				if ((filestat.st_mode & 0222) != 0)
+					/* The owner can write to it.  */
+					fatal("file %s seems to be locked "
+					    "by somebody else under %s\n",
+					    filename, cs);
+				/*
+				 * It might be checked out unlocked.  See if
+				 * it's safe to check out the default version
+				 * locked.
+				 */
+				if (verbose)
+					say("Comparing file %s to default "
+					    "%s version...\n",
+					    filename, cs);
+				if (system(lbuf))
+					fatal("can't check out file %s: "
+					    "differs from default %s version\n",
+					    filename, cs);
+			}
+			if (verbose)
+				say("Checking out file %s from %s...\n",
+				    filename, cs);
+			if (system(buf) || stat(filename, &filestat))
+				fatal("can't check out file %s from %s\n",
+				    filename, cs);
+		}
+	}
+	filemode = filestat.st_mode;
+	if (!S_ISREG(filemode))
+		fatal("%s is not a normal file--can't patch\n", filename);
+	i_size = filestat.st_size;
+	if (out_of_mem) {
+		set_hunkmax();	/* make sure dynamic arrays are allocated */
+		out_of_mem = false;
+		return false;	/* force plan b because plan a bombed */
+	}
+	if (i_size > SIZE_MAX) {
+		say("block too large to mmap\n");
+		return false;
+	}
+	if ((ifd = open(filename, O_RDONLY)) < 0)
+		pfatal("can't open file %s", filename);
+
+	i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
+	if (i_womp == MAP_FAILED) {
+		perror("mmap failed");
+		i_womp = NULL;
+		close(ifd);
+		return false;
+	}
+
+	close(ifd);
+	if (i_size)
+		madvise(i_womp, i_size, MADV_SEQUENTIAL);
+
+	/* estimate the number of lines */
+	lines_allocated = i_size / 25;
+	if (lines_allocated < 100)
+		lines_allocated = 100;
+
+	if (!reallocate_lines(&lines_allocated))
+		return false;
+
+	/* now scan the buffer and build pointer array */
+	iline = 1;
+	i_ptr[iline] = i_womp;
+	/* test for NUL too, to maintain the behavior of the original code */
+	for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
+		if (*s == '\n') {
+			if (iline == (LINENUM)lines_allocated) {
+				if (!reallocate_lines(&lines_allocated))
+					return false;
+			}
+			/* these are NOT NUL terminated */
+			i_ptr[++iline] = s + 1;
+		}
+	}
+	/* if the last line contains no EOL, append one */
+	if (i_size > 0 && i_womp[i_size - 1] != '\n') {
+		last_line_missing_eol = true;
+		/* fix last line */
+		sz = s - i_ptr[iline];
+		p = malloc(sz + 1);
+		if (p == NULL) {
+			free(i_ptr);
+			i_ptr = NULL;
+			munmap(i_womp, i_size);
+			i_womp = NULL;
+			return false;
+		}
+
+		memcpy(p, i_ptr[iline], sz);
+		p[sz] = '\n';
+		i_ptr[iline] = p;
+		/* count the extra line and make it point to some valid mem */
+		i_ptr[++iline] = empty_line;
+	} else
+		last_line_missing_eol = false;
+
+	input_lines = iline - 1;
+
+	/* now check for revision, if any */
+
+	if (revision != NULL) {
+		if (!rev_in_string(i_womp)) {
+			if (force) {
+				if (verbose)
+					say("Warning: this file doesn't appear "
+					    "to be the %s version--patching anyway.\n",
+					    revision);
+			} else if (batch) {
+				fatal("this file doesn't appear to be the "
+				    "%s version--aborting.\n",
+				    revision);
+			} else {
+				ask("This file doesn't appear to be the "
+				    "%s version--patch anyway? [n] ",
+				    revision);
+				if (*buf != 'y')
+					fatal("aborted\n");
+			}
+		} else if (verbose)
+			say("Good.  This file appears to be the %s version.\n",
+			    revision);
+	}
+	return true;		/* plan a will work */
+}
+
+/* Keep (virtually) nothing in memory. */
+
+static void
+plan_b(const char *filename)
+{
+	FILE	*ifp;
+	int	i = 0, j, maxlen = 1;
+	char	*p;
+	bool	found_revision = (revision == NULL);
+
+	using_plan_a = false;
+	if ((ifp = fopen(filename, "r")) == NULL)
+		pfatal("can't open file %s", filename);
+	unlink(TMPINNAME);
+	if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
+		pfatal("can't open file %s", TMPINNAME);
+	while (fgets(buf, buf_len, ifp) != NULL) {
+		if (revision != NULL && !found_revision && rev_in_string(buf))
+			found_revision = true;
+		if ((i = strlen(buf)) > maxlen)
+			maxlen = i;	/* find longest line */
+	}
+	last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
+	if (last_line_missing_eol && maxlen == i)
+		maxlen++;
+
+	if (revision != NULL) {
+		if (!found_revision) {
+			if (force) {
+				if (verbose)
+					say("Warning: this file doesn't appear "
+					    "to be the %s version--patching anyway.\n",
+					    revision);
+			} else if (batch) {
+				fatal("this file doesn't appear to be the "
+				    "%s version--aborting.\n",
+				    revision);
+			} else {
+				ask("This file doesn't appear to be the %s "
+				    "version--patch anyway? [n] ",
+				    revision);
+				if (*buf != 'y')
+					fatal("aborted\n");
+			}
+		} else if (verbose)
+			say("Good.  This file appears to be the %s version.\n",
+			    revision);
+	}
+	fseek(ifp, 0L, SEEK_SET);	/* rewind file */
+	lines_per_buf = BUFFERSIZE / maxlen;
+	tireclen = maxlen;
+	tibuf[0] = malloc(BUFFERSIZE + 1);
+	if (tibuf[0] == NULL)
+		fatal("out of memory\n");
+	tibuf[1] = malloc(BUFFERSIZE + 1);
+	if (tibuf[1] == NULL)
+		fatal("out of memory\n");
+	for (i = 1;; i++) {
+		p = tibuf[0] + maxlen * (i % lines_per_buf);
+		if (i % lines_per_buf == 0)	/* new block */
+			if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+				pfatal("can't write temp file");
+		if (fgets(p, maxlen + 1, ifp) == NULL) {
+			input_lines = i - 1;
+			if (i % lines_per_buf != 0)
+				if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+					pfatal("can't write temp file");
+			break;
+		}
+		j = strlen(p);
+		/* These are '\n' terminated strings, so no need to add a NUL */
+		if (j == 0 || p[j - 1] != '\n')
+			p[j] = '\n';
+	}
+	fclose(ifp);
+	close(tifd);
+	if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
+		pfatal("can't reopen file %s", TMPINNAME);
+}
+
+/*
+ * Fetch a line from the input file, \n terminated, not necessarily \0.
+ */
+char *
+ifetch(LINENUM line, int whichbuf)
+{
+	if (line < 1 || line > input_lines) {
+		if (warn_on_invalid_line) {
+			say("No such line %ld in input file, ignoring\n", line);
+			warn_on_invalid_line = false;
+		}
+		return NULL;
+	}
+	if (using_plan_a)
+		return i_ptr[line];
+	else {
+		LINENUM	offline = line % lines_per_buf;
+		LINENUM	baseline = line - offline;
+
+		if (tiline[0] == baseline)
+			whichbuf = 0;
+		else if (tiline[1] == baseline)
+			whichbuf = 1;
+		else {
+			tiline[whichbuf] = baseline;
+
+			lseek(tifd, (off_t) (baseline / lines_per_buf *
+			    BUFFERSIZE), SEEK_SET);
+
+			if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
+				pfatal("error reading tmp file %s", TMPINNAME);
+		}
+		return tibuf[whichbuf] + (tireclen * offline);
+	}
+}
+
+/*
+ * True if the string argument contains the revision number we want.
+ */
+static bool
+rev_in_string(const char *string)
+{
+	const char	*s;
+	int		patlen;
+
+	if (revision == NULL)
+		return true;
+	patlen = strlen(revision);
+	if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
+		return true;
+	for (s = string; *s; s++) {
+		if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
+		    isspace((unsigned char)s[patlen + 1])) {
+			return true;
+		}
+	}
+	return false;
+}
Index: usr.bin/patch/inp.h
===================================================================
RCS file: usr.bin/patch/inp.h
diff -N usr.bin/patch/inp.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/inp.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,34 @@
+/*
+ * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+void		re_input(void);
+void		scan_input(const char *);
+char		*ifetch(LINENUM, int);
Index: usr.bin/patch/patch.1
===================================================================
RCS file: usr.bin/patch/patch.1
diff -N usr.bin/patch/patch.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/patch.1	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,643 @@
+.\"	$OpenBSD: patch.1,v 1.17 2003/10/31 20:20:45 millert Exp $
+.\"	$DragonFly$
+.\"
+.\" Copyright 1986, Larry Wall
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following condition
+.\" is met:
+.\"  1. Redistributions of source code must retain the above copyright
+.\"     notice, this condition and the following disclaimer.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd July 23, 2003
+.Dt PATCH 1
+.Os
+.Sh NAME
+.Nm patch
+.Nd apply a diff file to an original
+.Sh SYNOPSIS
+.Nm patch
+.Op Cm options
+.Op Ar origfile Op Ar patchfile
+.Nm patch
+.Pf \*(Lt Ar patchfile
+.Sh DESCRIPTION
+.Nm
+will take a patch file containing any of the four forms of difference
+listing produced by the
+.Xr diff 1
+program and apply those differences to an original file,
+producing a patched version.
+If
+.Ar patchfile
+is omitted, or is a hyphen, the patch will be read from the standard input.
+.Pp
+.Nm
+will attempt to determine the type of the diff listing, unless over-ruled by a
+.Fl c ,
+.Fl e ,
+.Fl n ,
+or
+.Fl u
+option.
+Context diffs (old-style, new-style, and unified) and
+normal diffs are applied directly by the
+.Nm
+program itself, whereas ed diffs are simply fed to the
+.Xr ed 1
+editor via a pipe.
+.Pp
+If the
+.Ar patchfile
+contains more than one patch,
+.Nm
+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
+.Sx Filename Determination
+below).
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b , Fl Fl backup
+Save a backup copy of the file before it is modified.
+By default the original file is saved with a backup extension of
+.Qq .orig
+unless the file already has a numbered backup, in which case a numbered
+backup is made.
+This is equivalent to specifying
+.Qo Fl V Ar existing Qc .
+This option is currently the default but that will change in a future release.
+.It Fl B , Fl Fl prefix
+Causes the next argument to be interpreted as a prefix to the backup file
+name.
+If this argument is specified, any argument to
+.Fl z
+will be ignored.
+.It Fl c , Fl Fl context
+Forces
+.Nm
+to interpret the patch file as a context diff.
+.It Fl C , Fl Fl check
+Checks that the patch would apply cleanly, but does not modify anything.
+.It Fl d , Fl Fl directory
+Causes
+.Nm
+to interpret the next argument as a directory, and
+.Xr cd 1
+to it before doing anything else.
+.It Fl D , Fl Fl ifdef
+Causes
+.Nm
+to use the
+.Qq #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
+.Fl D
+and the argument.
+.It Fl e , Fl Fl ed
+Forces
+.Nm
+to interpret the patch file as an
+.Xr ed 1
+script.
+.It Fl E , Fl Fl remove-empty-files
+Causes
+.Nm
+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.
+.It Fl f , Fl Fl force
+Forces
+.Nm
+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
+.Qq 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
+.Fl s
+for that.
+.It Xo
+.Fl F Ns Aq Ar number ,
+.Fl Fl fuzz Aq Ar number
+.Xc
+Sets the maximum fuzz factor.
+This option only applies to context diffs, and causes
+.Nm
+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.
+.It Fl i , Fl Fl input
+Causes the next argument to be interpreted as the input file name
+(i.e. a patchfile).
+This option may be specified multiple times.
+.It Fl l , Fl Fl ignore-whitespace
+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.
+.It Fl n , Fl Fl normal
+Forces
+.Nm
+to interpret the patch file as a normal diff.
+.It Fl N , Fl Fl forward
+Causes
+.Nm
+to ignore patches that it thinks are reversed or already applied.
+See also
+.Fl R .
+.It Fl o , Fl Fl output
+Causes the next argument to be interpreted as the output file name.
+.It Xo
+.Fl p Ns Aq Ar number ,
+.Fl Fl strip Aq Ar number
+.Xc
+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
+.Pa /u/howard/src/blurfl/blurfl.c :
+.Pp
+Setting
+.Fl p Ns Ar 0
+gives the entire pathname unmodified.
+.Pp
+.Fl p Ns Ar 1
+gives
+.Pp
+.D1 Pa u/howard/src/blurfl/blurfl.c
+.Pp
+without the leading slash.
+.Pp
+.Fl p Ns Ar 4
+gives
+.Pp
+.D1 Pa blurfl/blurfl.c
+.Pp
+Not specifying
+.Fl p
+at all just gives you
+.Pa blurfl.c ,
+unless all of the directories in the leading path
+.Pq Pa 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
+.Fl d
+option.
+.It Fl r , Fl Fl reject-file
+Causes the next argument to be interpreted as the reject file name.
+.It Fl R , Fl Fl reverse
+Tells
+.Nm
+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.)
+.Nm
+will attempt to swap each hunk around before applying it.
+Rejects will come out in the swapped format.
+The
+.Fl R
+option will not work with ed diff scripts because there is too little
+information to reconstruct the reverse operation.
+.Pp
+If the first hunk of a patch fails,
+.Nm
+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
+.Fl 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.)
+.It Xo
+.Fl s , Fl Fl quiet ,
+.Fl Fl silent
+.Xc
+Makes
+.Nm
+do its work silently, unless an error occurs.
+.It Fl t , Fl Fl batch
+Similar to
+.Fl 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
+.Fl f ) ;
+skip patches for which the file has the wrong version for the
+.Qq Prereq:
+line in the patch;
+and assume that patches are reversed if they look like they are.
+.It Fl u , Fl Fl unified
+Forces
+.Nm
+to interpret the patch file as a unified context diff (a unidiff).
+.It Fl v , Fl Fl version
+Causes
+.Nm
+to print out its revision header and patch level.
+.It Fl V , Fl Fl version-control
+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
+.Ev PATCH_VERSION_CONTROL
+or
+.Ev VERSION_CONTROL
+environment variables, which are overridden by this option.
+The
+.Fl B
+option overrides this option, causing the prefix to always be used for
+making backup file names.
+The values of the
+.Ev PATCH_VERSION_CONTROL
+and
+.Ev VERSION_CONTROL
+environment variables and the argument to the
+.Fl V
+option are like the GNU Emacs
+.Dq version-control
+variable; they also recognize synonyms that are more descriptive.
+The valid values are (unique abbreviations are accepted):
+.Bl -tag -width Ds -offset indent
+.It t , numbered
+Always make numbered backups.
+.It nil , existing
+Make numbered backups of files that already have them,
+simple backups of the others.
+.It never , simple
+Always make simple backups.
+.El
+.It Xo
+.Fl x Ns Aq Ar number ,
+.Fl Fl debug Aq Ar number
+.Xc
+Sets internal debugging flags, and is of interest only to
+.Nm
+patchers.
+.It Fl z , Fl Fl suffix
+Causes the next argument to be interpreted as the backup extension, to be
+used in place of
+.Qq .orig .
+.It Fl Fl posix
+Enables strict
+.St -p1003.2
+conformance, specifically:
+.Bl -enum
+.It
+Backup files are not created unless the
+.Fl b
+option is specified.
+.It
+If unspecified, the file name used is the first of the old, new and
+index files that exists.
+.El
+.El
+.Ss Patch Application
+.Nm
+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
+.Nm patch ,
+and it should work.
+If the entire diff is indented by a consistent amount,
+this will be taken into account.
+.Pp
+With context diffs, and to a lesser extent with normal diffs,
+.Nm
+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,
+.Nm
+will scan both forwards and backwards for a set of lines matching the context
+given in the hunk.
+First
+.Nm
+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.
+.Pq The default maximum fuzz factor is 2.
+.Pp
+If
+.Nm
+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
+.Qq .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.
+.Pp
+As each hunk is completed, you will be told whether the hunk succeeded or
+failed, and which line (in the new file)
+.Nm
+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.
+.Ss Filename Determination
+If no original file is specified on the command line,
+.Nm
+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
+.Fl p
+option and the file's existence and writability are checked relative
+to the current working directory (or the directory specified by the
+.Fl d
+option).
+.Pp
+If the diff is a context or unified diff,
+.Nm
+is able to determine the old and new file names from the diff header.
+For context diffs, the
+.Dq old
+file is specified in the line beginning with
+.Qq ***
+and the
+.Dq new
+file is specified in the line beginning with
+.Qq --- .
+For a unified diff, the
+.Dq old
+file is specified in the line beginning with
+.Qq ---
+and the
+.Dq new
+file is specified in the line beginning with
+.Qq +++ .
+If there is an
+.Qq Index:
+line in the leading garbage (regardless of the diff type),
+.Nm
+will use the file name from that line as the
+.Dq index
+file.
+.Pp
+.Nm
+will choose the file name by performing the following steps, with the first
+match used:
+.Bl -enum
+.It
+If
+.Nm
+is operating in strict
+.St -p1003.2
+mode, the first of the
+.Dq old ,
+.Dq new
+and
+.Dq index
+file names that exist is used.
+Otherwise,
+.Nm
+will examine either the
+.Dq old
+and
+.Dq new
+file names or, for a non-context diff, the
+.Dq 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).
+.It
+If no file exists,
+.Nm
+checks for the existence of the files in an SCCS or RCS directory
+(using the appropriate prefix or suffix) using the criteria specified
+above.
+If found,
+.Nm
+will attempt to get or check out the file.
+.It
+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.
+.It
+If the file name still cannot be determined,
+.Nm
+will prompt the user for the file name to use.
+.El
+.Pp
+Additionally, if the leading garbage contains a
+.Qq Prereq:\ \&
+line,
+.Nm
+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,
+.Nm
+will ask for confirmation before proceeding.
+.Pp
+The upshot of all this is that you should be able to say, while in a news
+interface, the following:
+.Pp
+.Dl | patch -d /usr/src/local/blurfl
+.Pp
+and patch a file in the blurfl directory directly from the article containing
+the patch.
+.Ss Backup Files
+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
+.Qq .orig ,
+or as specified by the
+.Fl B ,
+.Fl V ,
+or
+.Fl z
+options.
+The extension used for making backup files may also be specified in the
+.Ev SIMPLE_BACKUP_SUFFIX
+environment variable, which is overridden by the options above.
+.Pp
+If the backup file is a symbolic or hard link to the original file,
+.Nm
+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.
+.Pp
+You may also specify where you want the output to go with the
+.Fl o
+option; if that file already exists, it is backed up first.
+.Ss Notes For Patch Senders
+There are several things you should bear in mind if you are going to
+be sending out patches:
+.Pp
+First, you can save people a lot of grief by keeping a
+.Pa 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
+.Qq Prereq:
+line in with the patch, it won't let them apply
+patches out of order without some warning.
+.Pp
+Second, make sure you've specified the file names right, either in a
+context diff header, or with an
+.Qq Index:
+line.
+If you are patching something in a subdirectory, be sure to tell the patch
+user to specify a
+.Fl p
+option as needed.
+.Pp
+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.
+.Pp
+Fourth, take care not to send out reversed patches, since it makes people wonder
+whether they already applied the patch.
+.Pp
+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.
+.Sh ENVIRONMENT
+.Bl -tag -width "PATCH_VERSION_CONTROL" -compact
+.It Ev POSIXLY_CORRECT
+When set,
+.Nm
+behaves as if the
+.Fl Fl posix
+option has been specified.
+.It Ev SIMPLE_BACKUP_SUFFIX
+Extension to use for backup file names instead of
+.Qq .orig .
+.It Ev TMPDIR
+Directory to put temporary files in; default is
+.Pa /tmp .
+.It Ev PATCH_VERSION_CONTROL
+Selects when numbered backup files are made.
+.It Ev VERSION_CONTROL
+Same as
+.Ev PATCH_VERSION_CONTROL .
+.El
+.Sh FILES
+.Bl -tag -width "$TMPDIR/patch*" -compact
+.It Pa $TMPDIR/patch*
+.Nm
+temporary files
+.It Pa /dev/tty
+used to read input when
+.Nm
+prompts the user
+.El
+.Sh DIAGNOSTICS
+Too many to list here, but generally indicative that
+.Nm
+couldn't parse your patch file.
+.Pp
+The message
+.Qq Hmm...
+indicates that there is unprocessed text in the patch file and that
+.Nm
+is attempting to intuit whether there is a patch in that text and, if so,
+what kind of patch it is.
+.Pp
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It \&0
+Successful completion.
+.It \&1
+One or more lines were written to a reject file.
+.It \*[Gt]\&1
+An error occurred.
+.El
+.Pp
+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.
+.Sh SEE ALSO
+.Xr diff 1
+.Sh AUTHORS
+.An Larry Wall
+with many other contributors.
+.Sh CAVEATS
+.Nm
+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
+.Qq change
+or a
+.Qq 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.
+.Pp
+.Nm
+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.
+.Sh BUGS
+Could be smarter about partial matches, excessively deviant offsets and
+swapped code, but that would take an extra pass.
+.Pp
+Check patch mode
+.Pq Fl C
+will fail if you try to check several patches in succession that build on
+each other.
+The entire
+.Nm
+code would have to be restructured to keep temporary files around so that it
+can handle this situation.
+.Pp
+If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
+#endif),
+.Nm
+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.
+.Pp
+If you apply a patch you've already applied,
+.Nm
+will think it is a reversed patch, and offer to un-apply the patch.
+This could be construed as a feature.
Index: usr.bin/patch/patch.c
===================================================================
RCS file: usr.bin/patch/patch.c
diff -N usr.bin/patch/patch.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/patch.c	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,1053 @@
+/*
+ * $OpenBSD: patch.c,v 1.43 2004/11/19 20:08:11 otto Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+#include "backupfile.h"
+#include "pathnames.h"
+
+int		filemode = 0644;
+
+char		buf[MAXLINELEN];	/* general purpose buffer */
+size_t		buf_len = sizeof(buf);
+
+bool		using_plan_a = true;	/* try to keep everything in memory */
+bool		out_of_mem = false;	/* ran out of memory in plan a */
+
+#define MAXFILEC 2
+
+char		*filearg[MAXFILEC];
+bool		ok_to_create_file = false;
+char		*outname = NULL;
+char		*origprae = NULL;
+char		*TMPOUTNAME;
+char		*TMPINNAME;
+char		*TMPREJNAME;
+char		*TMPPATNAME;
+bool		toutkeep = false;
+bool		trejkeep = false;
+bool		warn_on_invalid_line;
+bool		last_line_missing_eol;
+
+#ifdef DEBUGGING
+int		debug = 0;
+#endif
+
+bool		force = false;
+bool		batch = false;
+bool		verbose = true;
+bool		reverse = false;
+bool		noreverse = false;
+bool		skip_rest_of_patch = false;
+int		strippath = 957;
+bool		canonicalize = false;
+bool		check_only = false;
+int		diff_type = 0;
+char		*revision = NULL;	/* prerequisite revision, if any */
+LINENUM		input_lines = 0;	/* how long is input file in lines */
+int		posix = 0;		/* strict POSIX mode? */
+
+static void	reinitialize_almost_everything(void);
+static void	get_some_switches(void);
+static LINENUM	locate_hunk(LINENUM);
+static void	abort_context_hunk(void);
+static void	rej_line(int, LINENUM);
+static void	abort_hunk(void);
+static void	apply_hunk(LINENUM);
+static void	init_output(const char *);
+static void	init_reject(const char *);
+static void	copy_till(LINENUM, bool);
+static void	spew_output(void);
+static void	dump_line(LINENUM, bool);
+static bool	patch_match(LINENUM, LINENUM, LINENUM);
+static bool	similar(const char *, const char *, int);
+static void	usage(void);
+
+/* true if -E was specified on command line.  */
+static bool	remove_empty_files = false;
+
+/* true if -R was specified on command line.  */
+static bool	reverse_flag_specified = false;
+
+/* buffer holding the name of the rejected patch file. */
+static char	rejname[NAME_MAX + 1];
+
+/* buffer for stderr */
+static char	serrbuf[BUFSIZ];
+
+/* how many input lines have been irretractibly output */
+static LINENUM	last_frozen_line = 0;
+
+static int	Argc;		/* guess */
+static char	**Argv;
+static int	Argc_last;	/* for restarting plan_b */
+static char	**Argv_last;
+
+static FILE	*ofp = NULL;	/* output file pointer */
+static FILE	*rejfp = NULL;	/* reject file pointer */
+
+static int	filec = 0;	/* how many file arguments? */
+static LINENUM	last_offset = 0;
+static LINENUM	maxfuzz = 2;
+
+/* patch using ifdef, ifndef, etc. */
+static bool		do_defines = false;
+/* #ifdef xyzzy */
+static char		if_defined[128];
+/* #ifndef xyzzy */
+static char		not_defined[128];
+/* #else */
+static const char	else_defined[] = "#else\n";
+/* #endif xyzzy */
+static char		end_defined[128];
+
+
+/* Apply a set of diffs as appropriate. */
+
+int
+main(int argc, char *argv[])
+{
+	int	error = 0, hunk, failed, i, fd;
+	LINENUM	where = 0, newwhere, fuzz, mymaxfuzz;
+	const	char *tmpdir;
+	char	*v;
+
+	setbuf(stderr, serrbuf);
+	for (i = 0; i < MAXFILEC; i++)
+		filearg[i] = NULL;
+
+	/* Cons up the names of the temporary files.  */
+	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
+		tmpdir = _PATH_TMP;
+	for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
+		;
+	i++;
+	if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
+		fatal("cannot allocate memory");
+	if ((fd = mkstemp(TMPOUTNAME)) < 0)
+		pfatal("can't create %s", TMPOUTNAME);
+	close(fd);
+
+	if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
+		fatal("cannot allocate memory");
+	if ((fd = mkstemp(TMPINNAME)) < 0)
+		pfatal("can't create %s", TMPINNAME);
+	close(fd);
+
+	if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
+		fatal("cannot allocate memory");
+	if ((fd = mkstemp(TMPREJNAME)) < 0)
+		pfatal("can't create %s", TMPREJNAME);
+	close(fd);
+
+	if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
+		fatal("cannot allocate memory");
+	if ((fd = mkstemp(TMPPATNAME)) < 0)
+		pfatal("can't create %s", TMPPATNAME);
+	close(fd);
+
+	v = getenv("SIMPLE_BACKUP_SUFFIX");
+	if (v)
+		simple_backup_suffix = v;
+	else
+		simple_backup_suffix = ORIGEXT;
+
+	/* parse switches */
+	Argc = argc;
+	Argv = argv;
+	get_some_switches();
+
+	if (backup_type == none) {
+		if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
+			v = getenv("VERSION_CONTROL");
+		if (v != NULL || !posix)
+			backup_type = get_version(v);	/* OK to pass NULL. */
+	}
+
+	/* make sure we clean up /tmp in case of disaster */
+	set_signals(0);
+
+	for (open_patch_file(filearg[1]); there_is_another_patch();
+	    reinitialize_almost_everything()) {
+		/* for each patch in patch file */
+
+		warn_on_invalid_line = true;
+
+		if (outname == NULL)
+			outname = savestr(filearg[0]);
+
+		/* for ed script just up and do it and exit */
+		if (diff_type == ED_DIFF) {
+			do_ed_script();
+			continue;
+		}
+		/* initialize the patched file */
+		if (!skip_rest_of_patch)
+			init_output(TMPOUTNAME);
+
+		/* initialize reject file */
+		init_reject(TMPREJNAME);
+
+		/* find out where all the lines are */
+		if (!skip_rest_of_patch)
+			scan_input(filearg[0]);
+
+		/* from here on, open no standard i/o files, because malloc */
+		/* might misfire and we can't catch it easily */
+
+		/* apply each hunk of patch */
+		hunk = 0;
+		failed = 0;
+		out_of_mem = false;
+		while (another_hunk()) {
+			hunk++;
+			fuzz = 0;
+			mymaxfuzz = pch_context();
+			if (maxfuzz < mymaxfuzz)
+				mymaxfuzz = maxfuzz;
+			if (!skip_rest_of_patch) {
+				do {
+					where = locate_hunk(fuzz);
+					if (hunk == 1 && where == 0 && !force) {
+						/* dwim for reversed patch? */
+						if (!pch_swap()) {
+							if (fuzz == 0)
+								say("Not enough memory to try swapped hunk!  Assuming unswapped.\n");
+							continue;
+						}
+						reverse = !reverse;
+						/* try again */
+						where = locate_hunk(fuzz);
+						if (where == 0) {
+							/* didn't find it swapped */
+							if (!pch_swap())
+								/* put it back to normal */
+								fatal("lost hunk on alloc error!\n");
+							reverse = !reverse;
+						} else if (noreverse) {
+							if (!pch_swap())
+								/* put it back to normal */
+								fatal("lost hunk on alloc error!\n");
+							reverse = !reverse;
+							say("Ignoring previously applied (or reversed) patch.\n");
+							skip_rest_of_patch = true;
+						} else if (batch) {
+							if (verbose)
+								say("%seversed (or previously applied) patch detected!  %s -R.",
+								    reverse ? "R" : "Unr",
+								    reverse ? "Assuming" : "Ignoring");
+						} else {
+							ask("%seversed (or previously applied) patch detected!  %s -R? [y] ",
+							    reverse ? "R" : "Unr",
+							    reverse ? "Assume" : "Ignore");
+							if (*buf == 'n') {
+								ask("Apply anyway? [n] ");
+								if (*buf != 'y')
+									skip_rest_of_patch = true;
+								where = 0;
+								reverse = !reverse;
+								if (!pch_swap())
+									/* put it back to normal */
+									fatal("lost hunk on alloc error!\n");
+							}
+						}
+					}
+				} while (!skip_rest_of_patch && where == 0 &&
+				    ++fuzz <= mymaxfuzz);
+
+				if (skip_rest_of_patch) {	/* just got decided */
+					fclose(ofp);
+					ofp = NULL;
+				}
+			}
+			newwhere = pch_newfirst() + last_offset;
+			if (skip_rest_of_patch) {
+				abort_hunk();
+				failed++;
+				if (verbose)
+					say("Hunk #%d ignored at %ld.\n",
+					    hunk, newwhere);
+			} else if (where == 0) {
+				abort_hunk();
+				failed++;
+				if (verbose)
+					say("Hunk #%d failed at %ld.\n",
+					    hunk, newwhere);
+			} else {
+				apply_hunk(where);
+				if (verbose) {
+					say("Hunk #%d succeeded at %ld",
+					    hunk, newwhere);
+					if (fuzz != 0)
+						say(" with fuzz %ld", fuzz);
+					if (last_offset)
+						say(" (offset %ld line%s)",
+						    last_offset,
+						    last_offset == 1L ? "" : "s");
+					say(".\n");
+				}
+			}
+		}
+
+		if (out_of_mem && using_plan_a) {
+			Argc = Argc_last;
+			Argv = Argv_last;
+			say("\n\nRan out of memory using Plan A--trying again...\n\n");
+			if (ofp)
+				fclose(ofp);
+			ofp = NULL;
+			if (rejfp)
+				fclose(rejfp);
+			rejfp = NULL;
+			continue;
+		}
+		if (hunk == 0)
+			fatal("Internal error: hunk should not be 0\n");
+
+		/* finish spewing out the new file */
+		if (!skip_rest_of_patch)
+			spew_output();
+
+		/* and put the output where desired */
+		ignore_signals();
+		if (!skip_rest_of_patch) {
+			struct stat	statbuf;
+			char	*realout = outname;
+
+			if (!check_only) {
+				if (move_file(TMPOUTNAME, outname) < 0) {
+					toutkeep = true;
+					realout = TMPOUTNAME;
+					chmod(TMPOUTNAME, filemode);
+				} else
+					chmod(outname, filemode);
+
+				if (remove_empty_files &&
+				    stat(realout, &statbuf) == 0 &&
+				    statbuf.st_size == 0) {
+					if (verbose)
+						say("Removing %s (empty after patching).\n",
+						    realout);
+					unlink(realout);
+				}
+			}
+		}
+		fclose(rejfp);
+		rejfp = NULL;
+		if (failed) {
+			error = 1;
+			if (*rejname == '\0') {
+				if (strlcpy(rejname, outname,
+				    sizeof(rejname)) >= sizeof(rejname))
+					fatal("filename %s is too long\n", outname);
+				if (strlcat(rejname, REJEXT,
+				    sizeof(rejname)) >= sizeof(rejname))
+					fatal("filename %s is too long\n", outname);
+			}
+			if (skip_rest_of_patch) {
+				say("%d out of %d hunks ignored--saving rejects to %s\n",
+				    failed, hunk, rejname);
+			} else {
+				say("%d out of %d hunks failed--saving rejects to %s\n",
+				    failed, hunk, rejname);
+			}
+			if (!check_only && move_file(TMPREJNAME, rejname) < 0)
+				trejkeep = true;
+		}
+		set_signals(1);
+	}
+	my_exit(error);
+	/* NOTREACHED */
+}
+
+/* Prepare to find the next patch to do in the patch file. */
+
+static void
+reinitialize_almost_everything(void)
+{
+	re_patch();
+	re_input();
+
+	input_lines = 0;
+	last_frozen_line = 0;
+
+	filec = 0;
+	if (!out_of_mem) {
+		free(filearg[0]);
+		filearg[0] = NULL;
+	}
+
+	free(outname);
+	outname = NULL;
+
+	last_offset = 0;
+	diff_type = 0;
+
+	free(revision);
+	revision = NULL;
+
+	reverse = reverse_flag_specified;
+	skip_rest_of_patch = false;
+
+	get_some_switches();
+}
+
+/* Process switches and filenames. */
+
+static void
+get_some_switches(void)
+{
+	const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
+	static struct option longopts[] = {
+		{"backup",		no_argument,		0,	'b'},
+		{"batch",		no_argument,		0,	't'},
+		{"check",		no_argument,		0,	'C'},
+		{"context",		no_argument,		0,	'c'},
+		{"debug",		required_argument,	0,	'x'},
+		{"directory",		required_argument,	0,	'd'},
+		{"ed",			no_argument,		0,	'e'},
+		{"force",		no_argument,		0,	'f'},
+		{"forward",		no_argument,		0,	'N'},
+		{"fuzz",		required_argument,	0,	'F'},
+		{"ifdef",		required_argument,	0,	'D'},
+		{"input",		required_argument,	0,	'i'},
+		{"ignore-whitespace",	no_argument,		0,	'l'},
+		{"normal",		no_argument,		0,	'n'},
+		{"output",		required_argument,	0,	'o'},
+		{"prefix",		required_argument,	0,	'B'},
+		{"quiet",		no_argument,		0,	's'},
+		{"reject-file",		required_argument,	0,	'r'},
+		{"remove-empty-files",	no_argument,		0,	'E'},
+		{"reverse",		no_argument,		0,	'R'},
+		{"silent",		no_argument,		0,	's'},
+		{"strip",		required_argument,	0,	'p'},
+		{"suffix",		required_argument,	0,	'z'},
+		{"unified",		no_argument,		0,	'u'},
+		{"version",		no_argument,		0,	'v'},
+		{"version-control",	required_argument,	0,	'V'},
+		{"posix",		no_argument,		&posix,	1},
+		{NULL,			0,			0,	0}
+	};
+	int ch;
+
+	rejname[0] = '\0';
+	Argc_last = Argc;
+	Argv_last = Argv;
+	if (!Argc)
+		return;
+	optreset = optind = 1;
+	while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
+		switch (ch) {
+		case 'b':
+			if (backup_type == none)
+				backup_type = numbered_existing;
+			if (optarg == NULL)
+				break;
+			if (verbose)
+				say("Warning, the ``-b suffix'' option has been"
+				    " obsoleted by the -z option.\n");
+			/* FALLTHROUGH */
+		case 'z':
+			/* must directly follow 'b' case for backwards compat */
+			simple_backup_suffix = savestr(optarg);
+			break;
+		case 'B':
+			origprae = savestr(optarg);
+			break;
+		case 'c':
+			diff_type = CONTEXT_DIFF;
+			break;
+		case 'C':
+			check_only = true;
+			break;
+		case 'd':
+			if (chdir(optarg) < 0)
+				pfatal("can't cd to %s", optarg);
+			break;
+		case 'D':
+			do_defines = true;
+			if (!isalpha((unsigned char)*optarg) && *optarg != '_')
+				fatal("argument to -D is not an identifier\n");
+			snprintf(if_defined, sizeof if_defined,
+			    "#ifdef %s\n", optarg);
+			snprintf(not_defined, sizeof not_defined,
+			    "#ifndef %s\n", optarg);
+			snprintf(end_defined, sizeof end_defined,
+			    "#endif /* %s */\n", optarg);
+			break;
+		case 'e':
+			diff_type = ED_DIFF;
+			break;
+		case 'E':
+			remove_empty_files = true;
+			break;
+		case 'f':
+			force = true;
+			break;
+		case 'F':
+			maxfuzz = atoi(optarg);
+			break;
+		case 'i':
+			if (++filec == MAXFILEC)
+				fatal("too many file arguments\n");
+			filearg[filec] = savestr(optarg);
+			break;
+		case 'l':
+			canonicalize = true;
+			break;
+		case 'n':
+			diff_type = NORMAL_DIFF;
+			break;
+		case 'N':
+			noreverse = true;
+			break;
+		case 'o':
+			outname = savestr(optarg);
+			break;
+		case 'p':
+			strippath = atoi(optarg);
+			break;
+		case 'r':
+			if (strlcpy(rejname, optarg,
+			    sizeof(rejname)) >= sizeof(rejname))
+				fatal("argument for -r is too long\n");
+			break;
+		case 'R':
+			reverse = true;
+			reverse_flag_specified = true;
+			break;
+		case 's':
+			verbose = false;
+			break;
+		case 't':
+			batch = true;
+			break;
+		case 'u':
+			diff_type = UNI_DIFF;
+			break;
+		case 'v':
+			version();
+			break;
+		case 'V':
+			backup_type = get_version(optarg);
+			break;
+#ifdef DEBUGGING
+		case 'x':
+			debug = atoi(optarg);
+			break;
+#endif
+		default:
+			if (ch != '\0')
+				usage();
+			break;
+		}
+	}
+	Argc -= optind;
+	Argv += optind;
+
+	if (Argc > 0) {
+		filearg[0] = savestr(*Argv++);
+		Argc--;
+		while (Argc > 0) {
+			if (++filec == MAXFILEC)
+				fatal("too many file arguments\n");
+			filearg[filec] = savestr(*Argv++);
+			Argc--;
+		}
+	}
+
+	if (getenv("POSIXLY_CORRECT") != NULL)
+		posix = 1;
+}
+
+static void
+usage(void)
+{
+	fprintf(stderr,
+"usage: patch [-bcCeEflnNRstuv] [-B backup-prefix] [-d directory] [-D symbol]\n"
+"             [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
+"             [-r rej-name] [-V {numbered,existing,simple}] [-z backup-ext]\n"
+"             [origfile [patchfile]]\n");
+	my_exit(EXIT_SUCCESS);
+}
+
+/*
+ * Attempt to find the right place to apply this hunk of patch.
+ */
+static LINENUM
+locate_hunk(LINENUM fuzz)
+{
+	LINENUM	first_guess = pch_first() + last_offset;
+	LINENUM	offset;
+	LINENUM	pat_lines = pch_ptrn_lines();
+	LINENUM	max_pos_offset = input_lines - first_guess - pat_lines + 1;
+	LINENUM	max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
+
+	if (pat_lines == 0) {		/* null range matches always */
+		if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
+		    || diff_type == NEW_CONTEXT_DIFF
+		    || diff_type == UNI_DIFF)) {
+			say("Empty context always matches.\n");
+		}
+		if (diff_type == CONTEXT_DIFF
+		    || diff_type == NEW_CONTEXT_DIFF
+		    || diff_type == UNI_DIFF) {
+			if (fuzz == 0)
+				return (input_lines == 0 ? first_guess : 0);
+		} else
+			return (first_guess);
+	}
+	if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
+		max_neg_offset = first_guess - 1;
+	if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
+		return first_guess;
+	for (offset = 1; ; offset++) {
+		bool	check_after = (offset <= max_pos_offset);
+		bool	check_before = (offset <= max_neg_offset);
+
+		if (check_after && patch_match(first_guess, offset, fuzz)) {
+#ifdef DEBUGGING
+			if (debug & 1)
+				say("Offset changing from %ld to %ld\n",
+				    last_offset, offset);
+#endif
+			last_offset = offset;
+			return first_guess + offset;
+		} else if (check_before && patch_match(first_guess, -offset, fuzz)) {
+#ifdef DEBUGGING
+			if (debug & 1)
+				say("Offset changing from %ld to %ld\n",
+				    last_offset, -offset);
+#endif
+			last_offset = -offset;
+			return first_guess - offset;
+		} else if (!check_before && !check_after)
+			return 0;
+	}
+}
+
+/* We did not find the pattern, dump out the hunk so they can handle it. */
+
+static void
+abort_context_hunk(void)
+{
+	LINENUM	i;
+	const LINENUM	pat_end = pch_end();
+	/*
+	 * add in last_offset to guess the same as the previous successful
+	 * hunk
+	 */
+	const LINENUM	oldfirst = pch_first() + last_offset;
+	const LINENUM	newfirst = pch_newfirst() + last_offset;
+	const LINENUM	oldlast = oldfirst + pch_ptrn_lines() - 1;
+	const LINENUM	newlast = newfirst + pch_repl_lines() - 1;
+	const char	*stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
+	const char	*minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
+
+	fprintf(rejfp, "***************\n");
+	for (i = 0; i <= pat_end; i++) {
+		switch (pch_char(i)) {
+		case '*':
+			if (oldlast < oldfirst)
+				fprintf(rejfp, "*** 0%s\n", stars);
+			else if (oldlast == oldfirst)
+				fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
+			else
+				fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
+				    oldlast, stars);
+			break;
+		case '=':
+			if (newlast < newfirst)
+				fprintf(rejfp, "--- 0%s\n", minuses);
+			else if (newlast == newfirst)
+				fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
+			else
+				fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
+				    newlast, minuses);
+			break;
+		case '\n':
+			fprintf(rejfp, "%s", pfetch(i));
+			break;
+		case ' ':
+		case '-':
+		case '+':
+		case '!':
+			fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
+			break;
+		default:
+			fatal("fatal internal error in abort_context_hunk\n");
+		}
+	}
+}
+
+static void
+rej_line(int ch, LINENUM i)
+{
+	size_t len;
+	const char *line = pfetch(i);
+
+	len = strlen(line);
+
+	fprintf(rejfp, "%c%s", ch, line);
+	if (len == 0 || line[len-1] != '\n')
+		fprintf(rejfp, "\n\\ No newline at end of file\n");
+}
+
+static void
+abort_hunk(void)
+{
+	LINENUM		i, j, split;
+	int		ch1, ch2;
+	const LINENUM	pat_end = pch_end();
+	const LINENUM	oldfirst = pch_first() + last_offset;
+	const LINENUM	newfirst = pch_newfirst() + last_offset;
+
+	if (diff_type != UNI_DIFF) {
+		abort_context_hunk();
+		return;
+	}
+	split = -1;
+	for (i = 0; i <= pat_end; i++) {
+		if (pch_char(i) == '=') {
+			split = i;
+			break;
+		}
+	}
+	if (split == -1) {
+		fprintf(rejfp, "malformed hunk: no split found\n");
+		return;
+	}
+	i = 0;
+	j = split + 1;
+	fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
+	    pch_ptrn_lines() ? oldfirst : 0,
+	    pch_ptrn_lines(), newfirst, pch_repl_lines());
+	while (i < split || j <= pat_end) {
+		ch1 = i < split ? pch_char(i) : -1;
+		ch2 = j <= pat_end ? pch_char(j) : -1;
+		if (ch1 == '-') {
+			rej_line('-', i);
+			i++;
+		} else if (ch1 == ' ' && ch2 == ' ') {
+			rej_line(' ', i);
+			i++;
+			j++;
+		} else if (ch1 == '!' && ch2 == '!') {
+			while (i < split && ch1 == '!') {
+				rej_line('-', i);
+				i++;
+				ch1 = i < split ? pch_char(i) : -1;
+			}
+			while (j <= pat_end && ch2 == '!') {
+				rej_line('+', j);
+				j++;
+				ch2 = j <= pat_end ? pch_char(j) : -1;
+			}
+		} else if (ch1 == '*') {
+			i++;
+		} else if (ch2 == '+' || ch2 == ' ') {
+			rej_line(ch2, j);
+			j++;
+		} else {
+			fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
+			    i, split, j);
+			rej_line(ch1, i);
+			rej_line(ch2, j);
+			return;
+		}
+	}
+}
+
+/* We found where to apply it (we hope), so do it. */
+
+static void
+apply_hunk(LINENUM where)
+{
+	LINENUM		old = 1;
+	const LINENUM	lastline = pch_ptrn_lines();
+	LINENUM		new = lastline + 1;
+#define OUTSIDE 0
+#define IN_IFNDEF 1
+#define IN_IFDEF 2
+#define IN_ELSE 3
+	int		def_state = OUTSIDE;
+	const LINENUM	pat_end = pch_end();
+
+	where--;
+	while (pch_char(new) == '=' || pch_char(new) == '\n')
+		new++;
+
+	while (old <= lastline) {
+		if (pch_char(old) == '-') {
+			copy_till(where + old - 1, false);
+			if (do_defines) {
+				if (def_state == OUTSIDE) {
+					fputs(not_defined, ofp);
+					def_state = IN_IFNDEF;
+				} else if (def_state == IN_IFDEF) {
+					fputs(else_defined, ofp);
+					def_state = IN_ELSE;
+				}
+				fputs(pfetch(old), ofp);
+			}
+			last_frozen_line++;
+			old++;
+		} else if (new > pat_end) {
+			break;
+		} else if (pch_char(new) == '+') {
+			copy_till(where + old - 1, false);
+			if (do_defines) {
+				if (def_state == IN_IFNDEF) {
+					fputs(else_defined, ofp);
+					def_state = IN_ELSE;
+				} else if (def_state == OUTSIDE) {
+					fputs(if_defined, ofp);
+					def_state = IN_IFDEF;
+				}
+			}
+			fputs(pfetch(new), ofp);
+			new++;
+		} else if (pch_char(new) != pch_char(old)) {
+			say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
+			    pch_hunk_beg() + old,
+			    pch_hunk_beg() + new);
+#ifdef DEBUGGING
+			say("oldchar = '%c', newchar = '%c'\n",
+			    pch_char(old), pch_char(new));
+#endif
+			my_exit(2);
+		} else if (pch_char(new) == '!') {
+			copy_till(where + old - 1, false);
+			if (do_defines) {
+				fputs(not_defined, ofp);
+				def_state = IN_IFNDEF;
+			}
+			while (pch_char(old) == '!') {
+				if (do_defines) {
+					fputs(pfetch(old), ofp);
+				}
+				last_frozen_line++;
+				old++;
+			}
+			if (do_defines) {
+				fputs(else_defined, ofp);
+				def_state = IN_ELSE;
+			}
+			while (pch_char(new) == '!') {
+				fputs(pfetch(new), ofp);
+				new++;
+			}
+		} else {
+			if (pch_char(new) != ' ')
+				fatal("Internal error: expected ' '\n");
+			old++;
+			new++;
+			if (do_defines && def_state != OUTSIDE) {
+				fputs(end_defined, ofp);
+				def_state = OUTSIDE;
+			}
+		}
+	}
+	if (new <= pat_end && pch_char(new) == '+') {
+		copy_till(where + old - 1, false);
+		if (do_defines) {
+			if (def_state == OUTSIDE) {
+				fputs(if_defined, ofp);
+				def_state = IN_IFDEF;
+			} else if (def_state == IN_IFNDEF) {
+				fputs(else_defined, ofp);
+				def_state = IN_ELSE;
+			}
+		}
+		while (new <= pat_end && pch_char(new) == '+') {
+			fputs(pfetch(new), ofp);
+			new++;
+		}
+	}
+	if (do_defines && def_state != OUTSIDE) {
+		fputs(end_defined, ofp);
+	}
+}
+
+/*
+ * Open the new file.
+ */
+static void
+init_output(const char *name)
+{
+	ofp = fopen(name, "w");
+	if (ofp == NULL)
+		pfatal("can't create %s", name);
+}
+
+/*
+ * Open a file to put hunks we can't locate.
+ */
+static void
+init_reject(const char *name)
+{
+	rejfp = fopen(name, "w");
+	if (rejfp == NULL)
+		pfatal("can't create %s", name);
+}
+
+/*
+ * Copy input file to output, up to wherever hunk is to be applied.
+ * If endoffile is true, treat the last line specially since it may
+ * lack a newline.
+ */
+static void
+copy_till(LINENUM lastline, bool endoffile)
+{
+	if (last_frozen_line > lastline)
+		fatal("misordered hunks! output would be garbled\n");
+	while (last_frozen_line < lastline) {
+		if (++last_frozen_line == lastline && endoffile)
+			dump_line(last_frozen_line, !last_line_missing_eol);
+		else
+			dump_line(last_frozen_line, true);
+	}
+}
+
+/*
+ * Finish copying the input file to the output file.
+ */
+static void
+spew_output(void)
+{
+#ifdef DEBUGGING
+	if (debug & 256)
+		say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
+#endif
+	if (input_lines)
+		copy_till(input_lines, true);	/* dump remainder of file */
+	fclose(ofp);
+	ofp = NULL;
+}
+
+/*
+ * Copy one line from input to output.
+ */
+static void
+dump_line(LINENUM line, bool write_newline)
+{
+	char	*s;
+
+	s = ifetch(line, 0);
+	if (s == NULL)
+		return;
+	/* Note: string is not NUL terminated. */
+	for (; *s != '\n'; s++)
+		putc(*s, ofp);
+	if (write_newline)
+		putc('\n', ofp);
+}
+
+/*
+ * Does the patch pattern match at line base+offset?
+ */
+static bool
+patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
+{
+	LINENUM		pline = 1 + fuzz;
+	LINENUM		iline;
+	LINENUM		pat_lines = pch_ptrn_lines() - fuzz;
+	const char	*ilineptr;
+	const char	*plineptr;
+	short		plinelen;
+
+	for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
+		ilineptr = ifetch(iline, offset >= 0);
+		if (ilineptr == NULL)
+			return false;
+		plineptr = pfetch(pline);
+		plinelen = pch_line_len(pline);
+		if (canonicalize) {
+			if (!similar(ilineptr, plineptr, plinelen))
+				return false;
+		} else if (strnNE(ilineptr, plineptr, plinelen))
+			return false;
+		if (iline == input_lines) {
+			/*
+			 * We are looking at the last line of the file.
+			 * If the file has no eol, the patch line should
+			 * not have one either and vice-versa. Note that
+			 * plinelen > 0.
+			 */
+			if (last_line_missing_eol) {
+				if (plineptr[plinelen - 1] == '\n')
+					return false;
+			} else {
+				if (plineptr[plinelen - 1] != '\n')
+					return false;
+			}
+		}
+	}
+	return true;
+}
+
+/*
+ * Do two lines match with canonicalized white space?
+ */
+static bool
+similar(const char *a, const char *b, int len)
+{
+	while (len) {
+		if (isspace((unsigned char)*b)) {	/* whitespace (or \n) to match? */
+			if (!isspace((unsigned char)*a))	/* no corresponding whitespace? */
+				return false;
+			while (len && isspace((unsigned char)*b) && *b != '\n')
+				b++, len--;	/* skip pattern whitespace */
+			while (isspace((unsigned char)*a) && *a != '\n')
+				a++;	/* skip target whitespace */
+			if (*a == '\n' || *b == '\n')
+				return (*a == *b);	/* should end in sync */
+		} else if (*a++ != *b++)	/* match non-whitespace chars */
+			return false;
+		else
+			len--;	/* probably not necessary */
+	}
+	return true;		/* actually, this is not reached */
+	/* since there is always a \n */
+}
Index: usr.bin/patch/pathnames.h
===================================================================
RCS file: usr.bin/patch/pathnames.h
diff -N usr.bin/patch/pathnames.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/pathnames.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,14 @@
+/*
+ * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
+ * $DragonFly$
+ */
+
+/*
+ * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
+ * on July 29, 2003.
+ */
+
+#include <paths.h>
+
+#define	_PATH_ED		"/bin/ed"
+#define	_PATH_MKDIR		"/bin/mkdir"
Index: usr.bin/patch/pch.c
===================================================================
RCS file: usr.bin/patch/pch.c
diff -N usr.bin/patch/pch.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/pch.c	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,1551 @@
+/*
+ * $OpenBSD: pch.c,v 1.35 2004/08/05 21:47:24 deraadt Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "pathnames.h"
+
+/* Patch (diff listing) abstract type. */
+
+static long	p_filesize;	/* size of the patch file */
+static LINENUM	p_first;	/* 1st line number */
+static LINENUM	p_newfirst;	/* 1st line number of replacement */
+static LINENUM	p_ptrn_lines;	/* # lines in pattern */
+static LINENUM	p_repl_lines;	/* # lines in replacement text */
+static LINENUM	p_end = -1;	/* last line in hunk */
+static LINENUM	p_max;		/* max allowed value of p_end */
+static LINENUM	p_context = 3;	/* # of context lines */
+static LINENUM	p_input_line = 0;	/* current line # from patch file */
+static char	**p_line = NULL;/* the text of the hunk */
+static short	*p_len = NULL;	/* length of each line */
+static char	*p_char = NULL;	/* +, -, and ! */
+static int	hunkmax = INITHUNKMAX;	/* size of above arrays to begin with */
+static int	p_indent;	/* indent to patch */
+static LINENUM	p_base;		/* where to intuit this time */
+static LINENUM	p_bline;	/* line # of p_base */
+static LINENUM	p_start;	/* where intuit found a patch */
+static LINENUM	p_sline;	/* and the line number for it */
+static LINENUM	p_hunk_beg;	/* line number of current hunk */
+static LINENUM	p_efake = -1;	/* end of faked up lines--don't free */
+static LINENUM	p_bfake = -1;	/* beg of faked up lines */
+static FILE	*pfp = NULL;	/* patch file pointer */
+static char	*bestguess = NULL;	/* guess at correct filename */
+
+static void	grow_hunkmax(void);
+static int	intuit_diff_type(void);
+static void	next_intuit_at(LINENUM, LINENUM);
+static void	skip_to(LINENUM, LINENUM);
+static char	*pgets(char *, int, FILE *);
+static char	*best_name(const struct file_name *, bool);
+static char	*posix_name(const struct file_name *, bool);
+static size_t	num_components(const char *);
+
+/*
+ * Prepare to look for the next patch in the patch file.
+ */
+void
+re_patch(void)
+{
+	p_first = 0;
+	p_newfirst = 0;
+	p_ptrn_lines = 0;
+	p_repl_lines = 0;
+	p_end = (LINENUM) - 1;
+	p_max = 0;
+	p_indent = 0;
+}
+
+/*
+ * Open the patch file at the beginning of time.
+ */
+void
+open_patch_file(const char *filename)
+{
+	struct stat filestat;
+
+	if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
+		pfp = fopen(TMPPATNAME, "w");
+		if (pfp == NULL)
+			pfatal("can't create %s", TMPPATNAME);
+		while (fgets(buf, buf_len, stdin) != NULL)
+			fputs(buf, pfp);
+		fclose(pfp);
+		filename = TMPPATNAME;
+	}
+	pfp = fopen(filename, "r");
+	if (pfp == NULL)
+		pfatal("patch file %s not found", filename);
+	fstat(fileno(pfp), &filestat);
+	p_filesize = filestat.st_size;
+	next_intuit_at(0L, 1L);	/* start at the beginning */
+	set_hunkmax();
+}
+
+/*
+ * Make sure our dynamically realloced tables are malloced to begin with.
+ */
+void
+set_hunkmax(void)
+{
+	if (p_line == NULL)
+		p_line = malloc((size_t) hunkmax * sizeof(char *));
+	if (p_len == NULL)
+		p_len = malloc((size_t) hunkmax * sizeof(short));
+	if (p_char == NULL)
+		p_char = malloc((size_t) hunkmax * sizeof(char));
+}
+
+/*
+ * Enlarge the arrays containing the current hunk of patch.
+ */
+static void
+grow_hunkmax(void)
+{
+	int		new_hunkmax;
+	char		**new_p_line;
+	short		*new_p_len;
+	char		*new_p_char;
+
+	new_hunkmax = hunkmax * 2;
+
+	if (p_line == NULL || p_len == NULL || p_char == NULL)
+		fatal("Internal memory allocation error\n");
+
+	new_p_line = realloc(p_line, new_hunkmax * sizeof(char *));
+	if (new_p_line == NULL)
+		free(p_line);
+
+	new_p_len = realloc(p_len, new_hunkmax * sizeof(short));
+	if (new_p_len == NULL)
+		free(p_len);
+
+	new_p_char = realloc(p_char, new_hunkmax * sizeof(char));
+	if (new_p_char == NULL)
+		free(p_char);
+
+	p_char = new_p_char;
+	p_len = new_p_len;
+	p_line = new_p_line;
+
+	if (p_line != NULL && p_len != NULL && p_char != NULL) {
+		hunkmax = new_hunkmax;
+		return;
+	}
+
+	if (!using_plan_a)
+		fatal("out of memory\n");
+	out_of_mem = true;	/* whatever is null will be allocated again */
+				/* from within plan_a(), of all places */
+}
+
+/* True if the remainder of the patch file contains a diff of some sort. */
+
+bool
+there_is_another_patch(void)
+{
+	bool exists = false;
+
+	if (p_base != 0L && p_base >= p_filesize) {
+		if (verbose)
+			say("done\n");
+		return false;
+	}
+	if (verbose)
+		say("Hmm...");
+	diff_type = intuit_diff_type();
+	if (!diff_type) {
+		if (p_base != 0L) {
+			if (verbose)
+				say("  Ignoring the trailing garbage.\ndone\n");
+		} else
+			say("  I can't seem to find a patch in there anywhere.\n");
+		return false;
+	}
+	if (verbose)
+		say("  %sooks like %s to me...\n",
+		    (p_base == 0L ? "L" : "The next patch l"),
+		    diff_type == UNI_DIFF ? "a unified diff" :
+		    diff_type == CONTEXT_DIFF ? "a context diff" :
+		diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
+		    diff_type == NORMAL_DIFF ? "a normal diff" :
+		    "an ed script");
+	if (p_indent && verbose)
+		say("(Patch is indented %d space%s.)\n", p_indent,
+		    p_indent == 1 ? "" : "s");
+	skip_to(p_start, p_sline);
+	while (filearg[0] == NULL) {
+		if (force || batch) {
+			say("No file to patch.  Skipping...\n");
+			filearg[0] = savestr(bestguess);
+			skip_rest_of_patch = true;
+			return true;
+		}
+		ask("File to patch: ");
+		if (*buf != '\n') {
+			free(bestguess);
+			bestguess = savestr(buf);
+			filearg[0] = fetchname(buf, &exists, 0);
+		}
+		if (!exists) {
+			ask("No file found--skip this patch? [n] ");
+			if (*buf != 'y')
+				continue;
+			if (verbose)
+				say("Skipping patch...\n");
+			free(filearg[0]);
+			filearg[0] = fetchname(bestguess, &exists, 0);
+			skip_rest_of_patch = true;
+			return true;
+		}
+	}
+	return true;
+}
+
+/* Determine what kind of diff is in the remaining part of the patch file. */
+
+static int
+intuit_diff_type(void)
+{
+	long	this_line = 0, previous_line;
+	long	first_command_line = -1;
+	LINENUM	fcl_line = -1;
+	bool	last_line_was_command = false, this_is_a_command = false;
+	bool	stars_last_line = false, stars_this_line = false;
+	char	*s, *t;
+	int	indent, retval;
+	struct file_name names[MAX_FILE];
+
+	memset(names, 0, sizeof(names));
+	ok_to_create_file = false;
+	fseek(pfp, p_base, SEEK_SET);
+	p_input_line = p_bline - 1;
+	for (;;) {
+		previous_line = this_line;
+		last_line_was_command = this_is_a_command;
+		stars_last_line = stars_this_line;
+		this_line = ftell(pfp);
+		indent = 0;
+		p_input_line++;
+		if (fgets(buf, buf_len, pfp) == NULL) {
+			if (first_command_line >= 0L) {
+				/* nothing but deletes!? */
+				p_start = first_command_line;
+				p_sline = fcl_line;
+				retval = ED_DIFF;
+				goto scan_exit;
+			} else {
+				p_start = this_line;
+				p_sline = p_input_line;
+				retval = 0;
+				goto scan_exit;
+			}
+		}
+		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
+			if (*s == '\t')
+				indent += 8 - (indent % 8);
+			else
+				indent++;
+		}
+		for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
+			;
+		this_is_a_command = (isdigit((unsigned char)*s) &&
+		    (*t == 'd' || *t == 'c' || *t == 'a'));
+		if (first_command_line < 0L && this_is_a_command) {
+			first_command_line = this_line;
+			fcl_line = p_input_line;
+			p_indent = indent;	/* assume this for now */
+		}
+		if (!stars_last_line && strnEQ(s, "*** ", 4))
+			names[OLD_FILE].path = fetchname(s + 4,
+			    &names[OLD_FILE].exists, strippath);
+		else if (strnEQ(s, "--- ", 4))
+			names[NEW_FILE].path = fetchname(s + 4,
+			    &names[NEW_FILE].exists, strippath);
+		else if (strnEQ(s, "+++ ", 4))
+			/* pretend it is the old name */
+			names[OLD_FILE].path = fetchname(s + 4,
+			    &names[OLD_FILE].exists, strippath);
+		else if (strnEQ(s, "Index:", 6))
+			names[INDEX_FILE].path = fetchname(s + 6,
+			    &names[INDEX_FILE].exists, strippath);
+		else if (strnEQ(s, "Prereq:", 7)) {
+			for (t = s + 7; isspace((unsigned char)*t); t++)
+				;
+			revision = savestr(t);
+			for (t = revision; *t && !isspace((unsigned char)*t); t++)
+				;
+			*t = '\0';
+			if (*revision == '\0') {
+				free(revision);
+				revision = NULL;
+			}
+		}
+		if ((!diff_type || diff_type == ED_DIFF) &&
+		    first_command_line >= 0L &&
+		    strEQ(s, ".\n")) {
+			p_indent = indent;
+			p_start = first_command_line;
+			p_sline = fcl_line;
+			retval = ED_DIFF;
+			goto scan_exit;
+		}
+		if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
+			if (strnEQ(s + 4, "0,0", 3))
+				ok_to_create_file = true;
+			p_indent = indent;
+			p_start = this_line;
+			p_sline = p_input_line;
+			retval = UNI_DIFF;
+			goto scan_exit;
+		}
+		stars_this_line = strnEQ(s, "********", 8);
+		if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
+		    strnEQ(s, "*** ", 4)) {
+			if (atol(s + 4) == 0)
+				ok_to_create_file = true;
+			/*
+			 * If this is a new context diff the character just
+			 * before the newline is a '*'.
+			 */
+			while (*s != '\n')
+				s++;
+			p_indent = indent;
+			p_start = previous_line;
+			p_sline = p_input_line - 1;
+			retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
+			goto scan_exit;
+		}
+		if ((!diff_type || diff_type == NORMAL_DIFF) &&
+		    last_line_was_command &&
+		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
+			p_start = previous_line;
+			p_sline = p_input_line - 1;
+			p_indent = indent;
+			retval = NORMAL_DIFF;
+			goto scan_exit;
+		}
+	}
+scan_exit:
+	if (retval == UNI_DIFF) {
+		/* unswap old and new */
+		struct file_name tmp = names[OLD_FILE];
+		names[OLD_FILE] = names[NEW_FILE];
+		names[NEW_FILE] = tmp;
+	}
+	if (filearg[0] == NULL) {
+		if (posix)
+			filearg[0] = posix_name(names, ok_to_create_file);
+		else {
+			/* Ignore the Index: name for context diffs, like GNU */
+			if (names[OLD_FILE].path != NULL ||
+			    names[NEW_FILE].path != NULL) {
+				free(names[INDEX_FILE].path);
+				names[INDEX_FILE].path = NULL;
+			}
+			filearg[0] = best_name(names, ok_to_create_file);
+		}
+	}
+
+	free(bestguess);
+	bestguess = NULL;
+	if (filearg[0] != NULL)
+		bestguess = savestr(filearg[0]);
+	else if (!ok_to_create_file) {
+		/*
+		 * We don't want to create a new file but we need a
+		 * filename to set bestguess.  Avoid setting filearg[0]
+		 * so the file is not created automatically.
+		 */
+		if (posix)
+			bestguess = posix_name(names, true);
+		else
+			bestguess = best_name(names, true);
+	}
+	free(names[OLD_FILE].path);
+	free(names[NEW_FILE].path);
+	free(names[INDEX_FILE].path);
+	return retval;
+}
+
+/*
+ * Remember where this patch ends so we know where to start up again.
+ */
+static void
+next_intuit_at(LINENUM file_pos, LINENUM file_line)
+{
+	p_base = file_pos;
+	p_bline = file_line;
+}
+
+/*
+ * Basically a verbose fseek() to the actual diff listing.
+ */
+static void
+skip_to(LINENUM file_pos, LINENUM file_line)
+{
+	char	*ret;
+
+	if (p_base > file_pos)
+		fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
+	if (verbose && p_base < file_pos) {
+		fseek(pfp, p_base, SEEK_SET);
+		say("The text leading up to this was:\n--------------------------\n");
+		while (ftell(pfp) < file_pos) {
+			ret = fgets(buf, buf_len, pfp);
+			if (ret == NULL)
+				fatal("Unexpected end of file\n");
+			say("|%s", buf);
+		}
+		say("--------------------------\n");
+	} else
+		fseek(pfp, file_pos, SEEK_SET);
+	p_input_line = file_line - 1;
+}
+
+/* Make this a function for better debugging.  */
+static void
+malformed(void)
+{
+	fatal("malformed patch at line %ld: %s", p_input_line, buf);
+	/* about as informative as "Syntax error" in C */
+}
+
+/*
+ * True if the line has been discarded (i.e. it is a line saying
+ *  "\ No newline at end of file".)
+ */
+static bool
+remove_special_line(void)
+{
+	int	c;
+
+	c = fgetc(pfp);
+	if (c == '\\') {
+		do {
+			c = fgetc(pfp);
+		} while (c != EOF && c != '\n');
+
+		return true;
+	}
+	if (c != EOF)
+		fseek(pfp, -1L, SEEK_CUR);
+
+	return false;
+}
+
+/*
+ * True if there is more of the current diff listing to process.
+ */
+bool
+another_hunk(void)
+{
+	long	line_beginning;			/* file pos of the current line */
+	LINENUM	repl_beginning;			/* index of --- line */
+	LINENUM	fillcnt;			/* #lines of missing ptrn or repl */
+	LINENUM	fillsrc;			/* index of first line to copy */
+	LINENUM	filldst;			/* index of first missing line */
+	bool	ptrn_spaces_eaten;		/* ptrn was slightly misformed */
+	bool	repl_could_be_missing;		/* no + or ! lines in this hunk */
+	bool	repl_missing;			/* we are now backtracking */
+	long	repl_backtrack_position;	/* file pos of first repl line */
+	LINENUM	repl_patch_line;		/* input line number for same */
+	LINENUM	ptrn_copiable;			/* # of copiable lines in ptrn */
+	char	*s, *ret;
+	int	context = 0;
+
+	while (p_end >= 0) {
+		if (p_end == p_efake)
+			p_end = p_bfake;	/* don't free twice */
+		else
+			free(p_line[p_end]);
+		p_end--;
+	}
+	p_efake = -1;
+
+	p_max = hunkmax;	/* gets reduced when --- found */
+	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
+		line_beginning = ftell(pfp);
+		repl_beginning = 0;
+		fillcnt = 0;
+		fillsrc = 0;
+		filldst = 0;
+		ptrn_spaces_eaten = false;
+		repl_could_be_missing = true;
+		repl_missing = false;
+		repl_backtrack_position = 0;
+		repl_patch_line = 0;
+		ptrn_copiable = 0;
+
+		ret = pgets(buf, buf_len, pfp);
+		p_input_line++;
+		if (ret == NULL || strnNE(buf, "********", 8)) {
+			next_intuit_at(line_beginning, p_input_line);
+			return false;
+		}
+		p_context = 100;
+		p_hunk_beg = p_input_line + 1;
+		while (p_end < p_max) {
+			line_beginning = ftell(pfp);
+			ret = pgets(buf, buf_len, pfp);
+			p_input_line++;
+			if (ret == NULL) {
+				if (p_max - p_end < 4) {
+					/* assume blank lines got chopped */
+					strlcpy(buf, "  \n", buf_len);
+				} else {
+					if (repl_beginning && repl_could_be_missing) {
+						repl_missing = true;
+						goto hunk_done;
+					}
+					fatal("unexpected end of file in patch\n");
+				}
+			}
+			p_end++;
+			if (p_end >= hunkmax)
+				fatal("Internal error: hunk larger than hunk "
+				    "buffer size");
+			p_char[p_end] = *buf;
+			p_line[p_end] = NULL;
+			switch (*buf) {
+			case '*':
+				if (strnEQ(buf, "********", 8)) {
+					if (repl_beginning && repl_could_be_missing) {
+						repl_missing = true;
+						goto hunk_done;
+					} else
+						fatal("unexpected end of hunk "
+						    "at line %ld\n",
+						    p_input_line);
+				}
+				if (p_end != 0) {
+					if (repl_beginning && repl_could_be_missing) {
+						repl_missing = true;
+						goto hunk_done;
+					}
+					fatal("unexpected *** at line %ld: %s",
+					    p_input_line, buf);
+				}
+				context = 0;
+				p_line[p_end] = savestr(buf);
+				if (out_of_mem) {
+					p_end--;
+					return false;
+				}
+				for (s = buf; *s && !isdigit((unsigned char)*s); s++)
+					;
+				if (!*s)
+					malformed();
+				if (strnEQ(s, "0,0", 3))
+					memmove(s, s + 2, strlen(s + 2) + 1);
+				p_first = (LINENUM) atol(s);
+				while (isdigit((unsigned char)*s))
+					s++;
+				if (*s == ',') {
+					for (; *s && !isdigit((unsigned char)*s); s++)
+						;
+					if (!*s)
+						malformed();
+					p_ptrn_lines = ((LINENUM) atol(s)) - p_first + 1;
+				} else if (p_first)
+					p_ptrn_lines = 1;
+				else {
+					p_ptrn_lines = 0;
+					p_first = 1;
+				}
+
+				/* we need this much at least */
+				p_max = p_ptrn_lines + 6;
+				while (p_max >= hunkmax)
+					grow_hunkmax();
+				p_max = hunkmax;
+				break;
+			case '-':
+				if (buf[1] == '-') {
+					if (repl_beginning ||
+					    (p_end != p_ptrn_lines + 1 +
+					    (p_char[p_end - 1] == '\n'))) {
+						if (p_end == 1) {
+							/*
+							 * `old' lines were omitted;
+							 * set up to fill them in
+							 * from 'new' context lines.
+							 */
+							p_end = p_ptrn_lines + 1;
+							fillsrc = p_end + 1;
+							filldst = 1;
+							fillcnt = p_ptrn_lines;
+						} else {
+							if (repl_beginning) {
+								if (repl_could_be_missing) {
+									repl_missing = true;
+									goto hunk_done;
+								}
+								fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
+								    p_input_line, p_hunk_beg + repl_beginning);
+							} else {
+								fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
+								    (p_end <= p_ptrn_lines
+								    ? "Premature"
+								    : "Overdue"),
+								    p_input_line, p_hunk_beg);
+							}
+						}
+					}
+					repl_beginning = p_end;
+					repl_backtrack_position = ftell(pfp);
+					repl_patch_line = p_input_line;
+					p_line[p_end] = savestr(buf);
+					if (out_of_mem) {
+						p_end--;
+						return false;
+					}
+					p_char[p_end] = '=';
+					for (s = buf; *s && !isdigit((unsigned char)*s); s++)
+						;
+					if (!*s)
+						malformed();
+					p_newfirst = (LINENUM) atol(s);
+					while (isdigit((unsigned char)*s))
+						s++;
+					if (*s == ',') {
+						for (; *s && !isdigit((unsigned char)*s); s++)
+							;
+						if (!*s)
+							malformed();
+						p_repl_lines = ((LINENUM) atol(s)) -
+						    p_newfirst + 1;
+					} else if (p_newfirst)
+						p_repl_lines = 1;
+					else {
+						p_repl_lines = 0;
+						p_newfirst = 1;
+					}
+					p_max = p_repl_lines + p_end;
+					if (p_max > MAXHUNKSIZE)
+						fatal("hunk too large (%ld lines) at line %ld: %s",
+						    p_max, p_input_line, buf);
+					while (p_max >= hunkmax)
+						grow_hunkmax();
+					if (p_repl_lines != ptrn_copiable &&
+					    (p_context != 0 || p_repl_lines != 1))
+						repl_could_be_missing = false;
+					break;
+				}
+				goto change_line;
+			case '+':
+			case '!':
+				repl_could_be_missing = false;
+		change_line:
+				if (buf[1] == '\n' && canonicalize)
+					strlcpy(buf + 1, " \n", buf_len - 1);
+				if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
+				    buf[1] != '<' &&
+				    repl_beginning && repl_could_be_missing) {
+					repl_missing = true;
+					goto hunk_done;
+				}
+				if (context >= 0) {
+					if (context < p_context)
+						p_context = context;
+					context = -1000;
+				}
+				p_line[p_end] = savestr(buf + 2);
+				if (out_of_mem) {
+					p_end--;
+					return false;
+				}
+				if (p_end == p_ptrn_lines) {
+					if (remove_special_line()) {
+						int	len;
+
+						len = strlen(p_line[p_end]) - 1;
+						(p_line[p_end])[len] = 0;
+					}
+				}
+				break;
+			case '\t':
+			case '\n':	/* assume the 2 spaces got eaten */
+				if (repl_beginning && repl_could_be_missing &&
+				    (!ptrn_spaces_eaten ||
+				    diff_type == NEW_CONTEXT_DIFF)) {
+					repl_missing = true;
+					goto hunk_done;
+				}
+				p_line[p_end] = savestr(buf);
+				if (out_of_mem) {
+					p_end--;
+					return false;
+				}
+				if (p_end != p_ptrn_lines + 1) {
+					ptrn_spaces_eaten |= (repl_beginning != 0);
+					context++;
+					if (!repl_beginning)
+						ptrn_copiable++;
+					p_char[p_end] = ' ';
+				}
+				break;
+			case ' ':
+				if (!isspace((unsigned char)buf[1]) &&
+				    repl_beginning && repl_could_be_missing) {
+					repl_missing = true;
+					goto hunk_done;
+				}
+				context++;
+				if (!repl_beginning)
+					ptrn_copiable++;
+				p_line[p_end] = savestr(buf + 2);
+				if (out_of_mem) {
+					p_end--;
+					return false;
+				}
+				break;
+			default:
+				if (repl_beginning && repl_could_be_missing) {
+					repl_missing = true;
+					goto hunk_done;
+				}
+				malformed();
+			}
+			/* set up p_len for strncmp() so we don't have to */
+			/* assume null termination */
+			if (p_line[p_end])
+				p_len[p_end] = strlen(p_line[p_end]);
+			else
+				p_len[p_end] = 0;
+		}
+
+hunk_done:
+		if (p_end >= 0 && !repl_beginning)
+			fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
+
+		if (repl_missing) {
+
+			/* reset state back to just after --- */
+			p_input_line = repl_patch_line;
+			for (p_end--; p_end > repl_beginning; p_end--)
+				free(p_line[p_end]);
+			fseek(pfp, repl_backtrack_position, SEEK_SET);
+
+			/* redundant 'new' context lines were omitted - set */
+			/* up to fill them in from the old file context */
+			if (!p_context && p_repl_lines == 1) {
+				p_repl_lines = 0;
+				p_max--;
+			}
+			fillsrc = 1;
+			filldst = repl_beginning + 1;
+			fillcnt = p_repl_lines;
+			p_end = p_max;
+		} else if (!p_context && fillcnt == 1) {
+			/* the first hunk was a null hunk with no context */
+			/* and we were expecting one line -- fix it up. */
+			while (filldst < p_end) {
+				p_line[filldst] = p_line[filldst + 1];
+				p_char[filldst] = p_char[filldst + 1];
+				p_len[filldst] = p_len[filldst + 1];
+				filldst++;
+			}
+#if 0
+			repl_beginning--;	/* this doesn't need to be fixed */
+#endif
+			p_end--;
+			p_first++;	/* do append rather than insert */
+			fillcnt = 0;
+			p_ptrn_lines = 0;
+		}
+		if (diff_type == CONTEXT_DIFF &&
+		    (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
+			if (verbose)
+				say("%s\n%s\n%s\n",
+				    "(Fascinating--this is really a new-style context diff but without",
+				    "the telltale extra asterisks on the *** line that usually indicate",
+				    "the new style...)");
+			diff_type = NEW_CONTEXT_DIFF;
+		}
+		/* if there were omitted context lines, fill them in now */
+		if (fillcnt) {
+			p_bfake = filldst;	/* remember where not to free() */
+			p_efake = filldst + fillcnt - 1;
+			while (fillcnt-- > 0) {
+				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
+					fillsrc++;
+				if (fillsrc > p_end)
+					fatal("replacement text or line numbers mangled in hunk at line %ld\n",
+					    p_hunk_beg);
+				p_line[filldst] = p_line[fillsrc];
+				p_char[filldst] = p_char[fillsrc];
+				p_len[filldst] = p_len[fillsrc];
+				fillsrc++;
+				filldst++;
+			}
+			while (fillsrc <= p_end && fillsrc != repl_beginning &&
+			    p_char[fillsrc] != ' ')
+				fillsrc++;
+#ifdef DEBUGGING
+			if (debug & 64)
+				printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
+				fillsrc, filldst, repl_beginning, p_end + 1);
+#endif
+			if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
+				malformed();
+			if (filldst != p_end + 1 && filldst != repl_beginning)
+				malformed();
+		}
+		if (p_line[p_end] != NULL) {
+			if (remove_special_line()) {
+				p_len[p_end] -= 1;
+				(p_line[p_end])[p_len[p_end]] = 0;
+			}
+		}
+	} else if (diff_type == UNI_DIFF) {
+		LINENUM	fillold;	/* index of old lines */
+		LINENUM	fillnew;	/* index of new lines */
+		char	ch;
+
+		line_beginning = ftell(pfp); /* file pos of the current line */
+		ret = pgets(buf, buf_len, pfp);
+		p_input_line++;
+		if (ret == NULL || strnNE(buf, "@@ -", 4)) {
+			next_intuit_at(line_beginning, p_input_line);
+			return false;
+		}
+		s = buf + 4;
+		if (!*s)
+			malformed();
+		p_first = (LINENUM) atol(s);
+		while (isdigit((unsigned char)*s))
+			s++;
+		if (*s == ',') {
+			p_ptrn_lines = (LINENUM) atol(++s);
+			while (isdigit((unsigned char)*s))
+				s++;
+		} else
+			p_ptrn_lines = 1;
+		if (*s == ' ')
+			s++;
+		if (*s != '+' || !*++s)
+			malformed();
+		p_newfirst = (LINENUM) atol(s);
+		while (isdigit((unsigned char)*s))
+			s++;
+		if (*s == ',') {
+			p_repl_lines = (LINENUM) atol(++s);
+			while (isdigit((unsigned char)*s))
+				s++;
+		} else
+			p_repl_lines = 1;
+		if (*s == ' ')
+			s++;
+		if (*s != '@')
+			malformed();
+		if (!p_ptrn_lines)
+			p_first++;	/* do append rather than insert */
+		p_max = p_ptrn_lines + p_repl_lines + 1;
+		while (p_max >= hunkmax)
+			grow_hunkmax();
+		fillold = 1;
+		fillnew = fillold + p_ptrn_lines;
+		p_end = fillnew + p_repl_lines;
+		snprintf(buf, buf_len, "*** %ld,%ld ****\n", p_first,
+		    p_first + p_ptrn_lines - 1);
+		p_line[0] = savestr(buf);
+		if (out_of_mem) {
+			p_end = -1;
+			return false;
+		}
+		p_char[0] = '*';
+		snprintf(buf, buf_len, "--- %ld,%ld ----\n", p_newfirst,
+		    p_newfirst + p_repl_lines - 1);
+		p_line[fillnew] = savestr(buf);
+		if (out_of_mem) {
+			p_end = 0;
+			return false;
+		}
+		p_char[fillnew++] = '=';
+		p_context = 100;
+		context = 0;
+		p_hunk_beg = p_input_line + 1;
+		while (fillold <= p_ptrn_lines || fillnew <= p_end) {
+			line_beginning = ftell(pfp);
+			ret = pgets(buf, buf_len, pfp);
+			p_input_line++;
+			if (ret == NULL) {
+				if (p_max - fillnew < 3) {
+					/* assume blank lines got chopped */
+					strlcpy(buf, " \n", buf_len);
+				} else {
+					fatal("unexpected end of file in patch\n");
+				}
+			}
+			if (*buf == '\t' || *buf == '\n') {
+				ch = ' ';	/* assume the space got eaten */
+				s = savestr(buf);
+			} else {
+				ch = *buf;
+				s = savestr(buf + 1);
+			}
+			if (out_of_mem) {
+				while (--fillnew > p_ptrn_lines)
+					free(p_line[fillnew]);
+				p_end = fillold - 1;
+				return false;
+			}
+			switch (ch) {
+			case '-':
+				if (fillold > p_ptrn_lines) {
+					free(s);
+					p_end = fillnew - 1;
+					malformed();
+				}
+				p_char[fillold] = ch;
+				p_line[fillold] = s;
+				p_len[fillold++] = strlen(s);
+				if (fillold > p_ptrn_lines) {
+					if (remove_special_line()) {
+						p_len[fillold - 1] -= 1;
+						s[p_len[fillold - 1]] = 0;
+					}
+				}
+				break;
+			case '=':
+				ch = ' ';
+				/* FALL THROUGH */
+			case ' ':
+				if (fillold > p_ptrn_lines) {
+					free(s);
+					while (--fillnew > p_ptrn_lines)
+						free(p_line[fillnew]);
+					p_end = fillold - 1;
+					malformed();
+				}
+				context++;
+				p_char[fillold] = ch;
+				p_line[fillold] = s;
+				p_len[fillold++] = strlen(s);
+				s = savestr(s);
+				if (out_of_mem) {
+					while (--fillnew > p_ptrn_lines)
+						free(p_line[fillnew]);
+					p_end = fillold - 1;
+					return false;
+				}
+				if (fillold > p_ptrn_lines) {
+					if (remove_special_line()) {
+						p_len[fillold - 1] -= 1;
+						s[p_len[fillold - 1]] = 0;
+					}
+				}
+				/* FALL THROUGH */
+			case '+':
+				if (fillnew > p_end) {
+					free(s);
+					while (--fillnew > p_ptrn_lines)
+						free(p_line[fillnew]);
+					p_end = fillold - 1;
+					malformed();
+				}
+				p_char[fillnew] = ch;
+				p_line[fillnew] = s;
+				p_len[fillnew++] = strlen(s);
+				if (fillold > p_ptrn_lines) {
+					if (remove_special_line()) {
+						p_len[fillnew - 1] -= 1;
+						s[p_len[fillnew - 1]] = 0;
+					}
+				}
+				break;
+			default:
+				p_end = fillnew;
+				malformed();
+			}
+			if (ch != ' ' && context > 0) {
+				if (context < p_context)
+					p_context = context;
+				context = -1000;
+			}
+		}		/* while */
+	} else {		/* normal diff--fake it up */
+		char	hunk_type;
+		int	i;
+		LINENUM	min, max;
+
+		line_beginning = ftell(pfp);
+		p_context = 0;
+		ret = pgets(buf, buf_len, pfp);
+		p_input_line++;
+		if (ret == NULL || !isdigit((unsigned char)*buf)) {
+			next_intuit_at(line_beginning, p_input_line);
+			return false;
+		}
+		p_first = (LINENUM) atol(buf);
+		for (s = buf; isdigit((unsigned char)*s); s++)
+			;
+		if (*s == ',') {
+			p_ptrn_lines = (LINENUM) atol(++s) - p_first + 1;
+			while (isdigit((unsigned char)*s))
+				s++;
+		} else
+			p_ptrn_lines = (*s != 'a');
+		hunk_type = *s;
+		if (hunk_type == 'a')
+			p_first++;	/* do append rather than insert */
+		min = (LINENUM) atol(++s);
+		for (; isdigit((unsigned char)*s); s++)
+			;
+		if (*s == ',')
+			max = (LINENUM) atol(++s);
+		else
+			max = min;
+		if (hunk_type == 'd')
+			min++;
+		p_end = p_ptrn_lines + 1 + max - min + 1;
+		if (p_end > MAXHUNKSIZE)
+			fatal("hunk too large (%ld lines) at line %ld: %s",
+			    p_end, p_input_line, buf);
+		while (p_end >= hunkmax)
+			grow_hunkmax();
+		p_newfirst = min;
+		p_repl_lines = max - min + 1;
+		snprintf(buf, buf_len, "*** %ld,%ld\n", p_first,
+		    p_first + p_ptrn_lines - 1);
+		p_line[0] = savestr(buf);
+		if (out_of_mem) {
+			p_end = -1;
+			return false;
+		}
+		p_char[0] = '*';
+		for (i = 1; i <= p_ptrn_lines; i++) {
+			ret = pgets(buf, buf_len, pfp);
+			p_input_line++;
+			if (ret == NULL)
+				fatal("unexpected end of file in patch at line %ld\n",
+				    p_input_line);
+			if (*buf != '<')
+				fatal("< expected at line %ld of patch\n",
+				    p_input_line);
+			p_line[i] = savestr(buf + 2);
+			if (out_of_mem) {
+				p_end = i - 1;
+				return false;
+			}
+			p_len[i] = strlen(p_line[i]);
+			p_char[i] = '-';
+		}
+
+		if (remove_special_line()) {
+			p_len[i - 1] -= 1;
+			(p_line[i - 1])[p_len[i - 1]] = 0;
+		}
+		if (hunk_type == 'c') {
+			ret = pgets(buf, buf_len, pfp);
+			p_input_line++;
+			if (ret == NULL)
+				fatal("unexpected end of file in patch at line %ld\n",
+				    p_input_line);
+			if (*buf != '-')
+				fatal("--- expected at line %ld of patch\n",
+				    p_input_line);
+		}
+		snprintf(buf, buf_len, "--- %ld,%ld\n", min, max);
+		p_line[i] = savestr(buf);
+		if (out_of_mem) {
+			p_end = i - 1;
+			return false;
+		}
+		p_char[i] = '=';
+		for (i++; i <= p_end; i++) {
+			ret = pgets(buf, buf_len, pfp);
+			p_input_line++;
+			if (ret == NULL)
+				fatal("unexpected end of file in patch at line %ld\n",
+				    p_input_line);
+			if (*buf != '>')
+				fatal("> expected at line %ld of patch\n",
+				    p_input_line);
+			p_line[i] = savestr(buf + 2);
+			if (out_of_mem) {
+				p_end = i - 1;
+				return false;
+			}
+			p_len[i] = strlen(p_line[i]);
+			p_char[i] = '+';
+		}
+
+		if (remove_special_line()) {
+			p_len[i - 1] -= 1;
+			(p_line[i - 1])[p_len[i - 1]] = 0;
+		}
+	}
+	if (reverse)		/* backwards patch? */
+		if (!pch_swap())
+			say("Not enough memory to swap next hunk!\n");
+#ifdef DEBUGGING
+	if (debug & 2) {
+		int	i;
+		char	special;
+
+		for (i = 0; i <= p_end; i++) {
+			if (i == p_ptrn_lines)
+				special = '^';
+			else
+				special = ' ';
+			fprintf(stderr, "%3d %c %c %s", i, p_char[i],
+			    special, p_line[i]);
+			fflush(stderr);
+		}
+	}
+#endif
+	if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
+		p_char[p_end + 1] = '^';	/* add a stopper for apply_hunk */
+	return true;
+}
+
+/*
+ * Input a line from the patch file, worrying about indentation.
+ */
+static char *
+pgets(char *bf, int sz, FILE *fp)
+{
+	char	*s, *ret = fgets(bf, sz, fp);
+	int	indent = 0;
+
+	if (p_indent && ret != NULL) {
+		for (s = buf;
+		    indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
+		    s++) {
+			if (*s == '\t')
+				indent += 8 - (indent % 7);
+			else
+				indent++;
+		}
+		if (buf != s && strlcpy(buf, s, buf_len) >= buf_len)
+			fatal("buffer too small in pgets()\n");
+	}
+	return ret;
+}
+
+/*
+ * Reverse the old and new portions of the current hunk.
+ */
+bool
+pch_swap(void)
+{
+	char	**tp_line;	/* the text of the hunk */
+	short	*tp_len;	/* length of each line */
+	char	*tp_char;	/* +, -, and ! */
+	LINENUM	i;
+	LINENUM	n;
+	bool	blankline = false;
+	char	*s;
+
+	i = p_first;
+	p_first = p_newfirst;
+	p_newfirst = i;
+
+	/* make a scratch copy */
+
+	tp_line = p_line;
+	tp_len = p_len;
+	tp_char = p_char;
+	p_line = NULL;	/* force set_hunkmax to allocate again */
+	p_len = NULL;
+	p_char = NULL;
+	set_hunkmax();
+	if (p_line == NULL || p_len == NULL || p_char == NULL) {
+
+		free(p_line);
+		p_line = tp_line;
+		free(p_len);
+		p_len = tp_len;
+		free(p_char);
+		p_char = tp_char;
+		return false;	/* not enough memory to swap hunk! */
+	}
+	/* now turn the new into the old */
+
+	i = p_ptrn_lines + 1;
+	if (tp_char[i] == '\n') {	/* account for possible blank line */
+		blankline = true;
+		i++;
+	}
+	if (p_efake >= 0) {	/* fix non-freeable ptr range */
+		if (p_efake <= i)
+			n = p_end - i + 1;
+		else
+			n = -i;
+		p_efake += n;
+		p_bfake += n;
+	}
+	for (n = 0; i <= p_end; i++, n++) {
+		p_line[n] = tp_line[i];
+		p_char[n] = tp_char[i];
+		if (p_char[n] == '+')
+			p_char[n] = '-';
+		p_len[n] = tp_len[i];
+	}
+	if (blankline) {
+		i = p_ptrn_lines + 1;
+		p_line[n] = tp_line[i];
+		p_char[n] = tp_char[i];
+		p_len[n] = tp_len[i];
+		n++;
+	}
+	if (p_char[0] != '=')
+		fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
+		    p_input_line, p_char[0]);
+	p_char[0] = '*';
+	for (s = p_line[0]; *s; s++)
+		if (*s == '-')
+			*s = '*';
+
+	/* now turn the old into the new */
+
+	if (p_char[0] != '*')
+		fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
+		    p_input_line, p_char[0]);
+	tp_char[0] = '=';
+	for (s = tp_line[0]; *s; s++)
+		if (*s == '*')
+			*s = '-';
+	for (i = 0; n <= p_end; i++, n++) {
+		p_line[n] = tp_line[i];
+		p_char[n] = tp_char[i];
+		if (p_char[n] == '-')
+			p_char[n] = '+';
+		p_len[n] = tp_len[i];
+	}
+
+	if (i != p_ptrn_lines + 1)
+		fatal("Malformed patch at line %ld: expected %ld lines, "
+		    "got %ld\n",
+		    p_input_line, p_ptrn_lines + 1, i);
+
+	i = p_ptrn_lines;
+	p_ptrn_lines = p_repl_lines;
+	p_repl_lines = i;
+
+	free(tp_line);
+	free(tp_len);
+	free(tp_char);
+
+	return true;
+}
+
+/*
+ * Return the specified line position in the old file of the old context.
+ */
+LINENUM
+pch_first(void)
+{
+	return p_first;
+}
+
+/*
+ * Return the number of lines of old context.
+ */
+LINENUM
+pch_ptrn_lines(void)
+{
+	return p_ptrn_lines;
+}
+
+/*
+ * Return the probable line position in the new file of the first line.
+ */
+LINENUM
+pch_newfirst(void)
+{
+	return p_newfirst;
+}
+
+/*
+ * Return the number of lines in the replacement text including context.
+ */
+LINENUM
+pch_repl_lines(void)
+{
+	return p_repl_lines;
+}
+
+/*
+ * Return the number of lines in the whole hunk.
+ */
+LINENUM
+pch_end(void)
+{
+	return p_end;
+}
+
+/*
+ * Return the number of context lines before the first changed line.
+ */
+LINENUM
+pch_context(void)
+{
+	return p_context;
+}
+
+/*
+ * Return the length of a particular patch line.
+ */
+short
+pch_line_len(LINENUM line)
+{
+	return p_len[line];
+}
+
+/*
+ * Return the control character (+, -, *, !, etc) for a patch line.
+ */
+char
+pch_char(LINENUM line)
+{
+	return p_char[line];
+}
+
+/*
+ * Return a pointer to a particular patch line.
+ */
+char *
+pfetch(LINENUM line)
+{
+	return p_line[line];
+}
+
+/*
+ * Return where in the patch file this hunk began, for error messages.
+ */
+LINENUM
+pch_hunk_beg(void)
+{
+	return p_hunk_beg;
+}
+
+/*
+ * Apply an ed script by feeding ed itself.
+ */
+void
+do_ed_script(void)
+{
+	char	*t;
+	long	beginning_of_this_line;
+	FILE	*pipefp;
+
+	pipefp = NULL;
+	if (!skip_rest_of_patch) {
+		if (copy_file(filearg[0], TMPOUTNAME) < 0) {
+			unlink(TMPOUTNAME);
+			fatal("can't create temp file %s", TMPOUTNAME);
+		}
+		snprintf(buf, buf_len, "%s%s%s", _PATH_ED,
+		    verbose ? " " : " -s ", TMPOUTNAME);
+		pipefp = popen(buf, "w");
+	}
+	for (;;) {
+		beginning_of_this_line = ftell(pfp);
+		if (pgets(buf, buf_len, pfp) == NULL) {
+			next_intuit_at(beginning_of_this_line, p_input_line);
+			break;
+		}
+		p_input_line++;
+		for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
+			;
+		/* POSIX defines allowed commands as {a,c,d,i,s} */
+		if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
+		    *t == 'd' || *t == 'i' || *t == 's')) {
+			if (pipefp != NULL)
+				fputs(buf, pipefp);
+			if (*t != 'd') {
+				while (pgets(buf, buf_len, pfp) != NULL) {
+					p_input_line++;
+					if (pipefp != NULL)
+						fputs(buf, pipefp);
+					if (strEQ(buf, ".\n"))
+						break;
+				}
+			}
+		} else {
+			next_intuit_at(beginning_of_this_line, p_input_line);
+			break;
+		}
+	}
+	if (pipefp == NULL)
+		return;
+	fprintf(pipefp, "w\n");
+	fprintf(pipefp, "q\n");
+	fflush(pipefp);
+	pclose(pipefp);
+	ignore_signals();
+	if (!check_only) {
+		if (move_file(TMPOUTNAME, outname) < 0) {
+			toutkeep = true;
+			chmod(TMPOUTNAME, filemode);
+		} else
+			chmod(outname, filemode);
+	}
+	set_signals(1);
+}
+
+/*
+ * Choose the name of the file to be patched based on POSIX rules.
+ * NOTE: the POSIX rules are amazingly stupid and we only follow them
+ *       if the user specified --posix or set POSIXLY_CORRECT.
+ */
+static char *
+posix_name(const struct file_name *names, bool assume_exists)
+{
+	char *path = NULL;
+	int i;
+
+	/*
+	 * POSIX states that the filename will be chosen from one
+	 * of the old, new and index names (in that order) if
+	 * the file exists relative to CWD after -p stripping.
+	 */
+	for (i = 0; i < MAX_FILE; i++) {
+		if (names[i].path != NULL && names[i].exists) {
+			path = names[i].path;
+			break;
+		}
+	}
+	if (path == NULL && !assume_exists) {
+		/*
+		 * No files found, look for something we can checkout from
+		 * RCS/SCCS dirs.  Same order as above.
+		 */
+		for (i = 0; i < MAX_FILE; i++) {
+			if (names[i].path != NULL &&
+			    (path = checked_in(names[i].path)) != NULL)
+				break;
+		}
+		/*
+		 * Still no match?  Check to see if the diff could be creating
+		 * a new file.
+		 */
+		if (path == NULL && ok_to_create_file &&
+		    names[NEW_FILE].path != NULL)
+			path = names[NEW_FILE].path;
+	}
+
+	return path ? savestr(path) : NULL;
+}
+
+/*
+ * Choose the name of the file to be patched based the "best" one
+ * available.
+ */
+static char *
+best_name(const struct file_name *names, bool assume_exists)
+{
+	size_t min_components, min_baselen, min_len, tmp;
+	char *best = NULL;
+	int i;
+
+	/*
+	 * The "best" name is the one with the fewest number of path
+	 * components, the shortest basename length, and the shortest
+	 * overall length (in that order).  We only use the Index: file
+	 * if neither of the old or new files could be intuited from
+	 * the diff header.
+	 */
+	min_components = min_baselen = min_len = SIZE_MAX;
+	for (i = INDEX_FILE; i >= OLD_FILE; i--) {
+		if (names[i].path == NULL ||
+		    (!names[i].exists && !assume_exists))
+			continue;
+		if ((tmp = num_components(names[i].path)) > min_components)
+			continue;
+		min_components = tmp;
+		if ((tmp = strlen(basename(names[i].path))) > min_baselen)
+			continue;
+		min_baselen = tmp;
+		if ((tmp = strlen(names[i].path)) > min_len)
+			continue;
+		min_len = tmp;
+		best = names[i].path;
+	}
+	if (best == NULL) {
+		/*
+		 * No files found, look for something we can checkout from
+		 * RCS/SCCS dirs.  Logic is identical to that above...
+		 */
+		min_components = min_baselen = min_len = SIZE_MAX;
+		for (i = INDEX_FILE; i >= OLD_FILE; i--) {
+			if (names[i].path == NULL ||
+			    checked_in(names[i].path) == NULL)
+				continue;
+			if ((tmp = num_components(names[i].path)) > min_components)
+				continue;
+			min_components = tmp;
+			if ((tmp = strlen(basename(names[i].path))) > min_baselen)
+				continue;
+			min_baselen = tmp;
+			if ((tmp = strlen(names[i].path)) > min_len)
+				continue;
+			min_len = tmp;
+			best = names[i].path;
+		}
+		/*
+		 * Still no match?  Check to see if the diff could be creating
+		 * a new file.
+		 */
+		if (best == NULL && ok_to_create_file &&
+		    names[NEW_FILE].path != NULL)
+			best = names[NEW_FILE].path;
+	}
+
+	return best ? savestr(best) : NULL;
+}
+
+static size_t
+num_components(const char *path)
+{
+	size_t n;
+	const char *cp;
+
+	for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
+		while (*cp == '/')
+			cp++;		/* skip consecutive slashes */
+	}
+	return n;
+}
Index: usr.bin/patch/pch.h
===================================================================
RCS file: usr.bin/patch/pch.h
diff -N usr.bin/patch/pch.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/pch.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,58 @@
+/*
+ * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#define OLD_FILE	0
+#define NEW_FILE	1
+#define INDEX_FILE	2
+#define MAX_FILE	3
+
+struct file_name {
+	char *path;
+	bool exists;
+};
+
+void		re_patch(void);
+void		open_patch_file(const char *);
+void		set_hunkmax(void);
+bool		there_is_another_patch(void);
+bool		another_hunk(void);
+bool		pch_swap(void);
+char		*pfetch(LINENUM);
+short		pch_line_len(LINENUM);
+LINENUM		pch_first(void);
+LINENUM		pch_ptrn_lines(void);
+LINENUM		pch_newfirst(void);
+LINENUM		pch_repl_lines(void);
+LINENUM		pch_end(void);
+LINENUM		pch_context(void);
+LINENUM		pch_hunk_beg(void);
+char		pch_char(LINENUM);
+void		do_ed_script(void);
Index: usr.bin/patch/util.c
===================================================================
RCS file: usr.bin/patch/util.c
diff -N usr.bin/patch/util.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/util.c	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,435 @@
+/*
+ * $OpenBSD: util.c,v 1.29 2004/11/19 20:00:57 otto Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "util.h"
+#include "backupfile.h"
+#include "pathnames.h"
+
+
+/* Rename a file, copying it if necessary. */
+
+int
+move_file(const char *from, const char *to)
+{
+	int	fromfd;
+	ssize_t	i;
+
+	/* to stdout? */
+
+	if (strEQ(to, "-")) {
+#ifdef DEBUGGING
+		if (debug & 4)
+			say("Moving %s to stdout.\n", from);
+#endif
+		fromfd = open(from, O_RDONLY);
+		if (fromfd < 0)
+			pfatal("internal error, can't reopen %s", from);
+		while ((i = read(fromfd, buf, buf_len)) > 0)
+			if (write(STDOUT_FILENO, buf, i) != i)
+				pfatal("write failed");
+		close(fromfd);
+		return 0;
+	}
+	if (backup_file(to) < 0) {
+		say("Can't backup %s, output is in %s: %s\n", to, from,
+		    strerror(errno));
+		return -1;
+	}
+#ifdef DEBUGGING
+	if (debug & 4)
+		say("Moving %s to %s.\n", from, to);
+#endif
+	if (rename(from, to) < 0) {
+		if (errno != EXDEV || copy_file(from, to) < 0) {
+			say("Can't create %s, output is in %s: %s\n",
+			    to, from, strerror(errno));
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Backup the original file.  */
+
+int
+backup_file(const char *orig)
+{
+	struct stat	filestat;
+	char		bakname[MAXPATHLEN], *s, *simplename;
+	dev_t		orig_device;
+	ino_t		orig_inode;
+
+	if (backup_type == none || stat(orig, &filestat) != 0)
+		return 0;			/* nothing to do */
+	/*
+	 * If the user used zero prefixes or suffixes, then
+	 * he doesn't want backups.  Yet we have to remove
+	 * orig to break possible hardlinks.
+	 */
+	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
+		unlink(orig);
+		return 0;
+	}
+	orig_device = filestat.st_dev;
+	orig_inode = filestat.st_ino;
+
+	if (origprae) {
+		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
+		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
+			fatal("filename %s too long for buffer\n", origprae);
+	} else {
+		if ((s = find_backup_file_name(orig)) == NULL)
+			fatal("out of memory\n");
+		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
+			fatal("filename %s too long for buffer\n", s);
+		free(s);
+	}
+
+	if ((simplename = strrchr(bakname, '/')) != NULL)
+		simplename = simplename + 1;
+	else
+		simplename = bakname;
+
+	/*
+	 * Find a backup name that is not the same file. Change the
+	 * first lowercase char into uppercase; if that isn't
+	 * sufficient, chop off the first char and try again.
+	 */
+	while (stat(bakname, &filestat) == 0 &&
+	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
+		/* Skip initial non-lowercase chars.  */
+		for (s = simplename; *s && !islower((unsigned char)*s); s++)
+			;
+		if (*s)
+			*s = toupper((unsigned char)*s);
+		else
+			memmove(simplename, simplename + 1,
+			    strlen(simplename + 1) + 1);
+	}
+#ifdef DEBUGGING
+	if (debug & 4)
+		say("Moving %s to %s.\n", orig, bakname);
+#endif
+	if (rename(orig, bakname) < 0) {
+		if (errno != EXDEV || copy_file(orig, bakname) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+/*
+ * Copy a file.
+ */
+int
+copy_file(const char *from, const char *to)
+{
+	int	tofd, fromfd;
+	ssize_t	i;
+
+	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+	if (tofd < 0)
+		return -1;
+	fromfd = open(from, O_RDONLY, 0);
+	if (fromfd < 0)
+		pfatal("internal error, can't reopen %s", from);
+	while ((i = read(fromfd, buf, buf_len)) > 0)
+		if (write(tofd, buf, i) != i)
+			pfatal("write to %s failed", to);
+	close(fromfd);
+	close(tofd);
+	return 0;
+}
+
+/*
+ * Allocate a unique area for a string.
+ */
+char *
+savestr(const char *s)
+{
+	char	*rv;
+
+	if (!s)
+		s = "Oops";
+	rv = strdup(s);
+	if (rv == NULL) {
+		if (using_plan_a)
+			out_of_mem = true;
+		else
+			fatal("out of memory\n");
+	}
+	return rv;
+}
+
+/*
+ * Vanilla terminal output (buffered).
+ */
+void
+say(const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fflush(stderr);
+}
+
+/*
+ * Terminal output, pun intended.
+ */
+void
+fatal(const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	fprintf(stderr, "patch: **** ");
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	my_exit(2);
+}
+
+/*
+ * Say something from patch, something from the system, then silence . . .
+ */
+void
+pfatal(const char *fmt, ...)
+{
+	va_list	ap;
+	int	errnum = errno;
+
+	fprintf(stderr, "patch: **** ");
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, ": %s\n", strerror(errnum));
+	my_exit(2);
+}
+
+/*
+ * Get a response from the user via /dev/tty
+ */
+void
+ask(const char *fmt, ...)
+{
+	va_list	ap;
+	ssize_t	nr = 0;
+	static	int ttyfd = -1;
+
+	va_start(ap, fmt);
+	vfprintf(stdout, fmt, ap);
+	va_end(ap);
+	fflush(stdout);
+	if (ttyfd < 0)
+		ttyfd = open(_PATH_TTY, O_RDONLY);
+	if (ttyfd >= 0) {
+		if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
+		    buf[nr - 1] == '\n')
+			buf[nr - 1] = '\0';
+	}
+	if (ttyfd < 0 || nr <= 0) {
+		/* no tty or error reading, pretend user entered 'return' */
+		putchar('\n');
+		buf[0] = '\0';
+	}
+}
+
+/*
+ * How to handle certain events when not in a critical region.
+ */
+void
+set_signals(int reset)
+{
+	static sig_t	hupval, intval;
+
+	if (!reset) {
+		hupval = signal(SIGHUP, SIG_IGN);
+		if (hupval != SIG_IGN)
+			hupval = my_exit;
+		intval = signal(SIGINT, SIG_IGN);
+		if (intval != SIG_IGN)
+			intval = my_exit;
+	}
+	signal(SIGHUP, hupval);
+	signal(SIGINT, intval);
+}
+
+/*
+ * How to handle certain events when in a critical region.
+ */
+void
+ignore_signals(void)
+{
+	signal(SIGHUP, SIG_IGN);
+	signal(SIGINT, SIG_IGN);
+}
+
+/*
+ * Make sure we'll have the directories to create a file. If `striplast' is
+ * true, ignore the last element of `filename'.
+ */
+
+void
+makedirs(const char *filename, bool striplast)
+{
+	char	*tmpbuf;
+
+	if ((tmpbuf = strdup(filename)) == NULL)
+		fatal("out of memory\n");
+
+	if (striplast) {
+		char	*s = strrchr(tmpbuf, '/');
+		if (s == NULL)
+			return;	/* nothing to be done */
+		*s = '\0';
+	}
+	if (snprintf(buf, buf_len, "%s -p %s", _PATH_MKDIR, tmpbuf)
+	    >= (int)buf_len)
+		fatal("buffer too small to hold %.20s...\n", tmpbuf);
+
+	if (system(buf))
+		pfatal("%.40s failed", buf);
+}
+
+/*
+ * Make filenames more reasonable.
+ */
+char *
+fetchname(const char *at, bool *exists, int strip_leading)
+{
+	char		*fullname, *name, *t;
+	int		sleading, tab;
+	struct stat	filestat;
+
+	if (at == NULL || *at == '\0')
+		return NULL;
+	while (isspace((unsigned char)*at))
+		at++;
+#ifdef DEBUGGING
+	if (debug & 128)
+		say("fetchname %s %d\n", at, strip_leading);
+#endif
+	/* So files can be created by diffing against /dev/null.  */
+	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
+		return NULL;
+	name = fullname = t = savestr(at);
+
+	tab = strchr(t, '\t') != NULL;
+	/* Strip off up to `strip_leading' path components and NUL terminate. */
+	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
+	    !isspace((unsigned char)*t)); t++) {
+		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
+			if (--sleading >= 0)
+				name = t + 1;
+	}
+	*t = '\0';
+
+	/*
+	 * If no -p option was given (957 is the default value!), we were
+	 * given a relative pathname, and the leading directories that we
+	 * just stripped off all exist, put them back on.
+	 */
+	if (strip_leading == 957 && name != fullname && *fullname != '/') {
+		name[-1] = '\0';
+		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
+			name[-1] = '/';
+			name = fullname;
+		}
+	}
+	name = savestr(name);
+	free(fullname);
+
+	*exists = stat(name, &filestat) == 0;
+	return name;
+}
+
+/*
+ * Takes the name returned by fetchname and looks in RCS/SCCS directories
+ * for a checked in version.
+ */
+char *
+checked_in(char *file)
+{
+	char		*filebase, *filedir, tmpbuf[MAXPATHLEN];
+	struct stat	filestat;
+
+	filebase = basename(file);
+	filedir = dirname(file);
+
+#define try(f, a1, a2, a3) \
+(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
+
+	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
+	    try("%s/RCS/%s%s", filedir, filebase, "") ||
+	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
+	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
+	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
+		return file;
+
+	return NULL;
+}
+
+void
+version(void)
+{
+	fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n");
+	my_exit(EXIT_SUCCESS);
+}
+
+/*
+ * Exit with cleanup.
+ */
+void
+my_exit(int status)
+{
+	unlink(TMPINNAME);
+	if (!toutkeep)
+		unlink(TMPOUTNAME);
+	if (!trejkeep)
+		unlink(TMPREJNAME);
+	unlink(TMPPATNAME);
+	exit(status);
+}
Index: usr.bin/patch/util.h
===================================================================
RCS file: usr.bin/patch/util.h
diff -N usr.bin/patch/util.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ usr.bin/patch/util.h	2 Jun 2006 19:10:06 -0000
@@ -0,0 +1,50 @@
+/*
+ * $OpenBSD: util.h,v 1.13 2004/08/05 21:47:24 deraadt Exp $
+ * $DragonFly$
+ */
+
+/*
+ * patch - a program to apply diffs to original files
+ * 
+ * Copyright 1986, Larry Wall
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following condition is met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this condition and the following disclaimer.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * -C option added in 1998, original code by Marc Espie, based on FreeBSD
+ * behaviour
+ */
+
+char		*fetchname(const char *, bool *, int);
+char		*checked_in(char *);
+int		backup_file(const char *);
+int		move_file(const char *, const char *);
+int		copy_file(const char *, const char *);
+void		say(const char *, ...)
+		    __attribute__((__format__(__printf__, 1, 2)));
+void		fatal(const char *, ...)
+		    __attribute__((__format__(__printf__, 1, 2)));
+void		pfatal(const char *, ...)
+		    __attribute__((__format__(__printf__, 1, 2)));
+void		ask(const char *, ...)
+		    __attribute__((__format__(__printf__, 1, 2)));
+char		*savestr(const char *);
+void		set_signals(int);
+void		ignore_signals(void);
+void		makedirs(const char *, bool);
+void		version(void);
+void		my_exit(int) __attribute__((noreturn));
--- freebsd-lw-patch.diff ends here ---

--- freebsd-awk-patch.diff begins here ---
Index: usr.bin/awk/Makefile
===================================================================
RCS file: /storage/freebsd/cvs/src/usr.bin/awk/Makefile,v
retrieving revision 1.12
diff -u -r1.12 Makefile
--- usr.bin/awk/Makefile	17 May 2005 14:54:33 -0000	1.12
+++ usr.bin/awk/Makefile	15 Jun 2006 10:40:59 -0000
@@ -31,7 +31,7 @@
 
 .for f in b.c main.c run.c
 ${f}: ${AWKSRC}/${f} ${.CURDIR}/${f}.diff
-	patch -s -b .orig -o ${.TARGET} < ${.CURDIR}/${f}.diff ${AWKSRC}/${f}
+	SIMPLE_BACKUP_SUFFIX='.orig' patch -s -o ${.TARGET} < ${.CURDIR}/${f}.diff ${AWKSRC}/${f}
 CLEANFILES+= ${f}
 .endfor
 
--- freebsd-awk-patch.diff ends here ---



>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1150708638.5990>