/*
 *                     RCS utilities
 */
#ifndef lint

#endif

/* Copyright (C) 1982, 1988, 1989 Walter Tichy
   Distributed under license by the Free Software Foundation, Inc.

This file is part of RCS.

RCS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

RCS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with RCS; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

Report problems and direct all questions to:

    rcs-bugs@cs.purdue.edu

*/




/* */




#include <errno.h>

#ifdef VMS
#include <types.h>
#include <stat.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif /* !vms */

#include <signal.h>
#include "rcsbase.h"
#include "pwd.h"
#include <varargs.h>

#if defined(USG) || defined(V4_2BSD)
#include <fcntl.h>
#endif

#ifndef VMS
#ifndef V4_2BSD
#define vfork fork
#endif
#endif

extern char * bindex();
extern FILE * finptr;
extern char * RCSfilename;
extern char * getlogin();
extern struct passwd *getpwuid();
extern char * malloc();
extern int errno;

static char UTIL_user_name[30];

char * talloc(size)
unsigned size;
{
	char * p;
	if (!(p = malloc(size))) {
		faterror("out of memory");
	}
	return p;
}



char * getcaller()
/* Function: gets the callers login from his uid.
 * If the uid is root, tries to get the true login with getlogin().
 */
{

#ifdef VMS
	return( cuserid( UTIL_user_name));
#else
	char * name;
	int uid;

	uid=getuid();
	if (uid==0) {
		/* super user; try getlogin() to distinguish */
		name = getlogin();
		if (name!=nil && *name!='\0')
			return name;
	}
	return(getpwuid(uid)->pw_name);
#endif /* !VMS */

}



struct hshentry * findlock(who,delete)
char * who; int delete;
/* Finds the first lock held by who and returns a pointer
 * to the locked delta; also removes the lock if delete==true.
 * Returns nil if there is no lock held by who.
 */
{
        register struct lock * next, * trail;
        struct lock dummy;

        dummy.nextlock=next=Locks;
        trail = &dummy;
        while (next!=nil) {
                if(strcmp(who,next->login)==0) break; /*found a lock*/
                trail=next;
                next=next->nextlock;
        }
        if (next!=nil) {
		/* found one */
		if (delete) {
		    /* delete it */
		    trail->nextlock=next->nextlock;
		    Locks=dummy.nextlock;
		    next->delta->lockedby=nil; /* reset locked-by */
		}
                return next->delta;
        } else  return nil;
}







struct lock * addlock(delta,who)
struct hshentry * delta; char * who;
/* Given a delta, addlock checks whether
 * the delta is locked by somebody other than who.
 * If so, an error message is printed, and false returned.
 * If the delta is not reserved at all, a lock for it is added,
 * and a pointer for the lock returned.
 */
{
        struct lock * next;

        next=Locks;
        while (next!=nil) {
                if (cmpnum(delta->num,next->delta->num)==0) {
                        if (strcmp(who,next->login)==0)
                                return next;
                                /* lock exists already */
                        else {
                                error("revision %s already locked by %s",
                                      delta->num, next->login);
                                return false;
                        }
                } else {
                        if (strcmp(who,next->login)==0) {
                                error("you already locked %s; only one lock allowed per person.",
                                       next->delta->num);
                                return false;
                        } else {
                                next=next->nextlock;
                        }
                }
        }
        /* not found; set up new lockblock */
        next= (struct lock *) talloc(sizeof (struct lock));
        delta->lockedby=next->login=who;
        next->delta= delta;
        next->nextlock=Locks;
        Locks=next;
        return next;
}



int addsymbol(delta,name,rebind)
struct hshentry * delta; char * name; int rebind;
/* Function: adds a new symbolic name and associates it with node delta.
 * If name already exists and rebind is true, the name is associated
 * with the new delta; otherwise, an error message is printed and
 * false returned. Returns true it successful.
 */
{       register struct assoc * next;
        next=Symbols;
        while (next!=nil) {
                if (strcmp(name,next->symbol)==0) {
                        if (rebind) {
                                next->delta=delta;
                                return true;
                        } else {
                                error("symbolic name %s already bound to %s",
                                        name,next->delta->num);
                                return false;
                        }
                } else  next = next->nextassoc;
        }
        /* not found; insert new pair. */
        next = (struct assoc *) talloc(sizeof(struct assoc));
        next->symbol=name;
        next->delta=delta;
        next->nextassoc=Symbols;
        Symbols = next;
        return true;
}




