[comp.sources.unix] v17i090: Record interactive session

rsalz@uunet.uu.net (Rich Salz) (02/09/89)

Submitted-by: Kent Forschmiedt <happym!kent>
Posting-number: Volume 17, Issue 90
Archive-name: cscript

[  This is like the UCB "script" program except it uses pipes,
   not psuedo-tty's.  Some things break when run underneath it,
   but it's still worthwhile.  I wrote the Makefile.  --r$  ]

This is a very generic script recorder.  It works fine on most any Unix
system.  I think I got the original from Unix/World magazine, and I made
it work a lot better and gave it a man page.

I don't know if one like this has been posted before.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile cscript.1 cscript.c
# Wrapped by rsalz@fig.bbn.com on Wed Feb  8 17:23:59 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(146 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for cscript
X#
Xall: cscript
X
Xcscript:	cscript.c
X	$(CC) -o cscript $(CFLAGS) cscript.c
X
Xman: cscript.1
X	nroff -man cscript.1 |col >man
END_OF_FILE
if test 146 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'cscript.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cscript.1'\"
else
echo shar: Extracting \"'cscript.1'\" \(1439 characters\)
sed "s/^X//" >'cscript.1' <<'END_OF_FILE'
X.TH CSCRIPT 1l
X.SH NAME
Xcscript \- record a terminal session
X.SH SYNOPSIS
X.B cscript [ \-aq ] [ \-s
X.I shell
X.B ] [
X.I file
X.B ]
X.SH DESCRIPTION
X.I Cscript
Xrecords an interactive session in a file as it appears on the tty.
X.PP
XIf
X.I file
Xis specified, the script is recorded in
X.I file.
XOtherwise, the session is recorded in
X.B typescript
Xin the current directory.  
XThe
X.B \-s
Xoption causes
X.I shell
Xto be used instead of that specified by the
X.B SHELL
Xenvironment variable.  If
X.B SHELL
Xis not defined, the default is
X.B /bin/sh.
XOther options and their actions are:
X.TP "\w'\-a\ \ 'u"
X.B \-a
XAppend to the script file.  Otherwise, the file is truncated.
X.TP
X.B \-q
XQuiet mode.  Don't print the time and date at the beginning and
Xend of the script.
X.PD
X.PP
X.SH FILES
X.cc x
X./typescript
Xxcc
X.SH SEE ALSO
Xsh(1), csh(1), ksh(1) or whatever you use.
X.SH DIAGNOSTICS
XVarious complaints when \fIopen\fR(2), \fIfork\fR(2), \fIexec\fR(2),
Xor \fIpipe\fR(2) calls fail.
X.SH BUGS
XUser input and program output are intercepted by different processes;
Xthey might not appear in the same order in the script file as they did
Xon the tty.
X.PP
XThis is a generic version, using only pipes.  It should work on
Xany reasonable imitation of Unix, but knows nothing about sockets,
XStreams or ptys.  Programs which care whether they are talking to
Xa tty might not work right.  Commands like \fIps\fR(1)
Xwhich care about process groups probably won't work right.
END_OF_FILE
if test 1439 -ne `wc -c <'cscript.1'`; then
    echo shar: \"'cscript.1'\" unpacked with wrong size!
fi
# end of 'cscript.1'
fi
if test -f 'cscript.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cscript.c'\"
else
echo shar: Extracting \"'cscript.c'\" \(2905 characters\)
sed "s/^X//" >'cscript.c' <<'END_OF_FILE'
X/*
X * cscript - record a terminal session
X * Author:	Grant Dorman
X * Modified by Ray Swartz
X * Modified by Kent Forschmiedt 20jun88
X */
X#ifndef lint
Xstatic char *sccsid = "@(#)cscript.c	2.2  6/20/88";
X#endif
X
X#include <stdio.h>
X#include <signal.h>
X#include <fcntl.h>
X
X#define SHELL		"/bin/sh"
X#define SCRIPTOUT	"typescript"
X#define SHFLAGS		"-i"
X
Xvoid exit(), perror();
X
Xchar *getenv(), *ctime();
X
Xint fd;				/* record file */
Xchar buf[BUFSIZ];
Xchar *fname;
Xchar *shell;
Xint qflg;
Xint ipd[2], opd[2];	/* pipes */
X
Xmain(argc, argv)
Xint argc; char **argv;
X{
X	int getopt();
X	extern char *optarg;
X	extern int optind;
X
X	int c, oflags;
X
X	oflags = O_WRONLY + O_CREAT + O_TRUNC;
X	shell = (char *)0;
X
X	while ((c = getopt(argc, argv, "aqs:")) != EOF)
X		switch(c) {
X			case 'a':	/* append mode */
X				oflags = O_WRONLY + O_CREAT + O_APPEND;
X				break;
X			case 'q':	/* quiet mode */
X				qflg++;
X				break;
X			case 's':
X				shell = optarg;
X				break;
X			default:
X				fprintf(stderr, "Usage: cscript [-aq] [ -s shell ] [file]\n");
X				exit(3);
X		}
X
X	if (!shell && (shell = getenv("SHELL")) == (char *)0)
X		shell = SHELL;
X
X	fname = (optind < argc) ? argv[optind] : SCRIPTOUT;
X	if ( (fd = open(fname, oflags, 0666)) < 0) {
X		perror("cscript: open");
X		exit(4);
X	}
X
X	if (pipe(ipd) == -1) {
X		perror("cscript: pipe");
X		exit(2);
X	}
X	if (pipe(opd) == -1) {
X		perror("cscript: pipe");
X		exit(2);
X	}
X
X	switch(fork()) {
X		case -1: perror("cscript: fork 1"); exit(1);
X
X		case 0: switch(fork()) {
X			case -1:	perror("cscript: fork 2"); exit(1);
X
X			case 0:		do_stdin(); exit(6);
X			default:	do_shell(); exit(6);
X		}
X		default:	do_stdout(); exit(6);
X	}
X
X	/* NOTREACHED */
X}
X
X
Xdo_stdout()
X{
X	unsigned nread;
X	long time(), tloc;
X
X	/* this process will exit when the pipe closes */
X	signal(SIGHUP, SIG_IGN); /* */
X	signal(SIGINT, SIG_IGN); /* */
X	signal(SIGQUIT, SIG_IGN); /* */
X
X	close(ipd[0]); close(ipd[1]);
X
X	close(0); dup(opd[0]);
X	close(opd[0]); close(opd[1]);
X
X	fprintf(stderr, "Recording...\nExit shell to stop\n");
X
X	while((nread = read(0, buf, sizeof buf)) != 0) {
X		write(1, buf, nread);
X		write(fd, buf, nread);
X	}
X
X	if (!qflg) {
X		time(&tloc);
X		fprintf(stderr, "Script done, file is %s\n", fname);
X		sprintf(buf, "\nScript done %s", ctime(&tloc));
X		write(fd, buf, (unsigned) strlen(buf));
X	}
X
X	exit(0);
X}
X
Xdo_stdin()
X{
X	unsigned nread;
X
X	close(opd[0]); close(opd[1]);
X	close(1); dup(ipd[1]);
X	close(ipd[0]); close(ipd[1]);
X
X	while((nread = read(0, buf, sizeof buf)) != 0) {
X		write(fd, buf, nread);
X		write(1, buf, nread);
X	}
X
X	exit(0);
X}
X
Xdo_shell()
X{
X	long time(), tloc;
X
X	close(0); dup(ipd[0]);
X	close(1); dup(opd[1]);
X	/* mush stderr into stdout pipe */
X	close(2); dup(opd[1]);
X
X	close(opd[0]); close(opd[1]);
X	close(ipd[0]); close(ipd[1]);
X	close(fd);
X
X	if (!qflg) {
X		time(&tloc);
X		fprintf(stderr, "Script started %s", ctime(&tloc));
X	}
X
X	execl(shell, shell, SHFLAGS, 0);
X	perror("cscript: execl");
X	exit(5);
X}
END_OF_FILE
if test 2905 -ne `wc -c <'cscript.c'`; then
    echo shar: \"'cscript.c'\" unpacked with wrong size!
fi
# end of 'cscript.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.