View | Details | Raw Unified | Return to bug 99173 | Differences between
and this patch

Collapse All | Expand All

(-)usr.bin/Makefile (+1 lines)
Lines 146-151 Link Here
146
	pagesize \
146
	pagesize \
147
	passwd \
147
	passwd \
148
	paste \
148
	paste \
149
	patch \
149
	pathchk \
150
	pathchk \
150
	pkill \
151
	pkill \
151
	pr \
152
	pr \
(-)gnu/usr.bin/patch/Makefile (-1 / +6 lines)
Lines 1-7 Link Here
1
# $FreeBSD: src/gnu/usr.bin/patch/Makefile,v 1.8 2004/02/17 01:24:45 ache Exp $
1
# $FreeBSD: src/gnu/usr.bin/patch/Makefile,v 1.8 2004/02/17 01:24:45 ache Exp $
2
2
3
PROG= 	patch
3
PROG= 	gpatch
4
SRCS=   backupfile.c inp.c patch.c pch.c util.c version.c
4
SRCS=   backupfile.c inp.c patch.c pch.c util.c version.c
5
CFLAGS+=-DHAVE_CONFIG_H
5
CFLAGS+=-DHAVE_CONFIG_H
6
6
7
# "Rename" patch.1 to gpatch.1
8
CLEANFILES= gpatch.1
9
gpatch.1: patch.1
10
	cat ${.CURDIR}/patch.1 > ${.TARGET}
11
7
.include <bsd.prog.mk>
12
.include <bsd.prog.mk>
(-)usr.bin/patch/Makefile (+12 lines)
Added Link Here
1
#	$OpenBSD: Makefile,v 1.3 2003/07/18 02:00:09 deraadt Exp $
2
#	$DragonFly$
3
4
PROG=	patch
5
SRCS=	patch.c pch.c inp.c util.c backupfile.c
6
WARNS?=	6
7
8
#.if defined(BOOTSTRAPPING)
9
CFLAGS+= -DBOOTSTRAPPING
10
#.endif
11
12
.include <bsd.prog.mk>
(-)usr.bin/patch/README (+126 lines)
Added Link Here
1
$DragonFly$
2
#######################################################################
3
4
this version modified to fit in with the 386bsd release.
5
this isn't gnu software, so we're not obligated to give
6
you the original sources -- if you want them, get them
7
from prep.ai.mit.edu:pub/gnu/patch-2.0.12u8.tar.z
8
9
					-- cgd
10
11
#######################################################################
12
13
14
This version of patch contains modifications made by the Free Software
15
Foundation, summarized in the file ChangeLog.  Primarily they are to
16
support the unified context diff format that GNU diff can produce, and
17
to support making GNU Emacs-style backup files.  They also include
18
fixes for some bugs.
19
20
There are two GNU variants of patch: this one, which retains Larry
21
Wall's interactive Configure script and has patchlevels starting with
22
`12u'; and another one that has a GNU-style non-interactive configure
23
script and accepts long-named options, and has patchlevels starting
24
with `12g'.  Unlike the 12g variant, the 12u variant contains no
25
copylefted code, for the paranoid.  The two variants are otherwise the
26
same.  They should be available from the same places.
27
28
The FSF is distributing this version of patch independently because as
29
of this writing, Larry Wall has not released a new version of patch
30
since mid-1988.  I have heard that he has been too busy working on
31
other things, like Perl.
32
33
Here is a wish list of some projects to improve patch:
34
35
1.  Correctly handle files and patchfiles that contain NUL characters.
36
This is hard to do straightforwardly; it would be less work to
37
adopt a kind of escape encoding internally.
38
Let ESC be a "control prefix".  ESC @ stands for NUL.  ESC [ stands for ESC.
39
You need to crunch this when reading input (replace fgets),
40
and when writing the output file (replace fputs),
41
but otherwise everything can go along as it does now.
42
Be careful to handle reject files correctly;
43
I think they are currently created using `write', not `fputs'.
44
45
2.  Correctly handle patches produced by GNU diff for files that do
46
not end with a newline.
47
48
Please send bug reports for this version of patch to
49
bug-gnu-utils@prep.ai.mit.edu as well as to Larry Wall (lwall@netlabs.com).
50
 --djm@gnu.ai.mit.edu (David MacKenzie)
51
52
			Patch Kit, Version 2.0
53
54
		    Copyright (c) 1988, Larry Wall
55
56
Redistribution and use in source and binary forms, with or without
57
modification, are permitted provided that the following condition
58
is met:
59
 1. Redistributions of source code must retain the above copyright
60
    notice, this condition and the following disclaimer.
61
62
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
63
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65
ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
66
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72
SUCH DAMAGE.
73
--------------------------------------------------------------------------
74
75
Please read all the directions below before you proceed any further, and
76
then follow them carefully.  Failure to do so may void your warranty. :-)
77
78
After you have unpacked your kit, you should have all the files listed
79
in MANIFEST.
80
81
Installation
82
83
1)  Run Configure.  This will figure out various things about your system.
84
    Some things Configure will figure out for itself, other things it will
85
    ask you about.  It will then proceed to make config.h, config.sh, and
86
    Makefile.
87
88
    You might possibly have to trim # comments from the front of Configure
89
    if your sh doesn't handle them, but all other # comments will be taken
90
    care of.
91
92
    If you don't have sh, you'll have to rip the prototype of config.h out
93
    of Configure and generate the defines by hand.
94
95
2)  Glance through config.h to make sure system dependencies are correct.
96
    Most of them should have been taken care of by running the
97
    Configure script. 
98
99
    If you have any additional changes to make to the C definitions, they
100
    can be done in the Makefile, or in config.h.  Bear in mind that they may
101
    get undone next time you run Configure.
102
103
3)  make
104
105
    This will attempt to make patch in the current directory.
106
107
4)  make install
108
109
    This will put patch into a public directory (normally /usr/local/bin).
110
    It will also try to put the man pages in a reasonable place.  It will not
111
    nroff the man page, however.
112
113
5)  Read the manual entry before running patch.
114
115
6)  IMPORTANT!  Help save the world!  Communicate any problems and
116
    suggested patches to me, lwall@netlabs.com (Larry Wall),
117
    so we can keep the world in sync.  If you have a problem, there's
118
    someone else out there who either has had or will have the same problem.
119
120
    If possible, send in patches such that the patch program will apply them.
121
    Context diffs are the best, then normal diffs.  Don't send ed scripts--
122
    I've probably changed my copy since the version you have.
123
124
    Watch for patch patches in comp.sources.bugs.  Patches will generally be
125
    in a form usable by the patch program.  Your current patch level
126
    is shown in patchlevel.h.
(-)usr.bin/patch/backupfile.c (+248 lines)
Added Link Here
1
/*
2
 * $OpenBSD: backupfile.c,v 1.18 2004/08/05 21:47:24 deraadt Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * backupfile.c -- make Emacs style backup file names Copyright (C) 1990 Free
8
 * Software Foundation, Inc.
9
 * 
10
 * This program is free software; you can redistribute it and/or modify it
11
 * without restriction.
12
 * 
13
 * This program is distributed in the hope that it will be useful, but WITHOUT
14
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15
 * FITNESS FOR A PARTICULAR PURPOSE.
16
 */
17
18
/*
19
 * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
20
 */
21
22
#include <ctype.h>
23
#include <dirent.h>
24
#include <libgen.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <unistd.h>
29
30
#include "backupfile.h"
31
32
33
#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c))
34
35
/* Which type of backup file names are generated. */
36
enum backup_type backup_type = none;
37
38
/*
39
 * The extension added to file names to produce a simple (as opposed to
40
 * numbered) backup file name.
41
 */
42
const char	*simple_backup_suffix = "~";
43
44
static char	*concat(const char *, const char *);
45
static char	*make_version_name(const char *, int);
46
static int	max_backup_version(const char *, const char *);
47
static int	version_number(const char *, const char *, int);
48
static int	argmatch(const char *, const char **);
49
static void	invalid_arg(const char *, const char *, int);
50
51
/*
52
 * Return the name of the new backup file for file FILE, allocated with
53
 * malloc.  Return 0 if out of memory. FILE must not end with a '/' unless it
54
 * is the root directory. Do not call this function if backup_type == none.
55
 */
56
char *
57
find_backup_file_name(const char *file)
58
{
59
	char	*dir, *base_versions, *tmp_file;
60
	int	highest_backup;
61
62
	if (backup_type == simple)
63
		return concat(file, simple_backup_suffix);
64
	tmp_file = strdup(file);
65
	if (tmp_file == NULL)
66
		return NULL;
67
	base_versions = concat(basename(tmp_file), ".~");
68
	free(tmp_file);
69
	if (base_versions == NULL)
70
		return NULL;
71
	tmp_file = strdup(file);
72
	if (tmp_file == NULL) {
73
		free(base_versions);
74
		return NULL;
75
	}
76
	dir = dirname(tmp_file);
77
	if (dir == NULL) {
78
		free(base_versions);
79
		free(tmp_file);
80
		return NULL;
81
	}
82
	highest_backup = max_backup_version(base_versions, dir);
83
	free(base_versions);
84
	free(tmp_file);
85
	if (backup_type == numbered_existing && highest_backup == 0)
86
		return concat(file, simple_backup_suffix);
87
	return make_version_name(file, highest_backup + 1);
88
}
89
90
/*
91
 * Return the number of the highest-numbered backup file for file FILE in
92
 * directory DIR.  If there are no numbered backups of FILE in DIR, or an
93
 * error occurs reading DIR, return 0. FILE should already have ".~" appended
94
 * to it.
95
 */
96
static int
97
max_backup_version(const char *file, const char *dir)
98
{
99
	DIR	*dirp;
100
	struct dirent	*dp;
101
	int	highest_version, this_version, file_name_length;
102
103
	dirp = opendir(dir);
104
	if (dirp == NULL)
105
		return 0;
106
107
	highest_version = 0;
108
	file_name_length = strlen(file);
109
110
	while ((dp = readdir(dirp)) != NULL) {
111
		if (dp->d_namlen <= file_name_length)
112
			continue;
113
114
		this_version = version_number(file, dp->d_name, file_name_length);
115
		if (this_version > highest_version)
116
			highest_version = this_version;
117
	}
118
	closedir(dirp);
119
	return highest_version;
120
}
121
122
/*
123
 * Return a string, allocated with malloc, containing "FILE.~VERSION~".
124
 * Return 0 if out of memory.
125
 */
126
static char *
127
make_version_name(const char *file, int version)
128
{
129
	char	*backup_name;
130
131
	if (asprintf(&backup_name, "%s.~%d~", file, version) == -1)
132
		return NULL;
133
	return backup_name;
134
}
135
136
/*
137
 * If BACKUP is a numbered backup of BASE, return its version number;
138
 * otherwise return 0.  BASE_LENGTH is the length of BASE. BASE should
139
 * already have ".~" appended to it.
140
 */
141
static int
142
version_number(const char *base, const char *backup, int base_length)
143
{
144
	int		version;
145
	const char	*p;
146
147
	version = 0;
148
	if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
149
		for (p = &backup[base_length]; ISDIGIT(*p); ++p)
150
			version = version * 10 + *p - '0';
151
		if (p[0] != '~' || p[1])
152
			version = 0;
153
	}
154
	return version;
155
}
156
157
/*
158
 * Return the newly-allocated concatenation of STR1 and STR2. If out of
159
 * memory, return 0.
160
 */
161
static char  *
162
concat(const char *str1, const char *str2)
163
{
164
	char	*newstr;
165
166
	if (asprintf(&newstr, "%s%s", str1, str2) == -1)
167
		return NULL;
168
	return newstr;
169
}
170
171
/*
172
 * If ARG is an unambiguous match for an element of the null-terminated array
173
 * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it
174
 * does not match any element or -2 if it is ambiguous (is a prefix of more
175
 * than one element).
176
 */
177
static int
178
argmatch(const char *arg, const char **optlist)
179
{
180
	int	i;	/* Temporary index in OPTLIST. */
181
	size_t	arglen;	/* Length of ARG. */
182
	int	matchind = -1;	/* Index of first nonexact match. */
183
	int	ambiguous = 0;	/* If nonzero, multiple nonexact match(es). */
184
185
	arglen = strlen(arg);
186
187
	/* Test all elements for either exact match or abbreviated matches.  */
188
	for (i = 0; optlist[i]; i++) {
189
		if (!strncmp(optlist[i], arg, arglen)) {
190
			if (strlen(optlist[i]) == arglen)
191
				/* Exact match found.  */
192
				return i;
193
			else if (matchind == -1)
194
				/* First nonexact match found.  */
195
				matchind = i;
196
			else
197
				/* Second nonexact match found.  */
198
				ambiguous = 1;
199
		}
200
	}
201
	if (ambiguous)
202
		return -2;
203
	else
204
		return matchind;
205
}
206
207
/*
208
 * Error reporting for argmatch. KIND is a description of the type of entity
209
 * that was being matched. VALUE is the invalid value that was given. PROBLEM
210
 * is the return value from argmatch.
211
 */
212
static void
213
invalid_arg(const char *kind, const char *value, int problem)
214
{
215
	fprintf(stderr, "patch: ");
216
	if (problem == -1)
217
		fprintf(stderr, "invalid");
218
	else			/* Assume -2. */
219
		fprintf(stderr, "ambiguous");
220
	fprintf(stderr, " %s `%s'\n", kind, value);
221
}
222
223
static const char *backup_args[] = {
224
	"never", "simple", "nil", "existing", "t", "numbered", 0
225
};
226
227
static enum backup_type backup_types[] = {
228
	simple, simple, numbered_existing,
229
	numbered_existing, numbered, numbered
230
};
231
232
/*
233
 * Return the type of backup indicated by VERSION. Unique abbreviations are
234
 * accepted.
235
 */
236
enum backup_type
237
get_version(const char *version)
238
{
239
	int	i;
240
241
	if (version == NULL || *version == '\0')
242
		return numbered_existing;
243
	i = argmatch(version, backup_args);
244
	if (i >= 0)
245
		return backup_types[i];
246
	invalid_arg("version control type", version, i);
247
	exit(2);
248
}
(-)usr.bin/patch/backupfile.h (+41 lines)
Added Link Here
1
/*
2
 * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * backupfile.h -- declarations for making Emacs style backup file names
8
 * Copyright (C) 1990 Free Software Foundation, Inc.
9
 * 
10
 * This program is free software; you can redistribute it and/or modify it
11
 * without restriction.
12
 * 
13
 * This program is distributed in the hope that it will be useful, but WITHOUT
14
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15
 * FITNESS FOR A PARTICULAR PURPOSE.
16
 * 
17
 */
18
19
/* When to make backup files. */
20
enum backup_type {
21
	/* Never make backups. */
22
	none,
23
24
	/* Make simple backups of every file. */
25
	simple,
26
27
	/*
28
	 * Make numbered backups of files that already have numbered backups,
29
	 * and simple backups of the others.
30
	 */
31
	numbered_existing,
32
33
	/* Make numbered backups of every file. */
34
	numbered
35
};
36
37
extern enum backup_type backup_type;
38
extern const char	*simple_backup_suffix;
39
40
char		*find_backup_file_name(const char *file);
41
enum backup_type get_version(const char *vers);
(-)usr.bin/patch/common.h (+125 lines)
Added Link Here
1
/*
2
 * $OpenBSD: common.h,v 1.25 2003/10/31 20:20:45 millert Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#include <stdbool.h>
33
34
/*
35
 * SIZE_MAX: older BSD compatibility for buildworld bootstrap mode only
36
 */
