169793Sobrien/* $FreeBSD$ */
269793Sobrien
369793Sobrien#include <paths.h>
469793Sobrien
532672Sache#include "EXTERN.h"
632672Sache#include "common.h"
732672Sache#include "INTERN.h"
832672Sache#include "util.h"
932672Sache#include "backupfile.h"
1032672Sache
1195601Sgadvoid	my_exit(int _status);		/* in patch.c */
1232672Sache
1332672Sache#ifndef HAVE_STRERROR
1432672Sachestatic char *
1532672Sacheprivate_strerror (errnum)
1632672Sache     int errnum;
1732672Sache{
1832672Sache  extern char *sys_errlist[];
1932672Sache  extern int sys_nerr;
2032672Sache
2132672Sache  if (errnum > 0 && errnum <= sys_nerr)
2232672Sache    return sys_errlist[errnum];
2332672Sache  return "Unknown system error";
2432672Sache}
2532672Sache#define strerror private_strerror
2632672Sache#endif /* !HAVE_STRERROR */
2732672Sache
2897028Sgad/*
2997028Sgad * Rename a file, copying it if necessary.
3097028Sgad */
3132672Sacheint
3295601Sgadmove_file(char *from, char *to)
3332672Sache{
3497028Sgad	char bakname[512];
3597028Sgad	Reg1 char *s;
3697028Sgad	Reg2 int i;
3797028Sgad	Reg3 int fromfd;
3832672Sache
3997028Sgad	/* to stdout? */
4032672Sache
4197028Sgad	if (strEQ(to, "-")) {
4232672Sache#ifdef DEBUGGING
4397028Sgad		if (debug & 4)
4497028Sgad			say2("Moving %s to stdout.\n", from);
4532672Sache#endif
4697028Sgad		fromfd = open(from, 0);
4797028Sgad		if (fromfd < 0)
4897028Sgad			pfatal2("internal error, can't reopen %s", from);
49191910Ssepotvin		while ((i = read(fromfd, buf, buf_size)) > 0)
5097028Sgad			if (write(1, buf, i) != 1)
5197028Sgad				pfatal1("write failed");
5297028Sgad		Close(fromfd);
5397028Sgad		return 0;
5497028Sgad	}
5532672Sache
5697028Sgad	if (origprae) {
5797028Sgad		Strcpy(bakname, origprae);
5897028Sgad		Strcat(bakname, to);
5997028Sgad	} else {
6032672Sache#ifndef NODIR
6197028Sgad		char *backupname = find_backup_file_name(to);
6297028Sgad		if (backupname == (char *) 0)
6397028Sgad		    fatal1("out of memory\n");
6497028Sgad		Strcpy(bakname, backupname);
6597028Sgad		free(backupname);
6632672Sache#else /* NODIR */
6797028Sgad		Strcpy(bakname, to);
6897028Sgad		Strcat(bakname, simple_backup_suffix);
6932672Sache#endif /* NODIR */
7097028Sgad	}
7132672Sache
7297028Sgad	if (stat(to, &filestat) == 0) {	/* output file exists */
7397028Sgad		dev_t to_device = filestat.st_dev;
7497028Sgad		ino_t to_inode  = filestat.st_ino;
7597028Sgad		char *simplename = bakname;
7632672Sache
7797028Sgad		for (s = bakname; *s; s++) {
7897028Sgad			if (*s == '/')
7997028Sgad				simplename = s + 1;
8097028Sgad		}
8197028Sgad		/*
8297028Sgad		 * Find a backup name that is not the same file.
8397028Sgad		 * Change the first lowercase char into uppercase;
8497028Sgad		 * if that isn't sufficient, chop off the first char
8597028Sgad		 * and try again.
8697028Sgad		 */
8797028Sgad		while (stat(bakname, &filestat) == 0 &&
8897028Sgad		    to_device == filestat.st_dev &&
8997028Sgad		    to_inode == filestat.st_ino) {
9097028Sgad			/* Skip initial non-lowercase chars.  */
9197028Sgad			for (s=simplename; *s && !islower((unsigned char)*s);
9297028Sgad			    s++)
9397028Sgad				;
9497028Sgad			if (*s)
9597028Sgad				*s = toupper((unsigned char)*s);
9697028Sgad			else
9797028Sgad				Strcpy(simplename, simplename + 1);
9897028Sgad		}
9997028Sgad		while (unlink(bakname) >= 0)
10097028Sgad			;	/* while() is for benefit of Eunice */
10132672Sache#ifdef DEBUGGING
10297028Sgad		if (debug & 4)
10397028Sgad			say3("Moving %s to %s.\n", to, bakname);
10432672Sache#endif
10597028Sgad		if (rename(to, bakname) < 0) {
10697028Sgad			say4("Can't backup %s, output is in %s: %s\n", to, from,
10797028Sgad			    strerror(errno));
10897028Sgad			return -1;
10997028Sgad		}
11097028Sgad		while (unlink(to) >= 0)
11197028Sgad			;
11232672Sache	}
11332672Sache#ifdef DEBUGGING
11497028Sgad	if (debug & 4)
11597028Sgad		say3("Moving %s to %s.\n", from, to);
11632672Sache#endif
11797028Sgad	if (rename(from, to) < 0) {	/* different file system? */
11897028Sgad		Reg4 int tofd;
11932672Sache
12097028Sgad		tofd = creat(to, 0666);
12197028Sgad		if (tofd < 0) {
12297028Sgad			say4("Can't create %s, output is in %s: %s\n",
12397028Sgad			    to, from, strerror(errno));
12497028Sgad			return -1;
12597028Sgad		}
12697028Sgad		fromfd = open(from, 0);
12797028Sgad		if (fromfd < 0)
12897028Sgad			pfatal2("internal error, can't reopen %s", from);
129191910Ssepotvin		while ((i = read(fromfd, buf, buf_size)) > 0)
13097028Sgad			if (write(tofd, buf, i) != i)
13197028Sgad				pfatal1("write failed");
13297028Sgad		Close(fromfd);
13397028Sgad		Close(tofd);
13432672Sache	}
13597028Sgad	Unlink(from);
13697028Sgad	return 0;
13732672Sache}
13832672Sache
13997028Sgad/*
14097028Sgad * Copy a file.
14197028Sgad */
14232672Sachevoid
14395601Sgadcopy_file(char *from, char *to)
14432672Sache{
14597028Sgad	Reg3 int tofd;
14697028Sgad	Reg2 int fromfd;
14797028Sgad	Reg1 int i;
14832672Sache
14997028Sgad	tofd = creat(to, 0666);
15097028Sgad	if (tofd < 0)
15197028Sgad		pfatal2("can't create %s", to);
15297028Sgad	fromfd = open(from, 0);
15397028Sgad	if (fromfd < 0)
15497028Sgad		pfatal2("internal error, can't reopen %s", from);
155191910Ssepotvin	while ((i = read(fromfd, buf, buf_size)) > 0)
15697028Sgad		if (write(tofd, buf, i) != i)
15797028Sgad			pfatal2("write to %s failed", to);
15897028Sgad	Close(fromfd);
15997028Sgad	Close(tofd);
16032672Sache}
16132672Sache
16297028Sgad/*
16397028Sgad * Allocate a unique area for a string.
16497028Sgad */
16532672Sachechar *
16695601Sgadsavestr(char *s)
16732672Sache{
16897028Sgad	Reg3 char *rv;
16997028Sgad	Reg2 char *t;
17032672Sache
17197028Sgad	if (!s)
17297028Sgad		s = "Oops";
17397028Sgad	t = s;
17497028Sgad	while (*t++)
17597028Sgad		;
17697028Sgad	rv = malloc((MEM) (t - s));
17797028Sgad	if (rv == Nullch) {
17897028Sgad		if (using_plan_a)
17997028Sgad			out_of_mem = TRUE;
18097028Sgad		else
18197028Sgad			fatal1("out of memory\n");
18297028Sgad	} else {
18397028Sgad		t = rv;
18497028Sgad		while ((*t++ = *s++));
18597028Sgad	}
18697028Sgad	return rv;
18732672Sache}
18832672Sache
18932672Sache#if defined(lint) && defined(CANVARARG)
19032672Sache
19132672Sache/*VARARGS ARGSUSED*/
19232672Sachesay(pat) char *pat; { ; }
19332672Sache/*VARARGS ARGSUSED*/
19432672Sachefatal(pat) char *pat; { ; }
19532672Sache/*VARARGS ARGSUSED*/
19632672Sachepfatal(pat) char *pat; { ; }
19732672Sache/*VARARGS ARGSUSED*/
19832672Sacheask(pat) char *pat; { ; }
19932672Sache
20032672Sache#else
20132672Sache
20297028Sgad/*
20397028Sgad * Vanilla terminal output (buffered).
20497028Sgad */
20532672Sachevoid
20632672Sachesay(pat,arg1,arg2,arg3)
20732672Sachechar *pat;
20832672Sachelong arg1,arg2,arg3;
20932672Sache{
21097028Sgad	fprintf(stderr, pat, arg1, arg2, arg3);
21197028Sgad	Fflush(stderr);
21232672Sache}
21332672Sache
21497028Sgad/*
21597028Sgad * Terminal output, pun intended.
21697028Sgad */
21732672Sachevoid				/* very void */
21832672Sachefatal(pat,arg1,arg2,arg3)
21932672Sachechar *pat;
22032672Sachelong arg1,arg2,arg3;
22132672Sache{
22297028Sgad	fprintf(stderr, "patch: **** ");
22397028Sgad	fprintf(stderr, pat, arg1, arg2, arg3);
22497028Sgad	my_exit(1);
22532672Sache}
22632672Sache
22797028Sgad/*
22897028Sgad * Say something from patch, something from the system, then silence...
22997028Sgad */
23032672Sachevoid				/* very void */
23132672Sachepfatal(pat,arg1,arg2,arg3)
23232672Sachechar *pat;
23332672Sachelong arg1,arg2,arg3;
23432672Sache{
23597028Sgad	int errnum = errno;
23632672Sache
23797028Sgad	fprintf(stderr, "patch: **** ");
23897028Sgad	fprintf(stderr, pat, arg1, arg2, arg3);
23997028Sgad	fprintf(stderr, ": %s\n", strerror(errnum));
24097028Sgad	my_exit(1);
24132672Sache}
24232672Sache
24397028Sgad/*
24497028Sgad * Get a response from the user, somehow or other.
24597028Sgad */
24632672Sacheint
24732672Sacheask(pat,arg1,arg2,arg3)
24832672Sachechar *pat;
24932672Sachelong arg1,arg2,arg3;
25032672Sache{
25197028Sgad	int ttyfd;
25297028Sgad	int r;
25397028Sgad	bool tty2 = isatty(2);
25432672Sache
25597028Sgad	Sprintf(buf, pat, arg1, arg2, arg3);
25697028Sgad	Fflush(stderr);
25797028Sgad	write(2, buf, strlen(buf));
25897028Sgad	if (tty2) {			/* might be redirected to a file */
259191910Ssepotvin		r = read(2, buf, buf_size);
26097028Sgad	} else if (isatty(1)) {		/* this may be new file output */
26197028Sgad		Fflush(stdout);
26297028Sgad		write(1, buf, strlen(buf));
263191910Ssepotvin		r = read(1, buf, buf_size);
26497028Sgad	} else if ((ttyfd = open(_PATH_TTY, 2)) >= 0 && isatty(ttyfd)) {
26532672Sache					/* might be deleted or unwriteable */
26697028Sgad		write(ttyfd, buf, strlen(buf));
267191910Ssepotvin		r = read(ttyfd, buf, buf_size);
26897028Sgad		Close(ttyfd);
26997028Sgad	} else if (isatty(0)) {		/* this is probably patch input */
27097028Sgad		Fflush(stdin);
27197028Sgad		write(0, buf, strlen(buf));
272191910Ssepotvin		r = read(0, buf, buf_size);
27397028Sgad	} else {			/* no terminal at all--default it */
27497028Sgad		buf[0] = '\n';
27597028Sgad		buf[1] = 0;
27697028Sgad		say1(buf);
27797028Sgad		return 0;		/* signal possible error */
27897028Sgad	}
27997028Sgad	if (r <= 0)
28097028Sgad		buf[0] = 0;
28197028Sgad	else
28297028Sgad		buf[r] = '\0';
28397028Sgad	if (!tty2)
28497028Sgad		say1(buf);
28532672Sache
28697028Sgad	if (r <= 0)
28797028Sgad		return 0;		/* if there was an error, return it */
28897028Sgad	else
28997028Sgad		return 1;
29032672Sache}
29132672Sache#endif /* lint */
29232672Sache
29397028Sgad/*
29497028Sgad * How to handle certain events when not in a critical region.
29597028Sgad */
29632672Sachevoid
29795601Sgadset_signals(int reset)
29832672Sache{
29932672Sache#ifndef lint
30097028Sgad	static RETSIGTYPE (*hupval)(),(*intval)();
30132672Sache
30297028Sgad	if (!reset) {
30397028Sgad		hupval = signal(SIGHUP, SIG_IGN);
30497028Sgad		if (hupval != SIG_IGN)
30597028Sgad			hupval = (RETSIGTYPE(*)())my_exit;
30697028Sgad		intval = signal(SIGINT, SIG_IGN);
30797028Sgad		if (intval != SIG_IGN)
30897028Sgad			intval = (RETSIGTYPE(*)())my_exit;
30997028Sgad	}
31097028Sgad	Signal(SIGHUP, hupval);
31197028Sgad	Signal(SIGINT, intval);
31232672Sache#endif
31332672Sache}
31432672Sache
31597028Sgad/*
31697028Sgad * How to handle certain events when in a critical region.
31797028Sgad */
31832672Sachevoid
31995601Sgadignore_signals(void)
32032672Sache{
32132672Sache#ifndef lint
32297028Sgad	Signal(SIGHUP, SIG_IGN);
32397028Sgad	Signal(SIGINT, SIG_IGN);
32432672Sache#endif
32532672Sache}
32632672Sache
32797028Sgad/*
32897028Sgad * Make sure we'll have the directories to create a file.
32997028Sgad * If `striplast' is TRUE, ignore the last element of `filename'.
33097028Sgad */
33132672Sachevoid
33232672Sachemakedirs(filename,striplast)
33332672SacheReg1 char *filename;
33432672Sachebool striplast;
33532672Sache{
33697028Sgad	char tmpbuf[256];
33797028Sgad	Reg2 char *s = tmpbuf;
33897028Sgad	char *dirv[20];		/* Point to the NULs between elements.  */
33997028Sgad	Reg3 int i;
34097028Sgad	Reg4 int dirvp = 0;	/* Number of finished entries in dirv. */
34132672Sache
34297028Sgad	/*
34397028Sgad	 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
34497028Sgad	 * between the directories.
34597028Sgad	 */
34697028Sgad	while (*filename) {
34797028Sgad		if (*filename == '/') {
34897028Sgad			filename++;
34997028Sgad			dirv[dirvp++] = s;
35097028Sgad			*s++ = '\0';
35197028Sgad		} else {
35297028Sgad			*s++ = *filename++;
35397028Sgad		}
35432672Sache	}
35597028Sgad	*s = '\0';
35697028Sgad	dirv[dirvp] = s;
35797028Sgad	if (striplast)
35897028Sgad		dirvp--;
35997028Sgad	if (dirvp < 0)
36097028Sgad		return;
36132672Sache
36297028Sgad	strcpy(buf, "mkdir");
36397028Sgad	s = buf;
36497028Sgad	for (i = 0; i <= dirvp; i++) {
36597028Sgad		struct stat sbuf;
36632672Sache
36797028Sgad		if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
36897028Sgad			while (*s)
36997028Sgad				s++;
37097028Sgad			*s++ = ' ';
37197028Sgad			strcpy(s, tmpbuf);
37297028Sgad		}
37397028Sgad		*dirv[i] = '/';
37432672Sache	}
37597028Sgad	if (s != buf)
37697028Sgad		system(buf);
37732672Sache}
37832672Sache
37997028Sgad/*
38097028Sgad * Make filenames more reasonable.
38197028Sgad */
38232672Sachechar *
38395601Sgadfetchname(char *at, int strip_leading, int assume_exists)
38432672Sache{
38597028Sgad	char *fullname;
38697028Sgad	char *name;
38797028Sgad	Reg1 char *t;
38897028Sgad	char tmpbuf[200];
38997028Sgad	int sleading = strip_leading;
39032672Sache
39197028Sgad	if (!at)
39297028Sgad		return Nullch;
39397028Sgad	while (isspace((unsigned char)*at))
39497028Sgad		at++;
39532672Sache#ifdef DEBUGGING
39697028Sgad	if (debug & 128)
39797028Sgad		say4("fetchname %s %d %d\n",at,strip_leading,assume_exists);
39832672Sache#endif
39997028Sgad	if (strnEQ(at, _PATH_DEVNULL, sizeof _PATH_DEVNULL - 1))
40097028Sgad		/* So files can be created by diffing against /dev/null. */
40197028Sgad		return Nullch;
40297028Sgad	name = fullname = t = savestr(at);
40332672Sache
40497028Sgad	/* Strip off up to `sleading' leading slashes and null terminate. */
40597028Sgad	for (; *t && !isspace((unsigned char)*t); t++)
40697028Sgad		if (*t == '/')
40797028Sgad			if (--sleading >= 0)
40897028Sgad				name = t + 1;
40997028Sgad	*t = '\0';
41032672Sache
41197028Sgad	/*
41297028Sgad	 * If no -p option was given (957 is the default value!),
41397028Sgad	 * we were given a relative pathname,
41497028Sgad	 * and the leading directories that we just stripped off all exist,
41597028Sgad	 * put them back on.
41697028Sgad	 */
41797028Sgad	if (strip_leading == 957 && name != fullname && *fullname != '/') {
41897028Sgad		name[-1] = '\0';
41997028Sgad		if (stat(fullname, &filestat) == 0 &&
42097028Sgad		    S_ISDIR(filestat.st_mode)) {
42197028Sgad			name[-1] = '/';
42297028Sgad			name = fullname;
42397028Sgad		}
42432672Sache	}
42532672Sache
42697028Sgad	name = savestr(name);
42797028Sgad	free(fullname);
42832672Sache
42997028Sgad	if (stat(name, &filestat) && !assume_exists) {
43097028Sgad		char *filebase = basename(name);
43197028Sgad		int pathlen = filebase - name;
43232672Sache
43397028Sgad		/* Put any leading path into `tmpbuf'. */
43497028Sgad		strncpy(tmpbuf, name, pathlen);
43532672Sache
43697028Sgad#define try(f, a1, a2) \
43797028Sgad    (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
43897028Sgad		if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
43997028Sgad		    try("RCS/%s%s", filebase,        "") ||
44097028Sgad		    try(    "%s%s", filebase, RCSSUFFIX) ||
44197028Sgad		    try("SCCS/%s%s", SCCSPREFIX, filebase) ||
44297028Sgad		    try(     "%s%s", SCCSPREFIX, filebase))
44397028Sgad			return name;
44497028Sgad		free(name);
44597028Sgad		name = Nullch;
44697028Sgad	}
44732672Sache
44897028Sgad	return name;
44932672Sache}
45032672Sache
45132672Sachechar *
45295601Sgadxmalloc(unsigned int size)
45332672Sache{
45497028Sgad	register char *p = (char *) malloc (size);
45597028Sgad	if (!p)
45697028Sgad		fatal("out of memory");
45797028Sgad	return p;
45832672Sache}
459