
#define DBG(str) if (dflg) fprintf(stderr, "pid %d: %s\r\n", getpid(), str);
int dflg = 0;
int errno;

/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ifndef lint
static char rcsid[] = "$Header: script.c 1.1 89/12/11 $";
#endif

/*
 * script
 */

#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/termio.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/conf.h>

#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef BSDX
#include <sgtty.h>
#include <sys/time.h>
#endif
#include <sys/file.h>

char	*getenv();
char	*ctime();
char	*shell;
FILE	*fscript;
int	master;
int	slave;
int	child;
char	*fname = "typescript";
int	finish();
int	doexit();

#ifdef BSDX
struct	sgttyb b;
struct	tchars tc;
struct	ltchars lc;
#else
#include <termio.h>
struct termio ttytermio;
#endif
int	lb;
int	l;
char	*slave_line;
char	*master_line;
int	aflg;

main(argc, argv)
	int argc;
	char *argv[];
{
	int f;

#ifdef BSDX
	printf("BAD NEWS:  BSDX was defined during compilation\n");
#endif

	shell = getenv("SHELL");
	if (shell == 0)
		shell = "/bin/sh";
	argc--, argv++;
	while (argc > 0 && argv[0][0] == '-') {
		switch (argv[0][1]) {

		case 'a':
			aflg++;
			break;

		case 'd':
			dflg++;
			break;

		default:
			fprintf(stderr,
			    "usage: script [ -ad ] [ typescript ]\n");
			exit(1);
		}
		argc--, argv++;
	}
#ifdef BSDX
	ioctl(0, TIOCGETP, (char *)&b);
	ioctl(0, TIOCGETC, (char *)&tc);
	ioctl(0, TIOCGETD, (char *)&l);
	ioctl(0, TIOCGLTC, (char *)&lc);
	ioctl(0, TIOCLGET, (char *)&lb);
#else
        if (ioctl(0, TCGETA, &ttytermio) == -1) {
                perror("getmaster: TCGETA failed:");
                exit(1);
        }
#endif
	if (argc > 0)
		fname = argv[0];
	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
		perror(fname);
                exit(1);
	}
	getmaster();
	printf("Script started, file is %s\n", fname);
	/* fixtty(); moved to below */

	(void) signal(SIGCLD, finish);
	child = fork();
	if (child < 0) {
		perror("fork");
		fail();
	}
	if (child == 0) {
		signal(SIGCLD, doexit);
		f = fork();
		if (f < 0) {
			perror("fork");
			fail();
		}
		if (f)
			dooutput();
		else
			doshell();
	}
	fixtty();
	doinput();
}

doinput()
{
	char ibuf[BUFSIZ];
	int cc;

DBG("Enter doinput");
	(void) fclose(fscript);
	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
		(void) write(master, ibuf, cc);
DBG("Leave doinput");
	done();
}

#include <sys/wait.h>

finish()
{
#ifdef BSDX
	union wait status;

	if (wait3(&status, WNOHANG, 0) != child)
#else
	if (wait((int *)0) != child)
#endif
		return;
	done();
}

doexit()
{
	exit(0);
}

dooutput()
{
	time_t tvec;
	char obuf[BUFSIZ];
	int cc;

DBG("Enter dooutput");
	(void) close(0);
	tvec = time((time_t *)0);
	fprintf(fscript, "Script started on %s", ctime(&tvec));
	for (;;) {
		cc = read(master, obuf, sizeof (obuf));
		if (cc <= 0)
			break;
		(void) write(1, obuf, cc);
		(void) fwrite(obuf, 1, cc, fscript);
	}
	tvec = time((time_t *)0);
	fprintf(fscript,"\nscript done on %s", ctime(&tvec));
	(void) fclose(fscript);
	(void) close(master);
DBG("Leave dooutput");
	exit(0);
}


#include <fcntl.h>

