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