diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /usr.bin/patch |
initial import of NetBSD tree
Diffstat (limited to 'usr.bin/patch')
-rw-r--r-- | usr.bin/patch/ChangeLog | 274 | ||||
-rw-r--r-- | usr.bin/patch/EXTERN.h | 15 | ||||
-rw-r--r-- | usr.bin/patch/INTERN.h | 13 | ||||
-rw-r--r-- | usr.bin/patch/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/patch/README | 108 | ||||
-rw-r--r-- | usr.bin/patch/backupfile.c | 341 | ||||
-rw-r--r-- | usr.bin/patch/backupfile.h | 40 | ||||
-rw-r--r-- | usr.bin/patch/common.h | 184 | ||||
-rw-r--r-- | usr.bin/patch/config.h | 133 | ||||
-rw-r--r-- | usr.bin/patch/inp.c | 353 | ||||
-rw-r--r-- | usr.bin/patch/inp.h | 12 | ||||
-rw-r--r-- | usr.bin/patch/malloc.c | 468 | ||||
-rw-r--r-- | usr.bin/patch/patch.1 | 518 | ||||
-rw-r--r-- | usr.bin/patch/patch.c | 843 | ||||
-rw-r--r-- | usr.bin/patch/patchlevel.h | 3 | ||||
-rw-r--r-- | usr.bin/patch/pch.c | 1275 | ||||
-rw-r--r-- | usr.bin/patch/pch.h | 27 | ||||
-rw-r--r-- | usr.bin/patch/util.c | 441 | ||||
-rw-r--r-- | usr.bin/patch/util.h | 82 | ||||
-rw-r--r-- | usr.bin/patch/version.c | 21 | ||||
-rw-r--r-- | usr.bin/patch/version.h | 3 |
21 files changed, 5160 insertions, 0 deletions
diff --git a/usr.bin/patch/ChangeLog b/usr.bin/patch/ChangeLog new file mode 100644 index 00000000000..7e628cf8bf0 --- /dev/null +++ b/usr.bin/patch/ChangeLog @@ -0,0 +1,274 @@ +Tue Sep 15 00:36:15 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u8. + +Mon Sep 14 22:01:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Makefile.SH: Add uninstall target. Simplify install target. + + * util.c (fatal, pfatal): Add some asterisks to make fatal + messages stand out more. + +Tue Aug 25 22:13:36 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.c (main, get_some_switches), common.h, inp.c (plan_a, + plan_b), pch.c (there_is_another_aptch): Add -t option, + similar to -f. + +Mon Jul 27 11:27:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (plan_a, util.c (fetchname): Use a macro to simplify code. + * common.h: Define SCCSDIFF and RCSDIFF. + * inp.c (plan_a): Use them to make sure it's safe to check out + the default RCS or SCCS version. + From Paul Eggert. + +Wed Jul 22 14:37:08 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.man: Use the standard comment syntax -- '\" -- instead + of '''. + +Tue Jul 21 15:26:01 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Add /etc /usr/lib /lib to pth. + +Mon Jul 20 14:10:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.h: Declare basename. + * inp.c (plan_a), util.c (fetchname): Use it to isolate the + leading path when testing for RCS and SCCS files. + +Sat Jul 11 18:03:26 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Use the user's PATH and build pth from it. + +Fri Jul 10 16:03:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Change cc -S to cc -c and tr '[ - ]' '[\012-\012]' + to tr ' ' '\012' for AIX 3.2. + From chip@tct.com (Chip Salzenberg). + + * util.c (makedirs): Only make the directories that don't exist. + From chip@tct.com (Chip Salzenberg). + +Wed Jul 8 01:21:15 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (fatal, pfatal): Print "patch: " before message. + * pch.c, inp.c, patch.c, util.c: Remove "patch: " from the + callers that had it. + + * util.c (pfatal): New function. + * util.h: Declare it and pfatal[1-4] macros. + * various files: Use it instead of fatal where appropriate. + + * Configure: Make /usr/local/man/man1 the first choice for the + man pages. + + * patch.c (main): Open ofp after checking for ed script. + Close ofp and rejfp before trying plan B. + From epang@sfu.ca (Eugene Pang). + + * backupfile.h: Declare get_version. + + * Move decls of rindex and popen to common.h. + + * common.h (myuid): New variable. + * patch.c (main): Initialize it. + * inp.c (myuid): Function removed. + (plan_a): Use the variable, not the function. + + * patch.c: Reinstate -E option. + +Tue Jul 7 23:19:28 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (myuid): New function. + (plan_a): Call it. Optimize stat calls. Be smarter about + detecting checked out RCS and SCCS files. + From Paul Eggert (eggert@twinsun.com). + + * inp.c, util.c, patch.c: Don't bother checking for stat() > 0. + +Mon Jul 6 13:01:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * version.c (version): Don't print the RCS stuff, since we're + not updating it regularly. + + * patch.c (get_some_switches): Make the usage message more accurate. + + * patchlevel.h: PATCHLEVEL 12u7. + + * Makefile.SH (dist): New target. + Makedist: File removed. + + * inp.c (plan_a): Check whether the user can write to the + file, not whether anyone can write to the file. + +Sat Jul 4 00:06:58 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (plan_a): Try to check out read-only files from RCS or SCCS. + + * util.c (move_file): If backing up by linking fails, try copying. + From cek@sdc.boeing.com (Conrad Kimball). + + * patch.c (get_some_switches): Eliminate -E option; always + remove empty output files. + + * util.c (fetchname): Only undo slash removal for relative + paths if -p was not given. + + * Makefile.sh: Add mostlyclean target. + +Fri Jul 3 23:48:14 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (fetchname): Accept whitespace between `Index:' and filename. + Also plug a small memory leak for diffs against /dev/null. + From eggert@twinsun.com (Paul Eggert). + + * common.h: Don't define TRUE and FALSE if already defined. + From phk@data.fls.dk (Poul-Henning Kamp). + +Wed Apr 29 10:19:33 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * backupfile.c (get_version): Exit if given a bad backup type. + +Fri Mar 27 09:57:14 1992 Karl Berry (karl at hayley) + + * common.h (S_ISDIR, S_ISREG): define these. + * inp.c (plan_a): use S_ISREG, not S_IFREG. + * util.c (fetchname): use S_ISDIR, not S_IFDIR. + +Mon Mar 16 14:10:42 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u6. + +Sat Mar 14 13:13:29 1992 David J. MacKenzie (djm at frob.eng.umd.edu) + + * Configure, config.h.SH: Check for directory header and unistd.h. + + * patch.c (main): If -E was given and output file is empty after + patching, remove it. + (get_some_switches): Recognize -E option. + + * patch.c (copy_till): Make garbled output an error, not a warning + that doesn't change the exit status. + + * common.h: Protect against system declarations of malloc and realloc. + + * Makedist: Add backupfile.[ch]. + + * Configure: Look for C library where NeXT and SVR4 put it. + Look in /usr/ucb after /bin and /usr/bin for utilities, + and look in /usr/ccs/bin, to make SVR4 happier. + Recognize m68k predefine. + + * util.c (fetchname): Test of stat return value was backward. + From csss@scheme.cs.ubc.ca. + + * version.c (version): Exit with status 0, not 1. + + * Makefile.SH: Add backupfile.[cho]. + * patch.c (main): Initialize backup file generation. + (get_some_switches): Add -V option. + * common.h, util,c, patch.c: Replace origext with simple_backup_suffix. + * util.c (move_file): Use find_backup_file_name. + +Tue Dec 3 11:27:16 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u5. + + * Makefile.SH: Change clean, distclean, and realclean targets a + little so they agree with the GNU coding standards. + Add Makefile to addedbyconf, so distclean removes it. + + * Configure: Recognize Domain/OS C library in /lib/libc. + From mmuegel@mot.com (Michael S. Muegel). + + * pch.c: Fixes from Wayne Davison: + Patch now accepts no-context context diffs that are + specified with an assumed one line hunk (e.g. "*** 10 ****"). + Fixed a bug in both context and unified diff processing that would + put a zero-context hunk in the wrong place (one line too soon). + Fixed a minor problem with p_max in unified diffs where it would + set p_max to hunkmax unnecessarily (the only adverse effect was to + not supply empty lines at eof by assuming they were truncated). + +Tue Jul 2 03:25:51 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * Configure: Check for signal declaration in + /usr/include/sys/signal.h as well as /usr/include/signal.h. + + * Configure, common.h, config.h.SH: Comment out the sprintf + declaration and tests to determine its return value type. It + conflicts with ANSI C systems' prototypes in stdio.h and the + return value of sprintf is never used anyway -- it's always cast + to void. + +Thu Jun 27 13:05:32 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u4. + +Thu Feb 21 15:18:14 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * pch.c (another_hunk): Fix off by 1 error. From + iverson@xstor.com (Tim Iverson). + +Sun Jan 20 20:18:58 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * Makefile.SH (all): Don't make a dummy `all' file. + + * patchlevel.h: PATCHLEVEL 12u3. + + * patch.c (nextarg): New function. + (get_some_switches): Use it, to prevent dereferencing a null + pointer if an option that takes an arg is not given one (is last + on the command line). From Paul Eggert. + + * pch.c (another_hunk): Fix from Wayne Davison to recognize + single-line hunks in unified diffs (with a single line number + instead of a range). + + * inp.c (rev_in_string): Don't use `s' before defining it. From + Wayne Davison. + +Mon Jan 7 06:25:11 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u2. + + * pch.c (intuit_diff_type): Recognize `+++' in diff headers, for + unified diff format. From unidiff patch 1. + +Mon Dec 3 00:14:25 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * patch.c (get_some_switches): Make the usage message more + informative. + +Sun Dec 2 23:20:18 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Configure: When checking for C preprocessor, look for 'abc.*xyz' + instead of 'abc.xyz', so ANSI C preprocessors work. + + * Apply fix for -D from ksb@mentor.cc.purdue.edu (Kevin Braunsdorf). + + * Apply unidiff patches from davison@dri.com (Wayne Davison). + +Wed Mar 7 23:47:25 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * pch.c: Call malformed instead of goto malformed + (just allows easier debugging). + +Tue Jan 23 21:27:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * common.h (TMP*NAME): Make these char *, not char []. + patch.c (main): Use TMPDIR (if present) to set TMP*NAME. + common.h: Declare getenv. + +Sun Dec 17 17:29:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * patch.c (reverse_flag_specified): New variable. + (get_some_switches, reinitialize_almost_everything): Use it. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +end: diff --git a/usr.bin/patch/EXTERN.h b/usr.bin/patch/EXTERN.h new file mode 100644 index 00000000000..0238bf29056 --- /dev/null +++ b/usr.bin/patch/EXTERN.h @@ -0,0 +1,15 @@ +/* $Id: EXTERN.h,v 1.1 1995/10/18 08:45:54 deraadt Exp $ */ + +#ifdef EXT +#undef EXT +#endif +#define EXT extern + +#ifdef INIT +#undef INIT +#endif +#define INIT(x) + +#ifdef DOINIT +#undef DOINIT +#endif diff --git a/usr.bin/patch/INTERN.h b/usr.bin/patch/INTERN.h new file mode 100644 index 00000000000..da30be9879a --- /dev/null +++ b/usr.bin/patch/INTERN.h @@ -0,0 +1,13 @@ +/* $Id: INTERN.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ */ + +#ifdef EXT +#undef EXT +#endif +#define EXT + +#ifdef INIT +#undef INIT +#endif +#define INIT(x) = x + +#define DOINIT diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile new file mode 100644 index 00000000000..2c68ba3c675 --- /dev/null +++ b/usr.bin/patch/Makefile @@ -0,0 +1,6 @@ +# $Id: Makefile,v 1.1 1995/10/18 08:45:55 deraadt Exp $ + +PROG= patch +SRCS= patch.c pch.c inp.c version.c util.c backupfile.c + +.include <bsd.prog.mk> diff --git a/usr.bin/patch/README b/usr.bin/patch/README new file mode 100644 index 00000000000..5bd4744802a --- /dev/null +++ b/usr.bin/patch/README @@ -0,0 +1,108 @@ +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 + +You may copy the patch kit in whole or in part as long as you don't try to +make money off it, or pretend that you wrote it. +-------------------------------------------------------------------------- + +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. diff --git a/usr.bin/patch/backupfile.c b/usr.bin/patch/backupfile.c new file mode 100644 index 00000000000..910a70807b9 --- /dev/null +++ b/usr.bin/patch/backupfile.c @@ -0,0 +1,341 @@ +/* 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. */ + +#ifndef lint +static char rcsid[] = "$Id: backupfile.c,v 1.1 1995/10/18 08:45:55 deraadt Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include "backupfile.h" +#include "config.h" + +#ifdef DIRENT +#include <dirent.h> +#ifdef direct +#undef direct +#endif +#define direct dirent +#define NLENGTH(direct) (strlen((direct)->d_name)) +#else /* !DIRENT */ +#define NLENGTH(direct) ((direct)->d_namlen) +#ifdef USG +#ifdef SYSNDIR +#include <sys/ndir.h> +#else /* !SYSNDIR */ +#include <ndir.h> +#endif /* !SYSNDIR */ +#else /* !USG */ +#ifdef SYSDIR +#include <sys/dir.h> +#endif /* SYSDIR */ +#endif /* !USG */ +#endif /* !DIRENT */ + +#ifndef isascii +#define ISDIGIT(c) (isdigit ((unsigned char) (c))) +#else +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#endif + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#if defined (_POSIX_VERSION) +/* POSIX does not require that the d_ino field be present, and some + systems do not provide it. */ +#define REAL_DIR_ENTRY(dp) 1 +#else +#define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) +#endif + +/* 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. */ +char *simple_backup_suffix = "~"; + +char *basename (); +char *dirname (); +static char *concat (); +char *find_backup_file_name (); +static char *make_version_name (); +static int max_backup_version (); +static int version_number (); + +#ifndef NODIR +/* 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 (file) + char *file; +{ + char *dir; + char *base_versions; + int highest_backup; + + if (backup_type == simple) + return concat (file, simple_backup_suffix); + base_versions = concat (basename (file), ".~"); + if (base_versions == 0) + return 0; + dir = dirname (file); + if (dir == 0) + { + free (base_versions); + return 0; + } + highest_backup = max_backup_version (base_versions, dir); + free (base_versions); + free (dir); + 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 (file, dir) + char *file, *dir; +{ + DIR *dirp; + struct direct *dp; + int highest_version; + int this_version; + int file_name_length; + + dirp = opendir (dir); + if (!dirp) + return 0; + + highest_version = 0; + file_name_length = strlen (file); + + while ((dp = readdir (dirp)) != 0) + { + if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= 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 (file, version) + char *file; + int version; +{ + char *backup_name; + + backup_name = malloc (strlen (file) + 16); + if (backup_name == 0) + return 0; + sprintf (backup_name, "%s.~%d~", file, version); + 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 (base, backup, base_length) + char *base; + char *backup; + int base_length; +{ + int version; + 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 (str1, str2) + char *str1, *str2; +{ + char *newstr; + char str1_length = strlen (str1); + + newstr = malloc (str1_length + strlen (str2) + 1); + if (newstr == 0) + return 0; + strcpy (newstr, str1); + strcpy (newstr + str1_length, str2); + return newstr; +} + +/* Return NAME with any leading path stripped off. */ + +char * +basename (name) + char *name; +{ + char *base; + + base = rindex (name, '/'); + return base ? base + 1 : name; +} + +/* Return the leading directories part of PATH, + allocated with malloc. If out of memory, return 0. + Assumes that trailing slashes have already been + removed. */ + +char * +dirname (path) + char *path; +{ + char *newpath; + char *slash; + int length; /* Length of result, not including NUL. */ + + slash = rindex (path, '/'); + if (slash == 0) + { + /* File is in the current directory. */ + path = "."; + length = 1; + } + else + { + /* Remove any trailing slashes from result. */ + while (slash > path && *slash == '/') + --slash; + + length = slash - path + 1; + } + newpath = malloc (length + 1); + if (newpath == 0) + return 0; + strncpy (newpath, path, length); + newpath[length] = 0; + return newpath; +} + +/* 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). */ + +int +argmatch (arg, optlist) + char *arg; + char **optlist; +{ + int i; /* Temporary index in OPTLIST. */ + int 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. */ + +void +invalid_arg (kind, value, problem) + char *kind; + 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 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 (version) + char *version; +{ + int i; + + if (version == 0 || *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 (1); +} +#endif /* NODIR */ diff --git a/usr.bin/patch/backupfile.h b/usr.bin/patch/backupfile.h new file mode 100644 index 00000000000..5014fd1c02c --- /dev/null +++ b/usr.bin/patch/backupfile.h @@ -0,0 +1,40 @@ +/* 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. + + $Id: backupfile.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ +*/ + +/* 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 char *simple_backup_suffix; + +#ifdef __STDC__ +char *find_backup_file_name (char *file); +enum backup_type get_version (char *version); +#else +char *find_backup_file_name (); +enum backup_type get_version (); +#endif diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h new file mode 100644 index 00000000000..4c0f9b0c90f --- /dev/null +++ b/usr.bin/patch/common.h @@ -0,0 +1,184 @@ +/* $Id: common.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ */ + +#define DEBUGGING + +#define VOIDUSED 7 +#include "config.h" + +/* shut lint up about the following when return value ignored */ + +#define Signal (void)signal +#define Unlink (void)unlink +#define Lseek (void)lseek +#define Fseek (void)fseek +#define Fstat (void)fstat +#define Pclose (void)pclose +#define Close (void)close +#define Fclose (void)fclose +#define Fflush (void)fflush +#define Sprintf (void)sprintf +#define Mktemp (void)mktemp +#define Strcpy (void)strcpy +#define Strcat (void)strcat + +/* NeXT declares malloc and realloc incompatibly from us in some of + these files. Temporarily redefine them to prevent errors. */ +#define malloc system_malloc +#define realloc system_realloc +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <signal.h> +#undef malloc +#undef realloc + +/* constants */ + +/* AIX predefines these. */ +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif +#define TRUE (1) +#define FALSE (0) + +#define MAXHUNKSIZE 100000 /* is this enough lines? */ +#define INITHUNKMAX 125 /* initial dynamic allocation size */ +#define MAXLINELEN 1024 +#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" + +#ifdef FLEXFILENAMES +#define ORIGEXT ".orig" +#define REJEXT ".rej" +#else +#define ORIGEXT "~" +#define REJEXT "#" +#endif + +/* handy definitions */ + +#define Null(t) ((t)0) +#define Nullch Null(char *) +#define Nullfp Null(FILE *) +#define Nulline Null(LINENUM) + +#define Ctl(ch) ((ch) & 037) + +#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 char bool; +typedef long LINENUM; /* must be signed */ +typedef unsigned MEM; /* what to feed malloc */ + +/* globals */ + +EXT int Argc; /* guess */ +EXT char **Argv; +EXT int Argc_last; /* for restarting plan_b */ +EXT char **Argv_last; + +EXT struct stat filestat; /* file statistics area */ +EXT int filemode INIT(0644); + +EXT char buf[MAXLINELEN]; /* general purpose buffer */ +EXT FILE *ofp INIT(Nullfp); /* output file pointer */ +EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */ + +EXT int myuid; /* cache getuid return value */ + +EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */ +EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */ + +#define MAXFILEC 2 +EXT int filec INIT(0); /* how many file arguments? */ +EXT char *filearg[MAXFILEC]; +EXT bool ok_to_create_file INIT(FALSE); +EXT char *bestguess INIT(Nullch); /* guess at correct filename */ + +EXT char *outname INIT(Nullch); +EXT char rejname[128]; + +EXT char *origprae INIT(Nullch); + +EXT char *TMPOUTNAME; +EXT char *TMPINNAME; +EXT char *TMPREJNAME; +EXT char *TMPPATNAME; +EXT bool toutkeep INIT(FALSE); +EXT bool trejkeep INIT(FALSE); + +EXT LINENUM last_offset INIT(0); +#ifdef DEBUGGING +EXT int debug INIT(0); +#endif +EXT LINENUM maxfuzz INIT(2); +EXT bool force INIT(FALSE); +EXT bool batch INIT(FALSE); +EXT bool verbose INIT(TRUE); +EXT bool reverse INIT(FALSE); +EXT bool noreverse INIT(FALSE); +EXT bool skip_rest_of_patch INIT(FALSE); +EXT int strippath INIT(957); +EXT bool canonicalize INIT(FALSE); + +#define CONTEXT_DIFF 1 +#define NORMAL_DIFF 2 +#define ED_DIFF 3 +#define NEW_CONTEXT_DIFF 4 +#define UNI_DIFF 5 +EXT int diff_type INIT(0); + +EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */ +EXT char if_defined[128]; /* #ifdef xyzzy */ +EXT char not_defined[128]; /* #ifndef xyzzy */ +EXT char else_defined[] INIT("#else\n");/* #else */ +EXT char end_defined[128]; /* #endif xyzzy */ + +EXT char *revision INIT(Nullch); /* prerequisite revision, if any */ + +#include <errno.h> +#ifndef errno +extern int errno; +#endif + +FILE *popen(); +char *malloc(); +char *realloc(); +long atol(); +char *getenv(); +char *strcpy(); +char *strcat(); +char *rindex(); +char *mktemp(); +#if 0 /* This can cause a prototype conflict. */ +#ifdef CHARSPRINTF +char *sprintf(); +#else +int sprintf(); +#endif +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif diff --git a/usr.bin/patch/config.h b/usr.bin/patch/config.h new file mode 100644 index 00000000000..1c7f493ef2c --- /dev/null +++ b/usr.bin/patch/config.h @@ -0,0 +1,133 @@ +/* config.h + * This file was produced by running the config.h.SH script, which + * gets its values from config.sh, which is generally produced by + * running Configure. + * + * Feel free to modify any of this as the need arises. Note, however, + * that running config.h.SH again will wipe out any changes you've made. + * For a more permanent change edit config.sh and rerun config.h.SH. + * + * $Id: config.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ + */ + + +/* EUNICE: + * This symbol, if defined, indicates that the program is being compiled + * under the EUNICE package under VMS. The program will need to handle + * things like files that don't go away the first time you unlink them, + * due to version numbering. It will also need to compensate for lack + * of a respectable link() command. + */ +/* VMS: + * This symbol, if defined, indicates that the program is running under + * VMS. It is currently only set in conjunction with the EUNICE symbol. + */ +#/*undef EUNICE /**/ +#/*undef VMS /**/ + +/* CPPSTDIN: + * This symbol contains the first part of the string which will invoke + * the C preprocessor on the standard input and produce to standard + * output. Typical value of "cc -E" or "/lib/cpp". + */ +/* CPPMINUS: + * This symbol contains the second part of the string which will invoke + * the C preprocessor on the standard input and produce to standard + * output. This symbol will have the value "-" if CPPSTDIN needs a minus + * to specify standard input, otherwise the value is "". + */ +#define CPPSTDIN "/usr/bin/cpp" +#define CPPMINUS "" + +/* CHARSPRINTF: + * This symbol is defined if this system declares "char *sprintf()" in + * stdio.h. The trend seems to be to declare it as "int sprintf()". It + * is up to the package author to declare sprintf correctly based on the + * symbol. + */ +/* # CHARSPRINTF /**/ + +/* FLEXFILENAMES: + * This symbol, if defined, indicates that the system supports filenames + * longer than 14 characters. + */ +#define FLEXFILENAMES /**/ + +/* index: + * This preprocessor symbol is defined, along with rindex, if the system + * uses the strchr and strrchr routines instead. + */ +/* rindex: + * This preprocessor symbol is defined, along with index, if the system + * uses the strchr and strrchr routines instead. + */ +#/*undef index strchr /* cultural */ +#/*undef rindex strrchr /* differences? */ + +/* VOIDSIG: + * This symbol is defined if this system declares "void (*signal())()" in + * signal.h. The old way was to declare it as "int (*signal())()". It + * is up to the package author to declare things correctly based on the + * symbol. + */ +#define VOIDSIG /**/ + +/* DIRHEADER: + * This definition indicates which directory library header to use. + */ +#define DIRENT + +/* HAVE_UNISTD_H: + * This is defined if the system has unistd.h. + */ +#define HAVE_UNISTD_H /**/ + +/* Reg1: + * This symbol, along with Reg2, Reg3, etc. is either the word "register" + * or null, depending on whether the C compiler pays attention to this + * many register declarations. The intent is that you don't have to + * order your register declarations in the order of importance, so you + * can freely declare register variables in sub-blocks of code and as + * function parameters. Do not use Reg<n> more than once per routine. + */ + +#define Reg1 register /**/ +#define Reg2 register /**/ +#define Reg3 register /**/ +#define Reg4 register /**/ +#define Reg5 register /**/ +#define Reg6 register /**/ +#define Reg7 /**/ +#define Reg8 /**/ +#define Reg9 /**/ +#define Reg10 /**/ +#define Reg11 /**/ +#define Reg12 /**/ +#define Reg13 /**/ +#define Reg14 /**/ +#define Reg15 /**/ +#define Reg16 /**/ + +/* VOIDFLAGS: + * This symbol indicates how much support of the void type is given by this + * compiler. What various bits mean: + * + * 1 = supports declaration of void + * 2 = supports arrays of pointers to functions returning void + * 4 = supports comparisons between pointers to void functions and + * addresses of void functions + * + * The package designer should define VOIDUSED to indicate the requirements + * of the package. This can be done either by #defining VOIDUSED before + * including config.h, or by defining defvoidused in Myinit.U. If the + * level of void support necessary is not present, defines void to int. + */ +#ifndef VOIDUSED +#define VOIDUSED 7 +#endif +#define VOIDFLAGS 7 +#if (VOIDFLAGS & VOIDUSED) != VOIDUSED +#define void int /* is void to be avoided? */ +#define M_VOID /* Xenix strikes again */ +#endif + diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c new file mode 100644 index 00000000000..f061771e30a --- /dev/null +++ b/usr.bin/patch/inp.c @@ -0,0 +1,353 @@ +#ifndef lint +static char rcsid[] = "$Id: inp.c,v 1.1 1995/10/18 08:45:55 deraadt Exp $"; +#endif /* not lint */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "pch.h" +#include "INTERN.h" +#include "inp.h" + +/* Input-file-with-indexable-lines abstract type */ + +static long 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 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 */ + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + i_size = 0; +#ifndef lint + if (i_ptr != Null(char**)) + free((char *)i_ptr); +#endif + if (i_womp != Nullch) + free(i_womp); + i_womp = Nullch; + i_ptr = Null(char **); + } + 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] = Nullch; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Constuct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ + if (!plan_a(filename)) + plan_b(filename); + if (verbose) { + say3("Patching file %s using Plan %s...\n", filename, + (using_plan_a ? "A" : "B") ); + } +} + +/* Try keeping everything in memory. */ + +bool +plan_a(filename) +char *filename; +{ + int ifd, statfailed; + Reg1 char *s; + Reg2 LINENUM iline; + char lbuf[MAXLINELEN]; + + statfailed = stat(filename, &filestat); + if (statfailed && ok_to_create_file) { + if (verbose) + say2("(Creating file %s...)\n",filename); + makedirs(filename, TRUE); + close(creat(filename, 0666)); + statfailed = stat(filename, &filestat); + } + /* 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 != myuid)) { + struct stat cstat; + char *cs = Nullch; + char *filebase; + int pathlen; + + filebase = basename(filename); + pathlen = filebase - filename; + + /* Put any leading path into `s'. + Leave room in lbuf for the diff command. */ + s = lbuf + 20; + strncpy(s, filename, pathlen); + +#define try(f, a1, a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) + if ( try("RCS/%s%s", filebase, RCSSUFFIX) + || try("RCS/%s" , filebase, 0) + || try( "%s%s", filebase, RCSSUFFIX)) { + Sprintf(buf, CHECKOUT, filename); + Sprintf(lbuf, RCSDIFF, filename); + cs = "RCS"; + } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase) + || try( "%s%s", SCCSPREFIX, filebase)) { + Sprintf(buf, GET, s); + Sprintf(lbuf, SCCSDIFF, s, filename); + cs = "SCCS"; + } else if (statfailed) + fatal2("can't find %s\n", filename); + /* 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. */ + fatal3("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) + say3("Comparing file %s to default %s version...\n", + filename, cs); + if (system(lbuf)) + fatal3("can't check out file %s: differs from default %s version\n", + filename, cs); + } + if (verbose) + say3("Checking out file %s from %s...\n", filename, cs); + if (system(buf) || stat(filename, &filestat)) + fatal3("can't check out file %s from %s\n", filename, cs); + } + } + filemode = filestat.st_mode; + if (!S_ISREG(filemode)) + fatal2("%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 */ + } +#ifdef lint + i_womp = Nullch; +#else + i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ + /* i_size, but that's okay, I think. */ +#endif + if (i_womp == Nullch) + return FALSE; + if ((ifd = open(filename, 0)) < 0) + pfatal2("can't open file %s", filename); +#ifndef lint + if (read(ifd, i_womp, (int)i_size) != i_size) { + Close(ifd); /* probably means i_size > 15 or 16 bits worth */ + free(i_womp); /* at this point it doesn't matter if i_womp was */ + return FALSE; /* undersized. */ + } +#endif + Close(ifd); + if (i_size && i_womp[i_size-1] != '\n') + i_womp[i_size++] = '\n'; + i_womp[i_size] = '\0'; + + /* count the lines in the buffer so we know how many pointers we need */ + + iline = 0; + for (s=i_womp; *s; s++) { + if (*s == '\n') + iline++; + } +#ifdef lint + i_ptr = Null(char**); +#else + i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); +#endif + if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ + free((char *)i_womp); + return FALSE; + } + + /* now scan the buffer and build pointer array */ + + iline = 1; + i_ptr[iline] = i_womp; + for (s=i_womp; *s; s++) { + if (*s == '\n') + i_ptr[++iline] = s+1; /* these are NOT null terminated */ + } + input_lines = iline - 1; + + /* now check for revision, if any */ + + if (revision != Nullch) { + if (!rev_in_string(i_womp)) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else if (batch) { + fatal2( +"this file doesn't appear to be the %s version--aborting.\n", revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("aborted\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + return TRUE; /* plan a will work */ +} + +/* Keep (virtually) nothing in memory. */ + +void +plan_b(filename) +char *filename; +{ + Reg3 FILE *ifp; + Reg1 int i = 0; + Reg2 int maxlen = 1; + Reg4 bool found_revision = (revision == Nullch); + + using_plan_a = FALSE; + if ((ifp = fopen(filename, "r")) == Nullfp) + pfatal2("can't open file %s", filename); + if ((tifd = creat(TMPINNAME, 0666)) < 0) + pfatal2("can't open file %s", TMPINNAME); + while (fgets(buf, sizeof buf, ifp) != Nullch) { + if (revision != Nullch && !found_revision && rev_in_string(buf)) + found_revision = TRUE; + if ((i = strlen(buf)) > maxlen) + maxlen = i; /* find longest line */ + } + if (revision != Nullch) { + if (!found_revision) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else if (batch) { + fatal2( +"this file doesn't appear to be the %s version--aborting.\n", revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("aborted\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + Fseek(ifp, 0L, 0); /* rewind file */ + lines_per_buf = BUFFERSIZE / maxlen; + tireclen = maxlen; + tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); + tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); + if (tibuf[1] == Nullch) + fatal1("out of memory\n"); + for (i=1; ; i++) { + if (! (i % lines_per_buf)) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal1("can't write temp file"); + if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) + == Nullch) { + input_lines = i - 1; + if (i % lines_per_buf) + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal1("can't write temp file"); + break; + } + } + Fclose(ifp); + Close(tifd); + if ((tifd = open(TMPINNAME, 0)) < 0) { + pfatal2("can't reopen file %s", TMPINNAME); + } +} + +/* Fetch a line from the input file, \n terminated, not necessarily \0. */ + +char * +ifetch(line,whichbuf) +Reg1 LINENUM line; +int whichbuf; /* ignored when file in memory */ +{ + if (line < 1 || line > input_lines) + return ""; + 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; +#ifndef lint /* complains of long accuracy */ + Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); +#endif + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + pfatal2("error reading tmp file %s", TMPINNAME); + } + return tibuf[whichbuf] + (tireclen*offline); + } +} + +/* True if the string argument contains the revision number we want. */ + +bool +rev_in_string(string) +char *string; +{ + Reg1 char *s; + Reg2 int patlen; + + if (revision == Nullch) + return TRUE; + patlen = strlen(revision); + if (strnEQ(string,revision,patlen) && isspace(string[patlen])) + return TRUE; + for (s = string; *s; s++) { + if (isspace(*s) && strnEQ(s+1, revision, patlen) && + isspace(s[patlen+1] )) { + return TRUE; + } + } + return FALSE; +} diff --git a/usr.bin/patch/inp.h b/usr.bin/patch/inp.h new file mode 100644 index 00000000000..da25e6be9b0 --- /dev/null +++ b/usr.bin/patch/inp.h @@ -0,0 +1,12 @@ +/* $Id: inp.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ */ + +EXT LINENUM input_lines INIT(0); /* how long is input file in lines */ +EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */ + /* irretractibly output */ + +bool rev_in_string(); +void scan_input(); +bool plan_a(); /* returns false if insufficient memory */ +void plan_b(); +char *ifetch(); + diff --git a/usr.bin/patch/malloc.c b/usr.bin/patch/malloc.c new file mode 100644 index 00000000000..d262e242d4f --- /dev/null +++ b/usr.bin/patch/malloc.c @@ -0,0 +1,468 @@ +/* + * from: @(#)nmalloc.c 1 (Caltech) 2/21/82 + * $Id: malloc.c,v 1.1 1995/10/18 08:45:55 deraadt Exp $ + * + * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs + * + * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks + * that don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are (2^n)-4 (or -16) bytes long. + * This is designed for use in a program that uses vast quantities of + * memory, but bombs when it runs out. To make it a little better, it + * warns the user when he starts to get near the end. + * + * June 84, ACT: modified rcheck code to check the range given to malloc, + * rather than the range determined by the 2-power used. + * + * Jan 85, RMS: calls malloc_warning to issue warning on nearly full. + * No longer Emacs-specific; can serve as all-purpose malloc for GNU. + * You should call malloc_init to reinitialize after loading dumped Emacs. + * Call malloc_stats to get info on memory stats if MSTATS turned on. + * realloc knows how to return same block given, just changing its size, + * if the power of 2 is correct. + */ + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+3). The + * smallest allocatable block is 8 bytes. The overhead information will + * go in the first int of the block, and the returned pointer will point + * to the second. + * +#ifdef MSTATS + * nmalloc[i] is the difference between the number of mallocs and frees + * for a given block size. +#endif /* MSTATS */ + */ + +#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */ +#define ISFREE ((char) 0x54) /* magic byte that implies free block */ + /* this is for error checking only */ + +extern char etext; + +/* end of the program; can be changed by calling init_malloc */ +static char *endofpure = &etext; + +#ifdef MSTATS +static int nmalloc[30]; +static int nmal, nfre; +#endif /* MSTATS */ + +/* If range checking is not turned on, all we have is a flag indicating + whether memory is allocated, an index in nextf[], and a size field; to + realloc() memory we copy either size bytes or 1<<(index+3) bytes depending + on whether the former can hold the exact size (given the value of + 'index'). If range checking is on, we always need to know how much space + is allocated, so the 'size' field is never used. */ + +struct mhead { + char mh_alloc; /* ISALLOC or ISFREE */ + char mh_index; /* index in nextf[] */ +/* Remainder are valid only when block is allocated */ + unsigned short mh_size; /* size, if < 0x10000 */ +#ifdef rcheck + unsigned mh_nbytes; /* number of bytes allocated */ + int mh_magic4; /* should be == MAGIC4 */ +#endif /* rcheck */ + }; + +/* Access free-list pointer of a block. + It is stored at block + 4. + This is not a field in the mhead structure + because we want sizeof (struct mhead) + to describe the overhead for when the block is in use, + and we do not want the free-list pointer to count in that. */ + +#define CHAIN(a) \ + (*(struct mhead **) (sizeof (char *) + (char *) (a))) + +#ifdef rcheck + +/* To implement range checking, we write magic values in at the beginning and + end of each allocated block, and make sure they are undisturbed whenever a + free or a realloc occurs. */ +/* Written in each of the 4 bytes following the block's real space */ +#define MAGIC1 0x55 +/* Written in the 4 bytes before the block's real space */ +#define MAGIC4 0x55555555 +#define ASSERT(p) if (!(p)) botch("p"); else +static +botch(s) + char *s; +{ + + printf("assertion botched: %s\n", s); + abort(); +} +#define EXTRA 4 /* 4 bytes extra for MAGIC1s */ +#else +#define ASSERT(p) +#define EXTRA 0 +#endif /* rcheck */ + +/* nextf[i] is free list of blocks of size 2**(i + 3) */ + +static struct mhead *nextf[30]; + +#ifdef M_WARN +/* Number of bytes of writable memory we can expect to be able to get */ +static int lim_data; +/* Level number of warnings already issued. + 0 -- no warnings issued. + 1 -- 75% warning already issued. + 2 -- 85% warning already issued. +*/ +static int warnlevel; +#endif /* M_WARN */ + +/* nonzero once initial bunch of free blocks made */ +static int gotpool; + +/* Cause reinitialization based on job parameters; + also declare where the end of pure storage is. */ +malloc_init (end) + char *end; { + endofpure = end; +#ifdef M_WARN + lim_data = 0; + warnlevel = 0; +#endif /* M_WARN */ + } + +static +morecore (nu) /* ask system for more memory */ + register int nu; { /* size index to get more of */ + char *sbrk (); + register char *cp; + register int nblks; + register int siz; + +#ifdef M_WARN +#ifndef BSD42 +#ifdef USG + extern long ulimit (); + if (lim_data == 0) /* find out how much we can get */ + lim_data = ulimit (3, 0) - TEXT_START; +#else /*HMS: was endif */ + if (lim_data == 0) /* find out how much we can get */ + lim_data = vlimit (LIM_DATA, -1); +#endif /* USG */ /HMS:* was not here */ +#else + if (lim_data == 0) { + struct rlimit XXrlimit; + + getrlimit (RLIMIT_DATA, &XXrlimit); + lim_data = XXrlimit.rlim_cur;} /* soft limit */ +#endif /* BSD42 */ +#endif /* M_WARN */ + + /* On initial startup, get two blocks of each size up to 1k bytes */ + if (!gotpool) + getpool (), getpool (), gotpool = 1; + + /* Find current end of memory and issue warning if getting near max */ + + cp = sbrk (0); + siz = cp - endofpure; +#ifdef M_WARN + switch (warnlevel) { + case 0: + if (siz > (lim_data / 4) * 3) { + warnlevel++; + malloc_warning ("Warning: past 75% of memory limit");} + break; + case 1: + if (siz > (lim_data / 20) * 17) { + warnlevel++; + malloc_warning ("Warning: past 85% of memory limit");} + break; + case 2: + if (siz > (lim_data / 20) * 19) { + warnlevel++; + malloc_warning ("Warning: past 95% of memory limit");} + break;} +#endif /* M_WARN */ + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); + + /* Take at least 2k, and figure out how many blocks of the desired size we're about to get */ + nblks = 1; + if ((siz = nu) < 8) + nblks = 1 << ((siz = 8) - nu); + + if ((cp = sbrk (1 << (siz + 3))) == (char *) -1) + return; /* no more room! */ + if ((int) cp & 7) { /* shouldn't happen, but just in case */ + cp = (char *) (((int) cp + 8) & ~7); + nblks--;} + + /* save new header and link the nblks blocks together */ + nextf[nu] = (struct mhead *) cp; + siz = 1 << (nu + 3); + while (1) { + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + if (--nblks <= 0) break; + CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz); + cp += siz;} +/* CHAIN ((struct mhead *) cp) = 0; /* since sbrk() returns cleared core, this is already set */ + } + +static +getpool () { + register int nu; + register char *cp = sbrk (0); + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); + + /* Get 2k of storage */ + + cp = sbrk (04000); + if (cp == (char *) -1) + return; + + /* Divide it into an initial 8-word block + plus one block of size 2**nu for nu = 3 ... 10. */ + + CHAIN (cp) = nextf[0]; + nextf[0] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = 0; + cp += 8; + + for (nu = 0; nu < 7; nu++) { + CHAIN (cp) = nextf[nu]; + nextf[nu] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + cp += 8 << nu;}} + +char * +malloc (n) /* get a block */ + unsigned n; { + register struct mhead *p; + register unsigned int nbytes; + register int nunits = 0; + + /* Figure out how many bytes are required, rounding up to the nearest + multiple of 4, then figure out which nextf[] area to use */ + nbytes = (n + sizeof *p + EXTRA + 3) & ~3; + { + register unsigned int shiftr = (nbytes - 1) >> 2; + + while (shiftr >>= 1) + nunits++; + } + + /* If there are no blocks of the appropriate size, go get some */ + /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */ + if (nextf[nunits] == 0) + morecore (nunits); + + /* Get one block off the list, and set the new list head */ + if ((p = nextf[nunits]) == 0) + return 0; + nextf[nunits] = CHAIN (p); + + /* Check for free block clobbered */ + /* If not for this check, we would gobble a clobbered free chain ptr */ + /* and bomb out on the NEXT allocate of this size block */ + if (p -> mh_alloc != ISFREE || p -> mh_index != nunits) +#ifdef rcheck + botch ("block on free list clobbered"); +#else + abort (); +#endif /* rcheck */ + + /* Fill in the info, and if range checking, set up the magic numbers */ + p -> mh_alloc = ISALLOC; +#ifdef rcheck + p -> mh_nbytes = n; + p -> mh_magic4 = MAGIC4; + { + register char *m = (char *) (p + 1) + n; + + *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1; + } +#else + p -> mh_size = n; +#endif /* rcheck */ +#ifdef MSTATS + nmalloc[nunits]++; + nmal++; +#endif /* MSTATS */ + return (char *) (p + 1);} + +free (mem) + char *mem; { + register struct mhead *p; + { + register char *ap = mem; + + ASSERT (ap != 0); + p = (struct mhead *) ap - 1; + ASSERT (p -> mh_alloc == ISALLOC); +#ifdef rcheck + ASSERT (p -> mh_magic4 == MAGIC4); + ap += p -> mh_nbytes; + ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1); + ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1); +#endif /* rcheck */ + } + { + register int nunits = p -> mh_index; + + ASSERT (nunits <= 29); + p -> mh_alloc = ISFREE; + CHAIN (p) = nextf[nunits]; + nextf[nunits] = p; +#ifdef MSTATS + nmalloc[nunits]--; + nfre++; +#endif /* MSTATS */ + } + } + +char * +realloc (mem, n) + char *mem; + register unsigned n; { + register struct mhead *p; + register unsigned int tocopy; + register int nbytes; + register int nunits; + + if ((p = (struct mhead *) mem) == 0) + return malloc (n); + p--; + nunits = p -> mh_index; + ASSERT (p -> mh_alloc == ISALLOC); +#ifdef rcheck + ASSERT (p -> mh_magic4 == MAGIC4); + { + register char *m = mem + (tocopy = p -> mh_nbytes); + ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1); + ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1); + } +#else + if (p -> mh_index >= 13) + tocopy = (1 << (p -> mh_index + 3)) - sizeof *p; + else + tocopy = p -> mh_size; +#endif /* rcheck */ + + /* See if desired size rounds to same power of 2 as actual size. */ + nbytes = (n + sizeof *p + EXTRA + 7) & ~7; + + /* If ok, use the same block, just marking its size as changed. */ + if (nbytes > (4 << nunits) && nbytes <= (8 << nunits)) { +#ifdef rcheck + register char *m = mem + tocopy; + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; + p-> mh_nbytes = n; + m = mem + n; + *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; +#else + p -> mh_size = n; +#endif /* rcheck */ + return mem;} + + if (n < tocopy) + tocopy = n; + { + register char *new; + void bcopy(); /*HMS: here? */ + + if ((new = malloc (n)) == 0) + return 0; + bcopy (mem, new, tocopy); + free (mem); + return new; + } + } + +#ifdef MSTATS +/* Return statistics describing allocation of blocks of size 2**n. */ + +struct mstats_value { + int blocksize; + int nfree; + int nused; + }; + +struct mstats_value +malloc_stats (size) + int size; { + struct mstats_value v; + register int i; + register struct mhead *p; + + v.nfree = 0; + + if (size < 0 || size >= 30) { + v.blocksize = 0; + v.nused = 0; + return v;} + + v.blocksize = 1 << (size + 3); + v.nused = nmalloc[size]; + + for (p = nextf[size]; p; p = CHAIN (p)) + v.nfree++; + + return v;} +#endif + +/* how much space is available? */ + +unsigned freespace() { + register int i, j; + register struct mhead *p; + register unsigned space = 0; + int local; /* address only is used */ + + space = (char *)&local - sbrk(0); /* stack space */ + + for (i = 0; i < 30; i++) { + for (j = 0, p = nextf[i]; p; p = CHAIN (p), j++) ; + space += j * (1 << (i + 3));} + + return(space);} + +/* How big is this cell? */ + +unsigned mc_size(cp) + char *cp;{ + register struct mhead *p; + + if ((p = (struct mhead *) cp) == 0) { + /*HMS? */ + } + p--; +#ifdef rcheck + return p -> mh_nbytes; +#else + return (1 << (p -> mh_index + 3)) - sizeof *p; +/**/ +/* if (p -> mh_index >= 13) +/* return (1 << (p -> mh_index + 3)) - sizeof *p; +/* else +/* return p -> mh_size; +/**/ +#endif /* rcheck */ + } + +/*HMS: Really should use memcpy, if available... */ + +void bcopy(source, dest, len) + register char *source, *dest; + register len; { + register i; + + for (i = 0; i < len; i++) + *dest++ = *source++;} diff --git a/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1 new file mode 100644 index 00000000000..123937f2336 --- /dev/null +++ b/usr.bin/patch/patch.1 @@ -0,0 +1,518 @@ +.\" $Id: patch.1,v 1.1 1995/10/18 08:45:55 deraadt Exp $ -*- nroff -*- +.rn '' }` +.de Sh +.br +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +'\" +'\" Set up \*(-- to give an unbreakable dash; +'\" string Tr holds user defined translation string. +'\" Bell System Logo is used as a dummy character. +'\" +'\" Shut up a groff -ww warning. +.if \n(.g .if !dTr .ds Tr +.ie n \{\ +.tr \(*W-\*(Tr +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br \} +.el \{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +'br\} +.TH PATCH 1 +.SH NAME +patch - apply a diff file to an original +.SH SYNOPSIS +.B patch +[options] [origfile [patchfile]] [+ [options] [origfile]]... +.sp +but usually just +.sp +.B patch +<patchfile +.SH DESCRIPTION +.I Patch +will take a patch file containing any of the four forms of difference +listing produced by the +.I diff +program and apply those differences to an original file, producing a patched +version. +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 \*(L".orig\*(R" (\*(L"~\*(R" on systems that do not +support long filenames), or as specified by the +.BR -b , +.BR -B , +or +.B -V +switches. +The extension used for making backup files may also be specified in the +.B SIMPLE_BACKUP_SUFFIX +environment variable, which is overridden by above switches. +.PP +If the backup file already exists, +.B patch +creates a new backup file name by changing the first lowercase letter +in the last component of the file's name into uppercase. If there are +no more lowercase letters in the name, it removes the first character +from the name. It repeats this process until it comes up with a +backup file that does not already exist. +.PP +You may also specify where you want the output to go with a +.B -o +switch; if that file already exists, it is backed up first. +.PP +If +.I patchfile +is omitted, or is a hyphen, the patch will be read from standard input. +.PP +Upon startup, patch will attempt to determine the type of the diff listing, +unless over-ruled by a +.BR -c , +.BR -e , +.BR -n , +or +.B -u +switch. +Context diffs (old-style, new-style, and unified) and +normal diffs are applied by the +.I patch +program itself, while ed diffs are simply fed to the +.I ed +editor via a pipe. +.PP +.I Patch +will try to skip any leading garbage, apply the diff, +and then skip any trailing garbage. +Thus you could feed an article or message containing a +diff listing to +.IR 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, +.I patch +can detect when the line numbers mentioned in the patch are incorrect, +and will attempt to find the correct place to apply each hunk of the patch. +As a first guess, it takes the line number mentioned for the hunk, plus or +minus any offset used in applying the previous hunk. +If that is not the correct place, +.I patch +will scan both forwards and backwards for a set of lines matching the context +given in the hunk. +First +.I patch +looks for a place where all lines of the context match. +If no such place is found, and it's a context diff, and the maximum fuzz factor +is set to 1 or more, then another scan takes place ignoring the first and last +line of context. +If that fails, and the maximum fuzz factor is set to 2 or more, +the first two and last two lines of context are ignored, +and another scan is made. +(The default maximum fuzz factor is 2.) +If +.I patch +cannot find a place to install that hunk of the patch, it will put the +hunk out to a reject file, which normally is the name of the output file +plus \*(L".rej\*(R" (\*(L"#\*(R" on systems that do not support +long filenames). +(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) +.I patch +thought the hunk should go on. +If this is different from the line number specified in the diff you will +be told the offset. +A single large offset MAY be an indication that a hunk was installed in the +wrong place. +You will also be told if a fuzz factor was used to make the match, in which +case you should also be slightly suspicious. +.PP +If no original file is specified on the command line, +.I patch +will try to figure out from the leading garbage what the name of the file +to edit is. +In the header of a context diff, the filename is found from lines beginning +with \*(L"***\*(R" or \*(L"---\*(R", with the shortest name of an existing +file winning. +Only context diffs have lines like that, but if there is an \*(L"Index:\*(R" +line in the leading garbage, +.I patch +will try to use the filename from that line. +The context diff header takes precedence over an Index line. +If no filename can be intuited from the leading garbage, you will be asked +for the name of the file to patch. +.PP +If the original file cannot be found or is read-only, but a suitable +SCCS or RCS file is handy, +.I patch +will attempt to get or check out the file. +.PP +Additionally, if the leading garbage contains a \*(L"Prereq: \*(R" line, +.I patch +will take the first word from the prerequisites line (normally a version +number) and check the input file to see if that word can be found. +If not, +.I patch +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: +.Sp + | patch -d /usr/src/local/blurfl +.Sp +and patch a file in the blurfl directory directly from the article containing +the patch. +.PP +If the patch file contains more than one patch, +.I patch +will try to apply each of them as if they came from separate patch files. +This means, among other things, that it is assumed that the name of the file +to patch must be determined for each diff listing, +and that the garbage before each diff listing will +be examined for interesting things such as filenames and revision level, as +mentioned previously. +You can give switches (and another original file name) for the second and +subsequent patches by separating the corresponding argument lists +by a \*(L'+\*(R'. +(The argument list for a second or subsequent patch may not specify a new +patch file, however.) +.PP +.I Patch +recognizes the following switches: +.TP 5 +.B \-b +causes the next argument to be interpreted as the backup extension, to be +used in place of \*(L".orig\*(R" or \*(L"~\*(R". +.TP 5 +.B \-B +causes the next argument to be interpreted as a prefix to the backup file +name. If this argument is specified any argument from -b will be ignored. +.TP 5 +.B \-c +forces +.I patch +to interpret the patch file as a context diff. +.TP 5 +.B \-d +causes +.I patch +to interpret the next argument as a directory, and cd to it before doing +anything else. +.TP 5 +.B \-D +causes +.I patch +to use the "#ifdef...#endif" construct to mark changes. +The argument following will be used as the differentiating symbol. +Note that, unlike the C compiler, there must be a space between the +.B \-D +and the argument. +.TP 5 +.B \-e +forces +.I patch +to interpret the patch file as an ed script. +.TP 5 +.B \-E +causes +.I patch +to remove output files that are empty after the patches have been applied. +.TP 5 +.B \-f +forces +.I patch +to assume that the user knows exactly what he or she is doing, and to not +ask any questions. It assumes the following: skip patches for which a +file to patch can't be found; patch files even though they have the +wrong version for the ``Prereq:'' line in the patch; and assume that +patches are not reversed even if they look like they are. +This option does not suppress commentary; use +.B \-s +for that. +.TP 5 +.B \-t +similar to +.BR \-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 \fB\-f\fP); +skip patches for which the file has the wrong version for the ``Prereq:'' line +in the patch; and assume that patches are reversed if they look like +they are. +.TP 5 +.B \-F<number> +sets the maximum fuzz factor. +This switch only applies to context diffs, and causes +.I patch +to ignore up to that many lines in looking for places to install a hunk. +Note that a larger fuzz factor increases the odds of a faulty patch. +The default fuzz factor is 2, and it may not be set to more than +the number of lines of context in the context diff, ordinarily 3. +.TP 5 +.B \-l +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. +.TP 5 +.B \-n +forces +.I patch +to interpret the patch file as a normal diff. +.TP 5 +.B \-N +causes +.I patch +to ignore patches that it thinks are reversed or already applied. +See also +.B \-R . +.TP 5 +.B \-o +causes the next argument to be interpreted as the output file name. +.TP 5 +.B \-p<number> +sets the pathname strip count, +which controls how pathnames found in the patch file are treated, in case +the 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 filename in the patch file was +.sp + /u/howard/src/blurfl/blurfl.c +.sp +setting +.B \-p +or +.B \-p0 +gives the entire pathname unmodified, +.B \-p1 +gives +.sp + u/howard/src/blurfl/blurfl.c +.sp +without the leading slash, +.B \-p4 +gives +.sp + blurfl/blurfl.c +.sp +and not specifying +.B \-p +at all just gives you "blurfl.c", unless all of the directories in the +leading path (u/howard/src/blurfl) exist and that path is relative, +in which case you get the entire pathname unmodified. +Whatever you end up with is looked for either in the current directory, +or the directory specified by the +.B \-d +switch. +.TP 5 +.B \-r +causes the next argument to be interpreted as the reject file name. +.TP 5 +.B \-R +tells +.I patch +that this patch was created with the old and new files swapped. +(Yes, I'm afraid that does happen occasionally, human nature being what it +is.) +.I Patch +will attempt to swap each hunk around before applying it. +Rejects will come out in the swapped format. +The +.B \-R +switch will not work with ed diff scripts because there is too little +information to reconstruct the reverse operation. +.Sp +If the first hunk of a patch fails, +.I patch +will reverse the hunk to see if it can be applied that way. +If it can, you will be asked if you want to have the +.B \-R +switch 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.) +.TP 5 +.B \-s +makes +.I patch +do its work silently, unless an error occurs. +.TP 5 +.B \-S +causes +.I patch +to ignore this patch from the patch file, but continue on looking +for the next patch in the file. +Thus +.sp + patch -S + -S + <patchfile +.sp +will ignore the first and second of three patches. +.TP 5 +.B \-u +forces +.I patch +to interpret the patch file as a unified context diff (a unidiff). +.TP 5 +.B \-v +causes +.I patch +to print out its revision header and patch level. +.TP 5 +.B \-V +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 +.B VERSION_CONTROL +environment variable, which is overridden by this option. +The +.B -B +option overrides this option, causing the prefix to always be used for +making backup file names. +The value of the +.B VERSION_CONTROL +environment variable and the argument to the +.B -V +option are like the GNU +Emacs `version-control' variable; they also recognize synonyms that +are more descriptive. The valid values are (unique abbreviations are +accepted): +.RS +.TP +`t' or `numbered' +Always make numbered backups. +.TP +`nil' or `existing' +Make numbered backups of files that already +have them, simple backups of the others. +This is the default. +.TP +`never' or `simple' +Always make simple backups. +.RE +.TP 5 +.B \-x<number> +sets internal debugging flags, and is of interest only to +.I patch +patchers. +.SH AUTHOR +Larry Wall <lwall@netlabs.com> +.br +with many other contributors. +.SH ENVIRONMENT +.TP +.B TMPDIR +Directory to put temporary files in; default is /tmp. +.TP +.B SIMPLE_BACKUP_SUFFIX +Extension to use for backup file names instead of \*(L".orig\*(R" or +\*(L"~\*(R". +.TP +.B VERSION_CONTROL +Selects when numbered backup files are made. +.SH FILES +$TMPDIR/patch* +.SH SEE ALSO +diff(1) +.SH NOTES FOR PATCH SENDERS +There are several things you should bear in mind if you are going to +be sending out patches. +First, you can save people a lot of grief by keeping a patchlevel.h file +which is patched to increment the patch level as the first diff in the +patch file you send out. +If you put a Prereq: line in with the patch, it won't let them apply +patches out of order without some warning. +Second, make sure you've specified the filenames right, either in a +context diff header, or with an Index: line. +If you are patching something in a subdirectory, be sure to tell the patch +user to specify a +.B \-p +switch as needed. +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. +Fourth, take care not to send out reversed patches, since it makes people wonder +whether they already applied the patch. +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 DIAGNOSTICS +Too many to list here, but generally indicative that +.I patch +couldn't parse your patch file. +.PP +The message \*(L"Hmm...\*(R" indicates that there is unprocessed text in +the patch file and that +.I patch +is attempting to intuit whether there is a patch in that text and, if so, +what kind of patch it is. +.PP +.I Patch +will exit with a non-zero status if any reject files were created. +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 CAVEATS +.I Patch +cannot tell if the line numbers are off in an ed script, and can only detect +bad line numbers in a normal diff when it finds a \*(L"change\*(R" or +a \*(L"delete\*(R" 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 +.I Patch +usually produces the correct results, even when it has to do a lot of +guessing. +However, the results are guaranteed to be correct only when the patch is +applied to exactly the same version of the file that the patch was +generated from. +.SH BUGS +Could be smarter about partial matches, excessively \&deviant offsets and +swapped code, but that would take an extra pass. +.PP +If code has been duplicated (for instance with #ifdef OLDCODE ... #else ... +#endif), +.I patch +is incapable of patching both versions, and, if it works at all, will likely +patch the wrong one, and tell you that it succeeded to boot. +.PP +If you apply a patch you've already applied, +.I patch +will think it is a reversed patch, and offer to un-apply the patch. +This could be construed as a feature. +.rn }` '' diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c new file mode 100644 index 00000000000..610512288aa --- /dev/null +++ b/usr.bin/patch/patch.c @@ -0,0 +1,843 @@ +/* patch - a program to apply diffs to original files + * + * Copyright 1986, Larry Wall + * + * This program may be copied as long as you don't try to make any + * money off of it, or pretend that you wrote it. + */ + +#ifndef lint +static char rcsid[] = "$Id: patch.c,v 1.1 1995/10/18 08:45:55 deraadt Exp $"; +#endif /* not lint */ + +#include "INTERN.h" +#include "common.h" +#include "EXTERN.h" +#include "version.h" +#include "util.h" +#include "pch.h" +#include "inp.h" +#include "backupfile.h" + +/* procedures */ + +void reinitialize_almost_everything(); +void get_some_switches(); +LINENUM locate_hunk(); +void abort_hunk(); +void apply_hunk(); +void init_output(); +void init_reject(); +void copy_till(); +void spew_output(); +void dump_line(); +bool patch_match(); +bool similar(); +void re_input(); +void my_exit(); + +/* TRUE if -E was specified on command line. */ +static int remove_empty_files = FALSE; + +/* TRUE if -R was specified on command line. */ +static int reverse_flag_specified = FALSE; + +/* Apply a set of diffs as appropriate. */ + +int +main(argc,argv) +int argc; +char **argv; +{ + LINENUM where; + LINENUM newwhere; + LINENUM fuzz; + LINENUM mymaxfuzz; + int hunk = 0; + int failed = 0; + int failtotal = 0; + int i; + + setbuf(stderr, serrbuf); + for (i = 0; i<MAXFILEC; i++) + filearg[i] = Nullch; + + myuid = getuid(); + + /* Cons up the names of the temporary files. */ + { + /* Directory for temporary files. */ + char *tmpdir; + int tmpname_len; + + tmpdir = getenv ("TMPDIR"); + if (tmpdir == NULL) { + tmpdir = "/tmp"; + } + tmpname_len = strlen (tmpdir) + 20; + + TMPOUTNAME = (char *) malloc (tmpname_len); + strcpy (TMPOUTNAME, tmpdir); + strcat (TMPOUTNAME, "/patchoXXXXXX"); + Mktemp(TMPOUTNAME); + + TMPINNAME = (char *) malloc (tmpname_len); + strcpy (TMPINNAME, tmpdir); + strcat (TMPINNAME, "/patchiXXXXXX"); + Mktemp(TMPINNAME); + + TMPREJNAME = (char *) malloc (tmpname_len); + strcpy (TMPREJNAME, tmpdir); + strcat (TMPREJNAME, "/patchrXXXXXX"); + Mktemp(TMPREJNAME); + + TMPPATNAME = (char *) malloc (tmpname_len); + strcpy (TMPPATNAME, tmpdir); + strcat (TMPPATNAME, "/patchpXXXXXX"); + Mktemp(TMPPATNAME); + } + + { + char *v; + + v = getenv ("SIMPLE_BACKUP_SUFFIX"); + if (v) + simple_backup_suffix = v; + else + simple_backup_suffix = ORIGEXT; +#ifndef NODIR + v = getenv ("VERSION_CONTROL"); + backup_type = get_version (v); /* OK to pass NULL. */ +#endif + } + + /* parse switches */ + Argc = argc; + Argv = argv; + get_some_switches(); + + /* 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 */ + + if (outname == Nullch) + 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 = Nulline; + mymaxfuzz = pch_context(); + if (maxfuzz < mymaxfuzz) + mymaxfuzz = maxfuzz; + if (!skip_rest_of_patch) { + do { + where = locate_hunk(fuzz); + if (hunk == 1 && where == Nulline && !force) { + /* dwim for reversed patch? */ + if (!pch_swap()) { + if (fuzz == Nulline) + say1( +"Not enough memory to try swapped hunk! Assuming unswapped.\n"); + continue; + } + reverse = !reverse; + where = locate_hunk(fuzz); /* try again */ + if (where == Nulline) { /* didn't find it swapped */ + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + reverse = !reverse; + } + else if (noreverse) { + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + reverse = !reverse; + say1( +"Ignoring previously applied (or reversed) patch.\n"); + skip_rest_of_patch = TRUE; + } + else if (batch) { + if (verbose) + say3( +"%seversed (or previously applied) patch detected! %s -R.", + reverse ? "R" : "Unr", + reverse ? "Assuming" : "Ignoring"); + } + else { + ask3( +"%seversed (or previously applied) patch detected! %s -R? [y] ", + reverse ? "R" : "Unr", + reverse ? "Assume" : "Ignore"); + if (*buf == 'n') { + ask1("Apply anyway? [n] "); + if (*buf != 'y') + skip_rest_of_patch = TRUE; + where = Nulline; + reverse = !reverse; + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + } + } + } + } while (!skip_rest_of_patch && where == Nulline && + ++fuzz <= mymaxfuzz); + + if (skip_rest_of_patch) { /* just got decided */ + Fclose(ofp); + ofp = Nullfp; + } + } + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d ignored at %ld.\n", hunk, newwhere); + } + else if (where == Nulline) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d failed at %ld.\n", hunk, newwhere); + } + else { + apply_hunk(where); + if (verbose) { + say3("Hunk #%d succeeded at %ld", hunk, newwhere); + if (fuzz) + say2(" with fuzz %ld", fuzz); + if (last_offset) + say3(" (offset %ld line%s)", + last_offset, last_offset==1L?"":"s"); + say1(".\n"); + } + } + } + + if (out_of_mem && using_plan_a) { + Argc = Argc_last; + Argv = Argv_last; + say1("\n\nRan out of memory using Plan A--trying again...\n\n"); + if (ofp) + Fclose(ofp); + ofp = Nullfp; + if (rejfp) + Fclose(rejfp); + rejfp = Nullfp; + continue; + } + + assert(hunk); + + /* 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 (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) + say2("Removing %s (empty after patching).\n", realout); + while (unlink(realout) >= 0) ; /* while is for Eunice. */ + } + } + Fclose(rejfp); + rejfp = Nullfp; + if (failed) { + failtotal += failed; + if (!*rejname) { + Strcpy(rejname, outname); +#ifndef FLEXFILENAMES + { + char *s = rindex(rejname,'/'); + + if (!s) + s = rejname; + if (strlen(s) > 13) + if (s[12] == '.') /* try to preserve difference */ + s[12] = s[13]; /* between .h, .c, .y, etc. */ + s[13] = '\0'; + } +#endif + Strcat(rejname, REJEXT); + } + if (skip_rest_of_patch) { + say4("%d out of %d hunks ignored--saving rejects to %s\n", + failed, hunk, rejname); + } + else { + say4("%d out of %d hunks failed--saving rejects to %s\n", + failed, hunk, rejname); + } + if (move_file(TMPREJNAME, rejname) < 0) + trejkeep = TRUE; + } + set_signals(1); + } + my_exit(failtotal); +} + +/* Prepare to find the next patch to do in the patch file. */ + +void +reinitialize_almost_everything() +{ + re_patch(); + re_input(); + + input_lines = 0; + last_frozen_line = 0; + + filec = 0; + if (filearg[0] != Nullch && !out_of_mem) { + free(filearg[0]); + filearg[0] = Nullch; + } + + if (outname != Nullch) { + free(outname); + outname = Nullch; + } + + last_offset = 0; + + diff_type = 0; + + if (revision != Nullch) { + free(revision); + revision = Nullch; + } + + reverse = reverse_flag_specified; + skip_rest_of_patch = FALSE; + + get_some_switches(); + + if (filec >= 2) + fatal1("you may not change to a different patch file\n"); +} + +static char * +nextarg() +{ + if (!--Argc) + fatal2("missing argument after `%s'\n", *Argv); + return *++Argv; +} + +/* Process switches and filenames up to next '+' or end of list. */ + +void +get_some_switches() +{ + Reg1 char *s; + + rejname[0] = '\0'; + Argc_last = Argc; + Argv_last = Argv; + if (!Argc) + return; + for (Argc--,Argv++; Argc; Argc--,Argv++) { + s = Argv[0]; + if (strEQ(s, "+")) { + return; /* + will be skipped by for loop */ + } + if (*s != '-' || !s[1]) { + if (filec == MAXFILEC) + fatal1("too many file arguments\n"); + filearg[filec++] = savestr(s); + } + else { + switch (*++s) { + case 'b': + simple_backup_suffix = savestr(nextarg()); + break; + case 'B': + origprae = savestr(nextarg()); + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (!*++s) + s = nextarg(); + if (chdir(s) < 0) + pfatal2("can't cd to %s", s); + break; + case 'D': + do_defines = TRUE; + if (!*++s) + s = nextarg(); + if (!isalpha(*s) && '_' != *s) + fatal1("argument to -D is not an identifier\n"); + Sprintf(if_defined, "#ifdef %s\n", s); + Sprintf(not_defined, "#ifndef %s\n", s); + Sprintf(end_defined, "#endif /* %s */\n", s); + break; + case 'e': + diff_type = ED_DIFF; + break; + case 'E': + remove_empty_files = TRUE; + break; + case 'f': + force = TRUE; + break; + case 'F': + if (*++s == '=') + s++; + maxfuzz = atoi(s); + break; + case 'l': + canonicalize = TRUE; + break; + case 'n': + diff_type = NORMAL_DIFF; + break; + case 'N': + noreverse = TRUE; + break; + case 'o': + outname = savestr(nextarg()); + break; + case 'p': + if (*++s == '=') + s++; + strippath = atoi(s); + break; + case 'r': + Strcpy(rejname, nextarg()); + break; + case 'R': + reverse = TRUE; + reverse_flag_specified = TRUE; + break; + case 's': + verbose = FALSE; + break; + case 'S': + skip_rest_of_patch = TRUE; + break; + case 't': + batch = TRUE; + break; + case 'u': + diff_type = UNI_DIFF; + break; + case 'v': + version(); + break; + case 'V': +#ifndef NODIR + backup_type = get_version (nextarg ()); +#endif + break; +#ifdef DEBUGGING + case 'x': + debug = atoi(s+1); + break; +#endif + default: + fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]); + fprintf(stderr, "\ +Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\ +Options:\n\ + [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\ + [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\ + [-r rej-name] [-V {numbered,existing,simple}]\n"); + my_exit(1); + } + } + } +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +LINENUM +locate_hunk(fuzz) +LINENUM fuzz; +{ + Reg1 LINENUM first_guess = pch_first() + last_offset; + Reg2 LINENUM offset; + LINENUM pat_lines = pch_ptrn_lines(); + Reg3 LINENUM max_pos_offset = input_lines - first_guess + - pat_lines + 1; + Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + + pch_context(); + + if (!pat_lines) /* null range matches always */ + 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, Nulline, fuzz)) + return first_guess; + for (offset = 1; ; offset++) { + Reg5 bool check_after = (offset <= max_pos_offset); + Reg6 bool check_before = (offset <= max_neg_offset); + + if (check_after && patch_match(first_guess, offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("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) + say3("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 Nulline; + } +} + +/* We did not find the pattern, dump out the hunk so they can handle it. */ + +void +abort_hunk() +{ + Reg1 LINENUM i; + Reg2 LINENUM pat_end = pch_end(); + /* add in last_offset to guess the same as the previous successful hunk */ + LINENUM oldfirst = pch_first() + last_offset; + LINENUM newfirst = pch_newfirst() + last_offset; + LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; + LINENUM newlast = newfirst + pch_repl_lines() - 1; + char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : ""); + 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: + fatal1("fatal internal error in abort_hunk\n"); + } + } +} + +/* We found where to apply it (we hope), so do it. */ + +void +apply_hunk(where) +LINENUM where; +{ + Reg1 LINENUM old = 1; + Reg2 LINENUM lastline = pch_ptrn_lines(); + Reg3 LINENUM new = lastline+1; +#define OUTSIDE 0 +#define IN_IFNDEF 1 +#define IN_IFDEF 2 +#define IN_ELSE 3 + Reg4 int def_state = OUTSIDE; + Reg5 bool R_do_defines = do_defines; + Reg6 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); + if (R_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); + if (R_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)) { + say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", + pch_hunk_beg() + old, + pch_hunk_beg() + new); +#ifdef DEBUGGING + say3("oldchar = '%c', newchar = '%c'\n", + pch_char(old), pch_char(new)); +#endif + my_exit(1); + } + else if (pch_char(new) == '!') { + copy_till(where + old - 1); + if (R_do_defines) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + while (pch_char(old) == '!') { + if (R_do_defines) { + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + if (R_do_defines) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + while (pch_char(new) == '!') { + fputs(pfetch(new), ofp); + new++; + } + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + if (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + def_state = OUTSIDE; + } + } + } + if (new <= pat_end && pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_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 (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + } +} + +/* Open the new file. */ + +void +init_output(name) +char *name; +{ + ofp = fopen(name, "w"); + if (ofp == Nullfp) + pfatal2("can't create %s", name); +} + +/* Open a file to put hunks we can't locate. */ + +void +init_reject(name) +char *name; +{ + rejfp = fopen(name, "w"); + if (rejfp == Nullfp) + pfatal2("can't create %s", name); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +void +copy_till(lastline) +Reg1 LINENUM lastline; +{ + Reg2 LINENUM R_last_frozen_line = last_frozen_line; + + if (R_last_frozen_line > lastline) + fatal1("misordered hunks! output would be garbled\n"); + while (R_last_frozen_line < lastline) { + dump_line(++R_last_frozen_line); + } + last_frozen_line = R_last_frozen_line; +} + +/* Finish copying the input file to the output file. */ + +void +spew_output() +{ +#ifdef DEBUGGING + if (debug & 256) + say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line); +#endif + if (input_lines) + copy_till(input_lines); /* dump remainder of file */ + Fclose(ofp); + ofp = Nullfp; +} + +/* Copy one line from input to output. */ + +void +dump_line(line) +LINENUM line; +{ + Reg1 char *s; + Reg2 char R_newline = '\n'; + + /* Note: string is not null terminated. */ + for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ; +} + +/* Does the patch pattern match at line base+offset? */ + +bool +patch_match(base, offset, fuzz) +LINENUM base; +LINENUM offset; +LINENUM fuzz; +{ + Reg1 LINENUM pline = 1 + fuzz; + Reg2 LINENUM iline; + Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz; + + for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) { + if (canonicalize) { + if (!similar(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + else if (strnNE(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + return TRUE; +} + +/* Do two lines match with canonicalized white space? */ + +bool +similar(a,b,len) +Reg1 char *a; +Reg2 char *b; +Reg3 int len; +{ + while (len) { + if (isspace(*b)) { /* whitespace (or \n) to match? */ + if (!isspace(*a)) /* no corresponding whitespace? */ + return FALSE; + while (len && isspace(*b) && *b != '\n') + b++,len--; /* skip pattern whitespace */ + while (isspace(*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 */ +} + +/* Exit with cleanup. */ + +void +my_exit(status) +int status; +{ + Unlink(TMPINNAME); + if (!toutkeep) { + Unlink(TMPOUTNAME); + } + if (!trejkeep) { + Unlink(TMPREJNAME); + } + Unlink(TMPPATNAME); + exit(status); +} diff --git a/usr.bin/patch/patchlevel.h b/usr.bin/patch/patchlevel.h new file mode 100644 index 00000000000..3f5970c175c --- /dev/null +++ b/usr.bin/patch/patchlevel.h @@ -0,0 +1,3 @@ +/* $Id: patchlevel.h,v 1.1 1995/10/18 08:45:55 deraadt Exp $ */ + +#define PATCHLEVEL "12u8" diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c new file mode 100644 index 00000000000..98b93c6cfba --- /dev/null +++ b/usr.bin/patch/pch.c @@ -0,0 +1,1275 @@ +#ifndef lint +static char rcsid[] = "$Id: pch.c,v 1.1 1995/10/18 08:45:56 deraadt Exp $"; +#endif /* not lint */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "pch.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(char**); /* the text of the hunk */ +static short *p_len = Null(short*); /* length of each line */ +static char *p_char = Nullch; /* +, -, 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 */ + +/* Prepare to look for the next patch in the patch file. */ + +void +re_patch() +{ + p_first = Nulline; + p_newfirst = Nulline; + p_ptrn_lines = Nulline; + p_repl_lines = Nulline; + p_end = (LINENUM)-1; + p_max = Nulline; + p_indent = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file(filename) +char *filename; +{ + if (filename == Nullch || !*filename || strEQ(filename, "-")) { + pfp = fopen(TMPPATNAME, "w"); + if (pfp == Nullfp) + pfatal2("can't create %s", TMPPATNAME); + while (fgets(buf, sizeof buf, stdin) != Nullch) + fputs(buf, pfp); + Fclose(pfp); + filename = TMPPATNAME; + } + pfp = fopen(filename, "r"); + if (pfp == Nullfp) + pfatal2("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() +{ +#ifndef lint + if (p_line == Null(char**)) + p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); + if (p_len == Null(short*)) + p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); +#endif + if (p_char == Nullch) + p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); +} + +/* Enlarge the arrays containing the current hunk of patch. */ + +void +grow_hunkmax() +{ + hunkmax *= 2; + /* + * Note that on most systems, only the p_line array ever gets fresh memory + * since p_len can move into p_line's old space, and p_char can move into + * p_len's old space. Not on PDP-11's however. But it doesn't matter. + */ + assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); +#ifndef lint + p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); + p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); + p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); +#endif + if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) + return; + if (!using_plan_a) + fatal1("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() +{ + if (p_base != 0L && p_base >= p_filesize) { + if (verbose) + say1("done\n"); + return FALSE; + } + if (verbose) + say1("Hmm..."); + diff_type = intuit_diff_type(); + if (!diff_type) { + if (p_base != 0L) { + if (verbose) + say1(" Ignoring the trailing garbage.\ndone\n"); + } + else + say1(" I can't seem to find a patch in there anywhere.\n"); + return FALSE; + } + if (verbose) + say3(" %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) + say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); + skip_to(p_start,p_sline); + while (filearg[0] == Nullch) { + if (force || batch) { + say1("No file to patch. Skipping...\n"); + filearg[0] = savestr(bestguess); + return TRUE; + } + ask1("File to patch: "); + if (*buf != '\n') { + if (bestguess) + free(bestguess); + bestguess = savestr(buf); + filearg[0] = fetchname(buf, 0, FALSE); + } + if (filearg[0] == Nullch) { + ask1("No file found--skip this patch? [n] "); + if (*buf != 'y') { + continue; + } + if (verbose) + say1("Skipping patch...\n"); + filearg[0] = fetchname(bestguess, 0, TRUE); + skip_rest_of_patch = TRUE; + return TRUE; + } + } + return TRUE; +} + +/* Determine what kind of diff is in the remaining part of the patch file. */ + +int +intuit_diff_type() +{ + Reg4 long this_line = 0; + Reg5 long previous_line; + Reg6 long first_command_line = -1; + long fcl_line; + Reg7 bool last_line_was_command = FALSE; + Reg8 bool this_is_a_command = FALSE; + Reg9 bool stars_last_line = FALSE; + Reg10 bool stars_this_line = FALSE; + Reg3 int indent; + Reg1 char *s; + Reg2 char *t; + char *indtmp = Nullch; + char *oldtmp = Nullch; + char *newtmp = Nullch; + char *indname = Nullch; + char *oldname = Nullch; + char *newname = Nullch; + Reg11 int retval; + bool no_filearg = (filearg[0] == Nullch); + + ok_to_create_file = FALSE; + Fseek(pfp, p_base, 0); + 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, sizeof buf, pfp) == Nullch) { + 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(*t) || *t == ','; t++) ; + this_is_a_command = (isdigit(*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)) + oldtmp = savestr(s+4); + else if (strnEQ(s, "--- ", 4)) + newtmp = savestr(s+4); + else if (strnEQ(s, "+++ ", 4)) + oldtmp = savestr(s+4); /* pretend it is the old name */ + else if (strnEQ(s, "Index:", 6)) + indtmp = savestr(s+6); + else if (strnEQ(s, "Prereq:", 7)) { + for (t=s+7; isspace(*t); t++) ; + revision = savestr(t); + for (t=revision; *t && !isspace(*t); t++) ; + *t = '\0'; + if (!*revision) { + free(revision); + revision = Nullch; + } + } + 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 (!atol(s+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)) + 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 (no_filearg) { + if (indtmp != Nullch) + indname = fetchname(indtmp, strippath, ok_to_create_file); + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, ok_to_create_file); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, ok_to_create_file); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + filearg[0] = savestr(oldname); + else + filearg[0] = savestr(newname); + } + else if (oldname) + filearg[0] = savestr(oldname); + else if (newname) + filearg[0] = savestr(newname); + else if (indname) + filearg[0] = savestr(indname); + } + if (bestguess) { + free(bestguess); + bestguess = Nullch; + } + if (filearg[0] != Nullch) + bestguess = savestr(filearg[0]); + else if (indtmp != Nullch) + bestguess = fetchname(indtmp, strippath, TRUE); + else { + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, TRUE); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, TRUE); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + bestguess = savestr(oldname); + else + bestguess = savestr(newname); + } + else if (oldname) + bestguess = savestr(oldname); + else if (newname) + bestguess = savestr(newname); + } + if (indtmp != Nullch) + free(indtmp); + if (oldtmp != Nullch) + free(oldtmp); + if (newtmp != Nullch) + free(newtmp); + if (indname != Nullch) + free(indname); + if (oldname != Nullch) + free(oldname); + if (newname != Nullch) + free(newname); + return retval; +} + +/* Remember where this patch ends so we know where to start up again. */ + +void +next_intuit_at(file_pos,file_line) +long file_pos; +long file_line; +{ + p_base = file_pos; + p_bline = file_line; +} + +/* Basically a verbose fseek() to the actual diff listing. */ + +void +skip_to(file_pos,file_line) +long file_pos; +long file_line; +{ + char *ret; + + assert(p_base <= file_pos); + if (verbose && p_base < file_pos) { + Fseek(pfp, p_base, 0); + say1("The text leading up to this was:\n--------------------------\n"); + while (ftell(pfp) < file_pos) { + ret = fgets(buf, sizeof buf, pfp); + assert(ret != Nullch); + say2("|%s", buf); + } + say1("--------------------------\n"); + } + else + Fseek(pfp, file_pos, 0); + p_input_line = file_line - 1; +} + +/* Make this a function for better debugging. */ +static void +malformed () +{ + fatal3("malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ +} + +/* True if there is more of the current diff listing to process. */ + +bool +another_hunk() +{ + Reg1 char *s; + Reg8 char *ret; + Reg2 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--; + } + assert(p_end == -1); + p_efake = -1; + + p_max = hunkmax; /* gets reduced when --- found */ + if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + LINENUM repl_beginning = 0; /* index of --- line */ + Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ + Reg5 LINENUM fillsrc; /* index of first line to copy */ + Reg6 LINENUM filldst; /* index of first missing line */ + bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ + Reg9 bool repl_could_be_missing = TRUE; + /* no + or ! lines in this hunk */ + bool repl_missing = FALSE; /* we are now backtracking */ + long repl_backtrack_position = 0; + /* file pos of first repl line */ + LINENUM repl_patch_line; /* input line number for same */ + Reg7 LINENUM ptrn_copiable = 0; + /* # of copiable lines in ptrn */ + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || 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, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - p_end < 4) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal1("unexpected end of file in patch\n"); + } + } + p_end++; + assert(p_end < hunkmax); + p_char[p_end] = *buf; +#ifdef zilog + p_line[(short)p_end] = Nullch; +#else + p_line[p_end] = Nullch; +#endif + switch (*buf) { + case '*': + if (strnEQ(buf, "********", 8)) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + else + fatal2("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; + } + fatal3("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(*s); s++) ; + if (!*s) + malformed (); + if (strnEQ(s,"0,0",3)) + strcpy(s,s+2); + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*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; + } + p_max = p_ptrn_lines + 6; /* we need this much at least */ + 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; + } + fatal3( +"duplicate \"---\" at line %ld--check line numbers at line %ld\n", + p_input_line, p_hunk_beg + repl_beginning); + } + else { + fatal4( +"%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(*s); s++) ; + if (!*s) + malformed (); + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*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) + fatal4("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) + strcpy(buf+1," \n"); + if (!isspace(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; + } + 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(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) + fatal2("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, 0); + + /* 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) + say4("%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) + fatal2("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 + assert(fillsrc==p_end+1 || fillsrc==repl_beginning); + assert(filldst==p_end+1 || filldst==repl_beginning); + } + } + else if (diff_type == UNI_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + Reg4 LINENUM fillsrc; /* index of old lines */ + Reg5 LINENUM filldst; /* index of new lines */ + char ch; + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || 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(*s)) s++; + if (*s == ',') { + p_ptrn_lines = (LINENUM) atol(++s); + while (isdigit(*s)) s++; + } else + p_ptrn_lines = 1; + if (*s == ' ') s++; + if (*s != '+' || !*++s) + malformed (); + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + p_repl_lines = (LINENUM) atol(++s); + while (isdigit(*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(); + fillsrc = 1; + filldst = fillsrc + p_ptrn_lines; + p_end = filldst + p_repl_lines; + Sprintf(buf,"*** %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] = '*'; + Sprintf(buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1); + p_line[filldst] = savestr(buf); + if (out_of_mem) { + p_end = 0; + return FALSE; + } + p_char[filldst++] = '='; + p_context = 100; + context = 0; + p_hunk_beg = p_input_line + 1; + while (fillsrc <= p_ptrn_lines || filldst <= p_end) { + line_beginning = ftell(pfp); + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - filldst < 3) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + fatal1("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 (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return FALSE; + } + switch (ch) { + case '-': + if (fillsrc > p_ptrn_lines) { + free(s); + p_end = filldst-1; + malformed (); + } + p_char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = strlen(s); + break; + case '=': + ch = ' '; + /* FALL THROUGH */ + case ' ': + if (fillsrc > p_ptrn_lines) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + context++; + p_char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = strlen(s); + s = savestr(s); + if (out_of_mem) { + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return FALSE; + } + /* FALL THROUGH */ + case '+': + if (filldst > p_end) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + p_char[filldst] = ch; + p_line[filldst] = s; + p_len[filldst++] = strlen(s); + break; + default: + p_end = filldst; + malformed (); + } + if (ch != ' ' && context > 0) { + if (context < p_context) + p_context = context; + context = -1000; + } + }/* while */ + } + else { /* normal diff--fake it up */ + char hunk_type; + Reg3 int i; + LINENUM min, max; + long line_beginning = ftell(pfp); + + p_context = 0; + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || !isdigit(*buf)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_first = (LINENUM)atol(buf); + for (s=buf; isdigit(*s); s++) ; + if (*s == ',') { + p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; + while (isdigit(*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(*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) + fatal4("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; + Sprintf(buf, "*** %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, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '<') + fatal2("< 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 (hunk_type == 'c') { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '-') + fatal2("--- expected at line %ld of patch\n", p_input_line); + } + Sprintf(buf, "--- %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, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '>') + fatal2("> 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 (reverse) /* backwards patch? */ + if (!pch_swap()) + say1("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. */ + +char * +pgets(bf,sz,fp) +char *bf; +int sz; +FILE *fp; +{ + char *ret = fgets(bf, sz, fp); + Reg1 char *s; + Reg2 int indent = 0; + + if (p_indent && ret != Nullch) { + for (s=buf; + indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) { + if (*s == '\t') + indent += 8 - (indent % 7); + else + indent++; + } + if (buf != s) + Strcpy(buf, s); + } + return ret; +} + +/* Reverse the old and new portions of the current hunk. */ + +bool +pch_swap() +{ + char **tp_line; /* the text of the hunk */ + short *tp_len; /* length of each line */ + char *tp_char; /* +, -, and ! */ + Reg1 LINENUM i; + Reg2 LINENUM n; + bool blankline = FALSE; + Reg3 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(char**); /* force set_hunkmax to allocate again */ + p_len = Null(short*); + p_char = Nullch; + set_hunkmax(); + if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { +#ifndef lint + if (p_line == Null(char**)) + free((char*)p_line); + p_line = tp_line; + if (p_len == Null(short*)) + free((char*)p_len); + p_len = tp_len; +#endif + if (p_char == Nullch) + free((char*)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++; + } + assert(p_char[0] == '='); + p_char[0] = '*'; + for (s=p_line[0]; *s; s++) + if (*s == '-') + *s = '*'; + + /* now turn the old into the new */ + + assert(tp_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]; + } + assert(i == p_ptrn_lines + 1); + i = p_ptrn_lines; + p_ptrn_lines = p_repl_lines; + p_repl_lines = i; +#ifndef lint + if (tp_line == Null(char**)) + free((char*)tp_line); + if (tp_len == Null(short*)) + free((char*)tp_len); +#endif + if (tp_char == Nullch) + free((char*)tp_char); + return TRUE; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first() +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines() +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst() +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines() +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end() +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_context() +{ + return p_context; +} + +/* Return the length of a particular patch line. */ + +short +pch_line_len(line) +LINENUM line; +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char(line) +LINENUM line; +{ + return p_char[line]; +} + +/* Return a pointer to a particular patch line. */ + +char * +pfetch(line) +LINENUM line; +{ + return p_line[line]; +} + +/* Return where in the patch file this hunk began, for error messages. */ + +LINENUM +pch_hunk_beg() +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +void +do_ed_script() +{ + Reg1 char *t; + Reg2 long beginning_of_this_line; + Reg3 bool this_line_is_command = FALSE; + Reg4 FILE *pipefp; + + if (!skip_rest_of_patch) { + Unlink(TMPOUTNAME); + copy_file(filearg[0], TMPOUTNAME); + if (verbose) + Sprintf(buf, "/bin/ed %s", TMPOUTNAME); + else + Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); + pipefp = popen(buf, "w"); + } + for (;;) { + beginning_of_this_line = ftell(pfp); + if (pgets(buf, sizeof buf, pfp) == Nullch) { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + p_input_line++; + for (t=buf; isdigit(*t) || *t == ','; t++) ; + this_line_is_command = (isdigit(*buf) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (this_line_is_command) { + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (*t != 'd') { + while (pgets(buf, sizeof buf, pfp) != Nullch) { + p_input_line++; + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (strEQ(buf, ".\n")) + break; + } + } + } + else { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + } + if (skip_rest_of_patch) + return; + fprintf(pipefp, "w\n"); + fprintf(pipefp, "q\n"); + Fflush(pipefp); + Pclose(pipefp); + ignore_signals(); + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + set_signals(1); +} diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h new file mode 100644 index 00000000000..520e9c92bcf --- /dev/null +++ b/usr.bin/patch/pch.h @@ -0,0 +1,27 @@ +/* $Id: pch.h,v 1.1 1995/10/18 08:45:56 deraadt Exp $ */ + +EXT FILE *pfp INIT(Nullfp); /* patch file pointer */ + +void re_patch(); +void open_patch_file(); +void set_hunkmax(); +void grow_hunkmax(); +bool there_is_another_patch(); +int intuit_diff_type(); +void next_intuit_at(); +void skip_to(); +bool another_hunk(); +bool pch_swap(); +char *pfetch(); +short pch_line_len(); +LINENUM pch_first(); +LINENUM pch_ptrn_lines(); +LINENUM pch_newfirst(); +LINENUM pch_repl_lines(); +LINENUM pch_end(); +LINENUM pch_context(); +LINENUM pch_hunk_beg(); +char pch_char(); +char *pfetch(); +char *pgets(); +void do_ed_script(); diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c new file mode 100644 index 00000000000..6ae9e0fd5c7 --- /dev/null +++ b/usr.bin/patch/util.c @@ -0,0 +1,441 @@ +#ifndef lint +static char rcsid[] = "$Id: util.c,v 1.1 1995/10/18 08:45:56 deraadt Exp $"; +#endif /* not lint */ + +#include "EXTERN.h" +#include "common.h" +#include "INTERN.h" +#include "util.h" +#include "backupfile.h" + +void my_exit(); + +/* Rename a file, copying it if necessary. */ + +int +move_file(from,to) +char *from, *to; +{ + char bakname[512]; + Reg1 char *s; + Reg2 int i; + Reg3 int fromfd; + + /* to stdout? */ + + if (strEQ(to, "-")) { +#ifdef DEBUGGING + if (debug & 4) + say2("Moving %s to stdout.\n", from); +#endif + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(1, buf, i) != 1) + pfatal1("write failed"); + Close(fromfd); + return 0; + } + + if (origprae) { + Strcpy(bakname, origprae); + Strcat(bakname, to); + } else { +#ifndef NODIR + char *backupname = find_backup_file_name(to); + if (backupname == (char *) 0) + fatal1("out of memory\n"); + Strcpy(bakname, backupname); + free(backupname); +#else /* NODIR */ + Strcpy(bakname, to); + Strcat(bakname, simple_backup_suffix); +#endif /* NODIR */ + } + + if (stat(to, &filestat) == 0) { /* output file exists */ + dev_t to_device = filestat.st_dev; + ino_t to_inode = filestat.st_ino; + char *simplename = bakname; + + for (s=bakname; *s; s++) { + if (*s == '/') + simplename = s+1; + } + /* 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 && + to_device == filestat.st_dev && to_inode == filestat.st_ino) { + /* Skip initial non-lowercase chars. */ + for (s=simplename; *s && !islower(*s); s++) ; + if (*s) + *s = toupper(*s); + else + Strcpy(simplename, simplename+1); + } + while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", to, bakname); +#endif + if (link(to, bakname) < 0) { + /* Maybe `to' is a symlink into a different file system. + Copying replaces the symlink with a file; using rename + would be better. */ + Reg4 int tofd; + Reg5 int bakfd; + + bakfd = creat(bakname, 0666); + if (bakfd < 0) { + say4("Can't backup %s, output is in %s: %s\n", to, from, + strerror(errno)); + return -1; + } + tofd = open(to, 0); + if (tofd < 0) + pfatal2("internal error, can't open %s", to); + while ((i=read(tofd, buf, sizeof buf)) > 0) + if (write(bakfd, buf, i) != i) + pfatal1("write failed"); + Close(tofd); + Close(bakfd); + } + while (unlink(to) >= 0) ; + } +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", from, to); +#endif + if (link(from, to) < 0) { /* different file system? */ + Reg4 int tofd; + + tofd = creat(to, 0666); + if (tofd < 0) { + say4("Can't create %s, output is in %s: %s\n", + to, from, strerror(errno)); + return -1; + } + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + pfatal1("write failed"); + Close(fromfd); + Close(tofd); + } + Unlink(from); + return 0; +} + +/* Copy a file. */ + +void +copy_file(from,to) +char *from, *to; +{ + Reg3 int tofd; + Reg2 int fromfd; + Reg1 int i; + + tofd = creat(to, 0666); + if (tofd < 0) + pfatal2("can't create %s", to); + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + pfatal2("write to %s failed", to); + Close(fromfd); + Close(tofd); +} + +/* Allocate a unique area for a string. */ + +char * +savestr(s) +Reg1 char *s; +{ + Reg3 char *rv; + Reg2 char *t; + + if (!s) + s = "Oops"; + t = s; + while (*t++); + rv = malloc((MEM) (t - s)); + if (rv == Nullch) { + if (using_plan_a) + out_of_mem = TRUE; + else + fatal1("out of memory\n"); + } + else { + t = rv; + while (*t++ = *s++); + } + return rv; +} + +#if defined(lint) && defined(CANVARARG) + +/*VARARGS ARGSUSED*/ +say(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +fatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +pfatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +ask(pat) char *pat; { ; } + +#else + +/* Vanilla terminal output (buffered). */ + +void +say(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + fprintf(stderr, pat, arg1, arg2, arg3); + Fflush(stderr); +} + +/* Terminal output, pun intended. */ + +void /* very void */ +fatal(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + fprintf(stderr, "patch: **** "); + fprintf(stderr, pat, arg1, arg2, arg3); + my_exit(1); +} + +/* Say something from patch, something from the system, then silence . . . */ + +void /* very void */ +pfatal(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + int errnum = errno; + + fprintf(stderr, "patch: **** "); + fprintf(stderr, pat, arg1, arg2, arg3); + fprintf(stderr, ": %s\n", strerror(errnum)); + my_exit(1); +} + +/* Get a response from the user, somehow or other. */ + +void +ask(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + int ttyfd; + int r; + bool tty2 = isatty(2); + + Sprintf(buf, pat, arg1, arg2, arg3); + Fflush(stderr); + write(2, buf, strlen(buf)); + if (tty2) { /* might be redirected to a file */ + r = read(2, buf, sizeof buf); + } + else if (isatty(1)) { /* this may be new file output */ + Fflush(stdout); + write(1, buf, strlen(buf)); + r = read(1, buf, sizeof buf); + } + else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { + /* might be deleted or unwriteable */ + write(ttyfd, buf, strlen(buf)); + r = read(ttyfd, buf, sizeof buf); + Close(ttyfd); + } + else if (isatty(0)) { /* this is probably patch input */ + Fflush(stdin); + write(0, buf, strlen(buf)); + r = read(0, buf, sizeof buf); + } + else { /* no terminal at all--default it */ + buf[0] = '\n'; + r = 1; + } + if (r <= 0) + buf[0] = 0; + else + buf[r] = '\0'; + if (!tty2) + say1(buf); +} +#endif /* lint */ + +/* How to handle certain events when not in a critical region. */ + +void +set_signals(reset) +int reset; +{ +#ifndef lint +#ifdef VOIDSIG + static void (*hupval)(),(*intval)(); +#else + static int (*hupval)(),(*intval)(); +#endif + + if (!reset) { + hupval = signal(SIGHUP, SIG_IGN); + if (hupval != SIG_IGN) +#ifdef VOIDSIG + hupval = my_exit; +#else + hupval = (int(*)())my_exit; +#endif + intval = signal(SIGINT, SIG_IGN); + if (intval != SIG_IGN) +#ifdef VOIDSIG + intval = my_exit; +#else + intval = (int(*)())my_exit; +#endif + } + Signal(SIGHUP, hupval); + Signal(SIGINT, intval); +#endif +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#ifndef lint + Signal(SIGHUP, SIG_IGN); + Signal(SIGINT, SIG_IGN); +#endif +} + +/* Make sure we'll have the directories to create a file. + If `striplast' is TRUE, ignore the last element of `filename'. */ + +void +makedirs(filename,striplast) +Reg1 char *filename; +bool striplast; +{ + char tmpbuf[256]; + Reg2 char *s = tmpbuf; + char *dirv[20]; /* Point to the NULs between elements. */ + Reg3 int i; + Reg4 int dirvp = 0; /* Number of finished entries in dirv. */ + + /* Copy `filename' into `tmpbuf' with a NUL instead of a slash + between the directories. */ + while (*filename) { + if (*filename == '/') { + filename++; + dirv[dirvp++] = s; + *s++ = '\0'; + } + else { + *s++ = *filename++; + } + } + *s = '\0'; + dirv[dirvp] = s; + if (striplast) + dirvp--; + if (dirvp < 0) + return; + + strcpy(buf, "mkdir"); + s = buf; + for (i=0; i<=dirvp; i++) { + struct stat sbuf; + + if (stat(tmpbuf, &sbuf) && errno == ENOENT) { + while (*s) s++; + *s++ = ' '; + strcpy(s, tmpbuf); + } + *dirv[i] = '/'; + } + if (s != buf) + system(buf); +} + +/* Make filenames more reasonable. */ + +char * +fetchname(at,strip_leading,assume_exists) +char *at; +int strip_leading; +int assume_exists; +{ + char *fullname; + char *name; + Reg1 char *t; + char tmpbuf[200]; + int sleading = strip_leading; + + if (!at) + return Nullch; + while (isspace(*at)) + at++; +#ifdef DEBUGGING + if (debug & 128) + say4("fetchname %s %d %d\n",at,strip_leading,assume_exists); +#endif + if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ + return Nullch; /* against /dev/null. */ + name = fullname = t = savestr(at); + + /* Strip off up to `sleading' leading slashes and null terminate. */ + for (; *t && !isspace(*t); t++) + if (*t == '/') + 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); + + if (stat(name, &filestat) && !assume_exists) { + char *filebase = basename(name); + int pathlen = filebase - name; + + /* Put any leading path into `tmpbuf'. */ + strncpy(tmpbuf, name, pathlen); + +#define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0) + if ( try("RCS/%s%s", filebase, RCSSUFFIX) + || try("RCS/%s" , filebase, 0) + || try( "%s%s", filebase, RCSSUFFIX) + || try("SCCS/%s%s", SCCSPREFIX, filebase) + || try( "%s%s", SCCSPREFIX, filebase)) + return name; + free(name); + name = Nullch; + } + + return name; +} diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h new file mode 100644 index 00000000000..615e127365c --- /dev/null +++ b/usr.bin/patch/util.h @@ -0,0 +1,82 @@ +/* $Id: util.h,v 1.1 1995/10/18 08:45:56 deraadt Exp $ */ + +/* and for those machine that can't handle a variable argument list */ + +#ifdef CANVARARG + +#define say1 say +#define say2 say +#define say3 say +#define say4 say +#define ask1 ask +#define ask2 ask +#define ask3 ask +#define ask4 ask +#define fatal1 fatal +#define fatal2 fatal +#define fatal3 fatal +#define fatal4 fatal +#define pfatal1 pfatal +#define pfatal2 pfatal +#define pfatal3 pfatal +#define pfatal4 pfatal + +#else /* hope they allow multi-line macro actual arguments */ + +#ifdef lint + +#define say1(a) say(a, 0, 0, 0) +#define say2(a,b) say(a, (b)==(b), 0, 0) +#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0) +#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d)) +#define ask1(a) ask(a, 0, 0, 0) +#define ask2(a,b) ask(a, (b)==(b), 0, 0) +#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0) +#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d)) +#define fatal1(a) fatal(a, 0, 0, 0) +#define fatal2(a,b) fatal(a, (b)==(b), 0, 0) +#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0) +#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d)) +#define pfatal1(a) pfatal(a, 0, 0, 0) +#define pfatal2(a,b) pfatal(a, (b)==(b), 0, 0) +#define pfatal3(a,b,c) pfatal(a, (b)==(b), (c)==(c), 0) +#define pfatal4(a,b,c,d) pfatal(a, (b)==(b), (c)==(c), (d)==(d)) + +#else /* lint */ + /* if this doesn't work, try defining CANVARARG above */ +#define say1(a) say(a, Nullch, Nullch, Nullch) +#define say2(a,b) say(a, b, Nullch, Nullch) +#define say3(a,b,c) say(a, b, c, Nullch) +#define say4 say +#define ask1(a) ask(a, Nullch, Nullch, Nullch) +#define ask2(a,b) ask(a, b, Nullch, Nullch) +#define ask3(a,b,c) ask(a, b, c, Nullch) +#define ask4 ask +#define fatal1(a) fatal(a, Nullch, Nullch, Nullch) +#define fatal2(a,b) fatal(a, b, Nullch, Nullch) +#define fatal3(a,b,c) fatal(a, b, c, Nullch) +#define fatal4 fatal +#define pfatal1(a) pfatal(a, Nullch, Nullch, Nullch) +#define pfatal2(a,b) pfatal(a, b, Nullch, Nullch) +#define pfatal3(a,b,c) pfatal(a, b, c, Nullch) +#define pfatal4 pfatal + +#endif /* lint */ + +/* if neither of the above work, join all multi-line macro calls. */ +#endif + +EXT char serrbuf[BUFSIZ]; /* buffer for stderr */ + +char *fetchname(); +int move_file(); +void copy_file(); +void say(); +void fatal(); +void pfatal(); +void ask(); +char *savestr(); +void set_signals(); +void ignore_signals(); +void makedirs(); +char *basename(); diff --git a/usr.bin/patch/version.c b/usr.bin/patch/version.c new file mode 100644 index 00000000000..0687bc0f5a8 --- /dev/null +++ b/usr.bin/patch/version.c @@ -0,0 +1,21 @@ +#ifndef lint +static char rcsid[] = "$Id: version.c,v 1.1 1995/10/18 08:45:56 deraadt Exp $"; +#endif /* not lint */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "patchlevel.h" +#include "version.h" + +void my_exit(); + +/* Print out the version number and die. */ + +void +version() +{ + fprintf(stderr, "Patch version 2.0, patch level %s\n", PATCHLEVEL); + my_exit(0); +} diff --git a/usr.bin/patch/version.h b/usr.bin/patch/version.h new file mode 100644 index 00000000000..2dff743a295 --- /dev/null +++ b/usr.bin/patch/version.h @@ -0,0 +1,3 @@ +/* $Id: version.h,v 1.1 1995/10/18 08:45:56 deraadt Exp $ */ + +void version(); |