[comp.unix.xenix] script

ag@elgar.UUCP (Keith Gabryelski) (02/20/89)

Since this is close to my last day at Elgar, and I have already
requested my newsfeed to be turned off, and I haven't seen this come
over comp.sources.misc yet.  Here it is:

Below is my version of the BSD script(1) command.  It has been tested
on a SCO XENIX 2.3.1 machine with SCO ptys and a 3b1.

Bug reports can be sent to me here at Elgar (ag@elgar.UUCP) via email
until March 3rd.  There is no guarantee I will receive them after that
date since I will no longer be running this machine.

I be Pennsylvania bound.

Pax, Keith

Ps, ag@wheaties.ai.mit.edu should work, although I only login once a
week.
-- 
 "It took no computation to dance to the rock 'n roll station." -- Lou Reed
ag@elgar.CTS.COM         Keith Gabryelski          ...!{ucsd, crash}!elgar!ag

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	script.c
#	script.C
# This archive created: Sun Feb 19 19:43:42 1989
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
SHELL= /bin/sh
CFLAGS= -g
LIBS= -lx

script:	script.o
	cc $(CFLAGS) script.o $(LIBS) -o script

script.o: script.c

shar:
	shar Makefile script.c script.C > script.shar
SHAR_EOF
fi # end of overwriting check
if test -f 'script.c'
then
	echo shar: will not over-write existing file "'script.c'"
else
cat << \SHAR_EOF > 'script.c'
/* script - save a terminal session.
   Copyright (C) 1988 Keith Gabryelski (ag@wheaties.ai.mit.edu)

This file is part of the script distribution.

The script distribution is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY.  No author or distributor accepts
responsibility to anyone for the consequences of using it or for
whether it serves any particular purpose or works at all, unless he
says so in writing.  Refer to the GNU General Public License for full
details.

Everyone is granted permission to copy, modify and redistribute the
script distribution, but only under the conditions described in the
GNU General Public License.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/*
** Written by: Keith Gabryelski (ag@elgar.UUCP)
** Guest appearance by Michael `Ford' Ditto (ford@kenobi.UUCP)
*/

#include <stdio.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>
#include <errno.h>

/*
** Things that should be defined in system headers.
*/

extern int errno, sys_nerr;
extern char *sys_errlist[];

extern char *getenv(), *ctime();
extern long time();
extern FILE *fopen();

/*
** Some defines to make our life easier.
*/

#define DEFAULT_TYPESCRIPT	"typescript"
#define SCO_PTYS		/* If you have SCO PTYS */

#undef  TRUE
#define TRUE	1

#undef  FALSE
#define FALSE	0

/*
** Globals
*/

char *progname,
    *shell,
    slave_pty_filename[sizeof("/dev/ttypXXX")],
    *typescript_filename;

int append_flag = 0,
    master_pty_fd,
    slave_pty_fd,
    shell_child_pid,
    output_child_pid;

FILE *typescript_fp;

struct termio term, saveterm, slaveterm;

/*
** declare functions.
*/

void nice_exit();
char *long_error();
int done(), doneII();