int checkaccesslist(who)
char * who;
/* function: Returns true if who is the superuser, the owner of the
 * file, the access list is empty, or who is on the access list.
 * Prints an error message and returns false otherwise.
 */
{
        register struct access * next;
        struct stat statbuf;

        if ((AccessList==nil) || (strcmp(who,"root")==0))
                return true;

        next=AccessList;
        do {
                if (strcmp(who,next->login)==0)
                        return true;
                next=next->nextaccess;
        } while (next!=nil);

        VOID fstat(fileno(finptr),&statbuf);  /* get owner of file */
        if (getuid() == statbuf.st_uid) return true;

        error("User %s not on the access list",who);
        return false;
}


static SIGNAL_TYPE catchsig(s)
{
	ignoreints();
        diagnose("\nRCS: cleaning up\n");
        VOID cleanup();
	exit(2);
#ifdef lint
	catchsig(s);
#endif
}

static sig[] = {SIGINT,SIGHUP,SIGQUIT,SIGPIPE,SIGTERM};
#define SIGS (sizeof(sig)/sizeof(*sig))
static SIGNAL_TYPE (*catcher[SIGS])();
  
  void catchints()
  {
	register i;
	for (i=SIGS; 0<=--i; )
	    if (signal(sig[i],SIG_IGN) == SIG_IGN)
		catcher[i]  = SIG_IGN;
	    else
		catcher[i] = catchsig;
	restoreints();
  }

  void ignoreints()
  {
	register i;
	for (i=SIGS; 0<=--i; )
		VOID signal(sig[i], SIG_IGN);
  }

void restoreints()
{
	register i;
	for (i=SIGS; 0<=--i; )
		if (catcher[i] != SIG_IGN)
			VOID signal(sig[i], catcher[i]);
}

#ifndef VMS

fastcopy(inf,outf)
FILE *inf, *outf;
/* Function: copies the remainder of file inf to outf. First copies the
 * rest that is in the IO-buffer of inf character by character, and then
 * copies the remainder in blocks.
 */
{
	char buf[BUFSIZ];
        register int rcount, wcount;

        /* write the rest of the buffer to outf */
        while ((--(inf->_cnt)) >= 0)
	{
                VOID putc(*(inf->_ptr)++ &0377,outf);
        }
        if (fflush(outf) == EOF) {
		writeerror();
	}

        /*now read the rest of the file in blocks*/
        while ((rcount=read(fileno(inf),buf,BUFSIZ))>0) {
                wcount=write(fileno(outf),buf,rcount);
                if (wcount!=rcount) {
                    writeerror();
                }
        }

} /* fastcopy */

#endif /* !vms */




#ifdef SNOOPFILE

#include "time.h"
extern struct tm* localtime();
extern long time();

logcommand(commandname,delta, sequence,login)
char* commandname; struct hshentry * delta, * sequence[];char * login;
/* Function: start a process to write the file that
 * logs the RCS command.
 * Each line in the log file contains the following information:
 * operation, revision(r), backward deltas applied(b), forward deltas applied(f),
 * total deltas present(t), creation date of delta(d), date of operation(o),
 * login of caller, RCS file name.
 */
{
        char logline[200];
        char curdate[datelength];
	char *inoutargs[5];
        register int i, backward, forward;
        long clock;
        struct tm * tm;

        clock=time((long *)0);
        tm=localtime(&clock);

        VOID sprintf(curdate,DATEFORM,
                tm->tm_year, tm->tm_mon+1, tm->tm_mday,
                tm->tm_hour, tm->tm_min, tm->tm_sec);

        i= backward=forward=0;
        while(sequence[i]!=nil) {  /* count deltas to be applied*/
        if (countnumflds(sequence[i]->num) == 2)
                backward++;  /* reverse delta */
        else    forward++;   /* branch delta  */
        i++;
        }
	VOID sprintf(logline,"%s %10sr %3db %3df %3dt %sc %so %s %s",
		commandname,delta->num,backward,forward,TotalDeltas,delta->date,
		curdate,login,bindex(getfullRCSname(), RCS_DIR_CHAR));
	inoutargs[0] = nil;
	inoutargs[1] = nil;
	inoutargs[2] = SNOOP;
	inoutargs[3] = logline;
	inoutargs[4] = nil;
	VOID run_back(inoutargs);
}
#endif  /* snoopfile */


static int fdreopen(fd, file, flags, mode)
	char *file;
{
	int newfd;
	VOID close(fd);
#ifdef VMS
  /* allow users to update and write file */
	newfd = flags==-1 ? 
	  creat(file,mode, "shr=put", "shr=upd", "shr=mse" ) : 
	  open(file,flags,mode, "shr=put", "shr=upd", "shr=mse");
#else
	newfd = flags==-1 ? creat(file,mode) : open(file,flags,mode);
#endif	if (newfd < 0  ||  newfd == fd)
		return newfd;
#ifdef F_DUPFD
	fd = fcntl(newfd, F_DUPFD, fd);
#else
	fd = dup2(newfd, fd);
#endif
	VOID close(newfd);
	return fd;
}

