Home | History | Annotate | Line # | Download | only in patch
util.c revision 1.12
      1 /*	$NetBSD: util.c,v 1.12 2002/03/16 22:36:42 kristerw Exp $	*/
      2 #include <sys/cdefs.h>
      3 #ifndef lint
      4 __RCSID("$NetBSD: util.c,v 1.12 2002/03/16 22:36:42 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 	int 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 	int 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  * strdup with result test.
    186  */
    187 char *
    188 xstrdup(const char *s)
    189 {
    190 	char *p;
    191 
    192 	if ((p = strdup(s)) == NULL)
    193 		fatal("out of memory\n");
    194 	return p;
    195 }
    196 
    197 /*
    198  * Allocate a unique area for a string.
    199  */
    200 char *
    201 savestr(char *s)
    202 {
    203 	char *rv;
    204 	char *t;
    205 
    206 	if (!s)
    207 		s = "Oops";
    208 	t = s;
    209 	while (*t++)
    210 		;
    211 	rv = malloc(t - s);
    212 	if (rv == NULL) {
    213 		if (using_plan_a)
    214 			out_of_mem = TRUE;
    215 		else
    216 			fatal("out of memory\n");
    217 	} else {
    218 		t = rv;
    219 		while ((*t++ = *s++) != '\0');
    220 	}
    221 	return rv;
    222 }
    223 
    224 /*
    225  * Vanilla terminal output.
    226  */
    227 void
    228 say(const char *pat, ...)
    229 {
    230 	va_list ap;
    231 	va_start(ap, pat);
    232 
    233 	vfprintf(stderr, pat, ap);
    234 	va_end(ap);
    235 	Fflush(stderr);
    236 }
    237 
    238 /*
    239  * Terminal output, pun intended.
    240  */
    241 void				/* very void */
    242 fatal(const char *pat, ...)
    243 {
    244 	va_list ap;
    245 	va_start(ap, pat);
    246 
    247 	fprintf(stderr, "patch: **** ");
    248 	vfprintf(stderr, pat, ap);
    249 	va_end(ap);
    250 	my_exit(1);
    251 }
    252 
    253 /*
    254  * Say something from patch, something from the system, then silence...
    255  */
    256 void				/* very void */
    257 pfatal(const char *pat, ...)
    258 {
    259 	va_list ap;
    260 	int errnum = errno;
    261 	va_start(ap, pat);
    262 
    263 	fprintf(stderr, "patch: **** ");
    264 	vfprintf(stderr, pat, ap);
    265 	fprintf(stderr, ": %s\n", strerror(errnum));
    266 	va_end(ap);
    267 	my_exit(1);
    268 }
    269 
    270 /*
    271  * Get a response from the user, somehow or other.
    272  */
    273 void
    274 ask(const char *pat, ...)
    275 {
    276 	int ttyfd;
    277 	int r;
    278 	bool tty2 = isatty(2);
    279 	va_list ap;
    280 	va_start(ap, pat);
    281 
    282 	(void)vsprintf(buf, pat, ap);
    283 	va_end(ap);
    284 	Fflush(stderr);
    285 	write(2, buf, strlen(buf));
    286 	if (tty2) {			/* might be redirected to a file */
    287 		r = read(2, buf, sizeof buf);
    288 	} else if (isatty(1)) {		/* this may be new file output */
    289 		Fflush(stdout);
    290 		write(1, buf, strlen(buf));
    291 		r = read(1, buf, sizeof buf);
    292 	} else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
    293 					/* might be deleted or unwriteable */
    294 		write(ttyfd, buf, strlen(buf));
    295 		r = read(ttyfd, buf, sizeof buf);
    296 		Close(ttyfd);
    297 	} else if (isatty(0)) {		/* this is probably patch input */
    298 		Fflush(stdin);
    299 		write(0, buf, strlen(buf));
    300 		r = read(0, buf, sizeof buf);
    301 	} else {			/* no terminal at all--default it */
    302 		buf[0] = '\n';
    303 		r = 1;
    304 	}
    305 	if (r <= 0)
    306 		buf[0] = 0;
    307 	else
    308 		buf[r] = '\0';
    309 	if (!tty2)
    310 		say("%s", buf);
    311 }
    312 
    313 /*
    314  * How to handle certain events when not in a critical region.
    315  */
    316 void
    317 set_signals(int reset)
    318 {
    319 	static void (*hupval)(int);
    320 	static void (*intval)(int);
    321 
    322 	if (!reset) {
    323 		hupval = signal(SIGHUP, SIG_IGN);
    324 		if (hupval != SIG_IGN)
    325 			hupval = my_exit;
    326 		intval = signal(SIGINT, SIG_IGN);
    327 		if (intval != SIG_IGN)
    328 			intval = my_exit;
    329 	}
    330 	Signal(SIGHUP, hupval);
    331 	Signal(SIGINT, intval);
    332 }
    333 
    334 /*
    335  * How to handle certain events when in a critical region.
    336  */
    337 void
    338 ignore_signals()
    339 {
    340 	Signal(SIGHUP, SIG_IGN);
    341 	Signal(SIGINT, SIG_IGN);
    342 }
    343 
    344 /*
    345  * Make sure we'll have the directories to create a file.
    346  * If `striplast' is TRUE, ignore the last element of `filename'.
    347  */
    348 void
    349 makedirs(char *filename, bool striplast)
    350 {
    351 	char tmpbuf[MAXPATHLEN];
    352 	char *s = tmpbuf;
    353 	char *dirv[MAXPATHLEN];	/* Point to the NULs between elements.  */
    354 	int i;
    355 	int dirvp = 0;		/* Number of finished entries in dirv. */
    356 
    357 	/*
    358 	 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
    359 	 * between the directories.
    360 	 */
    361 	while (*filename) {
    362 		if (*filename == '/') {
    363 			filename++;
    364 			dirv[dirvp++] = s;
    365 			*s++ = '\0';
    366 		} else {
    367 			*s++ = *filename++;
    368 		}
    369 	}
    370 	*s = '\0';
    371 	dirv[dirvp] = s;
    372 	if (striplast)
    373 		dirvp--;
    374 	if (dirvp < 0)
    375 		return;
    376 
    377 	strcpy(buf, "mkdir");
    378 	s = buf;
    379 	for (i = 0; i <= dirvp; i++) {
    380 		struct stat sbuf;
    381 
    382 		if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
    383 			while (*s)
    384 				s++;
    385 			*s++ = ' ';
    386 			strcpy(s, tmpbuf);
    387 		}
    388 		*dirv[i] = '/';
    389 	}
    390 	if (s != buf)
    391 		system(buf);
    392 }
    393 
    394 /*
    395  * Make filenames more reasonable.
    396  */
    397 char *
    398 fetchname(char *at, int strip_leading, int assume_exists)
    399 {
    400 	char *fullname;
    401 	char *name;
    402 	char *t;
    403 	char tmpbuf[MAXPATHLEN];
    404 	int sleading = strip_leading;
    405 
    406 	if (!at)
    407 		return NULL;
    408 	while (isspace((unsigned char)*at))
    409 		at++;
    410 #ifdef DEBUGGING
    411 	if (debug & 128)
    412 		say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
    413 #endif
    414 	filename_is_dev_null = FALSE;
    415 	if (strnEQ(at, "/dev/null", 9)) {
    416 		/* So files can be created by diffing against /dev/null. */
    417 		filename_is_dev_null = TRUE;
    418 		return NULL;
    419 	}
    420 	name = fullname = t = xstrdup(at);
    421 
    422 	/* Strip off up to `sleading' leading slashes and null terminate. */
    423 	for (; *t && !isspace((unsigned char)*t); t++)
    424 		if (*t == '/')
    425 			if (--sleading >= 0)
    426 				name = t + 1;
    427 	*t = '\0';
    428 
    429 	/*
    430 	 * If no -p option was given (957 is the default value!),
    431 	 * we were given a relative pathname,
    432 	 * and the leading directories that we just stripped off all exist,
    433 	 * put them back on.
    434 	 */
    435 	if (strip_leading == 957 && name != fullname && *fullname != '/') {
    436 		name[-1] = '\0';
    437 		if (stat(fullname, &filestat) == 0 &&
    438 		    S_ISDIR(filestat.st_mode)) {
    439 			name[-1] = '/';
    440 			name = fullname;
    441 		}
    442 	}
    443 
    444 	name = xstrdup(name);
    445 	free(fullname);
    446 
    447 	if (stat(name, &filestat) && !assume_exists) {
    448 		char *filebase = basename(name);
    449 		int pathlen = filebase - name;
    450 
    451 		/* Put any leading path into `tmpbuf'. */
    452 		strncpy(tmpbuf, name, pathlen);
    453 
    454 #define try(f, a1, a2) \
    455     (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
    456 #define try1(f, a1) \
    457     (Sprintf(tmpbuf + pathlen, f, a1), stat(tmpbuf, &filestat) == 0)
    458 		if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
    459 		    try1("RCS/%s"  , filebase) ||
    460 		    try(    "%s%s", filebase, RCSSUFFIX) ||
    461 		    try("SCCS/%s%s", SCCSPREFIX, filebase) ||
    462 		    try(     "%s%s", SCCSPREFIX, filebase))
    463 			return name;
    464 		free(name);
    465 		name = NULL;
    466 	}
    467 
    468 	return name;
    469 }
    470