main(argc, argv)
int argc;
char *argv[];
{
    char buf[BUFSIZ];
    char master_pty_filename[sizeof("/dev/ptypXXX")];
    int n, i, found_pty;
    long clock;

    progname = *argv++; --argc;

    while (*argv != NULL && *argv[0] == '-')
    {
	/* A switch! */
	if (!strcmp(*argv, "--"))
	{
	    --argc; *argv++;
	    break;
	} else if (!strcmp(*argv, "-append"))
	{
	    append_flag = TRUE;
	    --argc; *argv++;
	} else
	{
	    (void) fprintf(stderr, "%s: invalid switch: \"%s\".\n", progname,
			   *argv);
	    usage();
	}
    }

    if (argc)
	typescript_filename = *argv;
    else
	typescript_filename = DEFAULT_TYPESCRIPT;

    if ((typescript_fp =
	 fopen(typescript_filename, append_flag ? "a" : "w")) == (FILE *)NULL)
    {
	(void) fprintf(stderr, "%s: Couldn't open typescript file: %s (%s).\n",
		progname, typescript_filename, long_error(errno));
	(void) exit(1);
    }

    if ((shell = getenv("SHELL")) == NULL)
	shell = "/etc/getty";

    /*
    ** find_master_pty() -	Get the next available master pseudo tty and
    **		       		return its corresponding slave pseudo tty
    **				name.
    **
    **		      	Master pseudo tty names are of the form:
    **
    **			    /dev/ptypX
    **
    **			Where `X' is the number of the pseudo tty
    **			starting with `0'.
    **
    **			Slave pseudo tty anmes are of the form:
    **
    **			    /dev/ttypX
    **
    **			Where `X' is the number of the pseudo tty
    **			starting with `0'.
    **
    **			This is compatible with SCO XENIX 2.3.1,
    **			but others may use a different scheme.
    */

    found_pty = FALSE;

    for (i=0; i < 255 && !found_pty; ++i)
    {
#ifdef SCO_PTYS
	(void) sprintf(master_pty_filename, "/dev/ptyp%d", i);
#else /* !SCO_PTYS aka The Real Thing */
	(void) sprintf(master_pty_filename, "/dev/pty%c%x", 'p'+i/16, i%16);
#endif /* !SCO_PTYS */

	if ((master_pty_fd = open(master_pty_filename, O_RDWR)) != -1)
	{
	    /*
	    ** Check both sides of pty 'cause I hear that rlogin()
	    ** has a bug.  Well, SCO doesn't come with rlogin(), but
	    ** maybe the 3rd party software has the bug.
 	    **
 	    ** (New note) This also makes sure there is no getty running
 	    ** on the slave.
	    */

#ifdef SCO_PTYS
 	    (void) sprintf(slave_pty_filename, "/dev/ttyp%d", i);
#else /* !SCO_PTYS aka The Real Thing */
 	    (void) sprintf(slave_pty_filename, "/dev/tty%c%x", 'p'+i/16,
			   i%16);
#endif /* !SCO_PTYS */

	    if ((slave_pty_fd = open(slave_pty_filename, O_RDWR)) < 0)
	    {
		(void)
		    fprintf(stderr,
			    "%s: Couldn't open slave pseudo tty: %s (%s).\n",
			       progname, slave_pty_filename,
			       long_error(errno));
		(void) exit(1);
	    }
 	    else
 		found_pty = TRUE;
	}
    }

    if (!found_pty)
    {
	(void) fprintf(stderr,
		       "%s: No available pseudo ttys; try again later.\n",
		       progname);
	(void) exit(1);
    }

    /*
    ** Setup terminal.
    */

    (void) ioctl(0, TCGETA, &term);
    slaveterm=saveterm=term;

    term.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR);
    term.c_lflag &= ~(ICANON|ISIG|ECHO);
    term.c_cc[VEOF] = 1;
    term.c_cc[VEOL] = 0;
    (void) ioctl(0, TCSETAW, &term);

    (void) signal(SIGCLD, doneII);

    if ((output_child_pid = fork()) < 0)
    {
	(void) fprintf(stderr,
		"%s: Couldn't fork child process to spawn shell (%s).\n",
		progname, long_error(errno));
	(void) exit(1);
    } else if (output_child_pid == 0)
    {
	if ((shell_child_pid = fork()) < 0)
	{
	    (void) fprintf(stderr,
		    "%s: Couldn't fork child process to do output (%s).\n",
		    progname, long_error(errno));
	    (void) exit(1);
	}
	else
	    if (shell_child_pid != 0)
	    {
		(void) signal(SIGCLD, done);
		(void) close(slave_pty_fd);

		clock = time((long *)0);

		(void) printf("Script started [%s] at %s", typescript_filename,
		       ctime(&clock));

		(void) fprintf(typescript_fp, "Script started [%s] at %s",
			typescript_filename, ctime(&clock));

		while (1)
		{
		    if ((n = read(master_pty_fd, buf, sizeof(buf))) < 1)
			break;

		    if (write(1, buf, (unsigned)n) != n)
			break;

		    if (fwrite(buf, 1, (unsigned)n, typescript_fp) != n)
		    {
			(void) fprintf(stderr,
				       "%s: Error writing to typescript file: %s (%s).\n",
				progname, typescript_filename,
				       long_error(errno));
			break;
		    }
		}

		done();
	    }
	    else
	    {
		int extra_fd;
		(void) fclose(typescript_fp);
		(void) close(master_pty_fd);

		(void) setpgrp();

		if ((extra_fd = open(slave_pty_filename, O_RDWR)) < 0)
		{
		    (void) fprintf(stderr,
			    "%s: Couldn't reopen slave pseudo tty: %s (%s).\n",
			    progname, slave_pty_filename, long_error(errno));
		    (void) exit(1);
		}
		(void) close(extra_fd);

		if (close(0) ||
		    dup(slave_pty_fd) < 0 ||
		    close(1) ||
		    dup(slave_pty_fd) < 0 ||
		    close(2) ||
		    dup(slave_pty_fd) < 0)
		{
		    (void) fprintf(stderr,
			    "%s: Couldn't duplicate slave pty file descriptors (%s).\n",
			    progname, long_error(errno));
		    
		    (void) exit(1);
		}

		(void) close(slave_pty_fd);

		/*
		** Setup pseudo tty.
		*/

		slaveterm.c_oflag &= ~(ONLCR);
		(void) ioctl(0, TCSETAW, &slaveterm);

		(void) execlp(shell, shell, "-i", NULL);

		(void) fprintf(stderr, "%s: Couldn't exec shell: %s (%s).\n",
			       progname, shell, long_error(errno));

		(void) exit(1);
	    }
    }

    (void) close(slave_pty_fd);
    (void) fclose(typescript_fp);

    while ((n = read(0, buf, BUFSIZ)) > 0)
	(void) write(master_pty_fd, buf, (unsigned)n);

    nice_exit(0);
}