37
#if defined(BOOTSTRAPPING) && !defined(SIZE_MAX)
38
#define SIZE_MAX	0x7fffffff
39
#endif
40
41
#define DEBUGGING
42
43
/* constants */
44
45
#define MAXHUNKSIZE 100000	/* is this enough lines? */
46
#define INITHUNKMAX 125		/* initial dynamic allocation size */
47
#define MAXLINELEN 8192
48
#define BUFFERSIZE 1024
49
50
#define SCCSPREFIX "s."
51
#define GET "get -e %s"
52
#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
53
54
#define RCSSUFFIX ",v"
55
#define CHECKOUT "co -l %s"
56
#define RCSDIFF "rcsdiff %s > /dev/null"
57
58
#define ORIGEXT ".orig"
59
#define REJEXT ".rej"
60
61
/* handy definitions */
62
63
#define strNE(s1,s2) (strcmp(s1, s2))
64
#define strEQ(s1,s2) (!strcmp(s1, s2))
65
#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
66
#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
67
68
/* typedefs */
69
70
typedef long    LINENUM;	/* must be signed */
71
72
/* globals */
73
74
extern int	filemode;
75
76
extern char	buf[MAXLINELEN];/* general purpose buffer */
77
extern size_t	buf_len;
78
79
extern bool	using_plan_a;	/* try to keep everything in memory */
80
extern bool	out_of_mem;	/* ran out of memory in plan a */
81
82
#define MAXFILEC 2
83
84
extern char	*filearg[MAXFILEC];
85
extern bool	ok_to_create_file;
86
extern char	*outname;
87
extern char	*origprae;
88
89
extern char	*TMPOUTNAME;
90
extern char	*TMPINNAME;
91
extern char	*TMPREJNAME;
92
extern char	*TMPPATNAME;
93
extern bool	toutkeep;
94
extern bool	trejkeep;
95
96
#ifdef DEBUGGING
97
extern int	debug;
98
#endif
99
100
extern bool	force;
101
extern bool	batch;
102
extern bool	verbose;
103
extern bool	reverse;
104
extern bool	noreverse;
105
extern bool	skip_rest_of_patch;
106
extern int	strippath;
107
extern bool	canonicalize;
108
/* TRUE if -C was specified on command line.  */
109
extern bool	check_only;
110
extern bool	warn_on_invalid_line;
111
extern bool	last_line_missing_eol;
112
113
114
#define CONTEXT_DIFF 1
115
#define NORMAL_DIFF 2
116
#define ED_DIFF 3
117
#define NEW_CONTEXT_DIFF 4
118
#define UNI_DIFF 5
119
120
extern int	diff_type;
121
extern char	*revision;	/* prerequisite revision, if any */
122
extern LINENUM	input_lines;	/* how long is input file in lines */
123
124
extern int	posix;
125
(-)usr.bin/patch/inp.c (+482 lines)
Added Link Here
1
/*
2
 * $OpenBSD: inp.c,v 1.32 2004/08/05 21:47:24 deraadt Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#include <sys/types.h>
33
#include <sys/file.h>
34
#include <sys/stat.h>
35
#include <sys/mman.h>
36
37
#include <ctype.h>
38
#include <libgen.h>
39
#include <limits.h>
40
#include <stddef.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#include "common.h"
47
#include "util.h"
48
#include "pch.h"
49
#include "inp.h"
50
51
52
/* Input-file-with-indexable-lines abstract type */
53
54
static off_t	i_size;		/* size of the input file */
55
static char	*i_womp;	/* plan a buffer for entire file */
56
static char	**i_ptr;	/* pointers to lines in i_womp */
57
static char	empty_line[] = { '\0' };
58
59
static int	tifd = -1;	/* plan b virtual string array */
60
static char	*tibuf[2];	/* plan b buffers */
61
static LINENUM	tiline[2] = {-1, -1};	/* 1st line in each buffer */
62
static LINENUM	lines_per_buf;	/* how many lines per buffer */
63
static int	tireclen;	/* length of records in tmp file */
64
65
static bool	rev_in_string(const char *);
66
static bool	reallocate_lines(size_t *);
67
68
/* returns false if insufficient memory */
69
static bool	plan_a(const char *);
70
71
static void	plan_b(const char *);
72
73
/* New patch--prepare to edit another file. */
74
75
void
76
re_input(void)
77
{
78
	if (using_plan_a) {
79
		i_size = 0;
80
		free(i_ptr);
81
		i_ptr = NULL;
82
		if (i_womp != NULL) {
83
			munmap(i_womp, i_size);
84
			i_womp = NULL;
85
		}
86
	} else {
87
		using_plan_a = true;	/* maybe the next one is smaller */
88
		close(tifd);
89
		tifd = -1;
90
		free(tibuf[0]);
91
		free(tibuf[1]);
92
		tibuf[0] = tibuf[1] = NULL;
93
		tiline[0] = tiline[1] = -1;
94
		tireclen = 0;
95
	}
96
}
97
98
/* Constuct the line index, somehow or other. */
99
100
void
101
scan_input(const char *filename)
102
{
103
	if (!plan_a(filename))
104
		plan_b(filename);
105
	if (verbose) {
106
		say("Patching file %s using Plan %s...\n", filename,
107
		    (using_plan_a ? "A" : "B"));
108
	}
109
}
110
111
static bool
112
reallocate_lines(size_t *lines_allocated)
113
{
114
	char	**p;
115
	size_t	new_size;
116
117
	new_size = *lines_allocated * 3 / 2;
118
	p = realloc(i_ptr, (new_size + 2) * sizeof(char *));
119
	if (p == NULL) {	/* shucks, it was a near thing */
120
		munmap(i_womp, i_size);
121
		i_womp = NULL;
122
		free(i_ptr);
123
		i_ptr = NULL;
124
		*lines_allocated = 0;
125
		return false;
126
	}
127
	*lines_allocated = new_size;
128
	i_ptr = p;
129
	return true;
130
}
131
132
/* Try keeping everything in memory. */
133
134
static bool
135
plan_a(const char *filename)
136
{
137
	int		ifd, statfailed;
138
	char		*p, *s, lbuf[MAXLINELEN];
139
	LINENUM		iline;
140
	struct stat	filestat;
141
	off_t		i;
142
	ptrdiff_t	sz;
143
	size_t		lines_allocated;
144
145
#ifdef DEBUGGING
146
	if (debug & 8)
147
		return false;
148
#endif
149
150
	if (filename == NULL || *filename == '\0')
151
		return false;
152
153
	statfailed = stat(filename, &filestat);
154
	if (statfailed && ok_to_create_file) {
155
		if (verbose)
156
			say("(Creating file %s...)\n", filename);
157
158
		/*
159
		 * in check_patch case, we still display `Creating file' even
160
		 * though we're not. The rule is that -C should be as similar
161
		 * to normal patch behavior as possible
162
		 */
163
		if (check_only)
164
			return true;
165
		makedirs(filename, true);
166
		close(creat(filename, 0666));
167
		statfailed = stat(filename, &filestat);
168
	}
169
	if (statfailed && check_only)
170
		fatal("%s not found, -C mode, can't probe further\n", filename);
171
	/* For nonexistent or read-only files, look for RCS or SCCS versions.  */
172
	if (statfailed ||
173
	    /* No one can write to it.  */
174
	    (filestat.st_mode & 0222) == 0 ||
175
	    /* I can't write to it.  */
176
	    ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
177
		const char	*cs = NULL, *filebase, *filedir;
178
		struct stat	cstat;
179
		char *tmp_filename1, *tmp_filename2;
180
181
		tmp_filename1 = strdup(filename);
182
		tmp_filename2 = strdup(filename);
183
		if (tmp_filename1 == NULL || tmp_filename2 == NULL)
184
			fatal("strdupping filename");
185
		filebase = basename(tmp_filename1);
186
		filedir = dirname(tmp_filename2);
187
188
		/* Leave room in lbuf for the diff command.  */
189
		s = lbuf + 20;
190
191
#define try(f, a1, a2, a3) \
192
	(snprintf(s, sizeof lbuf - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
193
194
		if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
195
		    try("%s/RCS/%s%s", filedir, filebase, "") ||
196
		    try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
197
			snprintf(buf, buf_len, CHECKOUT, filename);
198
			snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
199
			cs = "RCS";
200
		} else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
201
		    try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
202
			snprintf(buf, buf_len, GET, s);
203
			snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
204
			cs = "SCCS";
205
		} else if (statfailed)
206
			fatal("can't find %s\n", filename);
207
208
		free(tmp_filename1);
209
		free(tmp_filename2);
210
211
		/*
212
		 * else we can't write to it but it's not under a version
213
		 * control system, so just proceed.
214
		 */
215
		if (cs) {
216
			if (!statfailed) {
217
				if ((filestat.st_mode & 0222) != 0)
218
					/* The owner can write to it.  */
219
					fatal("file %s seems to be locked "
220
					    "by somebody else under %s\n",
221
					    filename, cs);
222
				/*
223
				 * It might be checked out unlocked.  See if
224
				 * it's safe to check out the default version
225
				 * locked.
226
				 */
227
				if (verbose)
228
					say("Comparing file %s to default "
229
					    "%s version...\n",
230
					    filename, cs);
231
				if (system(lbuf))
232
					fatal("can't check out file %s: "
233
					    "differs from default %s version\n",
234
					    filename, cs);
235
			}
236
			if (verbose)
237
				say("Checking out file %s from %s...\n",
238
				    filename, cs);
239
			if (system(buf) || stat(filename, &filestat))
240
				fatal("can't check out file %s from %s\n",
241
				    filename, cs);
242
		}
243
	}
244
	filemode = filestat.st_mode;
245
	if (!S_ISREG(filemode))
246
		fatal("%s is not a normal file--can't patch\n", filename);
247
	i_size = filestat.st_size;
248
	if (out_of_mem) {
249
		set_hunkmax();	/* make sure dynamic arrays are allocated */
250
		out_of_mem = false;
251
		return false;	/* force plan b because plan a bombed */
252
	}
253
	if (i_size > SIZE_MAX) {
254
		say("block too large to mmap\n");
255
		return false;
256
	}
257
	if ((ifd = open(filename, O_RDONLY)) < 0)
258
		pfatal("can't open file %s", filename);
259
260
	i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
261
	if (i_womp == MAP_FAILED) {
262
		perror("mmap failed");
263
		i_womp = NULL;
264
		close(ifd);
265
		return false;
266
	}
267
268
	close(ifd);
269
	if (i_size)
270
		madvise(i_womp, i_size, MADV_SEQUENTIAL);
271
272
	/* estimate the number of lines */
273
	lines_allocated = i_size / 25;
274
	if (lines_allocated < 100)
275
		lines_allocated = 100;
276
277
	if (!reallocate_lines(&lines_allocated))
278
		return false;
279
280
	/* now scan the buffer and build pointer array */
281
	iline = 1;
282
	i_ptr[iline] = i_womp;
283
	/* test for NUL too, to maintain the behavior of the original code */
284
	for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
285
		if (*s == '\n') {
286
			if (iline == (LINENUM)lines_allocated) {
287
				if (!reallocate_lines(&lines_allocated))
288
					return false;
289
			}
290
			/* these are NOT NUL terminated */
291
			i_ptr[++iline] = s + 1;
292
		}
293
	}
294
	/* if the last line contains no EOL, append one */
295
	if (i_size > 0 && i_womp[i_size - 1] != '\n') {
296
		last_line_missing_eol = true;
297
		/* fix last line */
298
		sz = s - i_ptr[iline];
299
		p = malloc(sz + 1);
300
		if (p == NULL) {
301
			free(i_ptr);
302
			i_ptr = NULL;
303
			munmap(i_womp, i_size);
304
			i_womp = NULL;
305
			return false;
306
		}
307
308
		memcpy(p, i_ptr[iline], sz);
309
		p[sz] = '\n';
310
		i_ptr[iline] = p;
311
		/* count the extra line and make it point to some valid mem */
312
		i_ptr[++iline] = empty_line;
313
	} else
314
		last_line_missing_eol = false;
315
316
	input_lines = iline - 1;
317
318
	/* now check for revision, if any */
319
320
	if (revision != NULL) {
321
		if (!rev_in_string(i_womp)) {
322
			if (force) {
323
				if (verbose)
324
					say("Warning: this file doesn't appear "
325
					    "to be the %s version--patching anyway.\n",
326
					    revision);
327
			} else if (batch) {
328
				fatal("this file doesn't appear to be the "
329
				    "%s version--aborting.\n",
330
				    revision);
331
			} else {
332
				ask("This file doesn't appear to be the "
333
				    "%s version--patch anyway? [n] ",
334
				    revision);
335
				if (*buf != 'y')
336
					fatal("aborted\n");
337
			}
338
		} else if (verbose)
339
			say("Good.  This file appears to be the %s version.\n",
340
			    revision);
341
	}
342
	return true;		/* plan a will work */
343
}
344
345
/* Keep (virtually) nothing in memory. */
346
347
static void
348
plan_b(const char *filename)
349
{
350
	FILE	*ifp;
351
	int	i = 0, j, maxlen = 1;
352
	char	*p;
353
	bool	found_revision = (revision == NULL);
354
355
	using_plan_a = false;
356
	if ((ifp = fopen(filename, "r")) == NULL)
357
		pfatal("can't open file %s", filename);
358
	unlink(TMPINNAME);
359
	if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
360
		pfatal("can't open file %s", TMPINNAME);
361
	while (fgets(buf, buf_len, ifp) != NULL) {
362
		if (revision != NULL && !found_revision && rev_in_string(buf))
363
			found_revision = true;
364
		if ((i = strlen(buf)) > maxlen)
365
			maxlen = i;	/* find longest line */
366
	}
367
	last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
368
	if (last_line_missing_eol && maxlen == i)
369
		maxlen++;
370
371
	if (revision != NULL) {
372
		if (!found_revision) {
373
			if (force) {
374
				if (verbose)
375
					say("Warning: this file doesn't appear "
376
					    "to be the %s version--patching anyway.\n",
377
					    revision);
378
			} else if (batch) {
379
				fatal("this file doesn't appear to be the "
380
				    "%s version--aborting.\n",
381
				    revision);
382
			} else {
383
				ask("This file doesn't appear to be the %s "
384
				    "version--patch anyway? [n] ",
385
				    revision);
386
				if (*buf != 'y')
387
					fatal("aborted\n");
388
			}
389
		} else if (verbose)
390
			say("Good.  This file appears to be the %s version.\n",
391
			    revision);
392
	}
393
	fseek(ifp, 0L, SEEK_SET);	/* rewind file */
394
	lines_per_buf = BUFFERSIZE / maxlen;
395
	tireclen = maxlen;
396
	tibuf[0] = malloc(BUFFERSIZE + 1);
397
	if (tibuf[0] == NULL)
398
		fatal("out of memory\n");
399
	tibuf[1] = malloc(BUFFERSIZE + 1);
400
	if (tibuf[1] == NULL)
401
		fatal("out of memory\n");
402
	for (i = 1;; i++) {
403
		p = tibuf[0] + maxlen * (i % lines_per_buf);
404
		if (i % lines_per_buf == 0)	/* new block */
405
			if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
406
				pfatal("can't write temp file");
407
		if (fgets(p, maxlen + 1, ifp) == NULL) {
408
			input_lines = i - 1;
409
			if (i % lines_per_buf != 0)
410
				if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
411
					pfatal("can't write temp file");
412
			break;
413
		}
414
		j = strlen(p);
415
		/* These are '\n' terminated strings, so no need to add a NUL */
416
		if (j == 0 || p[j - 1] != '\n')
417
			p[j] = '\n';
418
	}
419
	fclose(ifp);
420
	close(tifd);
421
	if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
422
		pfatal("can't reopen file %s", TMPINNAME);
423
}
424
425
/*
426
 * Fetch a line from the input file, \n terminated, not necessarily \0.
427
 */
428
char *
429
ifetch(LINENUM line, int whichbuf)
430
{
431
	if (line < 1 || line > input_lines) {
432
		if (warn_on_invalid_line) {
433
			say("No such line %ld in input file, ignoring\n", line);
434
			warn_on_invalid_line = false;
435
		}
436
		return NULL;
437
	}
438
	if (using_plan_a)
439
		return i_ptr[line];
440
	else {
441
		LINENUM	offline = line % lines_per_buf;
442
		LINENUM	baseline = line - offline;
443
444
		if (tiline[0] == baseline)
445
			whichbuf = 0;
446
		else if (tiline[1] == baseline)
447
			whichbuf = 1;
448
		else {
449
			tiline[whichbuf] = baseline;
450
451
			lseek(tifd, (off_t) (baseline / lines_per_buf *
452
			    BUFFERSIZE), SEEK_SET);
453
454
			if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
455
				pfatal("error reading tmp file %s", TMPINNAME);
456
		}
457
		return tibuf[whichbuf] + (tireclen * offline);
458
	}
459
}
460
461
/*
462
 * True if the string argument contains the revision number we want.
463
 */
464
static bool
465
rev_in_string(const char *string)
466
{
467
	const char	*s;
468
	int		patlen;
469
470
	if (revision == NULL)
471
		return true;
472
	patlen = strlen(revision);
473
	if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
474
		return true;
475
	for (s = string; *s; s++) {
476
		if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) &&
477
		    isspace((unsigned char)s[patlen + 1])) {
478
			return true;
479
		}
480
	}
481
	return false;
