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