done()
{
    long clock;

    clock = time((long *)0);
		
    (void) printf("Script ended [%s] at %s", typescript_filename,
		  ctime(&clock));

    (void) fprintf(typescript_fp, "Script ended [%s] at %s",
		   typescript_filename, ctime(&clock));

    (void) fclose(typescript_fp);
    (void) close(master_pty_fd);

    nice_exit(0);
}

doneII()
{
    nice_exit(0);
}

void
nice_exit(status)
int status;
{
    (void) ioctl(0, TCSETAW, &saveterm);

    (void) exit(status);
}

usage()
{
    (void) fprintf(stderr, "usage: %s [-append] [filename]\n", progname);
    (void) exit(1);
}

char *
long_error(error)
int error;
{
    static char qwerty[42];

    (void) sprintf(qwerty, "Unknown error %d", error);

    return ((unsigned)error >= sys_nerr) ? qwerty : sys_errlist[error];
}
SHAR_EOF
fi # end of overwriting check
if test -f 'script.C'
then
	echo shar: will not over-write existing file "'script.C'"
else
cat << \SHAR_EOF > 'script.C'
.TH SCRIPT C
.DA "January 31, 1989"
.SH Name
script - save a terminal session.
.SH Synopsis
.B script
[
.B -append
] [ filename ]
.SH Description
.B script
saves a terminal session by copying all output from your terminal to
the filename specified, called a typescript file.  If no filename is
given on the command line, the file
.B typescript
is used.
.SH Options
.TP
.B -append
Append the session to the typescript file instead of writing over it.
.SH Author
.B script
was written by Keith Gabryelski (ag@elgar.UUCP).  It was implemented
from BSD man page definition.  It is a copylefted product.
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
ag@elgar.CTS.COM         Keith Gabryelski          ...!{ucsd, crash}!elgar!ag