482
}
(-)usr.bin/patch/inp.h (+34 lines)
Added Link Here
1
/*
2
 * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
void		re_input(void);
33
void		scan_input(const char *);
34
char		*ifetch(LINENUM, int);
(-)usr.bin/patch/patch.1 (+643 lines)
Added Link Here
1
.\"	$OpenBSD: patch.1,v 1.17 2003/10/31 20:20:45 millert Exp $
2
.\"	$DragonFly$
3
.\"
4
.\" Copyright 1986, Larry Wall
5
.\"
6
.\" Redistribution and use in source and binary forms, with or without
7
.\" modification, are permitted provided that the following condition
8
.\" is met:
9
.\"  1. Redistributions of source code must retain the above copyright
10
.\"     notice, this condition and the following disclaimer.
11
.\"
12
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
.\" SUCH DAMAGE.
23
.\"
24
.Dd July 23, 2003
25
.Dt PATCH 1
26
.Os
27
.Sh NAME
28
.Nm patch
29
.Nd apply a diff file to an original
30
.Sh SYNOPSIS
31
.Nm patch
32
.Op Cm options
33
.Op Ar origfile Op Ar patchfile
34
.Nm patch
35
.Pf \*(Lt Ar patchfile
36
.Sh DESCRIPTION
37
.Nm
38
will take a patch file containing any of the four forms of difference
39
listing produced by the
40
.Xr diff 1
41
program and apply those differences to an original file,
42
producing a patched version.
43
If
44
.Ar patchfile
45
is omitted, or is a hyphen, the patch will be read from the standard input.
46
.Pp
47
.Nm
48
will attempt to determine the type of the diff listing, unless over-ruled by a
49
.Fl c ,
50
.Fl e ,
51
.Fl n ,
52
or
53
.Fl u
54
option.
55
Context diffs (old-style, new-style, and unified) and
56
normal diffs are applied directly by the
57
.Nm
58
program itself, whereas ed diffs are simply fed to the
59
.Xr ed 1
60
editor via a pipe.
61
.Pp
62
If the
63
.Ar patchfile
64
contains more than one patch,
65
.Nm
66
will try to apply each of them as if they came from separate patch files.
67
This means, among other things, that it is assumed that the name of the file
68
to patch must be determined for each diff listing, and that the garbage before
69
each diff listing will be examined for interesting things such as file names
70
and revision level (see the section on
71
.Sx Filename Determination
72
below).
73
.Pp
74
The options are as follows:
75
.Bl -tag -width Ds
76
.It Fl b , Fl Fl backup
77
Save a backup copy of the file before it is modified.
78
By default the original file is saved with a backup extension of
79
.Qq .orig
80
unless the file already has a numbered backup, in which case a numbered
81
backup is made.
82
This is equivalent to specifying
83
.Qo Fl V Ar existing Qc .
84
This option is currently the default but that will change in a future release.
85
.It Fl B , Fl Fl prefix
86
Causes the next argument to be interpreted as a prefix to the backup file
87
name.
88
If this argument is specified, any argument to
89
.Fl z
90
will be ignored.
91
.It Fl c , Fl Fl context
92
Forces
93
.Nm
94
to interpret the patch file as a context diff.
95
.It Fl C , Fl Fl check
96
Checks that the patch would apply cleanly, but does not modify anything.
97
.It Fl d , Fl Fl directory
98
Causes
99
.Nm
100
to interpret the next argument as a directory, and
101
.Xr cd 1
102
to it before doing anything else.
103
.It Fl D , Fl Fl ifdef
104
Causes
105
.Nm
106
to use the
107
.Qq #ifdef...#endif
108
construct to mark changes.
109
The argument following will be used as the differentiating symbol.
110
Note that, unlike the C compiler, there must be a space between the
111
.Fl D
112
and the argument.
113
.It Fl e , Fl Fl ed
114
Forces
115
.Nm
116
to interpret the patch file as an
117
.Xr ed 1
118
script.
119
.It Fl E , Fl Fl remove-empty-files
120
Causes
121
.Nm
122
to remove output files that are empty after the patches have been applied.
123
This option is useful when applying patches that create or remove files.
124
.It Fl f , Fl Fl force
125
Forces
126
.Nm
127
to assume that the user knows exactly what he or she is doing, and to not
128
ask any questions.
129
It assumes the following:
130
skip patches for which a file to patch can't be found;
131
patch files even though they have the wrong version for the
132
.Qq Prereq:
133
line in the patch;
134
and assume that patches are not reversed even if they look like they are.
135
This option does not suppress commentary; use
136
.Fl s
137
for that.
138
.It Xo
139
.Fl F Ns Aq Ar number ,
140
.Fl Fl fuzz Aq Ar number
141
.Xc
142
Sets the maximum fuzz factor.
143
This option only applies to context diffs, and causes
144
.Nm
145
to ignore up to that many lines in looking for places to install a hunk.
146
Note that a larger fuzz factor increases the odds of a faulty patch.
147
The default fuzz factor is 2, and it may not be set to more than
148
the number of lines of context in the context diff, ordinarily 3.
149
.It Fl i , Fl Fl input
150
Causes the next argument to be interpreted as the input file name
151
(i.e. a patchfile).
152
This option may be specified multiple times.
153
.It Fl l , Fl Fl ignore-whitespace
154
Causes the pattern matching to be done loosely, in case the tabs and
155
spaces have been munged in your input file.
156
Any sequence of whitespace in the pattern line will match any sequence
157
in the input file.
158
Normal characters must still match exactly.
159
Each line of the context must still match a line in the input file.
160
.It Fl n , Fl Fl normal
161
Forces
162
.Nm
163
to interpret the patch file as a normal diff.
164
.It Fl N , Fl Fl forward
165
Causes
166
.Nm
167
to ignore patches that it thinks are reversed or already applied.
168
See also
169
.Fl R .
170
.It Fl o , Fl Fl output
171
Causes the next argument to be interpreted as the output file name.
172
.It Xo
173
.Fl p Ns Aq Ar number ,
174
.Fl Fl strip Aq Ar number
175
.Xc
176
Sets the pathname strip count,
177
which controls how pathnames found in the patch file are treated,
178
in case you keep your files in a different directory than the person who sent
179
out the patch.
180
The strip count specifies how many slashes are to be stripped from
181
the front of the pathname.
182
(Any intervening directory names also go away.)
183
For example, supposing the file name in the patch file was
184
.Pa /u/howard/src/blurfl/blurfl.c :
185
.Pp
186
Setting
187
.Fl p Ns Ar 0
188
gives the entire pathname unmodified.
189
.Pp
190
.Fl p Ns Ar 1
191
gives
192
.Pp
193
.D1 Pa u/howard/src/blurfl/blurfl.c
194
.Pp
195
without the leading slash.
196
.Pp
197
.Fl p Ns Ar 4
198
gives
199
.Pp
200
.D1 Pa blurfl/blurfl.c
201
.Pp
202
Not specifying
203
.Fl p
204
at all just gives you
205
.Pa blurfl.c ,
206
unless all of the directories in the leading path
207
.Pq Pa u/howard/src/blurfl
208
exist and that path is relative,
209
in which case you get the entire pathname unmodified.
210
Whatever you end up with is looked for either in the current directory,
211
or the directory specified by the
212
.Fl d
213
option.
214
.It Fl r , Fl Fl reject-file
215
Causes the next argument to be interpreted as the reject file name.
216
.It Fl R , Fl Fl reverse
217
Tells
218
.Nm
219
that this patch was created with the old and new files swapped.
220
(Yes, I'm afraid that does happen occasionally, human nature being what it
221
is.)
222
.Nm
223
will attempt to swap each hunk around before applying it.
224
Rejects will come out in the swapped format.
225
The
226
.Fl R
227
option will not work with ed diff scripts because there is too little
228
information to reconstruct the reverse operation.
229
.Pp
230
If the first hunk of a patch fails,
231
.Nm
232
will reverse the hunk to see if it can be applied that way.
233
If it can, you will be asked if you want to have the
234
.Fl R
235
option set.
236
If it can't, the patch will continue to be applied normally.
237
(Note: this method cannot detect a reversed patch if it is a normal diff
238
and if the first command is an append (i.e. it should have been a delete)
239
since appends always succeed, due to the fact that a null context will match
240
anywhere.
241
Luckily, most patches add or change lines rather than delete them, so most
242
reversed normal diffs will begin with a delete, which will fail, triggering
243
the heuristic.)
244
.It Xo
245
.Fl s , Fl Fl quiet ,
246
.Fl Fl silent
247
.Xc
248
Makes
249
.Nm
250
do its work silently, unless an error occurs.
251
.It Fl t , Fl Fl batch
252
Similar to
253
.Fl f ,
254
in that it suppresses questions, but makes some different assumptions:
255
skip patches for which a file to patch can't be found (the same as
256
.Fl f ) ;
257
skip patches for which the file has the wrong version for the
258
.Qq Prereq:
259
line in the patch;
260
and assume that patches are reversed if they look like they are.
261
.It Fl u , Fl Fl unified
262
Forces
263
.Nm
264
to interpret the patch file as a unified context diff (a unidiff).
265
.It Fl v , Fl Fl version
266
Causes
267
.Nm
268
to print out its revision header and patch level.
269
.It Fl V , Fl Fl version-control
270
Causes the next argument to be interpreted as a method for creating
271
backup file names.
272
The type of backups made can also be given in the
273
.Ev PATCH_VERSION_CONTROL
274
or
275
.Ev VERSION_CONTROL
276
environment variables, which are overridden by this option.
277
The
278
.Fl B
279
option overrides this option, causing the prefix to always be used for
280
making backup file names.
281
The values of the
282
.Ev PATCH_VERSION_CONTROL
283
and
284
.Ev VERSION_CONTROL
285
environment variables and the argument to the
286
.Fl V
287
option are like the GNU Emacs
288
.Dq version-control
289
variable; they also recognize synonyms that are more descriptive.
290
The valid values are (unique abbreviations are accepted):
291
.Bl -tag -width Ds -offset indent
292
.It t , numbered
293
Always make numbered backups.
294
.It nil , existing
295
Make numbered backups of files that already have them,
296
simple backups of the others.
297
.It never , simple
298
Always make simple backups.
299
.El
300
.It Xo
301
.Fl x Ns Aq Ar number ,
302
.Fl Fl debug Aq Ar number
303
.Xc
304
Sets internal debugging flags, and is of interest only to
305
.Nm
306
patchers.
307
.It Fl z , Fl Fl suffix
308
Causes the next argument to be interpreted as the backup extension, to be
309
used in place of
310
.Qq .orig .
311
.It Fl Fl posix
312
Enables strict
313
.St -p1003.2
314
conformance, specifically:
315
.Bl -enum
316
.It
317
Backup files are not created unless the
318
.Fl b
319
option is specified.
320
.It
321
If unspecified, the file name used is the first of the old, new and
322
index files that exists.
323
.El
324
.El
325
.Ss Patch Application
326
.Nm
327
will try to skip any leading garbage, apply the diff,
328
and then skip any trailing garbage.
329
Thus you could feed an article or message containing a
330
diff listing to
331
.Nm patch ,
332
and it should work.
333
If the entire diff is indented by a consistent amount,
334
this will be taken into account.
335
.Pp
336
With context diffs, and to a lesser extent with normal diffs,
337
.Nm
338
can detect when the line numbers mentioned in the patch are incorrect,
339
and will attempt to find the correct place to apply each hunk of the patch.
340
As a first guess, it takes the line number mentioned for the hunk, plus or
341
minus any offset used in applying the previous hunk.
342
If that is not the correct place,
343
.Nm
344
will scan both forwards and backwards for a set of lines matching the context
345
given in the hunk.
346
First
347
.Nm
348
looks for a place where all lines of the context match.
349
If no such place is found, and it's a context diff, and the maximum fuzz factor
350
is set to 1 or more, then another scan takes place ignoring the first and last
351
line of context.
352
If that fails, and the maximum fuzz factor is set to 2 or more,
353
the first two and last two lines of context are ignored,
354
and another scan is made.
355
.Pq The default maximum fuzz factor is 2.
356
.Pp
357
If
358
.Nm
359
cannot find a place to install that hunk of the patch, it will put the hunk
360
out to a reject file, which normally is the name of the output file plus
361
.Qq .rej .
362
(Note that the rejected hunk will come out in context diff form whether the
363
input patch was a context diff or a normal diff.
364
If the input was a normal diff, many of the contexts will simply be null.)
365
The line numbers on the hunks in the reject file may be different than
366
in the patch file: they reflect the approximate location patch thinks the
367
failed hunks belong in the new file rather than the old one.
368
.Pp
369
As each hunk is completed, you will be told whether the hunk succeeded or
370
failed, and which line (in the new file)
371
.Nm
372
thought the hunk should go on.
373
If this is different from the line number specified in the diff,
374
you will be told the offset.
375
A single large offset MAY be an indication that a hunk was installed in the
376
wrong place.
377
You will also be told if a fuzz factor was used to make the match, in which
378
case you should also be slightly suspicious.
379
.Ss Filename Determination
380
If no original file is specified on the command line,
381
.Nm
382
will try to figure out from the leading garbage what the name of the file
383
to edit is.
384
When checking a prospective file name, pathname components are stripped
385
as specified by the
386
.Fl p
387
option and the file's existence and writability are checked relative
388
to the current working directory (or the directory specified by the
389
.Fl d
390
option).
391
.Pp
392
If the diff is a context or unified diff,
393
.Nm
394
is able to determine the old and new file names from the diff header.
395
For context diffs, the
396
.Dq old
397
file is specified in the line beginning with
398
.Qq ***
399
and the
400
.Dq new
401
file is specified in the line beginning with
402
.Qq --- .
403
For a unified diff, the
404
.Dq old
405
file is specified in the line beginning with
406
.Qq ---
407
and the
408
.Dq new
409
file is specified in the line beginning with
410
.Qq +++ .
411
If there is an
412
.Qq Index:
413
line in the leading garbage (regardless of the diff type),
414
.Nm
415
will use the file name from that line as the
416
.Dq index
417
file.
418
.Pp
419
.Nm
420
will choose the file name by performing the following steps, with the first
421
match used:
422
.Bl -enum
423
.It
424
If
425
.Nm
426
is operating in strict
427
.St -p1003.2
428
mode, the first of the
429
.Dq old ,
430
.Dq new
431
and
432
.Dq index
433
file names that exist is used.
434
Otherwise,
435
.Nm
436
will examine either the
437
.Dq old
438
and
439
.Dq new
440
file names or, for a non-context diff, the
441
.Dq index
442
file name, and choose the file name with the fewest path components,
443
the shortest basename, and the shortest total file name length (in that order).
444
.It
445
If no file exists,
446
.Nm
447
checks for the existence of the files in an SCCS or RCS directory
448
(using the appropriate prefix or suffix) using the criteria specified
449
above.
450
If found,
451
.Nm
452
will attempt to get or check out the file.
453
.It
454
If no suitable file was found to patch, the patch file is a context or
455
unified diff, and the old file was zero length, the new file name is
456
created and used.
457
.It
458
If the file name still cannot be determined,
459
.Nm
460
will prompt the user for the file name to use.
461
.El
462
.Pp
463
Additionally, if the leading garbage contains a
464
.Qq Prereq:\ \&
465
line,
466
.Nm
467
will take the first word from the prerequisites line (normally a version
468
number) and check the input file to see if that word can be found.
469
If not,
470
.Nm
471
will ask for confirmation before proceeding.
472
.Pp
473
The upshot of all this is that you should be able to say, while in a news
474
interface, the following:
475
.Pp
476
.Dl | patch -d /usr/src/local/blurfl
477
.Pp
478
and patch a file in the blurfl directory directly from the article containing
479
the patch.
480
.Ss Backup Files
481
By default, the patched version is put in place of the original, with
482
the original file backed up to the same name with the extension
483
.Qq .orig ,
484
or as specified by the
485
.Fl B ,
486
.Fl V ,
487
or
488
.Fl z
489
options.
490
The extension used for making backup files may also be specified in the
491
.Ev SIMPLE_BACKUP_SUFFIX
492
environment variable, which is overridden by the options above.
493
.Pp
494
If the backup file is a symbolic or hard link to the original file,
495
.Nm
496
creates a new backup file name by changing the first lowercase letter
497
in the last component of the file's name into uppercase.
498
If there are no more lowercase letters in the name,
499
it removes the first character from the name.
500
It repeats this process until it comes up with a
501
backup file that does not already exist or is not linked to the original file.
502
.Pp
503
You may also specify where you want the output to go with the
504
.Fl o
505
option; if that file already exists, it is backed up first.
506
.Ss Notes For Patch Senders
507
There are several things you should bear in mind if you are going to
508
be sending out patches:
509
.Pp
510
First, you can save people a lot of grief by keeping a
511
.Pa patchlevel.h
512
file which is patched to increment the patch level as the first diff in the
513
patch file you send out.
514
If you put a
515
.Qq Prereq:
516
line in with the patch, it won't let them apply
517
patches out of order without some warning.
518
.Pp
519
Second, make sure you've specified the file names right, either in a
520
context diff header, or with an
521
.Qq Index:
522
line.
523
If you are patching something in a subdirectory, be sure to tell the patch
524
user to specify a
525
.Fl p
526
option as needed.
527
.Pp
528
Third, you can create a file by sending out a diff that compares a
529
null file to the file you want to create.
530
This will only work if the file you want to create doesn't exist already in
531
the target directory.
532
.Pp
533
Fourth, take care not to send out reversed patches, since it makes people wonder
534
whether they already applied the patch.
535
.Pp
536
Fifth, while you may be able to get away with putting 582 diff listings into
537
one file, it is probably wiser to group related patches into separate files in
538
case something goes haywire.
539
.Sh ENVIRONMENT
540
.Bl -tag -width "PATCH_VERSION_CONTROL" -compact
541
.It Ev POSIXLY_CORRECT
542
When set,
543
.Nm
544
behaves as if the
545
.Fl Fl posix
546
option has been specified.
547
.It Ev SIMPLE_BACKUP_SUFFIX
548
Extension to use for backup file names instead of
549
.Qq .orig .
550
.It Ev TMPDIR
551
Directory to put temporary files in; default is
552
.Pa /tmp .
553
.It Ev PATCH_VERSION_CONTROL
554
Selects when numbered backup files are made.
555
.It Ev VERSION_CONTROL
556
Same as
557
.Ev PATCH_VERSION_CONTROL .
558
.El
559
.Sh FILES
560
.Bl -tag -width "$TMPDIR/patch*" -compact
561
.It Pa $TMPDIR/patch*
562
.Nm
563
temporary files
564
.It Pa /dev/tty
565
used to read input when
566
.Nm
567
prompts the user
568
.El
569
.Sh DIAGNOSTICS
570
Too many to list here, but generally indicative that
571
.Nm
572
couldn't parse your patch file.
573
.Pp
574
The message
575
.Qq Hmm...
576
indicates that there is unprocessed text in the patch file and that
577
.Nm
578
is attempting to intuit whether there is a patch in that text and, if so,
579
what kind of patch it is.
580
.Pp
581
The
582
.Nm
583
utility exits with one of the following values:
584
.Pp
585
.Bl -tag -width Ds -compact -offset indent
586
.It \&0
587
Successful completion.
588
.It \&1
589
One or more lines were written to a reject file.
590
.It \*[Gt]\&1
591
An error occurred.
592
.El
593
.Pp
594
When applying a set of patches in a loop it behooves you to check this
595
exit status so you don't apply a later patch to a partially patched file.
596
.Sh SEE ALSO
597
.Xr diff 1
598
.Sh AUTHORS
599
.An Larry Wall
600
with many other contributors.
601
.Sh CAVEATS
602
.Nm
603
cannot tell if the line numbers are off in an ed script, and can only detect
604
bad line numbers in a normal diff when it finds a
605
.Qq change
606
or a
607
.Qq delete
608
command.
609
A context diff using fuzz factor 3 may have the same problem.
610
Until a suitable interactive interface is added, you should probably do
611
a context diff in these cases to see if the changes made sense.
612
Of course, compiling without errors is a pretty good indication that the patch
613
worked, but not always.
614
.Pp
615
.Nm
616
usually produces the correct results, even when it has to do a lot of
617
guessing.
618
However, the results are guaranteed to be correct only when the patch is
619
applied to exactly the same version of the file that the patch was
620
generated from.
621
.Sh BUGS
622
Could be smarter about partial matches, excessively deviant offsets and
623
swapped code, but that would take an extra pass.
624
.Pp
625
Check patch mode
626
.Pq Fl C
627
will fail if you try to check several patches in succession that build on
628
each other.
629
The entire
630
.Nm
631
code would have to be restructured to keep temporary files around so that it
632
can handle this situation.
633
.Pp
634
If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
635
#endif),
636
.Nm
637
is incapable of patching both versions, and, if it works at all, will likely
638
patch the wrong one, and tell you that it succeeded to boot.
639
.Pp
640
If you apply a patch you've already applied,
641
.Nm
642
will think it is a reversed patch, and offer to un-apply the patch.
643
This could be construed as a feature.
(-)usr.bin/patch/patch.c (+1053 lines)
Added Link Here
1
/*
2
 * $OpenBSD: patch.c,v 1.43 2004/11/19 20:08:11 otto Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
35
#include <ctype.h>
36
#include <getopt.h>
37
#include <limits.h>
38
#include <stdio.h>
39
#include <string.h>
40
#include <stdlib.h>
41
#include <unistd.h>
42
43
#include "common.h"
44
#include "util.h"
45
#include "pch.h"
46
#include "inp.h"
47
#include "backupfile.h"
48
#include "pathnames.h"
49
50
int		filemode = 0644;
51
52
char		buf[MAXLINELEN];	/* general purpose buffer */
53
size_t		buf_len = sizeof(buf);
54
55
bool		using_plan_a = true;	/* try to keep everything in memory */
56
bool		out_of_mem = false;	/* ran out of memory in plan a */
57
58
#define MAXFILEC 2
59
60
char		*filearg[MAXFILEC];
61
bool		ok_to_create_file = false;
62
char		*outname = NULL;
63
char		*origprae = NULL;
64
char		*TMPOUTNAME;
65
char		*TMPINNAME;
66
char		*TMPREJNAME;
67
char		*TMPPATNAME;
68
bool		toutkeep = false;
69
bool		trejkeep = false;
70
bool		warn_on_invalid_line;
71
bool		last_line_missing_eol;
72
73
#ifdef DEBUGGING
74
int		debug = 0;
75
#endif
76
77
bool		force = false;
78
bool		batch = false;
79
bool		verbose = true;
80
bool		reverse = false;
81
bool		noreverse = false;
82
bool		skip_rest_of_patch = false;
83
int		strippath = 957;
84
bool		canonicalize = false;
85
bool		check_only = false;
86
int		diff_type = 0;
87
char		*revision = NULL;	/* prerequisite revision, if any */
88
LINENUM		input_lines = 0;	/* how long is input file in lines */
89
int		posix = 0;		/* strict POSIX mode? */
90
91
static void	reinitialize_almost_everything(void);
92
static void	get_some_switches(void);
93
static LINENUM	locate_hunk(LINENUM);
94
static void	abort_context_hunk(void);
95
static void	rej_line(int, LINENUM);
96
static void	abort_hunk(void);
97
static void	apply_hunk(LINENUM);
98
static void	init_output(const char *);
99
static void	init_reject(const char *);
100
static void	copy_till(LINENUM, bool);
101
static void	spew_output(void);
102
static void	dump_line(LINENUM, bool);
103
static bool	patch_match(LINENUM, LINENUM, LINENUM);
104
static bool	similar(const char *, const char *, int);
105
static void	usage(void);
106
107
/* true if -E was specified on command line.  */
108
static bool	remove_empty_files = false;
109
110
/* true if -R was specified on command line.  */
111
static bool	reverse_flag_specified = false;
112
113
/* buffer holding the name of the rejected patch file. */
114
static char	rejname[NAME_MAX + 1];
115
116
/* buffer for stderr */
117
static char	serrbuf[BUFSIZ];
118
119
/* how many input lines have been irretractibly output */
120
static LINENUM	last_frozen_line = 0;
121
122
static int	Argc;		/* guess */
123
static char	**Argv;
124
static int	Argc_last;	/* for restarting plan_b */
125
static char	**Argv_last;
126
127
static FILE	*ofp = NULL;	/* output file pointer */
128
static FILE	*rejfp = NULL;	/* reject file pointer */
129
130
static int	filec = 0;	/* how many file arguments? */
131
static LINENUM	last_offset = 0;
132
static LINENUM	maxfuzz = 2;
133
134
/* patch using ifdef, ifndef, etc. */
135
static bool		do_defines = false;
136
/* #ifdef xyzzy */
137
static char		if_defined[128];
138
/* #ifndef xyzzy */
139
static char		not_defined[128];
140
/* #else */
141
static const char	else_defined[] = "#else\n";
142
/* #endif xyzzy */
143
static char		end_defined[128];
144
145
146
/* Apply a set of diffs as appropriate. */
147
148
int
149
main(int argc, char *argv[])
150
{
151
	int	error = 0, hunk, failed, i, fd;
152
	LINENUM	where = 0, newwhere, fuzz, mymaxfuzz;
153
	const	char *tmpdir;
154
	char	*v;
155
156
	setbuf(stderr, serrbuf);
157
	for (i = 0; i < MAXFILEC; i++)
158
		filearg[i] = NULL;
159
160
	/* Cons up the names of the temporary files.  */
161
	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
162
		tmpdir = _PATH_TMP;
163
	for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
164
		;
165
	i++;
166
	if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
167
		fatal("cannot allocate memory");
168
	if ((fd = mkstemp(TMPOUTNAME)) < 0)
169
		pfatal("can't create %s", TMPOUTNAME);
170
	close(fd);
171
172
	if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
173
		fatal("cannot allocate memory");
174
	if ((fd = mkstemp(TMPINNAME)) < 0)
175
		pfatal("can't create %s", TMPINNAME);
176
	close(fd);
177
178
	if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
179
		fatal("cannot allocate memory");
180
	if ((fd = mkstemp(TMPREJNAME)) < 0)
181
		pfatal("can't create %s", TMPREJNAME);
182
	close(fd);
183
184
	if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
185
		fatal("cannot allocate memory");
186
	if ((fd = mkstemp(TMPPATNAME)) < 0)
187
		pfatal("can't create %s", TMPPATNAME);
188
	close(fd);
189
190
	v = getenv("SIMPLE_BACKUP_SUFFIX");
191
	if (v)
192
		simple_backup_suffix = v;
193
	else
194
		simple_backup_suffix = ORIGEXT;
195
196
	/* parse switches */
197
	Argc = argc;
198
	Argv = argv;
199
	get_some_switches();
200
201
	if (backup_type == none) {
202
		if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
203
			v = getenv("VERSION_CONTROL");
204
		if (v != NULL || !posix)
205
			backup_type = get_version(v);	/* OK to pass NULL. */
206
	}
207
208
	/* make sure we clean up /tmp in case of disaster */
209
	set_signals(0);
210
211
	for (open_patch_file(filearg[1]); there_is_another_patch();
212
	    reinitialize_almost_everything()) {
213
		/* for each patch in patch file */
214
215
		warn_on_invalid_line = true;
216
217
		if (outname == NULL)
218
			outname = savestr(filearg[0]);
219
220
		/* for ed script just up and do it and exit */
221
		if (diff_type == ED_DIFF) {
222
			do_ed_script();
223
			continue;
224
		}
225
		/* initialize the patched file */
226
		if (!skip_rest_of_patch)
227
			init_output(TMPOUTNAME);
228
229
		/* initialize reject file */
230
		init_reject(TMPREJNAME);
231
232
		/* find out where all the lines are */
233
		if (!skip_rest_of_patch)
234
			scan_input(filearg[0]);
235
236
		/* from here on, open no standard i/o files, because malloc */
237
		/* might misfire and we can't catch it easily */
238
239
		/* apply each hunk of patch */
240
		hunk = 0;
241
		failed = 0;
242
		out_of_mem = false;
243
		while (another_hunk()) {
244
			hunk++;
245
			fuzz = 0;
246
			mymaxfuzz = pch_context();
247
			if (maxfuzz < mymaxfuzz)
248
				mymaxfuzz = maxfuzz;
249
			if (!skip_rest_of_patch) {
250
				do {
251
					where = locate_hunk(fuzz);
252
					if (hunk == 1 && where == 0 && !force) {
253
						/* dwim for reversed patch? */
254
						if (!pch_swap()) {
255
							if (fuzz == 0)
256
								say("Not enough memory to try swapped hunk!  Assuming unswapped.\n");
257
							continue;
258
						}
259
						reverse = !reverse;
260
						/* try again */
261
						where = locate_hunk(fuzz);
262
						if (where == 0) {
263
							/* didn't find it swapped */
264
							if (!pch_swap())
265
								/* put it back to normal */
266
								fatal("lost hunk on alloc error!\n");
267
							reverse = !reverse;
268
						} else if (noreverse) {
269
							if (!pch_swap())
270
								/* put it back to normal */
271
								fatal("lost hunk on alloc error!\n");
272
							reverse = !reverse;
273
							say("Ignoring previously applied (or reversed) patch.\n");
274
							skip_rest_of_patch = true;
275
						} else if (batch) {
276
							if (verbose)
277
								say("%seversed (or previously applied) patch detected!  %s -R.",
278
								    reverse ? "R" : "Unr",
279
								    reverse ? "Assuming" : "Ignoring");
280
						} else {
281
							ask("%seversed (or previously applied) patch detected!  %s -R? [y] ",
282
							    reverse ? "R" : "Unr",
283
							    reverse ? "Assume" : "Ignore");
284
							if (*buf == 'n') {
285
								ask("Apply anyway? [n] ");
286
								if (*buf != 'y')
287
									skip_rest_of_patch = true;
288
								where = 0;
289
								reverse = !reverse;
290
								if (!pch_swap())
291
									/* put it back to normal */
292
									fatal("lost hunk on alloc error!\n");
293
							}
294
						}
295
					}
296
				} while (!skip_rest_of_patch && where == 0 &&
297
				    ++fuzz <= mymaxfuzz);
298
299
				if (skip_rest_of_patch) {	/* just got decided */
300
					fclose(ofp);
301
					ofp = NULL;
302
				}
303
			}
304
			newwhere = pch_newfirst() + last_offset;
305
			if (skip_rest_of_patch) {
306
				abort_hunk();
307
				failed++;
308
				if (verbose)
309
					say("Hunk #%d ignored at %ld.\n",
310
					    hunk, newwhere);
311
			} else if (where == 0) {
312
				abort_hunk();
313
				failed++;
314
				if (verbose)
315
					say("Hunk #%d failed at %ld.\n",
316
					    hunk, newwhere);
317
			} else {
318
				apply_hunk(where);
319
				if (verbose) {
320
					say("Hunk #%d succeeded at %ld",
321
					    hunk, newwhere);
322
					if (fuzz != 0)
323
						say(" with fuzz %ld", fuzz);
324
					if (last_offset)
325
						say(" (offset %ld line%s)",
326
						    last_offset,
327
						    last_offset == 1L ? "" : "s");
328
					say(".\n");
329
				}
330
			}
331
		}
332
333
		if (out_of_mem && using_plan_a) {
334
			Argc = Argc_last;
335
			Argv = Argv_last;
336
			say("\n\nRan out of memory using Plan A--trying again...\n\n");
337
			if (ofp)
338
				fclose(ofp);
339
			ofp = NULL;
340
			if (rejfp)
341
				fclose(rejfp);
342
			rejfp = NULL;
343
			continue;
344
		}
345
		if (hunk == 0)
346
			fatal("Internal error: hunk should not be 0\n");
347
348
		/* finish spewing out the new file */
349
		if (!skip_rest_of_patch)
350
			spew_output();
351
352
		/* and put the output where desired */
353
		ignore_signals();
354
		if (!skip_rest_of_patch) {
355
			struct stat	statbuf;
356
			char	*realout = outname;
357
358
			if (!check_only) {
359
				if (move_file(TMPOUTNAME, outname) < 0) {
360
					toutkeep = true;
361
					realout = TMPOUTNAME;
362
					chmod(TMPOUTNAME, filemode);
363
				} else
364
					chmod(outname, filemode);
365
366
				if (remove_empty_files &&
367
				    stat(realout, &statbuf) == 0 &&
368
				    statbuf.st_size == 0) {
369
					if (verbose)
370
						say("Removing %s (empty after patching).\n",
371
						    realout);
372
					unlink(realout);
373
				}
374
			}
375
		}
376
		fclose(rejfp);
377
		rejfp = NULL;
378
		if (failed) {
379
			error = 1;
380
			if (*rejname == '\0') {
381
				if (strlcpy(rejname, outname,
382
				    sizeof(rejname)) >= sizeof(rejname))
383
					fatal("filename %s is too long\n", outname);
384
				if (strlcat(rejname, REJEXT,
385
				    sizeof(rejname)) >= sizeof(rejname))
386
					fatal("filename %s is too long\n", outname);
387
			}
388
			if (skip_rest_of_patch) {
389
				say("%d out of %d hunks ignored--saving rejects to %s\n",
390
				    failed, hunk, rejname);
391
			} else {
392
				say("%d out of %d hunks failed--saving rejects to %s\n",
393
				    failed, hunk, rejname);
394
			}
395
			if (!check_only && move_file(TMPREJNAME, rejname) < 0)
396
				trejkeep = true;
397
		}
398
		set_signals(1);
399
	}
400
	my_exit(error);
401
	/* NOTREACHED */
402
}
403
404
/* Prepare to find the next patch to do in the patch file. */
405
406
static void
407
reinitialize_almost_everything(void)
408
{
409
	re_patch();
410
	re_input();
411
412
	input_lines = 0;
413
	last_frozen_line = 0;
414
415
	filec = 0;
416
	if (!out_of_mem) {
417
		free(filearg[0]);
418
		filearg[0] = NULL;
419
	}
420
421
	free(outname);
422
	outname = NULL;
423
424
	last_offset = 0;
425
	diff_type = 0;
426
427
	free(revision);
428
	revision = NULL;
429
430
	reverse = reverse_flag_specified;
431
	skip_rest_of_patch = false;
432
433
	get_some_switches();
434
}
435
436
/* Process switches and filenames. */
437
438
static void
439
get_some_switches(void)
440
{
441
	const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
442
	static struct option longopts[] = {
443
		{"backup",		no_argument,		0,	'b'},
444
		{"batch",		no_argument,		0,	't'},
445
		{"check",		no_argument,		0,	'C'},
446
		{"context",		no_argument,		0,	'c'},
447
		{"debug",		required_argument,	0,	'x'},
448
		{"directory",		required_argument,	0,	'd'},
449
		{"ed",			no_argument,		0,	'e'},
450
		{"force",		no_argument,		0,	'f'},
451
		{"forward",		no_argument,		0,	'N'},
452
		{"fuzz",		required_argument,	0,	'F'},
453
		{"ifdef",		required_argument,	0,	'D'},
454
		{"input",		required_argument,	0,	'i'},
455
		{"ignore-whitespace",	no_argument,		0,	'l'},
456
		{"normal",		no_argument,		0,	'n'},
457
		{"output",		required_argument,	0,	'o'},
458
		{"prefix",		required_argument,	0,	'B'},
459
		{"quiet",		no_argument,		0,	's'},
460
		{"reject-file",		required_argument,	0,	'r'},
461
		{"remove-empty-files",	no_argument,		0,	'E'},
462
		{"reverse",		no_argument,		0,	'R'},
463
		{"silent",		no_argument,		0,	's'},
464
		{"strip",		required_argument,	0,	'p'},
465
		{"suffix",		required_argument,	0,	'z'},
466
		{"unified",		no_argument,		0,	'u'},
467
		{"version",		no_argument,		0,	'v'},
468
		{"version-control",	required_argument,	0,	'V'},
469
		{"posix",		no_argument,		&posix,	1},
470
		{NULL,			0,			0,	0}
471
	};
472
	int ch;
473
474
	rejname[0] = '\0';
475
	Argc_last = Argc;
476
	Argv_last = Argv;
477
	if (!Argc)
478
		return;
479
	optreset = optind = 1;
480
	while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
481
		switch (ch) {
482
		case 'b':
483
			if (backup_type == none)
484
				backup_type = numbered_existing;
485
			if (optarg == NULL)
486
				break;
487
			if (verbose)
488
				say("Warning, the ``-b suffix'' option has been"
489
				    " obsoleted by the -z option.\n");
490
			/* FALLTHROUGH */
491
		case 'z':
492
			/* must directly follow 'b' case for backwards compat */
493
			simple_backup_suffix = savestr(optarg);
494
			break;
495
		case 'B':
496
			origprae = savestr(optarg);
497
			break;
498
		case 'c':
499
			diff_type = CONTEXT_DIFF;
500
			break;
501
		case 'C':
502
			check_only = true;
503
			break;
504
		case 'd':
505
			if (chdir(optarg) < 0)
506
				pfatal("can't cd to %s", optarg);
507
			break;
508
		case 'D':
509
			do_defines = true;
510
			if (!isalpha((unsigned char)*optarg) && *optarg != '_')
511
				fatal("argument to -D is not an identifier\n");
512
			snprintf(if_defined, sizeof if_defined,
513
			    "#ifdef %s\n", optarg);
514
			snprintf(not_defined, sizeof not_defined,
515
			    "#ifndef %s\n", optarg);
516
			snprintf(end_defined, sizeof end_defined,
517
			    "#endif /* %s */\n", optarg);
518
			break;
519
		case 'e':
520
			diff_type = ED_DIFF;
521
			break;
522
		case 'E':
523
			remove_empty_files = true;
524
			break;
525
		case 'f':
526
			force = true;
527
			break;
528
		case 'F':
529
			maxfuzz = atoi(optarg);
530
			break;
531
		case 'i':
532
			if (++filec == MAXFILEC)
533
				fatal("too many file arguments\n");
534
			filearg[filec] = savestr(optarg);
535
			break;
536
		case 'l':
537
			canonicalize = true;
538
			break;
539
		case 'n':
540
			diff_type = NORMAL_DIFF;
541
			break;
542
		case 'N':
543
			noreverse = true;
544
			break;
545
		case 'o':
546
			outname = savestr(optarg);
547
			break;
548
		case 'p':
549
			strippath = atoi(optarg);
550
			break;
551
		case 'r':
552
			if (strlcpy(rejname, optarg,
553
			    sizeof(rejname)) >= sizeof(rejname))
554
				fatal("argument for -r is too long\n");
555
			break;
556
		case 'R':
557
			reverse = true;
558
			reverse_flag_specified = true;
559
			break;
560
		case 's':
561
			verbose = false;
562
			break;
563
		case 't':
564
			batch = true;
565
			break;
566
		case 'u':
567
			diff_type = UNI_DIFF;
568
			break;
569
		case 'v':
570
			version();
571
			break;
572
		case 'V':
573
			backup_type = get_version(optarg);
574
			break;
575
#ifdef DEBUGGING
576
		case 'x':
577
			debug = atoi(optarg);
578
			break;
579
#endif
580
		default:
581
			if (ch != '\0')
582
				usage();
583
			break;
584
		}
585
	}