static int
dup2(ofd, nfd)
int ofd;	/* old fd */
int nfd;	/* new fd */
{
	close(nfd);
	return(fcntl(ofd, F_DUPFD, nfd));
}


doshell()
{
	int t;
	int err;

#ifdef BSDX
	t = open("/dev/tty", 2);
	if (t >= 0) {
		ioctl(t, TIOCNOTTY, (char *)0);
		(void) close(t);
	}
#else
	setpgrp();	/* form new session, lose controlling tty */
#endif
DBG("Enter doshell");
	getslave();
	(void) close(master);
	(void) fclose(fscript);
	err = dup(2);
	fcntl(err, F_SETFD, 1);
	dup2(slave, 0);
	dup2(slave, 1);
	dup2(slave, 2);
	(void) close(slave);
	execl(shell, "sh", "-i", 0);
	dup2(err, 2);
	perror(shell);
	fprintf(err, "pid %d: execl failed, errno = %d\n", getpid(), errno);
	fail();
}

fixtty()
{
#ifdef BSDX
	struct sgttyb sbuf;

	sbuf = b;
	sbuf.sg_flags |= RAW;
	sbuf.sg_flags &= ~ECHO;
	ioctl(0, TIOCSETP, (char *)&sbuf);
#else
	struct termio rawtermio;

DBG("Enter fixtty");
        /* Reset tty state to raw */

        rawtermio = ttytermio;
        rawtermio.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
        rawtermio.c_oflag &= ~(OPOST);
        rawtermio.c_lflag &= ~(ISIG | ICANON | ECHO);
        rawtermio.c_cc[VMIN]  = 1;
        rawtermio.c_cc[VTIME] = 0;

        if (ioctl(0, TCSETAF, &rawtermio) == -1) {
                perror("fixtty: TCSETAF failed:");
                fail();
        }
DBG("Leave fixtty");
#endif
    
}

fail()
{

#ifdef BSDX
	/* this assumes job control has placed us in our own pgrp */
	(void) kill(0, SIGTERM);
#endif
	done();
}

done()
{
#ifdef BSDX
	ioctl(0, TIOCSETP, (char *)&b);
#else
        if (ioctl(0, TCSETAF, &ttytermio) == -1) {
                perror("done: TCSETAF failed:");
                exit(1);
        }
#endif

	printf("Script done, file is %s\n", fname);
	exit(0);
}


getmaster()
{

	master = getpseudotty(&slave_line, &master_line);
	if (master < 0) {
		fprintf(stderr, "Out of pty's\n");
		fail();
	}
}

/* If no STREAMS module on ttyfd, I_PUSH "ld" line discipline */

pushld(ttyfd)
int ttyfd;
{
	char i_look[FMNAMESZ+1];

	if (ioctl(ttyfd, I_LOOK, i_look) == -1) {
		if (errno == EINVAL) {
DBG("I_PUSH ld");
			if (ioctl(ttyfd, I_PUSH, "ld") < 0) {
				perror("couldn't I_PUSH ld on tty:");
				exit(1);
			}
		} else {
			perror("Couldn't I_LOOK on tty:");
			exit(1);
		}
	} else {
DBG("STREAMS module already on slave");
	}
}

getslave()
{
DBG("Enter getslave");

	slave = open(slave_line, 2);
	if (slave < 0) {
		perror(slave_line);
		fail();
	}
#ifdef BSDX
	ioctl(slave, TIOCSETP, (char *)&b);
	ioctl(slave, TIOCSETC, (char *)&tc);
	ioctl(slave, TIOCSLTC, (char *)&lc);
	ioctl(slave, TIOCLSET, (char *)&lb);
	ioctl(slave, TIOCSETD, (char *)&l);
#else
	pushld(slave);
        if (ioctl(slave, TCSETAF, &ttytermio) == -1) {
                perror("getslave: TCSETAF failed:");
                fail();
        }
#endif
DBG("Leave getslave");
}
