Home | History | Annotate | Line # | Download | only in patch
util.c revision 1.22.12.1
      1 /*	$NetBSD: util.c,v 1.22.12.1 2007/11/06 23:36:10 matt Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1988, 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 
     25 #include <sys/cdefs.h>
     26 #ifndef lint
     27 __RCSID("$NetBSD: util.c,v 1.22.12.1 2007/11/06 23:36:10 matt Exp $");
     28 #endif /* not lint */
     29 
     30 #include <sys/param.h>
     31 
     32 #include "EXTERN.h"
     33 #include "common.h"
     34 #include "INTERN.h"
     35 #include "util.h"
     36 #include "backupfile.h"
     37 
     38 #include <stdarg.h>
     39 #include <stdlib.h>
     40 #include <unistd.h>
     41 #include <fcntl.h>
     42 
     43 /*
     44  * Rename a file, copying it if necessary.
     45  */
     46 int
     47 move_file(char *from, char *to)
     48 {
     49 	char bakname[MAXPATHLEN];
     50 	char *s;
     51 	ssize_t i;
     52 	int fromfd;
     53 
     54 	if ( check_only == TRUE )
     55 		return(0);
     56 
     57 	/* to stdout? */
     58 
     59 	if (strEQ(to, "-")) {
     60 #ifdef DEBUGGING
     61 		if (debug & 4)
     62 			say("Moving %s to stdout.\n", from);
     63 #endif
     64 		fromfd = open(from, 0);
     65 		if (fromfd < 0)
     66 			pfatal("internal error, can't reopen %s", from);
     67 		while ((i = read(fromfd, buf, sizeof buf)) > 0)
     68 			if (write(1, buf, i) != 1)
     69 				pfatal("write failed");
     70 		Close(fromfd);
     71 		return 0;
     72 	}
     73 
     74 	if (origprae) {
     75 		strlcpy(bakname, origprae, sizeof(bakname));
     76 		strlcat(bakname, to, sizeof(bakname));
     77 	} else {
     78 #ifndef NODIR
     79 		char *backupname = find_backup_file_name(to);
     80 		strlcpy(bakname, backupname, sizeof(bakname));
     81 		free(backupname);
     82 #else /* NODIR */
     83 		strlcpy(bakname, to, sizeof(bakname));
     84     		strlcat(bakname, simple_backup_suffix, sizeof(bakname));
     85 #endif /* NODIR */
     86 	}
     87 
     88 	if (stat(to, &filestat) == 0) {	/* output file exists */
     89 		dev_t to_device = filestat.st_dev;
     90 		ino_t to_inode  = filestat.st_ino;
     91 		char *simplename = bakname;
     92 
     93 		for (s = bakname; *s; s++) {
     94 			if (*s == '/')
     95 				simplename = s + 1;
     96 		}
     97 		/*
     98 		 * Find a backup name that is not the same file.
     99 		 * Change the first lowercase char into uppercase;
    100 		 * if that isn't sufficient, chop off the first char
    101 		 * and try again.
    102 		 */
    103 		while (stat(bakname, &filestat) == 0 &&
    104 		       to_device == filestat.st_dev &&
    105 		       to_inode == filestat.st_ino) {
    106 			/* Skip initial non-lowercase chars. */
    107 			for (s = simplename;
    108 			     *s && *s == toupper((unsigned char)*s);
    109 			     s++)
    110 				;
    111 			if (*s)
    112 				*s = toupper((unsigned char)*s);
    113 			else
    114 				strcpy(simplename, simplename + 1);
    115 		}
    116 		while (unlink(bakname) >= 0)
    117 			;	/* while() is for benefit of Eunice */
    118 #ifdef DEBUGGING
    119 		if (debug & 4)
    120 			say("Moving %s to %s.\n", to, bakname);
    121 #endif
    122 		if (link(to, bakname) < 0) {
    123 			/*
    124 			 * Maybe `to' is a symlink into a different file
    125 			 * system. Copying replaces the symlink with a file;
    126 			 * using rename would be better.
    127 			 */
    128 			int tofd;
    129 			int bakfd;
    130 
    131 			bakfd = creat(bakname, 0666);
    132 			if (bakfd < 0) {
    133 				say("Can't backup %s, output is in %s: %s\n",
    134 				    to, from, strerror(errno));
    135 				return -1;
    136 			}
    137 			tofd = open(to, 0);
    138 			if (tofd < 0)
    139 				pfatal("internal error, can't open %s", to);
    140 			while ((i = read(tofd, buf, sizeof buf)) > 0)
    141 				if (write(bakfd, buf, i) != i)
    142 					pfatal("write failed");
    143 			Close(tofd);
    144 			Close(bakfd);
    145 		}
    146 		while (unlink(to) >= 0) ;
    147 	}
    148 #ifdef DEBUGGING
    149 	if (debug & 4)
    150 		say("Moving %s to %s.\n", from, to);
    151 #endif
    152 	if (link(from, to) < 0) {		/* different file system? */
    153 		int tofd;
    154 
    155 		tofd = creat(to, 0666);
    156 		if (tofd < 0) {
    157 			say("Can't create %s, output is in %s: %s\n",
    158 			    to, from, strerror(errno));
    159 			return -1;
    160 		}
    161 		fromfd = open(from, 0);
    162 		if (fromfd < 0)
    163 			pfatal("internal error, can't reopen %s", from);
    164 		while ((i = read(fromfd, buf, sizeof buf)) > 0)
    165 			if (write(tofd, buf, i) != i)
    166 				pfatal("write failed");
    167 		Close(fromfd);
    168 		Close(tofd);
    169 	}
    170 	Unlink(from);
    171 	return 0;
    172 }
    173 
    174 /*
    175  * Copy a file.
    176  */
    177 void
    178 copy_file(char *from, char *to)
    179 {
    180 	int tofd;
    181 	int fromfd;
    182 	size_t i;
    183 
    184 	tofd = creat(to, 0666);
    185 	if (tofd < 0)
    186 		pfatal("can't create %s", to);
    187 	fromfd = open(from, 0);
    188 	if (fromfd < 0)
    189 		pfatal("internal error, can't reopen %s", from);
    190 	while ((i = read(fromfd, buf, sizeof buf)) > 0)
    191 		if (write(tofd, buf, i) != i)
    192 			pfatal("write to %s failed", to);
    193 	Close(fromfd);
    194 	Close(tofd);
    195 }
    196 
    197 /*
    198  * malloc with result test.
    199  */
    200 void *
    201 xmalloc(size_t size)
    202 {
    203 	void *p;
    204 
    205 	if ((p = malloc(size)) == NULL)
    206 		fatal("out of memory\n");
    207 	return p;
    208 }
    209 
    210 /*
    211  * realloc with result test.
    212  */
    213 void *
    214 xrealloc(void *ptr, size_t size)
    215 {
    216 	void *p;
    217 
    218 	if ((p = realloc(ptr, size)) == NULL)
    219 		fatal("out of memory\n");
    220 	return p;
    221 }
    222 
    223 /*
    224  * strdup with result test.
    225  */
    226 char *
    227 xstrdup(const char *s)
    228 {
    229 	char *p;
    230 
    231 	if ((p = strdup(s)) == NULL)
    232 		fatal("out of memory\n");
    233 	return p;
    234 }
    235 
    236 /*
    237  * Vanilla terminal output.
    238  */
    239 void
    240 say(const char *pat, ...)
    241 {
    242 	va_list ap;
    243 	va_start(ap, pat);
    244 
    245 	vfprintf(stderr, pat, ap);
    246 	va_end(ap);
    247 	Fflush(stderr);
    248 }
    249 
    250 /*
    251  * Terminal output, pun intended.
    252  */
    253 void				/* very void */
    254 fatal(const char *pat, ...)
    255 {
    256 	va_list ap;
    257 	va_start(ap, pat);
    258 
    259 	fprintf(stderr, "patch: **** ");
    260 	vfprintf(stderr, pat, ap);
    261 	va_end(ap);
    262 	my_exit(1);
    263 }
    264 
    265 /*
    266  * Say something from patch, something from the system, then silence...
    267  */
    268 void				/* very void */
    269 pfatal(const char *pat, ...)
    270 {
    271 	va_list ap;
    272 	int errnum = errno;
    273 	va_start(ap, pat);
    274 
    275 	fprintf(stderr, "patch: **** ");
    276 	vfprintf(stderr, pat, ap);
    277 	fprintf(stderr, ": %s\n", strerror(errnum));
    278 	va_end(ap);
    279 	my_exit(1);
    280 }
    281 
    282 /*
    283  * Get a response from the user, somehow or other.
    284  */
    285 void
    286 ask(const char *pat, ...)
    287 {
    288 	int ttyfd;
    289 	int r;
    290 	bool tty2 = isatty(2);
    291 	va_list ap;
    292 	va_start(ap, pat);
    293 
    294 	(void)vsprintf(buf, pat, ap);
    295 	va_end(ap);
    296 	Fflush(stderr);
    297 	write(2, buf, strlen(buf));
    298 	if (tty2) {			/* might be redirected to a file */
    299 		r = read(2, buf, sizeof buf);
    300 	} else if (isatty(1)) {		/* this may be new file output */
    301 		Fflush(stdout);
    302 		write(1, buf, strlen(buf));
    303 		r = read(1, buf, sizeof buf);
    304 	} else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
    305 					/* might be deleted or unwritable */
    306 		write(ttyfd, buf, strlen(buf));
    307 		r = read(ttyfd, buf, sizeof buf);
    308 		Close(ttyfd);
    309 	} else if (isatty(0)) {		/* this is probably patch input */
    310 		Fflush(stdin);
    311 		write(0, buf, strlen(buf));
    312 		r = read(0, buf, sizeof buf);
    313 	} else {			/* no terminal at all--default it */
    314 		buf[0] = '\n';
    315 		r = 1;
    316 	}
    317 	if (r <= 0)
    318 		buf[0] = 0;
    319 	else
    320 		buf[r] = '\0';
    321 	if (!tty2)
    322 		say("%s", buf);
    323 }
    324 
    325 /*
    326  * How to handle certain events when not in a critical region.
    327  */
    328 void
    329 set_signals(int reset)
    330 {
    331 	static void (*hupval)(int);
    332 	static void (*intval)(int);
    333 
    334 	if (!reset) {
    335 		hupval = signal(SIGHUP, SIG_IGN);
    336 		if (hupval != SIG_IGN)
    337 			hupval = my_sig_exit;
    338 		intval = signal(SIGINT, SIG_IGN);
    339 		if (intval != SIG_IGN)
    340 			intval = my_sig_exit;
    341 	}
    342 	Signal(SIGHUP, hupval);
    343 	Signal(SIGINT, intval);
    344 }
    345 
    346 /*
    347  * How to handle certain events when in a critical region.
    348  */
    349 void
    350 ignore_signals()
    351 {
    352 	Signal(SIGHUP, SIG_IGN);
    353 	Signal(SIGINT, SIG_IGN);
    354 }
    355 
    356 /*
    357  * Make sure we'll have the directories to create a file.
    358  * If `striplast' is TRUE, ignore the last element of `filename'.
    359  */
    360 void
    361 makedirs(char *filename, bool striplast)
    362 {
    363 	char tmpbuf[MAXPATHLEN];
    364 	char *s = tmpbuf;
    365 	char *dirv[MAXPATHLEN];	/* Point to the NULs between elements.  */
    366 	int i;
    367 	int dirvp = 0;		/* Number of finished entries in dirv. */
    368 
    369 	/*
    370 	 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
    371 	 * between the directories.
    372 	 */
    373 	while (*filename) {
    374 		if (*filename == '/') {
    375 			filename++;
    376 			dirv[dirvp++] = s;
    377 			*s++ = '\0';
    378 		} else {
    379 			*s++ = *filename++;
    380 		}
    381 	}
    382 	*s = '\0';
    383 	dirv[dirvp] = s;
    384 	if (striplast)
    385 		dirvp--;
    386 	if (dirvp < 0)
    387 		return;
    388 
    389 	strlcpy(buf, "mkdir", sizeof(buf));
    390 	s = buf;
    391 	for (i = 0; i <= dirvp; i++) {
    392 		struct stat sbuf;
    393 
    394 		if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
    395 			while (*s)
    396 				s++;
    397 			*s++ = ' ';
    398 			strlcpy(s, tmpbuf, sizeof(buf) - (s - buf));
    399 		}
    400 		*dirv[i] = '/';
    401 	}
    402 	if (s != buf)
    403 		system(buf);
    404 }
    405 
    406 /*
    407  * Make filenames more reasonable.
    408  */
    409 char *
    410 fetchname(char *at, int strip_leading, int assume_exists)
    411 {
    412 	char *fullname;
    413 	char *name;
    414 	char *t;
    415 	char tmpbuf[MAXPATHLEN];
    416 	int sleading = strip_leading;
    417 
    418 	if (!at)
    419 		return NULL;
    420 	while (isspace((unsigned char)*at))
    421 		at++;
    422 #ifdef DEBUGGING
    423 	if (debug & 128)
    424 		say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
    425 #endif
    426 	filename_is_dev_null = FALSE;
    427 	if (strnEQ(at, "/dev/null", 9)) {
    428 		/* So files can be created by diffing against /dev/null. */
    429 		filename_is_dev_null = TRUE;
    430 		return NULL;
    431 	}
    432 	name = fullname = t = xstrdup(at);
    433 
    434 	/* Strip off up to `sleading' leading slashes and null terminate. */
    435 	for (; *t && !isspace((unsigned char)*t); t++)
    436 		if (*t == '/')
    437 			if (--sleading >= 0)
    438 				name = t + 1;
    439 	*t = '\0';
    440 
    441 	/*
    442 	 * If no -p option was given (957 is the default value!),
    443 	 * we were given a relative pathname,
    444 	 * and the leading directories that we just stripped off all exist,
    445 	 * put them back on.
    446 	 */
    447 	if (strip_leading == 957 && name != fullname && *fullname != '/') {
    448 		name[-1] = '\0';
    449 		if (stat(fullname, &filestat) == 0 &&
    450 		    S_ISDIR(filestat.st_mode)) {
    451 			name[-1] = '/';
    452 			name = fullname;
    453 		}
    454 	}
    455 
    456 	name = xstrdup(name);
    457 	free(fullname);
    458 
    459 	if (stat(name, &filestat) && !assume_exists) {
    460 		char *filebase = basename(name);
    461 		size_t pathlen = filebase - name;
    462 
    463 		/* Put any leading path into `tmpbuf'. */
    464 		if (pathlen >= sizeof(tmpbuf))
    465 			return NULL;
    466 		strncpy(tmpbuf, name, pathlen);
    467 		tmpbuf[pathlen] = '\0';
    468 
    469 #define try(f, a1, a2) \
    470     (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1, a2), \
    471      stat(tmpbuf, &filestat) == 0)
    472 #define try1(f, a1) \
    473     (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1), \
    474      stat(tmpbuf, &filestat) == 0)
    475 		if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
    476 		    try1("RCS/%s"  , filebase) ||
    477 		    try(    "%s%s", filebase, RCSSUFFIX) ||
    478 		    try("SCCS/%s%s", SCCSPREFIX, filebase) ||
    479 		    try(     "%s%s", SCCSPREFIX, filebase))
    480 			return name;
    481 		free(name);
    482 		name = NULL;
    483 	}
    484 
    485 	return name;
    486 }
    487