586
	Argc -= optind;
587
	Argv += optind;
588
589
	if (Argc > 0) {
590
		filearg[0] = savestr(*Argv++);
591
		Argc--;
592
		while (Argc > 0) {
593
			if (++filec == MAXFILEC)
594
				fatal("too many file arguments\n");
595
			filearg[filec] = savestr(*Argv++);
596
			Argc--;
597
		}
598
	}
599
600
	if (getenv("POSIXLY_CORRECT") != NULL)
601
		posix = 1;
602
}
603
604
static void
605
usage(void)
606
{
607
	fprintf(stderr,
608
"usage: patch [-bcCeEflnNRstuv] [-B backup-prefix] [-d directory] [-D symbol]\n"
609
"             [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n"
610
"             [-r rej-name] [-V {numbered,existing,simple}] [-z backup-ext]\n"
611
"             [origfile [patchfile]]\n");
612
	my_exit(EXIT_SUCCESS);
613
}
614
615
/*
616
 * Attempt to find the right place to apply this hunk of patch.
617
 */
618
static LINENUM
619
locate_hunk(LINENUM fuzz)
620
{
621
	LINENUM	first_guess = pch_first() + last_offset;
622
	LINENUM	offset;
623
	LINENUM	pat_lines = pch_ptrn_lines();
624
	LINENUM	max_pos_offset = input_lines - first_guess - pat_lines + 1;
625
	LINENUM	max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
626
627
	if (pat_lines == 0) {		/* null range matches always */
628
		if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF
629
		    || diff_type == NEW_CONTEXT_DIFF
630
		    || diff_type == UNI_DIFF)) {
631
			say("Empty context always matches.\n");
632
		}
633
		if (diff_type == CONTEXT_DIFF
634
		    || diff_type == NEW_CONTEXT_DIFF
635
		    || diff_type == UNI_DIFF) {
636
			if (fuzz == 0)
637
				return (input_lines == 0 ? first_guess : 0);
638
		} else
639
			return (first_guess);
640
	}
641
	if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
642
		max_neg_offset = first_guess - 1;
643
	if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz))
644
		return first_guess;
645
	for (offset = 1; ; offset++) {
646
		bool	check_after = (offset <= max_pos_offset);
647
		bool	check_before = (offset <= max_neg_offset);
648
649
		if (check_after && patch_match(first_guess, offset, fuzz)) {
650
#ifdef DEBUGGING
651
			if (debug & 1)
652
				say("Offset changing from %ld to %ld\n",
653
				    last_offset, offset);
654
#endif
655
			last_offset = offset;
656
			return first_guess + offset;
657
		} else if (check_before && patch_match(first_guess, -offset, fuzz)) {
658
#ifdef DEBUGGING
659
			if (debug & 1)
660
				say("Offset changing from %ld to %ld\n",
661
				    last_offset, -offset);
662
#endif
663
			last_offset = -offset;
664
			return first_guess - offset;
665
		} else if (!check_before && !check_after)
666
			return 0;
667
	}
668
}
669
670
/* We did not find the pattern, dump out the hunk so they can handle it. */
671
672
static void
673
abort_context_hunk(void)
674
{
675
	LINENUM	i;
676
	const LINENUM	pat_end = pch_end();
677
	/*
678
	 * add in last_offset to guess the same as the previous successful
679
	 * hunk
680
	 */
681
	const LINENUM	oldfirst = pch_first() + last_offset;
682
	const LINENUM	newfirst = pch_newfirst() + last_offset;
683
	const LINENUM	oldlast = oldfirst + pch_ptrn_lines() - 1;
684
	const LINENUM	newlast = newfirst + pch_repl_lines() - 1;
685
	const char	*stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
686
	const char	*minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
687
688
	fprintf(rejfp, "***************\n");
689
	for (i = 0; i <= pat_end; i++) {
690
		switch (pch_char(i)) {
691
		case '*':
692
			if (oldlast < oldfirst)
693
				fprintf(rejfp, "*** 0%s\n", stars);
694
			else if (oldlast == oldfirst)
695
				fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
696
			else
697
				fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
698
				    oldlast, stars);
699
			break;
700
		case '=':
701
			if (newlast < newfirst)
702
				fprintf(rejfp, "--- 0%s\n", minuses);
703
			else if (newlast == newfirst)
704
				fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
705
			else
706
				fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
707
				    newlast, minuses);
708
			break;
709
		case '\n':
710
			fprintf(rejfp, "%s", pfetch(i));
711
			break;
712
		case ' ':
713
		case '-':
714
		case '+':
715
		case '!':
716
			fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
717
			break;
718
		default:
719
			fatal("fatal internal error in abort_context_hunk\n");
720
		}
721
	}
