util.c revision 95601
169793Sobrien/* $FreeBSD: head/gnu/usr.bin/patch/util.c 95601 2002-04-28 01:33:45Z gad $ */
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
2832672Sache/* Rename a file, copying it if necessary. */
2932672Sache
3032672Sacheint
3195601Sgadmove_file(char *from, char *to)
3232672Sache{
3332672Sache    char bakname[512];
3432672Sache    Reg1 char *s;
3532672Sache    Reg2 int i;
3632672Sache    Reg3 int fromfd;
3732672Sache
3832672Sache    /* to stdout? */
3932672Sache
4032672Sache    if (strEQ(to, "-")) {
4132672Sache#ifdef DEBUGGING
4232672Sache	if (debug & 4)
4332672Sache	    say2("Moving %s to stdout.\n", from);
4432672Sache#endif
4532672Sache	fromfd = open(from, 0);
4632672Sache	if (fromfd < 0)
4732672Sache	    pfatal2("internal error, can't reopen %s", from);
4832672Sache	while ((i=read(fromfd, buf, sizeof buf)) > 0)
4932672Sache	    if (write(1, buf, i) != 1)
5032672Sache		pfatal1("write failed");
5132672Sache	Close(fromfd);
5232672Sache	return 0;
5332672Sache    }
5432672Sache
5532672Sache    if (origprae) {
5632672Sache	Strcpy(bakname, origprae);
5732672Sache	Strcat(bakname, to);
5832672Sache    } else {
5932672Sache#ifndef NODIR
6032672Sache	char *backupname = find_backup_file_name(to);
6132672Sache	if (backupname == (char *) 0)
6232672Sache	    fatal1("out of memory\n");
6332672Sache	Strcpy(bakname, backupname);
6432672Sache	free(backupname);
6532672Sache#else /* NODIR */
6632672Sache	Strcpy(bakname, to);
6732672Sache    	Strcat(bakname, simple_backup_suffix);
6832672Sache#endif /* NODIR */
6932672Sache    }
7032672Sache
7132672Sache    if (stat(to, &filestat) == 0) {	/* output file exists */
7232672Sache	dev_t to_device = filestat.st_dev;
7332672Sache	ino_t to_inode  = filestat.st_ino;
7432672Sache	char *simplename = bakname;
7532672Sache
7632672Sache	for (s=bakname; *s; s++) {
7732672Sache	    if (*s == '/')
7832672Sache		simplename = s+1;
7932672Sache	}
8032672Sache	/* Find a backup name that is not the same file.
8132672Sache	   Change the first lowercase char into uppercase;
8232672Sache	   if that isn't sufficient, chop off the first char and try again.  */
8332672Sache	while (stat(bakname, &filestat) == 0 &&
8432672Sache		to_device == filestat.st_dev && to_inode == filestat.st_ino) {
8532672Sache	    /* Skip initial non-lowercase chars.  */
8632672Sache	    for (s=simplename; *s && !islower((unsigned char)*s); s++) ;
8732672Sache	    if (*s)
8832672Sache		*s = toupper((unsigned char)*s);
8932672Sache	    else
9032672Sache		Strcpy(simplename, simplename+1);
9132672Sache	}
9232672Sache	while (unlink(bakname) >= 0) ;	/* while() is for benefit of Eunice */
9332672Sache#ifdef DEBUGGING
9432672Sache	if (debug & 4)
9532672Sache	    say3("Moving %s to %s.\n", to, bakname);
9632672Sache#endif
9732672Sache	if (rename(to, bakname) < 0) {
9832672Sache	    say4("Can't backup %s, output is in %s: %s\n", to, from,
9932672Sache		 strerror(errno));
10032672Sache	    return -1;
10132672Sache	}
10232672Sache	while (unlink(to) >= 0) ;
10332672Sache    }
10432672Sache#ifdef DEBUGGING
10532672Sache    if (debug & 4)
10632672Sache	say3("Moving %s to %s.\n", from, to);
10732672Sache#endif
10832672Sache    if (rename(from, to) < 0) {		/* different file system? */
10932672Sache	Reg4 int tofd;
11032672Sache
11132672Sache	tofd = creat(to, 0666);
11232672Sache	if (tofd < 0) {
11332672Sache	    say4("Can't create %s, output is in %s: %s\n",
11432672Sache	      to, from, strerror(errno));
11532672Sache	    return -1;
11632672Sache	}
11732672Sache	fromfd = open(from, 0);
11832672Sache	if (fromfd < 0)
11932672Sache	    pfatal2("internal error, can't reopen %s", from);
12032672Sache	while ((i=read(fromfd, buf, sizeof buf)) > 0)
12132672Sache	    if (write(tofd, buf, i) != i)
12232672Sache		pfatal1("write failed");
12332672Sache	Close(fromfd);
12432672Sache	Close(tofd);
12532672Sache    }
12632672Sache    Unlink(from);
12732672Sache    return 0;
12832672Sache}
12932672Sache
13032672Sache/* Copy a file. */
13132672Sache
13232672Sachevoid
13395601Sgadcopy_file(char *from, char *to)
13432672Sache{
13532672Sache    Reg3 int tofd;
13632672Sache    Reg2 int fromfd;
13732672Sache    Reg1 int i;
13832672Sache
13932672Sache    tofd = creat(to, 0666);
14032672Sache    if (tofd < 0)
14132672Sache	pfatal2("can't create %s", to);
14232672Sache    fromfd = open(from, 0);
14332672Sache    if (fromfd < 0)
14432672Sache	pfatal2("internal error, can't reopen %s", from);
14532672Sache    while ((i=read(fromfd, buf, sizeof buf)) > 0)
14632672Sache	if (write(tofd, buf, i) != i)
14732672Sache	    pfatal2("write to %s failed", to);
14832672Sache    Close(fromfd);
14932672Sache    Close(tofd);
15032672Sache}
15132672Sache
15232672Sache/* Allocate a unique area for a string. */
15332672Sache
15432672Sachechar *
15595601Sgadsavestr(char *s)
15632672Sache{
15732672Sache    Reg3 char *rv;
15832672Sache    Reg2 char *t;
15932672Sache
16032672Sache    if (!s)
16132672Sache	s = "Oops";
16232672Sache    t = s;
16332672Sache    while (*t++);
16432672Sache    rv = malloc((MEM) (t - s));
16532672Sache    if (rv == Nullch) {
16632672Sache	if (using_plan_a)
16732672Sache	    out_of_mem = TRUE;
16832672Sache	else
16932672Sache	    fatal1("out of memory\n");
17032672Sache    }
17132672Sache    else {
17232672Sache	t = rv;
17332672Sache	while ((*t++ = *s++));
17432672Sache    }
17532672Sache    return rv;
17632672Sache}
17732672Sache
17832672Sache#if defined(lint) && defined(CANVARARG)
17932672Sache
18032672Sache/*VARARGS ARGSUSED*/
18132672Sachesay(pat) char *pat; { ; }
18232672Sache/*VARARGS ARGSUSED*/
18332672Sachefatal(pat) char *pat; { ; }
18432672Sache/*VARARGS ARGSUSED*/
18532672Sachepfatal(pat) char *pat; { ; }
18632672Sache/*VARARGS ARGSUSED*/
18732672Sacheask(pat) char *pat; { ; }
18832672Sache
18932672Sache#else
19032672Sache
19132672Sache/* Vanilla terminal output (buffered). */
19232672Sache
19332672Sachevoid
19432672Sachesay(pat,arg1,arg2,arg3)
19532672Sachechar *pat;
19632672Sachelong arg1,arg2,arg3;
19732672Sache{
19832672Sache    fprintf(stderr, pat, arg1, arg2, arg3);
19932672Sache    Fflush(stderr);
20032672Sache}
20132672Sache
20232672Sache/* Terminal output, pun intended. */
20332672Sache
20432672Sachevoid				/* very void */
20532672Sachefatal(pat,arg1,arg2,arg3)
20632672Sachechar *pat;
20732672Sachelong arg1,arg2,arg3;
20832672Sache{
20932672Sache    fprintf(stderr, "patch: **** ");
21032672Sache    fprintf(stderr, pat, arg1, arg2, arg3);
21132672Sache    my_exit(1);
21232672Sache}
21332672Sache
21432672Sache/* Say something from patch, something from the system, then silence . . . */
21532672Sache
21632672Sachevoid				/* very void */
21732672Sachepfatal(pat,arg1,arg2,arg3)
21832672Sachechar *pat;
21932672Sachelong arg1,arg2,arg3;
22032672Sache{
22132672Sache    int errnum = errno;
22232672Sache
22332672Sache    fprintf(stderr, "patch: **** ");
22432672Sache    fprintf(stderr, pat, arg1, arg2, arg3);
22532672Sache    fprintf(stderr, ": %s\n", strerror(errnum));
22632672Sache    my_exit(1);
22732672Sache}
22832672Sache
22932672Sache/* Get a response from the user, somehow or other. */
23032672Sache
23132672Sacheint
23232672Sacheask(pat,arg1,arg2,arg3)
23332672Sachechar *pat;
23432672Sachelong arg1,arg2,arg3;
23532672Sache{
23632672Sache    int ttyfd;
23732672Sache    int r;
23832672Sache    bool tty2 = isatty(2);
23932672Sache
24032672Sache    Sprintf(buf, pat, arg1, arg2, arg3);
24132672Sache    Fflush(stderr);
24232672Sache    write(2, buf, strlen(buf));
24332672Sache    if (tty2) {				/* might be redirected to a file */
24432672Sache	r = read(2, buf, sizeof buf);
24532672Sache    }
24632672Sache    else if (isatty(1)) {		/* this may be new file output */
24732672Sache	Fflush(stdout);
24832672Sache	write(1, buf, strlen(buf));
24932672Sache	r = read(1, buf, sizeof buf);
25032672Sache    }
25169793Sobrien    else if ((ttyfd = open(_PATH_TTY, 2)) >= 0 && isatty(ttyfd)) {
25232672Sache					/* might be deleted or unwriteable */
25332672Sache	write(ttyfd, buf, strlen(buf));
25432672Sache	r = read(ttyfd, buf, sizeof buf);
25532672Sache	Close(ttyfd);
25632672Sache    }
25732672Sache    else if (isatty(0)) {		/* this is probably patch input */
25832672Sache	Fflush(stdin);
25932672Sache	write(0, buf, strlen(buf));
26032672Sache	r = read(0, buf, sizeof buf);
26132672Sache    }
26232672Sache    else {				/* no terminal at all--default it */
26332672Sache	buf[0] = '\n';
26432672Sache	buf[1] = 0;
26532672Sache	say1(buf);
26632672Sache	return 0;			/* signal possible error */
26732672Sache    }
26832672Sache    if (r <= 0)
26932672Sache	buf[0] = 0;
27032672Sache    else
27132672Sache	buf[r] = '\0';
27232672Sache    if (!tty2)
27332672Sache	say1(buf);
27432672Sache
27532672Sache    if (r <= 0)
27632672Sache	return 0;			/* if there was an error, return it */
27732672Sache    else
27832672Sache	return 1;
27932672Sache}
28032672Sache#endif /* lint */
28132672Sache
28232672Sache/* How to handle certain events when not in a critical region. */
28332672Sache
28432672Sachevoid
28595601Sgadset_signals(int reset)
28632672Sache{
28732672Sache#ifndef lint
28832672Sache    static RETSIGTYPE (*hupval)(),(*intval)();
28932672Sache
29032672Sache    if (!reset) {
29132672Sache	hupval = signal(SIGHUP, SIG_IGN);
29232672Sache	if (hupval != SIG_IGN)
29332672Sache	    hupval = (RETSIGTYPE(*)())my_exit;
29432672Sache	intval = signal(SIGINT, SIG_IGN);
29532672Sache	if (intval != SIG_IGN)
29632672Sache	    intval = (RETSIGTYPE(*)())my_exit;
29732672Sache    }
29832672Sache    Signal(SIGHUP, hupval);
29932672Sache    Signal(SIGINT, intval);
30032672Sache#endif
30132672Sache}
30232672Sache
30332672Sache/* How to handle certain events when in a critical region. */
30432672Sache
30532672Sachevoid
30695601Sgadignore_signals(void)
30732672Sache{
30832672Sache#ifndef lint
30932672Sache    Signal(SIGHUP, SIG_IGN);
31032672Sache    Signal(SIGINT, SIG_IGN);
31132672Sache#endif
31232672Sache}
31332672Sache
31432672Sache/* Make sure we'll have the directories to create a file.
31532672Sache   If `striplast' is TRUE, ignore the last element of `filename'.  */
31632672Sache
31732672Sachevoid
31832672Sachemakedirs(filename,striplast)
31932672SacheReg1 char *filename;
32032672Sachebool striplast;
32132672Sache{
32232672Sache    char tmpbuf[256];
32332672Sache    Reg2 char *s = tmpbuf;
32432672Sache    char *dirv[20];		/* Point to the NULs between elements.  */
32532672Sache    Reg3 int i;
32632672Sache    Reg4 int dirvp = 0;		/* Number of finished entries in dirv. */
32732672Sache
32832672Sache    /* Copy `filename' into `tmpbuf' with a NUL instead of a slash
32932672Sache       between the directories.  */
33032672Sache    while (*filename) {
33132672Sache	if (*filename == '/') {
33232672Sache	    filename++;
33332672Sache	    dirv[dirvp++] = s;
33432672Sache	    *s++ = '\0';
33532672Sache	}
33632672Sache	else {
33732672Sache	    *s++ = *filename++;
33832672Sache	}
33932672Sache    }
34032672Sache    *s = '\0';
34132672Sache    dirv[dirvp] = s;
34232672Sache    if (striplast)
34332672Sache	dirvp--;
34432672Sache    if (dirvp < 0)
34532672Sache	return;
34632672Sache
34732672Sache    strcpy(buf, "mkdir");
34832672Sache    s = buf;
34932672Sache    for (i=0; i<=dirvp; i++) {
35032672Sache	struct stat sbuf;
35132672Sache
35232672Sache	if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
35332672Sache	    while (*s) s++;
35432672Sache	    *s++ = ' ';
35532672Sache	    strcpy(s, tmpbuf);
35632672Sache	}
35732672Sache	*dirv[i] = '/';
35832672Sache    }
35932672Sache    if (s != buf)
36032672Sache	system(buf);
36132672Sache}
36232672Sache
36332672Sache/* Make filenames more reasonable. */
36432672Sache
36532672Sachechar *
36695601Sgadfetchname(char *at, int strip_leading, int assume_exists)
36732672Sache{
36832672Sache    char *fullname;
36932672Sache    char *name;
37032672Sache    Reg1 char *t;
37132672Sache    char tmpbuf[200];
37232672Sache    int sleading = strip_leading;
37332672Sache
37432672Sache    if (!at)
37532672Sache	return Nullch;
37632672Sache    while (isspace((unsigned char)*at))
37732672Sache	at++;
37832672Sache#ifdef DEBUGGING
37932672Sache    if (debug & 128)
38032672Sache	say4("fetchname %s %d %d\n",at,strip_leading,assume_exists);
38132672Sache#endif
38269793Sobrien    if (strnEQ(at, _PATH_DEVNULL, sizeof _PATH_DEVNULL - 1))	/* so files can be created by diffing */
38332672Sache	return Nullch;			/*   against /dev/null. */
38432672Sache    name = fullname = t = savestr(at);
38532672Sache
38632672Sache    /* Strip off up to `sleading' leading slashes and null terminate.  */
38732672Sache    for (; *t && !isspace((unsigned char)*t); t++)
38832672Sache	if (*t == '/')
38932672Sache	    if (--sleading >= 0)
39032672Sache		name = t+1;
39132672Sache    *t = '\0';
39232672Sache
39332672Sache    /* If no -p option was given (957 is the default value!),
39432672Sache       we were given a relative pathname,
39532672Sache       and the leading directories that we just stripped off all exist,
39632672Sache       put them back on.  */
39732672Sache    if (strip_leading == 957 && name != fullname && *fullname != '/') {
39832672Sache	name[-1] = '\0';
39932672Sache	if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) {
40032672Sache	    name[-1] = '/';
40132672Sache	    name=fullname;
40232672Sache	}
40332672Sache    }
40432672Sache
40532672Sache    name = savestr(name);
40632672Sache    free(fullname);
40732672Sache
40832672Sache    if (stat(name, &filestat) && !assume_exists) {
40932672Sache	char *filebase = basename(name);
41032672Sache	int pathlen = filebase - name;
41132672Sache
41232672Sache	/* Put any leading path into `tmpbuf'.  */
41332672Sache	strncpy(tmpbuf, name, pathlen);
41432672Sache
41532672Sache#define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
41632672Sache	if (   try("RCS/%s%s", filebase, RCSSUFFIX)
41732672Sache	    || try("RCS/%s%s", filebase,        "")
41832672Sache	    || try(    "%s%s", filebase, RCSSUFFIX)
41932672Sache	    || try("SCCS/%s%s", SCCSPREFIX, filebase)
42032672Sache	    || try(     "%s%s", SCCSPREFIX, filebase))
42132672Sache	  return name;
42232672Sache	free(name);
42332672Sache	name = Nullch;
42432672Sache    }
42532672Sache
42632672Sache    return name;
42732672Sache}
42832672Sache
42932672Sachechar *
43095601Sgadxmalloc(unsigned int size)
43132672Sache{
43232672Sache  register char *p = (char *) malloc (size);
43332672Sache  if (!p)
43432672Sache    fatal("out of memory");
43532672Sache  return p;
43632672Sache}
437