Home | History | Annotate | Line # | Download | only in patch
util.c revision 1.25
      1  1.24     joerg /*
      2  1.24     joerg  * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
      3  1.24     joerg  * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
      4  1.25     joerg  * $NetBSD: util.c,v 1.25 2010/01/10 01:10:16 joerg Exp $
      5  1.24     joerg  */
      6  1.16    itojun 
      7  1.16    itojun /*
      8  1.24     joerg  * patch - a program to apply diffs to original files
      9  1.24     joerg  *
     10  1.24     joerg  * Copyright 1986, Larry Wall
     11  1.24     joerg  *
     12  1.16    itojun  * Redistribution and use in source and binary forms, with or without
     13  1.24     joerg  * modification, are permitted provided that the following condition is met:
     14  1.24     joerg  * 1. Redistributions of source code must retain the above copyright notice,
     15  1.24     joerg  * this condition and the following disclaimer.
     16  1.24     joerg  *
     17  1.24     joerg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
     18  1.24     joerg  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  1.24     joerg  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  1.24     joerg  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
     21  1.24     joerg  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  1.24     joerg  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     23  1.24     joerg  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     24  1.24     joerg  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  1.16    itojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  1.16    itojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  1.16    itojun  * SUCH DAMAGE.
     28  1.24     joerg  *
     29  1.24     joerg  * -C option added in 1998, original code by Marc Espie, based on FreeBSD
     30  1.24     joerg  * behaviour
     31  1.16    itojun  */
     32  1.16    itojun 
     33   1.5  christos #include <sys/cdefs.h>
     34  1.25     joerg __RCSID("$NetBSD: util.c,v 1.25 2010/01/10 01:10:16 joerg Exp $");
     35   1.2   mycroft 
     36  1.12  kristerw #include <sys/param.h>
     37  1.24     joerg #include <sys/stat.h>
     38  1.24     joerg 
     39  1.24     joerg #include <ctype.h>
     40  1.24     joerg #include <errno.h>
     41  1.24     joerg #include <fcntl.h>
     42  1.24     joerg #include <libgen.h>
     43  1.24     joerg #include <paths.h>
     44  1.24     joerg #include <signal.h>
     45  1.24     joerg #include <stdarg.h>
     46  1.24     joerg #include <stdlib.h>
     47  1.24     joerg #include <stdio.h>
     48  1.24     joerg #include <string.h>
     49  1.24     joerg #include <unistd.h>
     50  1.12  kristerw 
     51   1.1       cgd #include "common.h"
     52   1.1       cgd #include "util.h"
     53   1.1       cgd #include "backupfile.h"
     54  1.24     joerg #include "pathnames.h"
     55  1.10  kristerw 
     56  1.24     joerg /* Rename a file, copying it if necessary. */
     57   1.1       cgd 
     58   1.1       cgd int
     59  1.24     joerg move_file(const char *from, const char *to)
     60   1.1       cgd {
     61  1.24     joerg 	int	fromfd;
     62  1.24     joerg 	ssize_t	i;
     63  1.21       skd 
     64  1.11  kristerw 	/* to stdout? */
     65   1.1       cgd 
     66  1.11  kristerw 	if (strEQ(to, "-")) {
     67   1.1       cgd #ifdef DEBUGGING
     68  1.11  kristerw 		if (debug & 4)
     69  1.11  kristerw 			say("Moving %s to stdout.\n", from);
     70   1.1       cgd #endif
     71  1.24     joerg 		fromfd = open(from, O_RDONLY);
     72  1.11  kristerw 		if (fromfd < 0)
     73  1.11  kristerw 			pfatal("internal error, can't reopen %s", from);
     74  1.24     joerg 		while ((i = read(fromfd, buf, buf_len)) > 0)
     75  1.24     joerg 			if (write(STDOUT_FILENO, buf, i) != i)
     76  1.11  kristerw 				pfatal("write failed");
     77  1.24     joerg 		close(fromfd);
     78  1.11  kristerw 		return 0;
     79  1.11  kristerw 	}
     80  1.24     joerg 	if (backup_file(to) < 0) {
     81  1.24     joerg 		say("Can't backup %s, output is in %s: %s\n", to, from,
     82  1.24     joerg 		    strerror(errno));
     83  1.24     joerg 		return -1;
     84   1.1       cgd 	}
     85   1.1       cgd #ifdef DEBUGGING
     86   1.1       cgd 	if (debug & 4)
     87  1.11  kristerw 		say("Moving %s to %s.\n", from, to);
     88   1.1       cgd #endif
     89  1.24     joerg 	if (rename(from, to) < 0) {
     90  1.24     joerg 		if (errno != EXDEV || copy_file(from, to) < 0) {
     91  1.11  kristerw 			say("Can't create %s, output is in %s: %s\n",
     92  1.11  kristerw 			    to, from, strerror(errno));
     93  1.11  kristerw 			return -1;
     94  1.11  kristerw 		}
     95   1.1       cgd 	}
     96  1.24     joerg 	return 0;
     97  1.24     joerg }
     98  1.24     joerg 
     99  1.24     joerg /* Backup the original file.  */
    100  1.24     joerg 
    101  1.24     joerg int
    102  1.24     joerg backup_file(const char *orig)
    103  1.24     joerg {
    104  1.24     joerg 	struct stat	filestat;
    105  1.24     joerg 	char		bakname[MAXPATHLEN], *s, *simplename;
    106  1.24     joerg 	dev_t		orig_device;
    107  1.24     joerg 	ino_t		orig_inode;
    108  1.24     joerg 
    109  1.24     joerg 	if (backup_type == none || stat(orig, &filestat) != 0)
    110  1.24     joerg 		return 0;			/* nothing to do */
    111  1.24     joerg 	/*
    112  1.24     joerg 	 * If the user used zero prefixes or suffixes, then
    113  1.24     joerg 	 * he doesn't want backups.  Yet we have to remove
    114  1.24     joerg 	 * orig to break possible hardlinks.
    115  1.24     joerg 	 */
    116  1.24     joerg 	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
    117  1.24     joerg 		unlink(orig);
    118  1.24     joerg 		return 0;
    119  1.24     joerg 	}
    120  1.24     joerg 	orig_device = filestat.st_dev;
    121  1.24     joerg 	orig_inode = filestat.st_ino;
    122  1.24     joerg 
    123  1.24     joerg 	if (origprae) {
    124  1.24     joerg 		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
    125  1.24     joerg 		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
    126  1.24     joerg 			fatal("filename %s too long for buffer\n", origprae);
    127  1.24     joerg 	} else {
    128  1.24     joerg 		if ((s = find_backup_file_name(orig)) == NULL)
    129  1.24     joerg 			fatal("out of memory\n");
    130  1.24     joerg 		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
    131  1.24     joerg 			fatal("filename %s too long for buffer\n", s);
    132  1.24     joerg 		free(s);
    133  1.24     joerg 	}
    134  1.24     joerg 
    135  1.24     joerg 	if ((simplename = strrchr(bakname, '/')) != NULL)
    136  1.24     joerg 		simplename = simplename + 1;
    137  1.24     joerg 	else
    138  1.24     joerg 		simplename = bakname;
    139  1.24     joerg 
    140  1.24     joerg 	/*
    141  1.24     joerg 	 * Find a backup name that is not the same file. Change the
    142  1.24     joerg 	 * first lowercase char into uppercase; if that isn't
    143  1.24     joerg 	 * sufficient, chop off the first char and try again.
    144  1.24     joerg 	 */
    145  1.24     joerg 	while (stat(bakname, &filestat) == 0 &&
    146  1.24     joerg 	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
    147  1.24     joerg 		/* Skip initial non-lowercase chars.  */
    148  1.24     joerg 		for (s = simplename; *s && !islower((unsigned char)*s); s++)
    149  1.24     joerg 			;
    150  1.24     joerg 		if (*s)
    151  1.24     joerg 			*s = toupper((unsigned char)*s);
    152  1.24     joerg 		else
    153  1.24     joerg 			memmove(simplename, simplename + 1,
    154  1.24     joerg 			    strlen(simplename + 1) + 1);
    155  1.24     joerg 	}
    156  1.24     joerg #ifdef DEBUGGING
    157  1.24     joerg 	if (debug & 4)
    158  1.24     joerg 		say("Moving %s to %s.\n", orig, bakname);
    159  1.24     joerg #endif
    160  1.24     joerg 	if (rename(orig, bakname) < 0) {
    161  1.24     joerg 		if (errno != EXDEV || copy_file(orig, bakname) < 0)
    162  1.24     joerg 			return -1;
    163  1.24     joerg 	}
    164  1.11  kristerw 	return 0;
    165  1.11  kristerw }
    166  1.11  kristerw 
    167  1.11  kristerw /*
    168  1.11  kristerw  * Copy a file.
    169  1.11  kristerw  */
    170  1.24     joerg int
    171  1.24     joerg copy_file(const char *from, const char *to)
    172  1.11  kristerw {
    173  1.24     joerg 	int	tofd, fromfd;
    174  1.24     joerg 	ssize_t	i;
    175  1.11  kristerw 
    176  1.24     joerg 	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
    177  1.11  kristerw 	if (tofd < 0)
    178  1.24     joerg 		return -1;
    179  1.24     joerg 	fromfd = open(from, O_RDONLY, 0);
    180   1.1       cgd 	if (fromfd < 0)
    181  1.11  kristerw 		pfatal("internal error, can't reopen %s", from);
    182  1.24     joerg 	while ((i = read(fromfd, buf, buf_len)) > 0)
    183  1.11  kristerw 		if (write(tofd, buf, i) != i)
    184  1.11  kristerw 			pfatal("write to %s failed", to);
    185  1.24     joerg 	close(fromfd);
    186  1.24     joerg 	close(tofd);
    187  1.24     joerg 	return 0;
    188  1.12  kristerw }
    189  1.12  kristerw 
    190  1.12  kristerw /*
    191  1.24     joerg  * Allocate a unique area for a string.
    192  1.11  kristerw  */
    193   1.1       cgd char *
    194  1.24     joerg savestr(const char *s)
    195   1.1       cgd {
    196  1.24     joerg 	char	*rv;
    197   1.1       cgd 
    198  1.24     joerg 	if (!s)
    199  1.24     joerg 		s = "Oops";
    200  1.24     joerg 	rv = strdup(s);
    201  1.24     joerg 	if (rv == NULL) {
    202  1.24     joerg 		if (using_plan_a)
    203  1.24     joerg 			out_of_mem = true;
    204  1.24     joerg 		else
    205  1.24     joerg 			fatal("out of memory\n");
    206  1.24     joerg 	}
    207  1.24     joerg 	return rv;
    208   1.1       cgd }
    209   1.1       cgd 
    210  1.11  kristerw /*
    211  1.24     joerg  * Vanilla terminal output (buffered).
    212  1.11  kristerw  */
    213   1.1       cgd void
    214  1.24     joerg say(const char *fmt, ...)
    215   1.1       cgd {
    216  1.24     joerg 	va_list	ap;
    217  1.24     joerg 
    218  1.24     joerg 	va_start(ap, fmt);
    219  1.24     joerg 	vfprintf(stderr, fmt, ap);
    220  1.11  kristerw 	va_end(ap);
    221  1.24     joerg 	fflush(stderr);
    222   1.1       cgd }
    223   1.1       cgd 
    224  1.11  kristerw /*
    225  1.11  kristerw  * Terminal output, pun intended.
    226  1.11  kristerw  */
    227  1.24     joerg void
    228  1.24     joerg fatal(const char *fmt, ...)
    229   1.1       cgd {
    230  1.24     joerg 	va_list	ap;
    231  1.24     joerg 
    232  1.24     joerg 	va_start(ap, fmt);
    233  1.11  kristerw 	fprintf(stderr, "patch: **** ");
    234  1.24     joerg 	vfprintf(stderr, fmt, ap);
    235  1.11  kristerw 	va_end(ap);
    236  1.24     joerg 	my_exit(2);
    237   1.1       cgd }
    238   1.1       cgd 
    239  1.11  kristerw /*
    240  1.24     joerg  * Say something from patch, something from the system, then silence . . .
    241  1.11  kristerw  */
    242  1.24     joerg void
    243  1.24     joerg pfatal(const char *fmt, ...)
    244   1.1       cgd {
    245  1.24     joerg 	va_list	ap;
    246  1.24     joerg 	int	errnum = errno;
    247  1.24     joerg 
    248  1.11  kristerw 	fprintf(stderr, "patch: **** ");
    249  1.24     joerg 	va_start(ap, fmt);
    250  1.24     joerg 	vfprintf(stderr, fmt, ap);
    251  1.24     joerg 	va_end(ap);
    252  1.11  kristerw 	fprintf(stderr, ": %s\n", strerror(errnum));
    253  1.24     joerg 	my_exit(2);
    254   1.1       cgd }
    255   1.1       cgd 
    256  1.11  kristerw /*
    257  1.24     joerg  * Get a response from the user via /dev/tty
    258  1.11  kristerw  */
    259   1.1       cgd void
    260  1.24     joerg ask(const char *fmt, ...)
    261   1.1       cgd {
    262  1.24     joerg 	va_list	ap;
    263  1.24     joerg 	ssize_t	nr = 0;
    264  1.24     joerg 	static	int ttyfd = -1;
    265  1.11  kristerw 
    266  1.24     joerg 	va_start(ap, fmt);
    267  1.24     joerg 	vfprintf(stdout, fmt, ap);
    268  1.11  kristerw 	va_end(ap);
    269  1.24     joerg 	fflush(stdout);
    270  1.24     joerg 	if (ttyfd < 0)
    271  1.24     joerg 		ttyfd = open(_PATH_TTY, O_RDONLY);
    272  1.24     joerg 	if (ttyfd >= 0) {
    273  1.24     joerg 		if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
    274  1.24     joerg 		    buf[nr - 1] == '\n')
    275  1.24     joerg 			buf[nr - 1] = '\0';
    276  1.24     joerg 	}
    277  1.24     joerg 	if (ttyfd < 0 || nr <= 0) {
    278  1.24     joerg 		/* no tty or error reading, pretend user entered 'return' */
    279  1.24     joerg 		putchar('\n');
    280  1.24     joerg 		buf[0] = '\0';
    281  1.11  kristerw 	}
    282   1.1       cgd }
    283   1.1       cgd 
    284  1.11  kristerw /*
    285  1.11  kristerw  * How to handle certain events when not in a critical region.
    286  1.11  kristerw  */
    287   1.1       cgd void
    288   1.9  kristerw set_signals(int reset)
    289   1.1       cgd {
    290  1.24     joerg 	static sig_t	hupval, intval;
    291   1.1       cgd 
    292  1.11  kristerw 	if (!reset) {
    293  1.11  kristerw 		hupval = signal(SIGHUP, SIG_IGN);
    294  1.11  kristerw 		if (hupval != SIG_IGN)
    295  1.24     joerg 			hupval = my_exit;
    296  1.11  kristerw 		intval = signal(SIGINT, SIG_IGN);
    297  1.11  kristerw 		if (intval != SIG_IGN)
    298  1.24     joerg 			intval = my_exit;
    299  1.11  kristerw 	}
    300  1.24     joerg 	signal(SIGHUP, hupval);
    301  1.24     joerg 	signal(SIGINT, intval);
    302   1.1       cgd }
    303   1.1       cgd 
    304  1.11  kristerw /*
    305  1.11  kristerw  * How to handle certain events when in a critical region.
    306  1.11  kristerw  */
    307   1.1       cgd void
    308  1.24     joerg ignore_signals(void)
    309   1.1       cgd {
    310  1.24     joerg 	signal(SIGHUP, SIG_IGN);
    311  1.24     joerg 	signal(SIGINT, SIG_IGN);
    312   1.1       cgd }
    313   1.1       cgd 
    314  1.11  kristerw /*
    315  1.24     joerg  * Make sure we'll have the directories to create a file. If `striplast' is
    316  1.24     joerg  * true, ignore the last element of `filename'.
    317  1.11  kristerw  */
    318  1.24     joerg 
    319   1.1       cgd void
    320  1.24     joerg makedirs(const char *filename, bool striplast)
    321   1.1       cgd {
    322  1.24     joerg 	char	*tmpbuf;
    323  1.24     joerg 
    324  1.24     joerg 	if ((tmpbuf = strdup(filename)) == NULL)
    325  1.24     joerg 		fatal("out of memory\n");
    326  1.11  kristerw 
    327  1.24     joerg 	if (striplast) {
    328  1.24     joerg 		char	*s = strrchr(tmpbuf, '/');
    329  1.25     joerg 		if (s == NULL) {
    330  1.25     joerg 			free(tmpbuf);
    331  1.24     joerg 			return;	/* nothing to be done */
    332  1.25     joerg 		}
    333  1.24     joerg 		*s = '\0';
    334  1.24     joerg 	}
    335  1.24     joerg 	if (mkpath(tmpbuf) != 0)
    336  1.24     joerg 		pfatal("creation of %s failed", tmpbuf);
    337  1.24     joerg 	free(tmpbuf);
    338   1.1       cgd }
    339   1.1       cgd 
    340  1.11  kristerw /*
    341  1.11  kristerw  * Make filenames more reasonable.
    342  1.11  kristerw  */
    343   1.1       cgd char *
    344  1.24     joerg fetchname(const char *at, bool *exists, int strip_leading)
    345   1.1       cgd {
    346  1.24     joerg 	char		*fullname, *name, *t;
    347  1.24     joerg 	int		sleading, tab;
    348  1.24     joerg 	struct stat	filestat;
    349  1.11  kristerw 
    350  1.24     joerg 	if (at == NULL || *at == '\0')
    351  1.11  kristerw 		return NULL;
    352  1.11  kristerw 	while (isspace((unsigned char)*at))
    353  1.11  kristerw 		at++;
    354   1.1       cgd #ifdef DEBUGGING
    355  1.11  kristerw 	if (debug & 128)
    356  1.24     joerg 		say("fetchname %s %d\n", at, strip_leading);
    357   1.1       cgd #endif
    358  1.24     joerg 	/* So files can be created by diffing against /dev/null.  */
    359  1.24     joerg 	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
    360  1.11  kristerw 		return NULL;
    361  1.24     joerg 	name = fullname = t = savestr(at);
    362  1.11  kristerw 
    363  1.24     joerg 	tab = strchr(t, '\t') != NULL;
    364  1.24     joerg 	/* Strip off up to `strip_leading' path components and NUL terminate. */
    365  1.24     joerg 	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
    366  1.24     joerg 	    !isspace((unsigned char)*t)); t++) {
    367  1.24     joerg 		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
    368  1.11  kristerw 			if (--sleading >= 0)
    369  1.11  kristerw 				name = t + 1;
    370  1.24     joerg 	}
    371  1.11  kristerw 	*t = '\0';
    372  1.11  kristerw 
    373  1.11  kristerw 	/*
    374  1.24     joerg 	 * If no -p option was given (957 is the default value!), we were
    375  1.24     joerg 	 * given a relative pathname, and the leading directories that we
    376  1.24     joerg 	 * just stripped off all exist, put them back on.
    377  1.11  kristerw 	 */
    378  1.11  kristerw 	if (strip_leading == 957 && name != fullname && *fullname != '/') {
    379  1.11  kristerw 		name[-1] = '\0';
    380  1.24     joerg 		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
    381  1.11  kristerw 			name[-1] = '/';
    382  1.11  kristerw 			name = fullname;
    383  1.11  kristerw 		}
    384  1.11  kristerw 	}
    385  1.24     joerg 	name = savestr(name);
    386  1.24     joerg 	free(fullname);
    387  1.24     joerg 
    388  1.24     joerg 	*exists = stat(name, &filestat) == 0;
    389  1.24     joerg 	return name;
    390  1.24     joerg }
    391  1.24     joerg 
    392  1.24     joerg /*
    393  1.24     joerg  * Takes the name returned by fetchname and looks in RCS/SCCS directories
    394  1.24     joerg  * for a checked in version.
    395  1.24     joerg  */
    396  1.24     joerg char *
    397  1.24     joerg checked_in(char *file)
    398  1.24     joerg {
    399  1.24     joerg 	char		*filebase, *filedir, tmpbuf[MAXPATHLEN];
    400  1.24     joerg 	struct stat	filestat;
    401  1.11  kristerw 
    402  1.24     joerg 	filebase = basename(file);
    403  1.24     joerg 	filedir = dirname(file);
    404  1.24     joerg 
    405  1.24     joerg #define try(f, a1, a2, a3) \
    406  1.24     joerg (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
    407  1.24     joerg 
    408  1.24     joerg 	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
    409  1.24     joerg 	    try("%s/RCS/%s%s", filedir, filebase, "") ||
    410  1.24     joerg 	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
    411  1.24     joerg 	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
    412  1.24     joerg 	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
    413  1.24     joerg 		return file;
    414  1.24     joerg 
    415  1.24     joerg 	return NULL;
    416  1.24     joerg }
    417  1.11  kristerw 
    418  1.24     joerg void
    419  1.24     joerg version(void)
    420  1.24     joerg {
    421  1.24     joerg 	fprintf(stderr, "Patch version 2.0-12u8-NetBSD\n");
    422  1.24     joerg 	my_exit(EXIT_SUCCESS);
    423  1.24     joerg }
    424   1.1       cgd 
    425  1.24     joerg /*
    426  1.24     joerg  * Exit with cleanup.
    427  1.24     joerg  */
    428  1.24     joerg void
    429  1.24     joerg my_exit(int status)
    430  1.24     joerg {
    431  1.24     joerg 	unlink(TMPINNAME);
    432  1.24     joerg 	if (!toutkeep)
    433  1.24     joerg 		unlink(TMPOUTNAME);
    434  1.24     joerg 	if (!trejkeep)
    435  1.24     joerg 		unlink(TMPREJNAME);
    436  1.24     joerg 	unlink(TMPPATNAME);
    437  1.24     joerg 	exit(status);
    438   1.1       cgd }
    439