722
}
723
724
static void
725
rej_line(int ch, LINENUM i)
726
{
727
	size_t len;
728
	const char *line = pfetch(i);
729
730
	len = strlen(line);
731
732
	fprintf(rejfp, "%c%s", ch, line);
733
	if (len == 0 || line[len-1] != '\n')
734
		fprintf(rejfp, "\n\\ No newline at end of file\n");
735
}
736
737
static void
738
abort_hunk(void)
739
{
740
	LINENUM		i, j, split;
741
	int		ch1, ch2;
742
	const LINENUM	pat_end = pch_end();
743
	const LINENUM	oldfirst = pch_first() + last_offset;
744
	const LINENUM	newfirst = pch_newfirst() + last_offset;
745
746
	if (diff_type != UNI_DIFF) {
747
		abort_context_hunk();
748
		return;
749
	}
750
	split = -1;
751
	for (i = 0; i <= pat_end; i++) {
752
		if (pch_char(i) == '=') {
753
			split = i;
754
			break;
755
		}
756
	}
757
	if (split == -1) {
758
		fprintf(rejfp, "malformed hunk: no split found\n");
759
		return;
760
	}
761
	i = 0;
762
	j = split + 1;
763
	fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n",
764
	    pch_ptrn_lines() ? oldfirst : 0,
765
	    pch_ptrn_lines(), newfirst, pch_repl_lines());
766
	while (i < split || j <= pat_end) {
767
		ch1 = i < split ? pch_char(i) : -1;
768
		ch2 = j <= pat_end ? pch_char(j) : -1;
769
		if (ch1 == '-') {
770
			rej_line('-', i);
771
			i++;
772
		} else if (ch1 == ' ' && ch2 == ' ') {
773
			rej_line(' ', i);
774
			i++;
775
			j++;
776
		} else if (ch1 == '!' && ch2 == '!') {
777
			while (i < split && ch1 == '!') {
778
				rej_line('-', i);
779
				i++;
780
				ch1 = i < split ? pch_char(i) : -1;
781
			}
782
			while (j <= pat_end && ch2 == '!') {
783
				rej_line('+', j);
784
				j++;
785
				ch2 = j <= pat_end ? pch_char(j) : -1;
786
			}
787
		} else if (ch1 == '*') {
788
			i++;
789
		} else if (ch2 == '+' || ch2 == ' ') {
790
			rej_line(ch2, j);
791
			j++;
792
		} else {
793
			fprintf(rejfp, "internal error on (%ld %ld %ld)\n",
794
			    i, split, j);
795
			rej_line(ch1, i);
796
			rej_line(ch2, j);
797
			return;
798
		}
799
	}
800
}
801
802
/* We found where to apply it (we hope), so do it. */
803
804
static void
805
apply_hunk(LINENUM where)
806
{
807
	LINENUM		old = 1;
808
	const LINENUM	lastline = pch_ptrn_lines();
809
	LINENUM		new = lastline + 1;
810
#define OUTSIDE 0
811
#define IN_IFNDEF 1
812
#define IN_IFDEF 2
813
#define IN_ELSE 3
814
	int		def_state = OUTSIDE;
815
	const LINENUM	pat_end = pch_end();
816
817
	where--;
818
	while (pch_char(new) == '=' || pch_char(new) == '\n')
819
		new++;
820
821
	while (old <= lastline) {
822
		if (pch_char(old) == '-') {
823
			copy_till(where + old - 1, false);
824
			if (do_defines) {
825
				if (def_state == OUTSIDE) {
826
					fputs(not_defined, ofp);
827
					def_state = IN_IFNDEF;
828
				} else if (def_state == IN_IFDEF) {
829
					fputs(else_defined, ofp);
830
					def_state = IN_ELSE;
831
				}
832
				fputs(pfetch(old), ofp);
833
			}
834
			last_frozen_line++;
835
			old++;
836
		} else if (new > pat_end) {
837
			break;
838
		} else if (pch_char(new) == '+') {
839
			copy_till(where + old - 1, false);
840
			if (do_defines) {
841
				if (def_state == IN_IFNDEF) {
842
					fputs(else_defined, ofp);
843
					def_state = IN_ELSE;
844
				} else if (def_state == OUTSIDE) {
845
					fputs(if_defined, ofp);
846
					def_state = IN_IFDEF;
847
				}
848
			}
849
			fputs(pfetch(new), ofp);
850
			new++;
851
		} else if (pch_char(new) != pch_char(old)) {
852
			say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
853
			    pch_hunk_beg() + old,
854
			    pch_hunk_beg() + new);
855
#ifdef DEBUGGING
856
			say("oldchar = '%c', newchar = '%c'\n",
857
			    pch_char(old), pch_char(new));
858
#endif
859
			my_exit(2);
860
		} else if (pch_char(new) == '!') {
861
			copy_till(where + old - 1, false);
862
			if (do_defines) {
863
				fputs(not_defined, ofp);
864
				def_state = IN_IFNDEF;
865
			}
866
			while (pch_char(old) == '!') {
867
				if (do_defines) {
868
					fputs(pfetch(old), ofp);
869
				}
870
				last_frozen_line++;
871
				old++;
872
			}
873
			if (do_defines) {
874
				fputs(else_defined, ofp);
875
				def_state = IN_ELSE;
876
			}
877
			while (pch_char(new) == '!') {
878
				fputs(pfetch(new), ofp);
879
				new++;
880
			}
881
		} else {
882
			if (pch_char(new) != ' ')
883
				fatal("Internal error: expected ' '\n");
884
			old++;
885
			new++;
886
			if (do_defines && def_state != OUTSIDE) {
887
				fputs(end_defined, ofp);
888
				def_state = OUTSIDE;
889
			}
890
		}
891
	}
892
	if (new <= pat_end && pch_char(new) == '+') {
893
		copy_till(where + old - 1, false);
894
		if (do_defines) {
895
			if (def_state == OUTSIDE) {
896
				fputs(if_defined, ofp);
897
				def_state = IN_IFDEF;
898
			} else if (def_state == IN_IFNDEF) {
899
				fputs(else_defined, ofp);
900
				def_state = IN_ELSE;
901
			}
902
		}
903
		while (new <= pat_end && pch_char(new) == '+') {
904
			fputs(pfetch(new), ofp);
905
			new++;
906
		}
907
	}
908
	if (do_defines && def_state != OUTSIDE) {
909
		fputs(end_defined, ofp);
910
	}
911
}
912
913
/*
914
 * Open the new file.
915
 */
916
static void
917
init_output(const char *name)
918
{
919
	ofp = fopen(name, "w");
920
	if (ofp == NULL)
921
		pfatal("can't create %s", name);
922
}
923
924
/*
925
 * Open a file to put hunks we can't locate.
926
 */
927
static void
928
init_reject(const char *name)
929
{
930
	rejfp = fopen(name, "w");
931
	if (rejfp == NULL)
932
		pfatal("can't create %s", name);
933
}
934
935
/*
936
 * Copy input file to output, up to wherever hunk is to be applied.
937
 * If endoffile is true, treat the last line specially since it may
938
 * lack a newline.
939
 */
940
static void
941
copy_till(LINENUM lastline, bool endoffile)
942
{
943
	if (last_frozen_line > lastline)
944
		fatal("misordered hunks! output would be garbled\n");
945
	while (last_frozen_line < lastline) {
946
		if (++last_frozen_line == lastline && endoffile)
947
			dump_line(last_frozen_line, !last_line_missing_eol);
948
		else
949
			dump_line(last_frozen_line, true);
950
	}
951
}
952
953
/*
954
 * Finish copying the input file to the output file.
955
 */
956
static void
957
spew_output(void)
958
{
959
#ifdef DEBUGGING
960
	if (debug & 256)
961
		say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
962
#endif
963
	if (input_lines)
964
		copy_till(input_lines, true);	/* dump remainder of file */
965
	fclose(ofp);
966
	ofp = NULL;
967
}
968
969
/*
970
 * Copy one line from input to output.
971
 */
972
static void
973
dump_line(LINENUM line, bool write_newline)
974
{
975
	char	*s;
976
977
	s = ifetch(line, 0);
978
	if (s == NULL)
979
		return;
980
	/* Note: string is not NUL terminated. */
981
	for (; *s != '\n'; s++)
982
		putc(*s, ofp);
983
	if (write_newline)
984
		putc('\n', ofp);
985
}
986
987
/*
988
 * Does the patch pattern match at line base+offset?
989
 */
990
static bool
991
patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
992
{
993
	LINENUM		pline = 1 + fuzz;
994
	LINENUM		iline;
995
	LINENUM		pat_lines = pch_ptrn_lines() - fuzz;
996
	const char	*ilineptr;
997
	const char	*plineptr;
998
	short		plinelen;
999
1000
	for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
1001
		ilineptr = ifetch(iline, offset >= 0);
1002
		if (ilineptr == NULL)
1003
			return false;
1004
		plineptr = pfetch(pline);
1005
		plinelen = pch_line_len(pline);
1006
		if (canonicalize) {
1007
			if (!similar(ilineptr, plineptr, plinelen))
1008
				return false;
1009
		} else if (strnNE(ilineptr, plineptr, plinelen))
1010
			return false;
1011
		if (iline == input_lines) {
1012
			/*
1013
			 * We are looking at the last line of the file.
1014
			 * If the file has no eol, the patch line should
1015
			 * not have one either and vice-versa. Note that
1016
			 * plinelen > 0.
1017
			 */
1018
			if (last_line_missing_eol) {
1019
				if (plineptr[plinelen - 1] == '\n')
1020
					return false;
1021
			} else {
1022
				if (plineptr[plinelen - 1] != '\n')
1023
					return false;
1024
			}
1025
		}
1026
	}
1027
	return true;
1028
}
1029
1030
/*
1031
 * Do two lines match with canonicalized white space?
1032
 */
1033
static bool
1034
similar(const char *a, const char *b, int len)
1035
{
1036
	while (len) {
1037
		if (isspace((unsigned char)*b)) {	/* whitespace (or \n) to match? */
1038
			if (!isspace((unsigned char)*a))	/* no corresponding whitespace? */
1039
				return false;
1040
			while (len && isspace((unsigned char)*b) && *b != '\n')
1041
				b++, len--;	/* skip pattern whitespace */
1042
			while (isspace((unsigned char)*a) && *a != '\n')
1043
				a++;	/* skip target whitespace */
1044
			if (*a == '\n' || *b == '\n')
1045
				return (*a == *b);	/* should end in sync */
1046
		} else if (*a++ != *b++)	/* match non-whitespace chars */
1047
			return false;
1048
		else
1049
			len--;	/* probably not necessary */
1050
	}
1051
	return true;		/* actually, this is not reached */
1052
	/* since there is always a \n */
1053
}
(-)usr.bin/patch/pathnames.h (+14 lines)
Added Link Here
1
/*
2
 * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
8
 * on July 29, 2003.
9
 */
10
11
#include <paths.h>
12
13
#define	_PATH_ED		"/bin/ed"
14
#define	_PATH_MKDIR		"/bin/mkdir"
(-)usr.bin/patch/pch.c (+1551 lines)
Added Link Here
1
/*
2
 * $OpenBSD: pch.c,v 1.35 2004/08/05 21:47:24 deraadt Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
35
#include <ctype.h>
36
#include <libgen.h>
37
#include <limits.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
#include "common.h"
44
#include "util.h"
45
#include "pch.h"
46
#include "pathnames.h"
47
48
/* Patch (diff listing) abstract type. */
49
50
static long	p_filesize;	/* size of the patch file */
51
static LINENUM	p_first;	/* 1st line number */
52
static LINENUM	p_newfirst;	/* 1st line number of replacement */
53
static LINENUM	p_ptrn_lines;	/* # lines in pattern */
54
static LINENUM	p_repl_lines;	/* # lines in replacement text */
55
static LINENUM	p_end = -1;	/* last line in hunk */
56
static LINENUM	p_max;		/* max allowed value of p_end */
57
static LINENUM	p_context = 3;	/* # of context lines */
58
static LINENUM	p_input_line = 0;	/* current line # from patch file */
59
static char	**p_line = NULL;/* the text of the hunk */
60
static short	*p_len = NULL;	/* length of each line */
61
static char	*p_char = NULL;	/* +, -, and ! */
62
static int	hunkmax = INITHUNKMAX;	/* size of above arrays to begin with */
63
static int	p_indent;	/* indent to patch */
64
static LINENUM	p_base;		/* where to intuit this time */
65
static LINENUM	p_bline;	/* line # of p_base */
66
static LINENUM	p_start;	/* where intuit found a patch */
67
static LINENUM	p_sline;	/* and the line number for it */
68
static LINENUM	p_hunk_beg;	/* line number of current hunk */
69
static LINENUM	p_efake = -1;	/* end of faked up lines--don't free */
70
static LINENUM	p_bfake = -1;	/* beg of faked up lines */
71
static FILE	*pfp = NULL;	/* patch file pointer */
72
static char	*bestguess = NULL;	/* guess at correct filename */
73
74
static void	grow_hunkmax(void);
75
static int	intuit_diff_type(void);
76
static void	next_intuit_at(LINENUM, LINENUM);
77
static void	skip_to(LINENUM, LINENUM);
78
static char	*pgets(char *, int, FILE *);
79
static char	*best_name(const struct file_name *, bool);
80
static char	*posix_name(const struct file_name *, bool);
81
static size_t	num_components(const char *);
82
83
/*
84
 * Prepare to look for the next patch in the patch file.
85
 */
86
void
87
re_patch(void)
88
{
89
	p_first = 0;
90
	p_newfirst = 0;
91
	p_ptrn_lines = 0;
92
	p_repl_lines = 0;
93
	p_end = (LINENUM) - 1;
94
	p_max = 0;
95
	p_indent = 0;
96
}
97
98
/*
99
 * Open the patch file at the beginning of time.
100
 */
101
void
102
open_patch_file(const char *filename)
103
{
104
	struct stat filestat;
105
106
	if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
107
		pfp = fopen(TMPPATNAME, "w");
108
		if (pfp == NULL)
109
			pfatal("can't create %s", TMPPATNAME);
110
		while (fgets(buf, buf_len, stdin) != NULL)
111
			fputs(buf, pfp);
112
		fclose(pfp);
113
		filename = TMPPATNAME;
114
	}
115
	pfp = fopen(filename, "r");
116
	if (pfp == NULL)
117
		pfatal("patch file %s not found", filename);
118
	fstat(fileno(pfp), &filestat);
119
	p_filesize = filestat.st_size;
120
	next_intuit_at(0L, 1L);	/* start at the beginning */
121
	set_hunkmax();
122
}
123
124
/*
125
 * Make sure our dynamically realloced tables are malloced to begin with.
126
 */
127
void
128
set_hunkmax(void)
129
{
130
	if (p_line == NULL)
131
		p_line = malloc((size_t) hunkmax * sizeof(char *));
132
	if (p_len == NULL)
133
		p_len = malloc((size_t) hunkmax * sizeof(short));
134
	if (p_char == NULL)
135
		p_char = malloc((size_t) hunkmax * sizeof(char));
136
}
137
138
/*
139
 * Enlarge the arrays containing the current hunk of patch.
140
 */
141
static void
142
grow_hunkmax(void)
143
{
144
	int		new_hunkmax;
145
	char		**new_p_line;
146
	short		*new_p_len;
147
	char		*new_p_char;
148
149
	new_hunkmax = hunkmax * 2;
150
151
	if (p_line == NULL || p_len == NULL || p_char == NULL)
152
		fatal("Internal memory allocation error\n");
153
154
	new_p_line = realloc(p_line, new_hunkmax * sizeof(char *));
155
	if (new_p_line == NULL)
156
		free(p_line);
157
158
	new_p_len = realloc(p_len, new_hunkmax * sizeof(short));
159
	if (new_p_len == NULL)
160
		free(p_len);
161
162
	new_p_char = realloc(p_char, new_hunkmax * sizeof(char));
163
	if (new_p_char == NULL)
164
		free(p_char);
165
166
	p_char = new_p_char;
167
	p_len = new_p_len;
168
	p_line = new_p_line;
169
170
	if (p_line != NULL && p_len != NULL && p_char != NULL) {
171
		hunkmax = new_hunkmax;
172
		return;
173
	}
174
175
	if (!using_plan_a)
176
		fatal("out of memory\n");
177
	out_of_mem = true;	/* whatever is null will be allocated again */
178
				/* from within plan_a(), of all places */
179
}
180
181
/* True if the remainder of the patch file contains a diff of some sort. */
182
183
bool
184
there_is_another_patch(void)
185
{
186
	bool exists = false;
187
188
	if (p_base != 0L && p_base >= p_filesize) {
189
		if (verbose)
190
			say("done\n");
191
		return false;
192
	}
193
	if (verbose)
194
		say("Hmm...");
195
	diff_type = intuit_diff_type();
196
	if (!diff_type) {
197
		if (p_base != 0L) {
198
			if (verbose)
199
				say("  Ignoring the trailing garbage.\ndone\n");
200
		} else
201
			say("  I can't seem to find a patch in there anywhere.\n");
202
		return false;
203
	}
204
	if (verbose)
205
		say("  %sooks like %s to me...\n",
206
		    (p_base == 0L ? "L" : "The next patch l"),
207
		    diff_type == UNI_DIFF ? "a unified diff" :
208
		    diff_type == CONTEXT_DIFF ? "a context diff" :
209
		diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
210
		    diff_type == NORMAL_DIFF ? "a normal diff" :
211
		    "an ed script");
212
	if (p_indent && verbose)
213
		say("(Patch is indented %d space%s.)\n", p_indent,
214
		    p_indent == 1 ? "" : "s");
215
	skip_to(p_start, p_sline);
216
	while (filearg[0] == NULL) {
217
		if (force || batch) {
218
			say("No file to patch.  Skipping...\n");
219
			filearg[0] = savestr(bestguess);
220
			skip_rest_of_patch = true;
221
			return true;
222
		}
223
		ask("File to patch: ");
224
		if (*buf != '\n') {
225
			free(bestguess);
226
			bestguess = savestr(buf);
227
			filearg[0] = fetchname(buf, &exists, 0);
228
		}
229
		if (!exists) {
230
			ask("No file found--skip this patch? [n] ");
231
			if (*buf != 'y')
232
				continue;
233
			if (verbose)
234
				say("Skipping patch...\n");
235
			free(filearg[0]);
236
			filearg[0] = fetchname(bestguess, &exists, 0);
237
			skip_rest_of_patch = true;
238
			return true;
239
		}
240
	}
241
	return true;
242
}
243
244
/* Determine what kind of diff is in the remaining part of the patch file. */
245
246
static int
247
intuit_diff_type(void)
248
{
249
	long	this_line = 0, previous_line;
250
	long	first_command_line = -1;
251
	LINENUM	fcl_line = -1;
252
	bool	last_line_was_command = false, this_is_a_command = false;
253
	bool	stars_last_line = false, stars_this_line = false;
254
	char	*s, *t;
255
	int	indent, retval;
256
	struct file_name names[MAX_FILE];
257
258
	memset(names, 0, sizeof(names));
259
	ok_to_create_file = false;
260
	fseek(pfp, p_base, SEEK_SET);
261
	p_input_line = p_bline - 1;
262
	for (;;) {
263
		previous_line = this_line;
264
		last_line_was_command = this_is_a_command;
265
		stars_last_line = stars_this_line;
266
		this_line = ftell(pfp);
267
		indent = 0;
268
		p_input_line++;
269
		if (fgets(buf, buf_len, pfp) == NULL) {
270
			if (first_command_line >= 0L) {
271
				/* nothing but deletes!? */
272
				p_start = first_command_line;
273
				p_sline = fcl_line;
274
				retval = ED_DIFF;
275
				goto scan_exit;
276
			} else {
277
				p_start = this_line;
278
				p_sline = p_input_line;
279
				retval = 0;
280
				goto scan_exit;
281
			}
282
		}
283
		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
284
			if (*s == '\t')
285
				indent += 8 - (indent % 8);
286
			else
287
				indent++;
288
		}
289
		for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
290
			;
291
		this_is_a_command = (isdigit((unsigned char)*s) &&
292
		    (*t == 'd' || *t == 'c' || *t == 'a'));
293
		if (first_command_line < 0L && this_is_a_command) {
294
			first_command_line = this_line;
295
			fcl_line = p_input_line;
296
			p_indent = indent;	/* assume this for now */
297
		}
298
		if (!stars_last_line && strnEQ(s, "*** ", 4))
299
			names[OLD_FILE].path = fetchname(s + 4,
300
			    &names[OLD_FILE].exists, strippath);
301
		else if (strnEQ(s, "--- ", 4))
302
			names[NEW_FILE].path = fetchname(s + 4,
303
			    &names[NEW_FILE].exists, strippath);
304
		else if (strnEQ(s, "+++ ", 4))
305
			/* pretend it is the old name */
306
			names[OLD_FILE].path = fetchname(s + 4,
307
			    &names[OLD_FILE].exists, strippath);
308
		else if (strnEQ(s, "Index:", 6))
309
			names[INDEX_FILE].path = fetchname(s + 6,
310
			    &names[INDEX_FILE].exists, strippath);
311
		else if (strnEQ(s, "Prereq:", 7)) {
312
			for (t = s + 7; isspace((unsigned char)*t); t++)
313
				;
314
			revision = savestr(t);
315
			for (t = revision; *t && !isspace((unsigned char)*t); t++)
316
				;
317
			*t = '\0';
318
			if (*revision == '\0') {
319
				free(revision);
320
				revision = NULL;
321
			}
322
		}
323
		if ((!diff_type || diff_type == ED_DIFF) &&
324
		    first_command_line >= 0L &&
325
		    strEQ(s, ".\n")) {
326
			p_indent = indent;
327
			p_start = first_command_line;
328
			p_sline = fcl_line;
329
			retval = ED_DIFF;
330
			goto scan_exit;
331
		}
332
		if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
333
			if (strnEQ(s + 4, "0,0", 3))
334
				ok_to_create_file = true;
335
			p_indent = indent;
336
			p_start = this_line;
337
			p_sline = p_input_line;
338
			retval = UNI_DIFF;
339
			goto scan_exit;
340
		}
