FreeBSD Bugzilla – Attachment 67274 Details for
Bug 99173
[patch] replace gnu patch with a bsd-licensed one.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
freebsd-lw-patch.diff
freebsd-lw-patch.diff (text/plain), 135.89 KB, created by
Andreas Kohn
on 2006-06-19 10:20:13 UTC
(
hide
)
Description:
freebsd-lw-patch.diff
Filename:
MIME Type:
Creator:
Andreas Kohn
Created:
2006-06-19 10:20:13 UTC
Size:
135.89 KB
patch
obsolete
>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));
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 99173
: 67274 |
67275