#ifdef VMS

int GETC( FILE *in, FILE *out, int echo)
{
int c;

	c = getc( in);
	if( echo)
	{
	  putc( c, out);
	}
	return( c);
} /* GETC for vms */

/* unlink (delete) for vms */

int unlink( char *str)
{
char tmpstr[100];
int rc;

	if( access( str, 2) == EOF)
	{
	  /* file not found */
	  rc = -1;
	}
	else
	{
	  delete( str);
	  rc = 0;
	}
	return( rc);
}

#endif /* vms */


static void tryopen(fd,file,flags)
	char *file;
{
	if (file  &&  fdreopen(fd,file,flags,0660) != fd) {
		VOID write(fileno(stderr), file, strlen(file));
		VOID write(fileno(stderr), ": cannot open\n", 14);
		_exit(2);
	}
}

/*
/* Run in the background a command specified by the strings in 'inoutargs'.
/* inoutargs[0], if nonnil, is the name of the input file.
/* inoutargs[1], if nonnil, is the name of the output file.
/* inoutargs[2..] form the command to be run in the background.
/*/
static int run_back(inoutargs)
	char **inoutargs;
{
	int pid;
	register char **p;
	if (fflush(stdout) == EOF  ||  fflush(stderr) == EOF)
		return -1;
	if (!(pid = vfork())) {
		p = inoutargs;
		tryopen(fileno(stdin), *p++, 0);
		tryopen(fileno(stdout), *p++, -1);
		VOID execv(*p, p);
		if (errno == ENOEXEC) {
			*--p = "/bin/sh";
			VOID execv(*p, p);
		}
		VOID write(fileno(stderr), *p, strlen(*p));
		VOID write(fileno(stderr), ": not found\n", 12);
		_exit(2);
	}
	return pid;
}

#define CARGSMAX 20
/*
/* Run a command.
/* The first two arguments are the input and output files (if nonnil);
/* the rest specify the command and its arguments.
/*/
int run(va_alist)
	va_dcl
{
	va_list ap;
	int pid, wstatus, w;
	char *rgargs[CARGSMAX];
#ifdef VMS
	char command_string[ 200 ];
#endif /* vms */

	register i = 0;
	va_start(ap);
	rgargs[0] = va_arg(ap, char *);  /* stdin file name */
	rgargs[1] = va_arg(ap, char *);  /* stdout file name */
	for (i = 2; i < CARGSMAX; i++) {
	    rgargs[i] = va_arg(ap, char *);
	    if (rgargs[i] == NULL)
		break;
	}
	va_end(ap);

#ifdef VMS

	for( i = 1; i < CARGSMAX; i++)
	{
	  if( rgargs[ i ] == 0) rgargs[ i ] = " ";
	}
	pid = -1;
	if( S1EQS2( rgargs[2], SYSTEM_MAIL_PROGRAM))
	{
	  sprintf( command_string, "%s /subject=\"Unlocked %s\" %s %s", 
	    rgargs[2], rgargs[4], rgargs[0], rgargs[3]);
	  system( command_string);
	  pid = 0;
	}

	if( S1EQS2( rgargs[2], DIFF_TO_FILE))
	{
	/* run gnu_diff when doing a checkin */
	  sprintf( command_string, "%s %s %s %s %s %s", rgargs[2],
	    rgargs[3], rgargs[4], rgargs[5], rgargs[6], rgargs[1]);
	  system( command_string);
	  pid = 0;
	}
	if( S1EQS2( rgargs[2], DIFF_NO_FILE))
	{
	/* run vms diff from rcsdiff or set a symbol 
	   difference == "anyway you want this to be run" */
	  sprintf( command_string, "%s %s %s %s %s %s ", rgargs[2],
	    rgargs[3], rgargs[4], rgargs[5], rgargs[6], rgargs[1]);
	  system( command_string);
	  pid = 0;
	}

	if( S1EQS2( rgargs[2], CO))
	{
	  sprintf( command_string, "%s %s %s %s > %s", CO, rgargs[3],
	    rgargs[4], rgargs[5], rgargs[1]);
	  system( command_string);
	  pid = 0;
	}
	return( pid);
	
#else
	pid = run_back(rgargs);
	if (pid < 0)
		return pid;
	for (;;)
		if ((w = wait(&wstatus)) < 0)
			return w;
		else if (w == pid)
			return wstatus;
#endif /* !vms */

} /* run() */

/* eof */