341
		stars_this_line = strnEQ(s, "********", 8);
342
		if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
343
		    strnEQ(s, "*** ", 4)) {
344
			if (atol(s + 4) == 0)
345
				ok_to_create_file = true;
346
			/*
347
			 * If this is a new context diff the character just
348
			 * before the newline is a '*'.
349
			 */
350
			while (*s != '\n')
351
				s++;
352
			p_indent = indent;
353
			p_start = previous_line;
354
			p_sline = p_input_line - 1;
355
			retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
356
			goto scan_exit;
357
		}
358
		if ((!diff_type || diff_type == NORMAL_DIFF) &&
359
		    last_line_was_command &&
360
		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
361
			p_start = previous_line;
362
			p_sline = p_input_line - 1;
363
			p_indent = indent;
364
			retval = NORMAL_DIFF;
365
			goto scan_exit;
366
		}
367
	}
368
scan_exit:
369
	if (retval == UNI_DIFF) {
370
		/* unswap old and new */
371
		struct file_name tmp = names[OLD_FILE];
372
		names[OLD_FILE] = names[NEW_FILE];
373
		names[NEW_FILE] = tmp;
374
	}
375
	if (filearg[0] == NULL) {
376
		if (posix)
377
			filearg[0] = posix_name(names, ok_to_create_file);
378
		else {
379
			/* Ignore the Index: name for context diffs, like GNU */
380
			if (names[OLD_FILE].path != NULL ||
381
			    names[NEW_FILE].path != NULL) {
382
				free(names[INDEX_FILE].path);
383
				names[INDEX_FILE].path = NULL;
384
			}
385
			filearg[0] = best_name(names, ok_to_create_file);
386
		}
387
	}
388
389
	free(bestguess);
390
	bestguess = NULL;
391
	if (filearg[0] != NULL)
392
		bestguess = savestr(filearg[0]);
393
	else if (!ok_to_create_file) {
394
		/*
395
		 * We don't want to create a new file but we need a
396
		 * filename to set bestguess.  Avoid setting filearg[0]
397
		 * so the file is not created automatically.
398
		 */
399
		if (posix)
400
			bestguess = posix_name(names, true);
401
		else
402
			bestguess = best_name(names, true);
403
	}
404
	free(names[OLD_FILE].path);
405
	free(names[NEW_FILE].path);
406
	free(names[INDEX_FILE].path);
407
	return retval;
408
}
409
410
/*
411
 * Remember where this patch ends so we know where to start up again.
412
 */
413
static void
414
next_intuit_at(LINENUM file_pos, LINENUM file_line)
415
{
416
	p_base = file_pos;
417
	p_bline = file_line;
418
}
419
420
/*
421
 * Basically a verbose fseek() to the actual diff listing.
422
 */
423
static void
424
skip_to(LINENUM file_pos, LINENUM file_line)
425
{
426
	char	*ret;
427
428
	if (p_base > file_pos)
429
		fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
430
	if (verbose && p_base < file_pos) {
431
		fseek(pfp, p_base, SEEK_SET);
432
		say("The text leading up to this was:\n--------------------------\n");
433
		while (ftell(pfp) < file_pos) {
434
			ret = fgets(buf, buf_len, pfp);
435
			if (ret == NULL)
436
				fatal("Unexpected end of file\n");
437
			say("|%s", buf);
438
		}
439
		say("--------------------------\n");
440
	} else
441
		fseek(pfp, file_pos, SEEK_SET);
442
	p_input_line = file_line - 1;
443
}
444
445
/* Make this a function for better debugging.  */
446
static void
447
malformed(void)
448
{
449
	fatal("malformed patch at line %ld: %s", p_input_line, buf);
450
	/* about as informative as "Syntax error" in C */
451
}
452
453
/*
454
 * True if the line has been discarded (i.e. it is a line saying
455
 *  "\ No newline at end of file".)
456
 */
457
static bool
458
remove_special_line(void)
459
{
460
	int	c;
461
462
	c = fgetc(pfp);
463
	if (c == '\\') {
464
		do {
465
			c = fgetc(pfp);
466
		} while (c != EOF && c != '\n');
467
468
		return true;
469
	}
470
	if (c != EOF)
471
		fseek(pfp, -1L, SEEK_CUR);
472
473
	return false;
474
}
475
476
/*
477
 * True if there is more of the current diff listing to process.
478
 */
479
bool
480
another_hunk(void)
481
{
482
	long	line_beginning;			/* file pos of the current line */
483
	LINENUM	repl_beginning;			/* index of --- line */
484
	LINENUM	fillcnt;			/* #lines of missing ptrn or repl */
485
	LINENUM	fillsrc;			/* index of first line to copy */
486
	LINENUM	filldst;			/* index of first missing line */
487
	bool	ptrn_spaces_eaten;		/* ptrn was slightly misformed */
488
	bool	repl_could_be_missing;		/* no + or ! lines in this hunk */
489
	bool	repl_missing;			/* we are now backtracking */
490
	long	repl_backtrack_position;	/* file pos of first repl line */
491
	LINENUM	repl_patch_line;		/* input line number for same */
492
	LINENUM	ptrn_copiable;			/* # of copiable lines in ptrn */
493
	char	*s, *ret;
494
	int	context = 0;
495
496
	while (p_end >= 0) {
497
		if (p_end == p_efake)
498
			p_end = p_bfake;	/* don't free twice */
499
		else
500
			free(p_line[p_end]);
501
		p_end--;
502
	}
503
	p_efake = -1;
504
505
	p_max = hunkmax;	/* gets reduced when --- found */
506
	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
507
		line_beginning = ftell(pfp);
508
		repl_beginning = 0;
509
		fillcnt = 0;
510
		fillsrc = 0;
511
		filldst = 0;
512
		ptrn_spaces_eaten = false;
513
		repl_could_be_missing = true;
514
		repl_missing = false;
515
		repl_backtrack_position = 0;
516
		repl_patch_line = 0;
517
		ptrn_copiable = 0;
518
519
		ret = pgets(buf, buf_len, pfp);
520
		p_input_line++;
521
		if (ret == NULL || strnNE(buf, "********", 8)) {
522
			next_intuit_at(line_beginning, p_input_line);
523
			return false;
524
		}
525
		p_context = 100;
526
		p_hunk_beg = p_input_line + 1;
527
		while (p_end < p_max) {
528
			line_beginning = ftell(pfp);
529
			ret = pgets(buf, buf_len, pfp);
530
			p_input_line++;
531
			if (ret == NULL) {
532
				if (p_max - p_end < 4) {
533
					/* assume blank lines got chopped */
534
					strlcpy(buf, "  \n", buf_len);
535
				} else {
536
					if (repl_beginning && repl_could_be_missing) {
537
						repl_missing = true;
538
						goto hunk_done;
539
					}
540
					fatal("unexpected end of file in patch\n");
541
				}
542
			}
543
			p_end++;
544
			if (p_end >= hunkmax)
545
				fatal("Internal error: hunk larger than hunk "
546
				    "buffer size");
547
			p_char[p_end] = *buf;
548
			p_line[p_end] = NULL;
549
			switch (*buf) {
550
			case '*':
551
				if (strnEQ(buf, "********", 8)) {
552
					if (repl_beginning && repl_could_be_missing) {
553
						repl_missing = true;
554
						goto hunk_done;
555
					} else
556
						fatal("unexpected end of hunk "
557
						    "at line %ld\n",
558
						    p_input_line);
559
				}
560
				if (p_end != 0) {
561
					if (repl_beginning && repl_could_be_missing) {
562
						repl_missing = true;
563
						goto hunk_done;
564
					}
565
					fatal("unexpected *** at line %ld: %s",
566
					    p_input_line, buf);
567
				}
568
				context = 0;
569
				p_line[p_end] = savestr(buf);
570
				if (out_of_mem) {
571
					p_end--;
572
					return false;
573
				}
574
				for (s = buf; *s && !isdigit((unsigned char)*s); s++)
575
					;
576
				if (!*s)
577
					malformed();
578
				if (strnEQ(s, "0,0", 3))
579
					memmove(s, s + 2, strlen(s + 2) + 1);
580
				p_first = (LINENUM) atol(s);
581
				while (isdigit((unsigned char)*s))
582
					s++;
583
				if (*s == ',') {
584
					for (; *s && !isdigit((unsigned char)*s); s++)
585
						;
586
					if (!*s)
587
						malformed();
588
					p_ptrn_lines = ((LINENUM) atol(s)) - p_first + 1;
589
				} else if (p_first)
590
					p_ptrn_lines = 1;
591
				else {
592
					p_ptrn_lines = 0;
593
					p_first = 1;
594
				}
595
596
				/* we need this much at least */
597
				p_max = p_ptrn_lines + 6;
598
				while (p_max >= hunkmax)
599
					grow_hunkmax();
600
				p_max = hunkmax;
601
				break;
602
			case '-':
603
				if (buf[1] == '-') {
604
					if (repl_beginning ||
605
					    (p_end != p_ptrn_lines + 1 +
606
					    (p_char[p_end - 1] == '\n'))) {
607
						if (p_end == 1) {
608
							/*
609
							 * `old' lines were omitted;
610
							 * set up to fill them in
611
							 * from 'new' context lines.
612
							 */
613
							p_end = p_ptrn_lines + 1;
614
							fillsrc = p_end + 1;
615
							filldst = 1;
616
							fillcnt = p_ptrn_lines;
617
						} else {
618
							if (repl_beginning) {
619
								if (repl_could_be_missing) {
620
									repl_missing = true;
621
									goto hunk_done;
622
								}
623
								fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
624
								    p_input_line, p_hunk_beg + repl_beginning);
625
							} else {
626
								fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
627
								    (p_end <= p_ptrn_lines
628
								    ? "Premature"
629
								    : "Overdue"),
630
								    p_input_line, p_hunk_beg);
631
							}
632
						}
633
					}
634
					repl_beginning = p_end;
635
					repl_backtrack_position = ftell(pfp);
636
					repl_patch_line = p_input_line;
637
					p_line[p_end] = savestr(buf);
638
					if (out_of_mem) {
639
						p_end--;
640
						return false;
641
					}
642
					p_char[p_end] = '=';
643
					for (s = buf; *s && !isdigit((unsigned char)*s); s++)
644
						;
645
					if (!*s)
646
						malformed();
647
					p_newfirst = (LINENUM) atol(s);
648
					while (isdigit((unsigned char)*s))
649
						s++;
650
					if (*s == ',') {
651
						for (; *s && !isdigit((unsigned char)*s); s++)
652
							;
653
						if (!*s)
654
							malformed();
655
						p_repl_lines = ((LINENUM) atol(s)) -
656
						    p_newfirst + 1;
657
					} else if (p_newfirst)
658
						p_repl_lines = 1;
659
					else {
660
						p_repl_lines = 0;
661
						p_newfirst = 1;
662
					}
663
					p_max = p_repl_lines + p_end;
664
					if (p_max > MAXHUNKSIZE)
665
						fatal("hunk too large (%ld lines) at line %ld: %s",
666
						    p_max, p_input_line, buf);
667
					while (p_max >= hunkmax)
668
						grow_hunkmax();
669
					if (p_repl_lines != ptrn_copiable &&
670
					    (p_context != 0 || p_repl_lines != 1))
671
						repl_could_be_missing = false;
672
					break;
673
				}
674
				goto change_line;
675
			case '+':
676
			case '!':
677
				repl_could_be_missing = false;
678
		change_line:
679
				if (buf[1] == '\n' && canonicalize)
680
					strlcpy(buf + 1, " \n", buf_len - 1);
681
				if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
682
				    buf[1] != '<' &&
683
				    repl_beginning && repl_could_be_missing) {
684
					repl_missing = true;
685
					goto hunk_done;
686
				}
687
				if (context >= 0) {
688
					if (context < p_context)
689
						p_context = context;
690
					context = -1000;
691
				}
692
				p_line[p_end] = savestr(buf + 2);
693
				if (out_of_mem) {
694
					p_end--;
695
					return false;
696
				}
697
				if (p_end == p_ptrn_lines) {
698
					if (remove_special_line()) {
699
						int	len;
700
701
						len = strlen(p_line[p_end]) - 1;
702
						(p_line[p_end])[len] = 0;
703
					}
704
				}
705
				break;
706
			case '\t':
707
			case '\n':	/* assume the 2 spaces got eaten */
708
				if (repl_beginning && repl_could_be_missing &&
709
				    (!ptrn_spaces_eaten ||
710
				    diff_type == NEW_CONTEXT_DIFF)) {
711
					repl_missing = true;
712
					goto hunk_done;
713
				}
714
				p_line[p_end] = savestr(buf);
715
				if (out_of_mem) {
716
					p_end--;
717
					return false;
718
				}
719
				if (p_end != p_ptrn_lines + 1) {
720
					ptrn_spaces_eaten |= (repl_beginning != 0);
721
					context++;
722
					if (!repl_beginning)
723
						ptrn_copiable++;
724
					p_char[p_end] = ' ';
725
				}
726
				break;
727
			case ' ':
728
				if (!isspace((unsigned char)buf[1]) &&
729
				    repl_beginning && repl_could_be_missing) {
730
					repl_missing = true;
731
					goto hunk_done;
732
				}
733
				context++;
734
				if (!repl_beginning)
735
					ptrn_copiable++;
736
				p_line[p_end] = savestr(buf + 2);
737
				if (out_of_mem) {
738
					p_end--;
739
					return false;
740
				}
741
				break;
742
			default:
743
				if (repl_beginning && repl_could_be_missing) {
744
					repl_missing = true;
745
					goto hunk_done;
746
				}
747
				malformed();
748
			}
749
			/* set up p_len for strncmp() so we don't have to */
750
			/* assume null termination */
751
			if (p_line[p_end])
752
				p_len[p_end] = strlen(p_line[p_end]);
753
			else
754
				p_len[p_end] = 0;
755
		}
756
757
hunk_done:
758
		if (p_end >= 0 && !repl_beginning)
759
			fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
760
761
		if (repl_missing) {
762
763
			/* reset state back to just after --- */
764
			p_input_line = repl_patch_line;
765
			for (p_end--; p_end > repl_beginning; p_end--)
766
				free(p_line[p_end]);
767
			fseek(pfp, repl_backtrack_position, SEEK_SET);
768
769
			/* redundant 'new' context lines were omitted - set */
770
			/* up to fill them in from the old file context */
771
			if (!p_context && p_repl_lines == 1) {
772
				p_repl_lines = 0;
773
				p_max--;
774
			}
775
			fillsrc = 1;
776
			filldst = repl_beginning + 1;
777
			fillcnt = p_repl_lines;
778
			p_end = p_max;
779
		} else if (!p_context && fillcnt == 1) {
780
			/* the first hunk was a null hunk with no context */
781
			/* and we were expecting one line -- fix it up. */
782
			while (filldst < p_end) {
783
				p_line[filldst] = p_line[filldst + 1];
784
				p_char[filldst] = p_char[filldst + 1];
785
				p_len[filldst] = p_len[filldst + 1];
786
				filldst++;
787
			}
788
#if 0
789
			repl_beginning--;	/* this doesn't need to be fixed */
790
#endif
791
			p_end--;
792
			p_first++;	/* do append rather than insert */
793
			fillcnt = 0;
794
			p_ptrn_lines = 0;
795
		}
796
		if (diff_type == CONTEXT_DIFF &&
797
		    (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
798
			if (verbose)
799
				say("%s\n%s\n%s\n",
800
				    "(Fascinating--this is really a new-style context diff but without",
801
				    "the telltale extra asterisks on the *** line that usually indicate",
802
				    "the new style...)");
803
			diff_type = NEW_CONTEXT_DIFF;
804
		}
805
		/* if there were omitted context lines, fill them in now */
806
		if (fillcnt) {
807
			p_bfake = filldst;	/* remember where not to free() */
808
			p_efake = filldst + fillcnt - 1;
809
			while (fillcnt-- > 0) {
810
				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
811
					fillsrc++;
812
				if (fillsrc > p_end)
813
					fatal("replacement text or line numbers mangled in hunk at line %ld\n",
814
					    p_hunk_beg);
815
				p_line[filldst] = p_line[fillsrc];
816
				p_char[filldst] = p_char[fillsrc];
817
				p_len[filldst] = p_len[fillsrc];
818
				fillsrc++;
819
				filldst++;
820
			}
821
			while (fillsrc <= p_end && fillsrc != repl_beginning &&
822
			    p_char[fillsrc] != ' ')
823
				fillsrc++;
824
#ifdef DEBUGGING
825
			if (debug & 64)
826
				printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
827
				fillsrc, filldst, repl_beginning, p_end + 1);
828
#endif
829
			if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
830
				malformed();
831
			if (filldst != p_end + 1 && filldst != repl_beginning)
832
				malformed();
833
		}
