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: stable/11/usr.bin/patch/util.c 355351 2019-12-03 18:55:09Z kevans $
28246074Sgabor */
29246074Sgabor
30246074Sgabor#include <sys/stat.h>
31246074Sgabor
32246074Sgabor#include <ctype.h>
33246074Sgabor#include <errno.h>
34246074Sgabor#include <fcntl.h>
35246074Sgabor#include <libgen.h>
36281800Spfg#include <limits.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;
99281800Spfg	char		bakname[PATH_MAX], *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/*
205276218Spfg * Allocate a unique area for a string.  Call fatal if out of memory.
206276218Spfg */
207276218Spfgchar *
208276218Spfgxstrdup(const char *s)
209276218Spfg{
210276218Spfg	char	*rv;
211276218Spfg
212276218Spfg	if (!s)
213276218Spfg		s = "Oops";
214276218Spfg	rv = strdup(s);
215276218Spfg	if (rv == NULL)
216276218Spfg		fatal("out of memory\n");
217276218Spfg	return rv;
218276218Spfg}
219276218Spfg
220276218Spfg/*
221246074Sgabor * Vanilla terminal output (buffered).
222246074Sgabor */
223246074Sgaborvoid
224246074Sgaborsay(const char *fmt, ...)
225246074Sgabor{
226246074Sgabor	va_list	ap;
227246074Sgabor
228246074Sgabor	va_start(ap, fmt);
229246091Sdelphij	vfprintf(stdout, fmt, ap);
230246074Sgabor	va_end(ap);
231246091Sdelphij	fflush(stdout);
232246074Sgabor}
233246074Sgabor
234246074Sgabor/*
235246074Sgabor * Terminal output, pun intended.
236246074Sgabor */
237246074Sgaborvoid
238246074Sgaborfatal(const char *fmt, ...)
239246074Sgabor{
240246074Sgabor	va_list	ap;
241246074Sgabor
242246074Sgabor	va_start(ap, fmt);
243246074Sgabor	fprintf(stderr, "patch: **** ");
244246074Sgabor	vfprintf(stderr, fmt, ap);
245246074Sgabor	va_end(ap);
246246074Sgabor	my_exit(2);
247246074Sgabor}
248246074Sgabor
249246074Sgabor/*
250246074Sgabor * Say something from patch, something from the system, then silence . . .
251246074Sgabor */
252246074Sgaborvoid
253246074Sgaborpfatal(const char *fmt, ...)
254246074Sgabor{
255246074Sgabor	va_list	ap;
256246074Sgabor	int	errnum = errno;
257246074Sgabor
258246074Sgabor	fprintf(stderr, "patch: **** ");
259246074Sgabor	va_start(ap, fmt);
260246074Sgabor	vfprintf(stderr, fmt, ap);
261246074Sgabor	va_end(ap);
262246074Sgabor	fprintf(stderr, ": %s\n", strerror(errnum));
263246074Sgabor	my_exit(2);
264246074Sgabor}
265246074Sgabor
266246074Sgabor/*
267246074Sgabor * Get a response from the user via /dev/tty
268246074Sgabor */
269246074Sgaborvoid
270246074Sgaborask(const char *fmt, ...)
271246074Sgabor{
272246074Sgabor	va_list	ap;
273246074Sgabor	ssize_t	nr = 0;
274246074Sgabor	static	int ttyfd = -1;
275246074Sgabor
276246074Sgabor	va_start(ap, fmt);
277246074Sgabor	vfprintf(stdout, fmt, ap);
278246074Sgabor	va_end(ap);
279246074Sgabor	fflush(stdout);
280246074Sgabor	if (ttyfd < 0)
281246074Sgabor		ttyfd = open(_PATH_TTY, O_RDONLY);
282246074Sgabor	if (ttyfd >= 0) {
283246074Sgabor		if ((nr = read(ttyfd, buf, buf_size)) > 0 &&
284246074Sgabor		    buf[nr - 1] == '\n')
285246074Sgabor			buf[nr - 1] = '\0';
286246074Sgabor	}
287246074Sgabor	if (ttyfd < 0 || nr <= 0) {
288246074Sgabor		/* no tty or error reading, pretend user entered 'return' */
289246074Sgabor		putchar('\n');
290246074Sgabor		buf[0] = '\0';
291246074Sgabor	}
292246074Sgabor}
293246074Sgabor
294246074Sgabor/*
295246074Sgabor * How to handle certain events when not in a critical region.
296246074Sgabor */
297246074Sgaborvoid
298246074Sgaborset_signals(int reset)
299246074Sgabor{
300246074Sgabor	static sig_t	hupval, intval;
301246074Sgabor
302246074Sgabor	if (!reset) {
303246074Sgabor		hupval = signal(SIGHUP, SIG_IGN);
304246074Sgabor		if (hupval != SIG_IGN)
305246074Sgabor			hupval = my_exit;
306246074Sgabor		intval = signal(SIGINT, SIG_IGN);
307246074Sgabor		if (intval != SIG_IGN)
308246074Sgabor			intval = my_exit;
309246074Sgabor	}
310246074Sgabor	signal(SIGHUP, hupval);
311246074Sgabor	signal(SIGINT, intval);
312246074Sgabor}
313246074Sgabor
314246074Sgabor/*
315246074Sgabor * How to handle certain events when in a critical region.
316246074Sgabor */
317246074Sgaborvoid
318246074Sgaborignore_signals(void)
319246074Sgabor{
320246074Sgabor	signal(SIGHUP, SIG_IGN);
321246074Sgabor	signal(SIGINT, SIG_IGN);
322246074Sgabor}
323246074Sgabor
324246074Sgabor/*
325246074Sgabor * Make sure we'll have the directories to create a file. If `striplast' is
326246074Sgabor * true, ignore the last element of `filename'.
327246074Sgabor */
328246074Sgabor
329246074Sgaborvoid
330246074Sgabormakedirs(const char *filename, bool striplast)
331246074Sgabor{
332246074Sgabor	char	*tmpbuf;
333246074Sgabor
334246074Sgabor	if ((tmpbuf = strdup(filename)) == NULL)
335246074Sgabor		fatal("out of memory\n");
336246074Sgabor
337246074Sgabor	if (striplast) {
338246074Sgabor		char	*s = strrchr(tmpbuf, '/');
339246074Sgabor		if (s == NULL) {
340246074Sgabor			free(tmpbuf);
341246074Sgabor			return;	/* nothing to be done */
342246074Sgabor		}
343246074Sgabor		*s = '\0';
344246074Sgabor	}
345246074Sgabor	if (mkpath(tmpbuf) != 0)
346246074Sgabor		pfatal("creation of %s failed", tmpbuf);
347246074Sgabor	free(tmpbuf);
348246074Sgabor}
349246074Sgabor
350246074Sgabor/*
351246074Sgabor * Make filenames more reasonable.
352246074Sgabor */
353246074Sgaborchar *
354246074Sgaborfetchname(const char *at, bool *exists, int strip_leading)
355246074Sgabor{
356246074Sgabor	char		*fullname, *name, *t;
357246074Sgabor	int		sleading, tab;
358246074Sgabor	struct stat	filestat;
359246074Sgabor
360246074Sgabor	if (at == NULL || *at == '\0')
361246074Sgabor		return NULL;
362246074Sgabor	while (isspace((unsigned char)*at))
363246074Sgabor		at++;
364246074Sgabor#ifdef DEBUGGING
365246074Sgabor	if (debug & 128)
366246074Sgabor		say("fetchname %s %d\n", at, strip_leading);
367246074Sgabor#endif
368246074Sgabor	/* So files can be created by diffing against /dev/null.  */
369355351Skevans	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) {
370355351Skevans		*exists = true;
371246074Sgabor		return NULL;
372355351Skevans	}
373246074Sgabor	name = fullname = t = savestr(at);
374246074Sgabor
375246074Sgabor	tab = strchr(t, '\t') != NULL;
376246074Sgabor	/* Strip off up to `strip_leading' path components and NUL terminate. */
377246074Sgabor	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
378246074Sgabor	    !isspace((unsigned char)*t)); t++) {
379246074Sgabor		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
380246074Sgabor			if (--sleading >= 0)
381246074Sgabor				name = t + 1;
382246074Sgabor	}
383246074Sgabor	*t = '\0';
384246074Sgabor
385246074Sgabor	/*
386246074Sgabor	 * If no -p option was given (957 is the default value!), we were
387246074Sgabor	 * given a relative pathname, and the leading directories that we
388246074Sgabor	 * just stripped off all exist, put them back on.
389246074Sgabor	 */
390246074Sgabor	if (strip_leading == 957 && name != fullname && *fullname != '/') {
391246074Sgabor		name[-1] = '\0';
392246074Sgabor		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
393246074Sgabor			name[-1] = '/';
394246074Sgabor			name = fullname;
395246074Sgabor		}
396246074Sgabor	}
397246074Sgabor	name = savestr(name);
398246074Sgabor	free(fullname);
399246074Sgabor
400246074Sgabor	*exists = stat(name, &filestat) == 0;
401246074Sgabor	return name;
402246074Sgabor}
403246074Sgabor
404246074Sgaborvoid
405246074Sgaborversion(void)
406246074Sgabor{
407286795Sdelphij	printf("patch 2.0-12u11 FreeBSD\n");
408246074Sgabor	my_exit(EXIT_SUCCESS);
409246074Sgabor}
410246074Sgabor
411246074Sgabor/*
412246074Sgabor * Exit with cleanup.
413246074Sgabor */
414246074Sgaborvoid
415246074Sgabormy_exit(int status)
416246074Sgabor{
417246074Sgabor	unlink(TMPINNAME);
418246074Sgabor	if (!toutkeep)
419246074Sgabor		unlink(TMPOUTNAME);
420246074Sgabor	if (!trejkeep)
421246074Sgabor		unlink(TMPREJNAME);
422246074Sgabor	unlink(TMPPATNAME);
423246074Sgabor	exit(status);
424246074Sgabor}
425