1246074Sgabor/*-
2246074Sgabor * Copyright 1986, Larry Wall
3246074Sgabor *
4246074Sgabor * Redistribution and use in source and binary forms, with or without
5246074Sgabor * modification, are permitted provided that the following condition is met:
6246074Sgabor * 1. Redistributions of source code must retain the above copyright notice,
7246074Sgabor * this condition and the following disclaimer.
8246074Sgabor *
9246074Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
10246074Sgabor * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
11246074Sgabor * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12246074Sgabor * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
13246074Sgabor * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14246074Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15246074Sgabor * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16246074Sgabor * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
17246074Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
18246074Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
19246074Sgabor * SUCH DAMAGE.
20246074Sgabor *
21246074Sgabor * patch - a program to apply diffs to original files
22246074Sgabor *
23246074Sgabor * -C option added in 1998, original code by Marc Espie, based on FreeBSD
24246074Sgabor * behaviour
25246074Sgabor *
26246091Sdelphij * $OpenBSD: util.c,v 1.35 2010/07/24 01:10:12 ray Exp $
27246091Sdelphij * $FreeBSD$
28246074Sgabor */
29246074Sgabor
30246074Sgabor#include <sys/param.h>
31246074Sgabor#include <sys/stat.h>
32246074Sgabor
33246074Sgabor#include <ctype.h>
34246074Sgabor#include <errno.h>
35246074Sgabor#include <fcntl.h>
36246074Sgabor#include <libgen.h>
37246074Sgabor#include <paths.h>
38246074Sgabor#include <signal.h>
39246074Sgabor#include <stdarg.h>
40246074Sgabor#include <stdlib.h>
41246074Sgabor#include <stdio.h>
42246074Sgabor#include <string.h>
43246074Sgabor#include <unistd.h>
44246074Sgabor
45246074Sgabor#include "common.h"
46246074Sgabor#include "util.h"
47246074Sgabor#include "backupfile.h"
48246074Sgabor#include "pathnames.h"
49246074Sgabor
50246074Sgabor/* Rename a file, copying it if necessary. */
51246074Sgabor
52246074Sgaborint
53246074Sgabormove_file(const char *from, const char *to)
54246074Sgabor{
55246074Sgabor	int	fromfd;
56246074Sgabor	ssize_t	i;
57246074Sgabor
58246074Sgabor	/* to stdout? */
59246074Sgabor
60246074Sgabor	if (strEQ(to, "-")) {
61246074Sgabor#ifdef DEBUGGING
62246074Sgabor		if (debug & 4)
63246074Sgabor			say("Moving %s to stdout.\n", from);
64246074Sgabor#endif
65246074Sgabor		fromfd = open(from, O_RDONLY);
66246074Sgabor		if (fromfd < 0)
67246074Sgabor			pfatal("internal error, can't reopen %s", from);
68246074Sgabor		while ((i = read(fromfd, buf, buf_size)) > 0)
69246074Sgabor			if (write(STDOUT_FILENO, buf, i) != i)
70246074Sgabor				pfatal("write failed");
71246074Sgabor		close(fromfd);
72246074Sgabor		return 0;
73246074Sgabor	}
74246074Sgabor	if (backup_file(to) < 0) {
75246074Sgabor		say("Can't backup %s, output is in %s: %s\n", to, from,
76246074Sgabor		    strerror(errno));
77246074Sgabor		return -1;
78246074Sgabor	}
79246074Sgabor#ifdef DEBUGGING
80246074Sgabor	if (debug & 4)
81246074Sgabor		say("Moving %s to %s.\n", from, to);
82246074Sgabor#endif
83246074Sgabor	if (rename(from, to) < 0) {
84246074Sgabor		if (errno != EXDEV || copy_file(from, to) < 0) {
85246074Sgabor			say("Can't create %s, output is in %s: %s\n",
86246074Sgabor			    to, from, strerror(errno));
87246074Sgabor			return -1;
88246074Sgabor		}
89246074Sgabor	}
90246074Sgabor	return 0;
91246074Sgabor}
92246074Sgabor
93246074Sgabor/* Backup the original file.  */
94246074Sgabor
95246074Sgaborint
96246074Sgaborbackup_file(const char *orig)
97246074Sgabor{
98246074Sgabor	struct stat	filestat;
99246074Sgabor	char		bakname[MAXPATHLEN], *s, *simplename;
100246074Sgabor	dev_t		orig_device;
101246074Sgabor	ino_t		orig_inode;
102246074Sgabor
103246074Sgabor	if (backup_type == none || stat(orig, &filestat) != 0)
104246074Sgabor		return 0;			/* nothing to do */
105246074Sgabor	/*
106246074Sgabor	 * If the user used zero prefixes or suffixes, then
107246074Sgabor	 * he doesn't want backups.  Yet we have to remove
108246074Sgabor	 * orig to break possible hardlinks.
109246074Sgabor	 */
110246074Sgabor	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
111246074Sgabor		unlink(orig);
112246074Sgabor		return 0;
113246074Sgabor	}
114246074Sgabor	orig_device = filestat.st_dev;
115246074Sgabor	orig_inode = filestat.st_ino;
116246074Sgabor
117246074Sgabor	if (origprae) {
118246074Sgabor		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
119246074Sgabor		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
120246074Sgabor			fatal("filename %s too long for buffer\n", origprae);
121246074Sgabor	} else {
122246074Sgabor		if ((s = find_backup_file_name(orig)) == NULL)
123246074Sgabor			fatal("out of memory\n");
124246074Sgabor		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
125246074Sgabor			fatal("filename %s too long for buffer\n", s);
126246074Sgabor		free(s);
127246074Sgabor	}
128246074Sgabor
129246074Sgabor	if ((simplename = strrchr(bakname, '/')) != NULL)
130246074Sgabor		simplename = simplename + 1;
131246074Sgabor	else
132246074Sgabor		simplename = bakname;
133246074Sgabor
134246074Sgabor	/*
135246074Sgabor	 * Find a backup name that is not the same file. Change the
136246074Sgabor	 * first lowercase char into uppercase; if that isn't
137246074Sgabor	 * sufficient, chop off the first char and try again.
138246074Sgabor	 */
139246074Sgabor	while (stat(bakname, &filestat) == 0 &&
140246074Sgabor	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
141246074Sgabor		/* Skip initial non-lowercase chars.  */
142246074Sgabor		for (s = simplename; *s && !islower((unsigned char)*s); s++)
143246074Sgabor			;
144246074Sgabor		if (*s)
145246074Sgabor			*s = toupper((unsigned char)*s);
146246074Sgabor		else
147246074Sgabor			memmove(simplename, simplename + 1,
148246074Sgabor			    strlen(simplename + 1) + 1);
149246074Sgabor	}
150246074Sgabor#ifdef DEBUGGING
151246074Sgabor	if (debug & 4)
152246074Sgabor		say("Moving %s to %s.\n", orig, bakname);
153246074Sgabor#endif
154246074Sgabor	if (rename(orig, bakname) < 0) {
155246074Sgabor		if (errno != EXDEV || copy_file(orig, bakname) < 0)
156246074Sgabor			return -1;
157246074Sgabor	}
158246074Sgabor	return 0;
159246074Sgabor}
160246074Sgabor
161246074Sgabor/*
162246074Sgabor * Copy a file.
163246074Sgabor */
164246074Sgaborint
165246074Sgaborcopy_file(const char *from, const char *to)
166246074Sgabor{
167246074Sgabor	int	tofd, fromfd;
168246074Sgabor	ssize_t	i;
169246074Sgabor
170246074Sgabor	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
171246074Sgabor	if (tofd < 0)
172246074Sgabor		return -1;
173246074Sgabor	fromfd = open(from, O_RDONLY, 0);
174246074Sgabor	if (fromfd < 0)
175246074Sgabor		pfatal("internal error, can't reopen %s", from);
176246074Sgabor	while ((i = read(fromfd, buf, buf_size)) > 0)
177246074Sgabor		if (write(tofd, buf, i) != i)
178246074Sgabor			pfatal("write to %s failed", to);
179246074Sgabor	close(fromfd);
180246074Sgabor	close(tofd);
181246074Sgabor	return 0;
182246074Sgabor}
183246074Sgabor
184246074Sgabor/*
185246074Sgabor * Allocate a unique area for a string.
186246074Sgabor */
187246074Sgaborchar *
188246074Sgaborsavestr(const char *s)
189246074Sgabor{
190246074Sgabor	char	*rv;
191246074Sgabor
192246074Sgabor	if (!s)
193246074Sgabor		s = "Oops";
194246074Sgabor	rv = strdup(s);
195246074Sgabor	if (rv == NULL) {
196246074Sgabor		if (using_plan_a)
197246074Sgabor			out_of_mem = true;
198246074Sgabor		else
199246074Sgabor			fatal("out of memory\n");
200246074Sgabor	}
201246074Sgabor	return rv;
202246074Sgabor}
203246074Sgabor
204246074Sgabor/*
205246074Sgabor * Vanilla terminal output (buffered).
206246074Sgabor */
207246074Sgaborvoid
208246074Sgaborsay(const char *fmt, ...)
209246074Sgabor{
210246074Sgabor	va_list	ap;
211246074Sgabor
212246074Sgabor	va_start(ap, fmt);
213246091Sdelphij	vfprintf(stdout, fmt, ap);
214246074Sgabor	va_end(ap);
215246091Sdelphij	fflush(stdout);
216246074Sgabor}
217246074Sgabor
218246074Sgabor/*
219246074Sgabor * Terminal output, pun intended.
220246074Sgabor */
221246074Sgaborvoid
222246074Sgaborfatal(const char *fmt, ...)
223246074Sgabor{
224246074Sgabor	va_list	ap;
225246074Sgabor
226246074Sgabor	va_start(ap, fmt);
227246074Sgabor	fprintf(stderr, "patch: **** ");
228246074Sgabor	vfprintf(stderr, fmt, ap);
229246074Sgabor	va_end(ap);
230246074Sgabor	my_exit(2);
231246074Sgabor}
232246074Sgabor
233246074Sgabor/*
234246074Sgabor * Say something from patch, something from the system, then silence . . .
235246074Sgabor */
236246074Sgaborvoid
237246074Sgaborpfatal(const char *fmt, ...)
238246074Sgabor{
239246074Sgabor	va_list	ap;
240246074Sgabor	int	errnum = errno;
241246074Sgabor
242246074Sgabor	fprintf(stderr, "patch: **** ");
243246074Sgabor	va_start(ap, fmt);
244246074Sgabor	vfprintf(stderr, fmt, ap);
245246074Sgabor	va_end(ap);
246246074Sgabor	fprintf(stderr, ": %s\n", strerror(errnum));
247246074Sgabor	my_exit(2);
248246074Sgabor}
249246074Sgabor
250246074Sgabor/*
251246074Sgabor * Get a response from the user via /dev/tty
252246074Sgabor */
253246074Sgaborvoid
254246074Sgaborask(const char *fmt, ...)
255246074Sgabor{
256246074Sgabor	va_list	ap;
257246074Sgabor	ssize_t	nr = 0;
258246074Sgabor	static	int ttyfd = -1;
259246074Sgabor
260246074Sgabor	va_start(ap, fmt);
261246074Sgabor	vfprintf(stdout, fmt, ap);
262246074Sgabor	va_end(ap);
263246074Sgabor	fflush(stdout);
264246074Sgabor	if (ttyfd < 0)
265246074Sgabor		ttyfd = open(_PATH_TTY, O_RDONLY);
266246074Sgabor	if (ttyfd >= 0) {
267246074Sgabor		if ((nr = read(ttyfd, buf, buf_size)) > 0 &&
268246074Sgabor		    buf[nr - 1] == '\n')
269246074Sgabor			buf[nr - 1] = '\0';
270246074Sgabor	}
271246074Sgabor	if (ttyfd < 0 || nr <= 0) {
272246074Sgabor		/* no tty or error reading, pretend user entered 'return' */
273246074Sgabor		putchar('\n');
274246074Sgabor		buf[0] = '\0';
275246074Sgabor	}
276246074Sgabor}
277246074Sgabor
278246074Sgabor/*
279246074Sgabor * How to handle certain events when not in a critical region.
280246074Sgabor */
281246074Sgaborvoid
282246074Sgaborset_signals(int reset)
283246074Sgabor{
284246074Sgabor	static sig_t	hupval, intval;
285246074Sgabor
286246074Sgabor	if (!reset) {
287246074Sgabor		hupval = signal(SIGHUP, SIG_IGN);
288246074Sgabor		if (hupval != SIG_IGN)
289246074Sgabor			hupval = my_exit;
290246074Sgabor		intval = signal(SIGINT, SIG_IGN);
291246074Sgabor		if (intval != SIG_IGN)
292246074Sgabor			intval = my_exit;
293246074Sgabor	}
294246074Sgabor	signal(SIGHUP, hupval);
295246074Sgabor	signal(SIGINT, intval);
296246074Sgabor}
297246074Sgabor
298246074Sgabor/*
299246074Sgabor * How to handle certain events when in a critical region.
300246074Sgabor */
301246074Sgaborvoid
302246074Sgaborignore_signals(void)
303246074Sgabor{
304246074Sgabor	signal(SIGHUP, SIG_IGN);
305246074Sgabor	signal(SIGINT, SIG_IGN);
306246074Sgabor}
307246074Sgabor
308246074Sgabor/*
309246074Sgabor * Make sure we'll have the directories to create a file. If `striplast' is
310246074Sgabor * true, ignore the last element of `filename'.
311246074Sgabor */
312246074Sgabor
313246074Sgaborvoid
314246074Sgabormakedirs(const char *filename, bool striplast)
315246074Sgabor{
316246074Sgabor	char	*tmpbuf;
317246074Sgabor
318246074Sgabor	if ((tmpbuf = strdup(filename)) == NULL)
319246074Sgabor		fatal("out of memory\n");
320246074Sgabor
321246074Sgabor	if (striplast) {
322246074Sgabor		char	*s = strrchr(tmpbuf, '/');
323246074Sgabor		if (s == NULL) {
324246074Sgabor			free(tmpbuf);
325246074Sgabor			return;	/* nothing to be done */
326246074Sgabor		}
327246074Sgabor		*s = '\0';
328246074Sgabor	}
329246074Sgabor	if (mkpath(tmpbuf) != 0)
330246074Sgabor		pfatal("creation of %s failed", tmpbuf);
331246074Sgabor	free(tmpbuf);
332246074Sgabor}
333246074Sgabor
334246074Sgabor/*
335246074Sgabor * Make filenames more reasonable.
336246074Sgabor */
337246074Sgaborchar *
338246074Sgaborfetchname(const char *at, bool *exists, int strip_leading)
339246074Sgabor{
340246074Sgabor	char		*fullname, *name, *t;
341246074Sgabor	int		sleading, tab;
342246074Sgabor	struct stat	filestat;
343246074Sgabor
344246074Sgabor	if (at == NULL || *at == '\0')
345246074Sgabor		return NULL;
346246074Sgabor	while (isspace((unsigned char)*at))
347246074Sgabor		at++;
348246074Sgabor#ifdef DEBUGGING
349246074Sgabor	if (debug & 128)
350246074Sgabor		say("fetchname %s %d\n", at, strip_leading);
351246074Sgabor#endif
352246074Sgabor	/* So files can be created by diffing against /dev/null.  */
353246074Sgabor	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
354246074Sgabor		return NULL;
355246074Sgabor	name = fullname = t = savestr(at);
356246074Sgabor
357246074Sgabor	tab = strchr(t, '\t') != NULL;
358246074Sgabor	/* Strip off up to `strip_leading' path components and NUL terminate. */
359246074Sgabor	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
360246074Sgabor	    !isspace((unsigned char)*t)); t++) {
361246074Sgabor		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
362246074Sgabor			if (--sleading >= 0)
363246074Sgabor				name = t + 1;
364246074Sgabor	}
365246074Sgabor	*t = '\0';
366246074Sgabor
367246074Sgabor	/*
368246074Sgabor	 * If no -p option was given (957 is the default value!), we were
369246074Sgabor	 * given a relative pathname, and the leading directories that we
370246074Sgabor	 * just stripped off all exist, put them back on.
371246074Sgabor	 */
372246074Sgabor	if (strip_leading == 957 && name != fullname && *fullname != '/') {
373246074Sgabor		name[-1] = '\0';
374246074Sgabor		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
375246074Sgabor			name[-1] = '/';
376246074Sgabor			name = fullname;
377246074Sgabor		}
378246074Sgabor	}
379246074Sgabor	name = savestr(name);
380246074Sgabor	free(fullname);
381246074Sgabor
382246074Sgabor	*exists = stat(name, &filestat) == 0;
383246074Sgabor	return name;
384246074Sgabor}
385246074Sgabor
386246074Sgabor/*
387246074Sgabor * Takes the name returned by fetchname and looks in RCS/SCCS directories
388246074Sgabor * for a checked in version.
389246074Sgabor */
390246074Sgaborchar *
391246074Sgaborchecked_in(char *file)
392246074Sgabor{
393246074Sgabor	char		*filebase, *filedir, tmpbuf[MAXPATHLEN];
394246074Sgabor	struct stat	filestat;
395246074Sgabor
396246074Sgabor	filebase = basename(file);
397246074Sgabor	filedir = dirname(file);
398246074Sgabor
399246074Sgabor#define try(f, a1, a2, a3) \
400246074Sgabor(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
401246074Sgabor
402246074Sgabor	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
403246074Sgabor	    try("%s/RCS/%s%s", filedir, filebase, "") ||
404246074Sgabor	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
405246074Sgabor	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
406246074Sgabor	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
407246074Sgabor		return file;
408246074Sgabor
409246074Sgabor	return NULL;
410246074Sgabor}
411246074Sgabor
412246074Sgaborvoid
413246074Sgaborversion(void)
414246074Sgabor{
415255894Sdelphij	fprintf(stderr, "patch 2.0-12u10 FreeBSD\n");
416246074Sgabor	my_exit(EXIT_SUCCESS);
417246074Sgabor}
418246074Sgabor
419246074Sgabor/*
420246074Sgabor * Exit with cleanup.
421246074Sgabor */
422246074Sgaborvoid
423246074Sgabormy_exit(int status)
424246074Sgabor{
425246074Sgabor	unlink(TMPINNAME);
426246074Sgabor	if (!toutkeep)
427246074Sgabor		unlink(TMPOUTNAME);
428246074Sgabor	if (!trejkeep)
429246074Sgabor		unlink(TMPREJNAME);
430246074Sgabor	unlink(TMPPATNAME);
431246074Sgabor	exit(status);
432246074Sgabor}
433