834
		if (p_line[p_end] != NULL) {
835
			if (remove_special_line()) {
836
				p_len[p_end] -= 1;
837
				(p_line[p_end])[p_len[p_end]] = 0;
838
			}
839
		}
840
	} else if (diff_type == UNI_DIFF) {
841
		LINENUM	fillold;	/* index of old lines */
842
		LINENUM	fillnew;	/* index of new lines */
843
		char	ch;
844
845
		line_beginning = ftell(pfp); /* file pos of the current line */
846
		ret = pgets(buf, buf_len, pfp);
847
		p_input_line++;
848
		if (ret == NULL || strnNE(buf, "@@ -", 4)) {
849
			next_intuit_at(line_beginning, p_input_line);
850
			return false;
851
		}
852
		s = buf + 4;
853
		if (!*s)
854
			malformed();
855
		p_first = (LINENUM) atol(s);
856
		while (isdigit((unsigned char)*s))
857
			s++;
858
		if (*s == ',') {
859
			p_ptrn_lines = (LINENUM) atol(++s);
860
			while (isdigit((unsigned char)*s))
861
				s++;
862
		} else
863
			p_ptrn_lines = 1;
864
		if (*s == ' ')
865
			s++;
866
		if (*s != '+' || !*++s)
867
			malformed();
868
		p_newfirst = (LINENUM) atol(s);
869
		while (isdigit((unsigned char)*s))
870
			s++;
871
		if (*s == ',') {
872
			p_repl_lines = (LINENUM) atol(++s);
873
			while (isdigit((unsigned char)*s))
874
				s++;
875
		} else
876
			p_repl_lines = 1;
877
		if (*s == ' ')
878
			s++;
879
		if (*s != '@')
880
			malformed();
881
		if (!p_ptrn_lines)
882
			p_first++;	/* do append rather than insert */
883
		p_max = p_ptrn_lines + p_repl_lines + 1;
884
		while (p_max >= hunkmax)
885
			grow_hunkmax();
886
		fillold = 1;
887
		fillnew = fillold + p_ptrn_lines;
888
		p_end = fillnew + p_repl_lines;
889
		snprintf(buf, buf_len, "*** %ld,%ld ****\n", p_first,
890
		    p_first + p_ptrn_lines - 1);
891
		p_line[0] = savestr(buf);
892
		if (out_of_mem) {
893
			p_end = -1;
894
			return false;
895
		}
896
		p_char[0] = '*';
897
		snprintf(buf, buf_len, "--- %ld,%ld ----\n", p_newfirst,
898
		    p_newfirst + p_repl_lines - 1);
899
		p_line[fillnew] = savestr(buf);
900
		if (out_of_mem) {
901
			p_end = 0;
902
			return false;
903
		}
904
		p_char[fillnew++] = '=';
905
		p_context = 100;
906
		context = 0;
907
		p_hunk_beg = p_input_line + 1;
908
		while (fillold <= p_ptrn_lines || fillnew <= p_end) {
909
			line_beginning = ftell(pfp);
910
			ret = pgets(buf, buf_len, pfp);
911
			p_input_line++;
912
			if (ret == NULL) {
913
				if (p_max - fillnew < 3) {
914
					/* assume blank lines got chopped */
915
					strlcpy(buf, " \n", buf_len);
916
				} else {
917
					fatal("unexpected end of file in patch\n");
918
				}
919
			}
920
			if (*buf == '\t' || *buf == '\n') {
921
				ch = ' ';	/* assume the space got eaten */
922
				s = savestr(buf);
923
			} else {
924
				ch = *buf;
925
				s = savestr(buf + 1);
926
			}
927
			if (out_of_mem) {
928
				while (--fillnew > p_ptrn_lines)
929
					free(p_line[fillnew]);
930
				p_end = fillold - 1;
931
				return false;
932
			}
933
			switch (ch) {
934
			case '-':
935
				if (fillold > p_ptrn_lines) {
936
					free(s);
937
					p_end = fillnew - 1;
938
					malformed();
939
				}
940
				p_char[fillold] = ch;
941
				p_line[fillold] = s;
942
				p_len[fillold++] = strlen(s);
943
				if (fillold > p_ptrn_lines) {
944
					if (remove_special_line()) {
945
						p_len[fillold - 1] -= 1;
946
						s[p_len[fillold - 1]] = 0;
947
					}
948
				}
949
				break;
950
			case '=':
951
				ch = ' ';
952
				/* FALL THROUGH */
953
			case ' ':
954
				if (fillold > p_ptrn_lines) {
955
					free(s);
956
					while (--fillnew > p_ptrn_lines)
957
						free(p_line[fillnew]);
958
					p_end = fillold - 1;
959
					malformed();
960
				}
961
				context++;
962
				p_char[fillold] = ch;
963
				p_line[fillold] = s;
964
				p_len[fillold++] = strlen(s);
965
				s = savestr(s);
966
				if (out_of_mem) {
967
					while (--fillnew > p_ptrn_lines)
968
						free(p_line[fillnew]);
969
					p_end = fillold - 1;
970
					return false;
971
				}
972
				if (fillold > p_ptrn_lines) {
973
					if (remove_special_line()) {
974
						p_len[fillold - 1] -= 1;
975
						s[p_len[fillold - 1]] = 0;
976
					}
977
				}
978
				/* FALL THROUGH */
979
			case '+':
980
				if (fillnew > p_end) {
981
					free(s);
982
					while (--fillnew > p_ptrn_lines)
983
						free(p_line[fillnew]);
984
					p_end = fillold - 1;
985
					malformed();
986
				}
987
				p_char[fillnew] = ch;
988
				p_line[fillnew] = s;
989
				p_len[fillnew++] = strlen(s);
990
				if (fillold > p_ptrn_lines) {
991
					if (remove_special_line()) {
992
						p_len[fillnew - 1] -= 1;
993
						s[p_len[fillnew - 1]] = 0;
994
					}
995
				}
996
				break;
997
			default:
998
				p_end = fillnew;
999
				malformed();
1000
			}
1001
			if (ch != ' ' && context > 0) {
1002
				if (context < p_context)
1003
					p_context = context;
1004
				context = -1000;
1005
			}
1006
		}		/* while */
1007
	} else {		/* normal diff--fake it up */
1008
		char	hunk_type;
1009
		int	i;
1010
		LINENUM	min, max;
1011
1012
		line_beginning = ftell(pfp);
1013
		p_context = 0;
1014
		ret = pgets(buf, buf_len, pfp);
1015
		p_input_line++;
1016
		if (ret == NULL || !isdigit((unsigned char)*buf)) {
1017
			next_intuit_at(line_beginning, p_input_line);
1018
			return false;
1019
		}
1020
		p_first = (LINENUM) atol(buf);
1021
		for (s = buf; isdigit((unsigned char)*s); s++)
1022
			;
1023
		if (*s == ',') {
1024
			p_ptrn_lines = (LINENUM) atol(++s) - p_first + 1;
1025
			while (isdigit((unsigned char)*s))
1026
				s++;
1027
		} else
1028
			p_ptrn_lines = (*s != 'a');
1029
		hunk_type = *s;
1030
		if (hunk_type == 'a')
1031
			p_first++;	/* do append rather than insert */
1032
		min = (LINENUM) atol(++s);
1033
		for (; isdigit((unsigned char)*s); s++)
1034
			;
1035
		if (*s == ',')
1036
			max = (LINENUM) atol(++s);
1037
		else
1038
			max = min;
1039
		if (hunk_type == 'd')
1040
			min++;
1041
		p_end = p_ptrn_lines + 1 + max - min + 1;
1042
		if (p_end > MAXHUNKSIZE)
1043
			fatal("hunk too large (%ld lines) at line %ld: %s",
1044
			    p_end, p_input_line, buf);
1045
		while (p_end >= hunkmax)
1046
			grow_hunkmax();
1047
		p_newfirst = min;
1048
		p_repl_lines = max - min + 1;
1049
		snprintf(buf, buf_len, "*** %ld,%ld\n", p_first,
1050
		    p_first + p_ptrn_lines - 1);
1051
		p_line[0] = savestr(buf);
1052
		if (out_of_mem) {
1053
			p_end = -1;
1054
			return false;
1055
		}
1056
		p_char[0] = '*';
1057
		for (i = 1; i <= p_ptrn_lines; i++) {
1058
			ret = pgets(buf, buf_len, pfp);
1059
			p_input_line++;
1060
			if (ret == NULL)
1061
				fatal("unexpected end of file in patch at line %ld\n",
1062
				    p_input_line);
1063
			if (*buf != '<')
1064
				fatal("< expected at line %ld of patch\n",
1065
				    p_input_line);
1066
			p_line[i] = savestr(buf + 2);
1067
			if (out_of_mem) {
1068
				p_end = i - 1;
1069
				return false;
1070
			}
1071
			p_len[i] = strlen(p_line[i]);
1072
			p_char[i] = '-';
1073
		}
1074
1075
		if (remove_special_line()) {
1076
			p_len[i - 1] -= 1;
1077
			(p_line[i - 1])[p_len[i - 1]] = 0;
1078
		}
1079
		if (hunk_type == 'c') {
1080
			ret = pgets(buf, buf_len, pfp);
1081
			p_input_line++;
1082
			if (ret == NULL)
1083
				fatal("unexpected end of file in patch at line %ld\n",
1084
				    p_input_line);
1085
			if (*buf != '-')
1086
				fatal("--- expected at line %ld of patch\n",
1087
				    p_input_line);
1088
		}
1089
		snprintf(buf, buf_len, "--- %ld,%ld\n", min, max);
1090
		p_line[i] = savestr(buf);
1091
		if (out_of_mem) {
1092
			p_end = i - 1;
1093
			return false;
1094
		}
1095
		p_char[i] = '=';
1096
		for (i++; i <= p_end; i++) {
1097
			ret = pgets(buf, buf_len, pfp);
1098
			p_input_line++;
1099
			if (ret == NULL)
1100
				fatal("unexpected end of file in patch at line %ld\n",
1101
				    p_input_line);
1102
			if (*buf != '>')
1103
				fatal("> expected at line %ld of patch\n",
1104
				    p_input_line);
1105
			p_line[i] = savestr(buf + 2);
1106
			if (out_of_mem) {
1107
				p_end = i - 1;
1108
				return false;
1109
			}
1110
			p_len[i] = strlen(p_line[i]);
1111
			p_char[i] = '+';
1112
		}
1113
1114
		if (remove_special_line()) {
1115
			p_len[i - 1] -= 1;
1116
			(p_line[i - 1])[p_len[i - 1]] = 0;
1117
		}
1118
	}
1119
	if (reverse)		/* backwards patch? */
1120
		if (!pch_swap())
1121
			say("Not enough memory to swap next hunk!\n");
1122
#ifdef DEBUGGING
1123
	if (debug & 2) {
1124
		int	i;
1125
		char	special;
1126
1127
		for (i = 0; i <= p_end; i++) {
1128
			if (i == p_ptrn_lines)
1129
				special = '^';
1130
			else
1131
				special = ' ';
1132
			fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1133
			    special, p_line[i]);
1134
			fflush(stderr);
1135
		}
1136
	}
1137
#endif
1138
	if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1139
		p_char[p_end + 1] = '^';	/* add a stopper for apply_hunk */
1140
	return true;
1141
}
1142
1143
/*
1144
 * Input a line from the patch file, worrying about indentation.
1145
 */
1146
static char *
1147
pgets(char *bf, int sz, FILE *fp)
1148
{
1149
	char	*s, *ret = fgets(bf, sz, fp);
1150
	int	indent = 0;
1151
1152
	if (p_indent && ret != NULL) {
1153
		for (s = buf;
1154
		    indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1155
		    s++) {
1156
			if (*s == '\t')
1157
				indent += 8 - (indent % 7);
1158
			else
1159
				indent++;
1160
		}
1161
		if (buf != s && strlcpy(buf, s, buf_len) >= buf_len)
1162
			fatal("buffer too small in pgets()\n");
1163
	}
1164
	return ret;
1165
}
1166
1167
/*
1168
 * Reverse the old and new portions of the current hunk.
1169
 */
1170
bool
1171
pch_swap(void)
1172
{
1173
	char	**tp_line;	/* the text of the hunk */
1174
	short	*tp_len;	/* length of each line */
1175
	char	*tp_char;	/* +, -, and ! */
1176
	LINENUM	i;
1177
	LINENUM	n;
1178
	bool	blankline = false;
1179
	char	*s;
1180
1181
	i = p_first;
1182
	p_first = p_newfirst;
1183
	p_newfirst = i;
1184
1185
	/* make a scratch copy */
1186
1187
	tp_line = p_line;
1188
	tp_len = p_len;
1189
	tp_char = p_char;
1190
	p_line = NULL;	/* force set_hunkmax to allocate again */
1191
	p_len = NULL;
1192
	p_char = NULL;
1193
	set_hunkmax();
1194
	if (p_line == NULL || p_len == NULL || p_char == NULL) {
1195
1196
		free(p_line);
1197
		p_line = tp_line;
1198
		free(p_len);
1199
		p_len = tp_len;
1200
		free(p_char);
1201
		p_char = tp_char;
1202
		return false;	/* not enough memory to swap hunk! */
1203
	}
1204
	/* now turn the new into the old */
1205
1206
	i = p_ptrn_lines + 1;
1207
	if (tp_char[i] == '\n') {	/* account for possible blank line */
1208
		blankline = true;
1209
		i++;
1210
	}
1211
	if (p_efake >= 0) {	/* fix non-freeable ptr range */
1212
		if (p_efake <= i)
1213
			n = p_end - i + 1;
1214
		else
1215
			n = -i;
1216
		p_efake += n;
1217
		p_bfake += n;
1218
	}
1219
	for (n = 0; i <= p_end; i++, n++) {
1220
		p_line[n] = tp_line[i];
1221
		p_char[n] = tp_char[i];
1222
		if (p_char[n] == '+')
1223
			p_char[n] = '-';
1224
		p_len[n] = tp_len[i];
1225
	}
1226
	if (blankline) {
1227
		i = p_ptrn_lines + 1;
1228
		p_line[n] = tp_line[i];
1229
		p_char[n] = tp_char[i];
1230
		p_len[n] = tp_len[i];
1231
		n++;
1232
	}
1233
	if (p_char[0] != '=')
1234
		fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1235
		    p_input_line, p_char[0]);
1236
	p_char[0] = '*';
1237
	for (s = p_line[0]; *s; s++)
1238
		if (*s == '-')
1239
			*s = '*';
1240
1241
	/* now turn the old into the new */
1242
1243
	if (p_char[0] != '*')
1244
		fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1245
		    p_input_line, p_char[0]);
1246
	tp_char[0] = '=';
1247
	for (s = tp_line[0]; *s; s++)
1248
		if (*s == '*')
1249
			*s = '-';
1250
	for (i = 0; n <= p_end; i++, n++) {
1251
		p_line[n] = tp_line[i];
1252
		p_char[n] = tp_char[i];
1253
		if (p_char[n] == '-')
1254
			p_char[n] = '+';
1255
		p_len[n] = tp_len[i];
1256
	}
1257
1258
	if (i != p_ptrn_lines + 1)
1259
		fatal("Malformed patch at line %ld: expected %ld lines, "
1260
		    "got %ld\n",
1261
		    p_input_line, p_ptrn_lines + 1, i);
1262
1263
	i = p_ptrn_lines;
1264
	p_ptrn_lines = p_repl_lines;
1265
	p_repl_lines = i;
1266
1267
	free(tp_line);
1268
	free(tp_len);
1269
	free(tp_char);
1270
1271
	return true;
1272
}
1273
1274
/*
1275
 * Return the specified line position in the old file of the old context.
1276
 */
1277
LINENUM
1278
pch_first(void)
1279
{
1280
	return p_first;
1281
}
1282
1283
/*
1284
 * Return the number of lines of old context.
1285
 */
1286
LINENUM
1287
pch_ptrn_lines(void)
1288
{
1289
	return p_ptrn_lines;
1290
}
1291
1292
/*
1293
 * Return the probable line position in the new file of the first line.
1294
 */
1295
LINENUM
1296
pch_newfirst(void)
1297
{
1298
	return p_newfirst;
1299
}
1300
1301
/*
1302
 * Return the number of lines in the replacement text including context.
1303
 */
1304
LINENUM
1305
pch_repl_lines(void)
1306
{
1307
	return p_repl_lines;
1308
}
1309
1310
/*
1311
 * Return the number of lines in the whole hunk.
1312
 */
1313
LINENUM
1314
pch_end(void)
1315
{
1316
	return p_end;
1317
}
1318
1319
/*
1320
 * Return the number of context lines before the first changed line.
1321
 */
1322
LINENUM
1323
pch_context(void)
1324
{
1325
	return p_context;
1326
}
1327
1328
/*
1329
 * Return the length of a particular patch line.
1330
 */
1331
short
1332
pch_line_len(LINENUM line)
1333
{
1334
	return p_len[line];
1335
}
1336
1337
/*
1338
 * Return the control character (+, -, *, !, etc) for a patch line.
1339
 */
1340
char
1341
pch_char(LINENUM line)
1342
{
1343
	return p_char[line];
1344
}
1345
1346
/*
1347
 * Return a pointer to a particular patch line.
1348
 */
1349
char *
1350
pfetch(LINENUM line)
1351
{
1352
	return p_line[line];
1353
}
1354
1355
/*
1356
 * Return where in the patch file this hunk began, for error messages.
1357
 */
1358
LINENUM
1359
pch_hunk_beg(void)
1360
{
1361
	return p_hunk_beg;
1362
}
1363
1364
/*
1365
 * Apply an ed script by feeding ed itself.
1366
 */
1367
void
1368
do_ed_script(void)
1369
{
1370
	char	*t;
1371
	long	beginning_of_this_line;
1372
	FILE	*pipefp;
1373
1374
	pipefp = NULL;
1375
	if (!skip_rest_of_patch) {
1376
		if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1377
			unlink(TMPOUTNAME);
1378
			fatal("can't create temp file %s", TMPOUTNAME);
1379
		}
1380
		snprintf(buf, buf_len, "%s%s%s", _PATH_ED,
1381
		    verbose ? " " : " -s ", TMPOUTNAME);
1382
		pipefp = popen(buf, "w");
1383
	}
1384
	for (;;) {
1385
		beginning_of_this_line = ftell(pfp);
1386
		if (pgets(buf, buf_len, pfp) == NULL) {
1387
			next_intuit_at(beginning_of_this_line, p_input_line);
1388
			break;
1389
		}
1390
		p_input_line++;
1391
		for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1392
			;
1393
		/* POSIX defines allowed commands as {a,c,d,i,s} */
1394
		if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
1395
		    *t == 'd' || *t == 'i' || *t == 's')) {
1396
			if (pipefp != NULL)
1397
				fputs(buf, pipefp);
1398
			if (*t != 'd') {
1399
				while (pgets(buf, buf_len, pfp) != NULL) {
1400
					p_input_line++;
1401
					if (pipefp != NULL)
1402
						fputs(buf, pipefp);
1403
					if (strEQ(buf, ".\n"))
1404
						break;
1405
				}
1406
			}
1407
		} else {
1408
			next_intuit_at(beginning_of_this_line, p_input_line);
1409
			break;
1410
		}
1411
	}
1412
	if (pipefp == NULL)
1413
		return;
1414
	fprintf(pipefp, "w\n");
1415
	fprintf(pipefp, "q\n");
1416
	fflush(pipefp);
1417
	pclose(pipefp);
1418
	ignore_signals();
1419
	if (!check_only) {
1420
		if (move_file(TMPOUTNAME, outname) < 0) {
1421
			toutkeep = true;
1422
			chmod(TMPOUTNAME, filemode);
1423
		} else
1424
			chmod(outname, filemode);
1425
	}
1426
	set_signals(1);
1427
}
1428
1429
/*
1430
 * Choose the name of the file to be patched based on POSIX rules.
1431
 * NOTE: the POSIX rules are amazingly stupid and we only follow them
1432
 *       if the user specified --posix or set POSIXLY_CORRECT.
1433
 */
1434
static char *
1435
posix_name(const struct file_name *names, bool assume_exists)
1436
{
1437
	char *path = NULL;
1438
	int i;
1439
1440
	/*
1441
	 * POSIX states that the filename will be chosen from one
1442
	 * of the old, new and index names (in that order) if
1443
	 * the file exists relative to CWD after -p stripping.
1444
	 */
1445
	for (i = 0; i < MAX_FILE; i++) {
1446
		if (names[i].path != NULL && names[i].exists) {
1447
			path = names[i].path;
1448
			break;
1449
		}
1450
	}
1451
	if (path == NULL && !assume_exists) {
1452
		/*
1453
		 * No files found, look for something we can checkout from
1454
		 * RCS/SCCS dirs.  Same order as above.
1455
		 */
1456
		for (i = 0; i < MAX_FILE; i++) {
1457
			if (names[i].path != NULL &&
1458
			    (path = checked_in(names[i].path)) != NULL)
1459
				break;
1460
		}
1461
		/*
1462
		 * Still no match?  Check to see if the diff could be creating
1463
		 * a new file.
1464
		 */
1465
		if (path == NULL && ok_to_create_file &&
1466
		    names[NEW_FILE].path != NULL)
1467
			path = names[NEW_FILE].path;
1468
	}
1469
1470
	return path ? savestr(path) : NULL;
1471
}
1472
1473
/*
1474
 * Choose the name of the file to be patched based the "best" one
1475
 * available.
1476
 */
1477
static char *
1478
best_name(const struct file_name *names, bool assume_exists)
1479
{
1480
	size_t min_components, min_baselen, min_len, tmp;
1481
	char *best = NULL;
1482
	int i;
1483
1484
	/*
1485
	 * The "best" name is the one with the fewest number of path
1486
	 * components, the shortest basename length, and the shortest
1487
	 * overall length (in that order).  We only use the Index: file
1488
	 * if neither of the old or new files could be intuited from
1489
	 * the diff header.
1490
	 */
1491
	min_components = min_baselen = min_len = SIZE_MAX;
1492
	for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1493
		if (names[i].path == NULL ||
1494
		    (!names[i].exists && !assume_exists))
1495
			continue;
1496
		if ((tmp = num_components(names[i].path)) > min_components)
1497
			continue;
1498
		min_components = tmp;
1499
		if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1500
			continue;
1501
		min_baselen = tmp;
1502
		if ((tmp = strlen(names[i].path)) > min_len)
1503
			continue;
1504
		min_len = tmp;
1505
		best = names[i].path;
1506
	}
1507
	if (best == NULL) {
1508
		/*
1509
		 * No files found, look for something we can checkout from
1510
		 * RCS/SCCS dirs.  Logic is identical to that above...
1511
		 */
1512
		min_components = min_baselen = min_len = SIZE_MAX;
1513
		for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1514
			if (names[i].path == NULL ||
1515
			    checked_in(names[i].path) == NULL)
1516
				continue;
1517
			if ((tmp = num_components(names[i].path)) > min_components)
1518
				continue;
1519
			min_components = tmp;
1520
			if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1521
				continue;
1522
			min_baselen = tmp;
1523
			if ((tmp = strlen(names[i].path)) > min_len)
1524
				continue;
1525
			min_len = tmp;
1526
			best = names[i].path;
1527
		}
1528
		/*
1529
		 * Still no match?  Check to see if the diff could be creating
1530
		 * a new file.
1531
		 */
1532
		if (best == NULL && ok_to_create_file &&
1533
		    names[NEW_FILE].path != NULL)
1534
			best = names[NEW_FILE].path;
1535
	}
1536
1537
	return best ? savestr(best) : NULL;
1538
}
1539
1540
static size_t
1541
num_components(const char *path)
1542
{
1543
	size_t n;
1544
	const char *cp;
1545
1546
	for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1547
		while (*cp == '/')
1548
			cp++;		/* skip consecutive slashes */
1549
	}
1550
	return n;
1551
}
(-)usr.bin/patch/pch.h (+58 lines)
Added Link Here
1
/*
2
 * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#define OLD_FILE	0
33
#define NEW_FILE	1
34
#define INDEX_FILE	2
35
#define MAX_FILE	3
36
37
struct file_name {
38
	char *path;
39
	bool exists;
40
};
41
42
void		re_patch(void);
43
void		open_patch_file(const char *);
44
void		set_hunkmax(void);
45
bool		there_is_another_patch(void);
46
bool		another_hunk(void);
47
bool		pch_swap(void);
48
char		*pfetch(LINENUM);
49
short		pch_line_len(LINENUM);
50
LINENUM		pch_first(void);
51
LINENUM		pch_ptrn_lines(void);
52
LINENUM		pch_newfirst(void);
53
LINENUM		pch_repl_lines(void);
54
LINENUM		pch_end(void);
55
LINENUM		pch_context(void);
56
LINENUM		pch_hunk_beg(void);
57
char		pch_char(LINENUM);
58
void		do_ed_script(void);
(-)usr.bin/patch/util.c (+435 lines)
Added Link Here
1
/*
2
 * $OpenBSD: util.c,v 1.29 2004/11/19 20:00:57 otto Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
#include <sys/param.h>
33
#include <sys/stat.h>
34
35
#include <ctype.h>
36
#include <errno.h>
37
#include <fcntl.h>
38
#include <libgen.h>
39
#include <paths.h>
40
#include <stdarg.h>
41
#include <stdlib.h>
42
#include <stdio.h>
43
#include <string.h>
44
#include <unistd.h>
45
46
#include "common.h"
47
#include "util.h"
48
#include "backupfile.h"
49
#include "pathnames.h"
50
51
52
/* Rename a file, copying it if necessary. */
53
54
int
55
move_file(const char *from, const char *to)
56
{
57
	int	fromfd;
58
	ssize_t	i;
59
60
	/* to stdout? */
61
62
	if (strEQ(to, "-")) {
63
#ifdef DEBUGGING
64
		if (debug & 4)
65
			say("Moving %s to stdout.\n", from);
66
#endif
67
		fromfd = open(from, O_RDONLY);
68
		if (fromfd < 0)
69
			pfatal("internal error, can't reopen %s", from);
70
		while ((i = read(fromfd, buf, buf_len)) > 0)
71
			if (write(STDOUT_FILENO, buf, i) != i)
72
				pfatal("write failed");
73
		close(fromfd);
74
		return 0;
75
	}
76
	if (backup_file(to) < 0) {
77
		say("Can't backup %s, output is in %s: %s\n", to, from,
78
		    strerror(errno));
79
		return -1;
80
	}
81
#ifdef DEBUGGING
82
	if (debug & 4)
83
		say("Moving %s to %s.\n", from, to);
84
#endif
85
	if (rename(from, to) < 0) {
86
		if (errno != EXDEV || copy_file(from, to) < 0) {
87
			say("Can't create %s, output is in %s: %s\n",
88
			    to, from, strerror(errno));
89
			return -1;
90
		}
91
	}
92
	return 0;
93
}
94
95
/* Backup the original file.  */
96
97
int
98
backup_file(const char *orig)
99
{
100
	struct stat	filestat;
101
	char		bakname[MAXPATHLEN], *s, *simplename;
102
	dev_t		orig_device;
103
	ino_t		orig_inode;
104
105
	if (backup_type == none || stat(orig, &filestat) != 0)
106
		return 0;			/* nothing to do */
107
	/*
108
	 * If the user used zero prefixes or suffixes, then
109
	 * he doesn't want backups.  Yet we have to remove
110
	 * orig to break possible hardlinks.
111
	 */
112
	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
113
		unlink(orig);
114
		return 0;
115
	}
116
	orig_device = filestat.st_dev;
117
	orig_inode = filestat.st_ino;
118
119
	if (origprae) {
120
		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
121
		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
122
			fatal("filename %s too long for buffer\n", origprae);
123
	} else {
124
		if ((s = find_backup_file_name(orig)) == NULL)
125
			fatal("out of memory\n");
126
		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
127
			fatal("filename %s too long for buffer\n", s);
128
		free(s);
129
	}
130
131
	if ((simplename = strrchr(bakname, '/')) != NULL)
132
		simplename = simplename + 1;
133
	else
134
		simplename = bakname;
135
136
	/*
137
	 * Find a backup name that is not the same file. Change the
138
	 * first lowercase char into uppercase; if that isn't
139
	 * sufficient, chop off the first char and try again.
140
	 */
141
	while (stat(bakname, &filestat) == 0 &&
142
	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
143
		/* Skip initial non-lowercase chars.  */
144
		for (s = simplename; *s && !islower((unsigned char)*s); s++)
145
			;
146
		if (*s)
147
			*s = toupper((unsigned char)*s);
148
		else
149
			memmove(simplename, simplename + 1,
150
			    strlen(simplename + 1) + 1);
151
	}
152
#ifdef DEBUGGING
153
	if (debug & 4)
154
		say("Moving %s to %s.\n", orig, bakname);
155
#endif
156
	if (rename(orig, bakname) < 0) {
157
		if (errno != EXDEV || copy_file(orig, bakname) < 0)
158
			return -1;
159
	}
160
	return 0;
161
}
162
163
/*
164
 * Copy a file.
165
 */
166
int
167
copy_file(const char *from, const char *to)
168
{
169
	int	tofd, fromfd;
170
	ssize_t	i;
171
172
	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
173
	if (tofd < 0)
174
		return -1;
175
	fromfd = open(from, O_RDONLY, 0);
176
	if (fromfd < 0)
177
		pfatal("internal error, can't reopen %s", from);
178
	while ((i = read(fromfd, buf, buf_len)) > 0)
179
		if (write(tofd, buf, i) != i)
180
			pfatal("write to %s failed", to);
181
	close(fromfd);
182
	close(tofd);
183
	return 0;
184
}
185
186
/*
187
 * Allocate a unique area for a string.
188
 */
189
char *
190
savestr(const char *s)
191
{
192
	char	*rv;
193
194
	if (!s)
195
		s = "Oops";
196
	rv = strdup(s);
197
	if (rv == NULL) {
198
		if (using_plan_a)
199
			out_of_mem = true;
200
		else
201
			fatal("out of memory\n");
202
	}
203
	return rv;
204
}
205
206
/*
207
 * Vanilla terminal output (buffered).
208
 */
209
void
210
say(const char *fmt, ...)
211
{
212
	va_list	ap;
213
214
	va_start(ap, fmt);
215
	vfprintf(stderr, fmt, ap);
216
	va_end(ap);
217
	fflush(stderr);
218
}
219
220
/*
221
 * Terminal output, pun intended.
222
 */
223
void
224
fatal(const char *fmt, ...)
225
{
226
	va_list	ap;
227
228
	va_start(ap, fmt);
229
	fprintf(stderr, "patch: **** ");
230
	vfprintf(stderr, fmt, ap);
231
	va_end(ap);
232
	my_exit(2);
233
}
234
235
/*
236
 * Say something from patch, something from the system, then silence . . .
237
 */
238
void
239
pfatal(const char *fmt, ...)
240
{
241
	va_list	ap;
242
	int	errnum = errno;
243
244
	fprintf(stderr, "patch: **** ");
245
	va_start(ap, fmt);
246
	vfprintf(stderr, fmt, ap);
247
	va_end(ap);
248
	fprintf(stderr, ": %s\n", strerror(errnum));
249
	my_exit(2);
250
}
251
252
/*
253
 * Get a response from the user via /dev/tty
254
 */
255
void
256
ask(const char *fmt, ...)
257
{
258
	va_list	ap;
259
	ssize_t	nr = 0;
260
	static	int ttyfd = -1;
261
262
	va_start(ap, fmt);
263
	vfprintf(stdout, fmt, ap);
264
	va_end(ap);
265
	fflush(stdout);
266
	if (ttyfd < 0)
267
		ttyfd = open(_PATH_TTY, O_RDONLY);
268
	if (ttyfd >= 0) {
269
		if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
270
		    buf[nr - 1] == '\n')
271
			buf[nr - 1] = '\0';
272
	}
273
	if (ttyfd < 0 || nr <= 0) {
274
		/* no tty or error reading, pretend user entered 'return' */
275
		putchar('\n');
276
		buf[0] = '\0';
277
	}
278
}
279
280
/*
281
 * How to handle certain events when not in a critical region.
282
 */
283
void
284
set_signals(int reset)
285
{
286
	static sig_t	hupval, intval;
287
288
	if (!reset) {
289
		hupval = signal(SIGHUP, SIG_IGN);
290
		if (hupval != SIG_IGN)
291
			hupval = my_exit;
292
		intval = signal(SIGINT, SIG_IGN);
293
		if (intval != SIG_IGN)
294
			intval = my_exit;
295
	}
296
	signal(SIGHUP, hupval);
297
	signal(SIGINT, intval);
298
}
299
300
/*
301
 * How to handle certain events when in a critical region.
302
 */
303
void
304
ignore_signals(void)
305
{
306
	signal(SIGHUP, SIG_IGN);
307
	signal(SIGINT, SIG_IGN);
308
}
309
310
/*
311
 * Make sure we'll have the directories to create a file. If `striplast' is
312
 * true, ignore the last element of `filename'.
313
 */
314
315
void
316
makedirs(const char *filename, bool striplast)
317
{
318
	char	*tmpbuf;
319
320
	if ((tmpbuf = strdup(filename)) == NULL)
321
		fatal("out of memory\n");
322
323
	if (striplast) {
324
		char	*s = strrchr(tmpbuf, '/');
325
		if (s == NULL)
326
			return;	/* nothing to be done */
327
		*s = '\0';
328
	}
329
	if (snprintf(buf, buf_len, "%s -p %s", _PATH_MKDIR, tmpbuf)
330
	    >= (int)buf_len)
331
		fatal("buffer too small to hold %.20s...\n", tmpbuf);
332
333
	if (system(buf))
334
		pfatal("%.40s failed", buf);
335
}
336
337
/*
338
 * Make filenames more reasonable.
339
 */
340
char *
341
fetchname(const char *at, bool *exists, int strip_leading)
342
{
343
	char		*fullname, *name, *t;
344
	int		sleading, tab;
345
	struct stat	filestat;
346
347
	if (at == NULL || *at == '\0')
348
		return NULL;
349
	while (isspace((unsigned char)*at))
350
		at++;
351
#ifdef DEBUGGING
352
	if (debug & 128)
353
		say("fetchname %s %d\n", at, strip_leading);
354
#endif
355
	/* So files can be created by diffing against /dev/null.  */
356
	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
357
		return NULL;
358
	name = fullname = t = savestr(at);
359
360
	tab = strchr(t, '\t') != NULL;
361
	/* Strip off up to `strip_leading' path components and NUL terminate. */
362
	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
363
	    !isspace((unsigned char)*t)); t++) {
364
		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
365
			if (--sleading >= 0)
366
				name = t + 1;
367
	}
368
	*t = '\0';
369
370
	/*
371
	 * If no -p option was given (957 is the default value!), we were
372
	 * given a relative pathname, and the leading directories that we
373
	 * just stripped off all exist, put them back on.
374
	 */
375
	if (strip_leading == 957 && name != fullname && *fullname != '/') {
376
		name[-1] = '\0';
377
		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
378
			name[-1] = '/';
379
			name = fullname;
380
		}
381
	}
382
	name = savestr(name);
383
	free(fullname);
384
385
	*exists = stat(name, &filestat) == 0;
386
	return name;
387
}
388
389
/*
390
 * Takes the name returned by fetchname and looks in RCS/SCCS directories
391
 * for a checked in version.
392
 */
393
char *
394
checked_in(char *file)
395
{
396
	char		*filebase, *filedir, tmpbuf[MAXPATHLEN];
397
	struct stat	filestat;
398
399
	filebase = basename(file);
400
	filedir = dirname(file);
401
402
#define try(f, a1, a2, a3) \
403
(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
404
405
	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
406
	    try("%s/RCS/%s%s", filedir, filebase, "") ||
407
	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
408
	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
409
	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
410
		return file;
411
412
	return NULL;
413
}
414
415
void
416
version(void)
417
{
418
	fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n");
419
	my_exit(EXIT_SUCCESS);
420
}
421
422
/*
423
 * Exit with cleanup.
424
 */
425
void
426
my_exit(int status)
427
{
428
	unlink(TMPINNAME);
429
	if (!toutkeep)
430
		unlink(TMPOUTNAME);
431
	if (!trejkeep)
432
		unlink(TMPREJNAME);
433
	unlink(TMPPATNAME);
434
	exit(status);
435
}
(-)usr.bin/patch/util.h (+50 lines)
Added Link Here
1
/*
2
 * $OpenBSD: util.h,v 1.13 2004/08/05 21:47:24 deraadt Exp $
3
 * $DragonFly$
4
 */
5
6
/*
7
 * patch - a program to apply diffs to original files
8
 * 
9
 * Copyright 1986, Larry Wall
10
 * 
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following condition is met:
13
 * 1. Redistributions of source code must retain the above copyright notice,
14
 * this condition and the following disclaimer.
15
 * 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 * 
28
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29
 * behaviour
30
 */
31
32
char		*fetchname(const char *, bool *, int);
33
char		*checked_in(char *);
34
int		backup_file(const char *);
35
int		move_file(const char *, const char *);
36
int		copy_file(const char *, const char *);
37
void		say(const char *, ...)
38
		    __attribute__((__format__(__printf__, 1, 2)));
39
void		fatal(const char *, ...)
40
		    __attribute__((__format__(__printf__, 1, 2)));
41
void		pfatal(const char *, ...)
42
		    __attribute__((__format__(__printf__, 1, 2)));
43
void		ask(const char *, ...)
44
		    __attribute__((__format__(__printf__, 1, 2)));
45
char		*savestr(const char *);
46
void		set_signals(int);
47
void		ignore_signals(void);
48
void		makedirs(const char *, bool);
49
void		version(void);
50
void		my_exit(int) __attribute__((noreturn));

Return to bug 99173