bbanerje@sjuvax.UUCP (B. Banerjee) (11/14/83)
What follows is a listing of Mark Horton's (...cbosgd!mark) script.c . This allows "capturing" many interactive terminal sessions, saving the results in the file typescript (default). I didn't write it folks, so if you have questions, ask the author. ------------------------------------------------------------------ static char *sccsid = "@(#)script.c 4.1 (Berkeley) 10/1/80"; /* * script - makes copy of terminal conversation. usage: * * script [ -n ] [ -s ] [ -q ] [ -a ] [ -S shell ] [ file ] * conversation saved in file. default is DFNAME */ #define DFNAME "typescript" #ifdef HOUXP #define STDSHELL "/bin/sh" #define NEWSHELL "/p4/3723mrh/bin/csh" char *shell = NEWSHELL; #endif #ifdef HOUXT #define STDSHELL "/bin/sh" #define NEWSHELL "/t1/bruce/ucb/bin/csh" char *shell = NEWSHELL; #endif #ifdef CORY #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell = NEWSHELL; #endif #ifdef CC #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell = NEWSHELL; #endif #ifndef STDSHELL # define V7ENV #endif #ifdef V7ENV #include <signal.h> /* used for version 7 with environments - gets your environment shell */ #define STDSHELL "/bin/sh" #define NEWSHELL "/bin/csh" char *shell; /* initialized in the code */ # include <sys/types.h> # include <sys/stat.h> # define MODE st_mode # define STAT stat char *getenv(); #else /* * The following is the structure of the block returned by * the stat and fstat system calls. */ struct inode { char i_minor; /* +0: minor device of i-node */ char i_major; /* +1: major device */ int i_number; /* +2 */ int i_flags; /* +4: see below */ char i_nlinks; /* +6: number of links to file */ char i_uid; /* +7: user ID of owner */ char i_gid; /* +8: group ID of owner */ char i_size0; /* +9: high byte of 24-bit size */ int i_size1; /* +10: low word of 24-bit size */ int i_addr[8]; /* +12: block numbers or device number */ int i_actime[2]; /* +28: time of last access */ int i_modtime[2]; /* +32: time of last modification */ }; #define IALLOC 0100000 #define IFMT 060000 #define IFDIR 040000 #define IFCHR 020000 #define IFBLK 060000 #define MODE i_flags #define STAT inode #endif char *tty; /* name of users tty so can turn off writes */ char *ttyname(); /* std subroutine */ int mode = 0622; /* old permission bits for users tty */ int outpipe[2]; /* pipe from shell to output */ int fd; /* file descriptor of typescript file */ int inpipe[2]; /* pipe from input to shell */ long tvec; /* current time */ char buffer[256]; /* for block I/O's */ int n; /* number of chars read */ int status; /* dummy for wait sys call */ char *fname; /* name of typescript file */ int forkval, ttn; /* temps for error checking */ int qflg; /* true if -q (quiet) flag */ int aflg; /* true if -q (append) flag */ struct STAT sbuf; int flsh(); main(argc,argv) int argc; char **argv; { if ((tty = ttyname(2)) < 0) { printf("Nested script not allowed.\n"); fail(); } #ifdef V7ENV shell = getenv("SHELL"); #endif while ( argc > 1 && argv[1][0] == '-') { switch(argv[1][1]) { case 'n': shell = NEWSHELL; break; case 's': shell = STDSHELL; break; case 'S': shell = argv[2]; argc--; argv++; break; case 'q': qflg++; break; case 'a': aflg++; break; default: printf("Bad flag %s - ignored\n",argv[1]); } argc--; argv++; } if (argc > 1) { fname = argv[1]; if (!aflg && stat(fname,&sbuf) >= 0) { printf("File %s already exists.\n",fname); done(); } } else fname = DFNAME; if (!aflg) { fd = creat(fname,0); /* so can't cat/lpr typescript from inside */ } else { /* try to append to existing file first */ fd = open(fname,1); if (fd >= 0) lseek(fd,0l,2); else fd = creat(fname,0); } if (fd<0) { printf("Can't create %s\n",fname); if (unlink(fname)==0) { printf("because of previous typescript bomb - try again\n"); } fail(); } chmod(fname,0); /* in case it already exists */ fixtty(); if (!qflg) { printf("Script started, file is %s\n",fname); check(write(fd,"Script started on ",18)); time(&tvec); check(write(fd,ctime(&tvec),25)); } pipe(inpipe); pipe(outpipe); forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) { forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) dooutput(); forkval = fork(); if (forkval < 0) goto ffail; if (forkval == 0) doinput(); doshell(); } close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); signal(SIGINT, SIG_IGN); signal(SIGQUIT, done); wait(&status); done(); /*NOTREACHED*/ ffail: printf("Fork failed. Try again.\n"); fail(); } /* input process - copy tty to pipe and file */ doinput() { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); close(inpipe[0]); close(outpipe[0]); close(outpipe[1]); /* main input loop - copy until end of file (ctrl D) */ while ((n=read(0,buffer,256)) > 0) { check(write(fd,buffer,n)); write(inpipe[1],buffer,n); } /* end of script - close files and exit */ close(inpipe[1]); close(fd); done(); } /* do output process - copy to tty & file */ dooutput() { signal(SIGINT, flsh); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); close(0); close(inpipe[0]); close(inpipe[1]); close(outpipe[1]); /* main output proc loop */ while (n=read(outpipe[0],buffer,256)) { if (n > 0) { /* -1 means trap to flsh just happened */ write(1,buffer,n); check(write(fd,buffer,n)); } } /* output sees eof - close files and exit */ if (!qflg) { printf("Script done, file is %s\n",fname); check(write(fd,"\nscript done on ",16)); time(&tvec); check(write(fd,ctime(&tvec),25)); } close(fd); exit(0); } /* exec shell, after diverting std input & output */ doshell() { close(0); dup(inpipe[0]); close(1); dup(outpipe[1]); close(2); dup(outpipe[1]); /* close useless files */ close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); execl(shell, "sh", "-i", 0); execl(STDSHELL, "sh", "-i", 0); execl(NEWSHELL, "sh", "-i", 0); printf("Can't execute shell\n"); fail(); } fixtty() { fstat(2, &sbuf); mode = sbuf.MODE&0777; chmod(tty, 0600); } /* come here on rubout to flush output - this doesn't work */ flsh() { signal(SIGINT, flsh); /* lseek(outpipe[0],0l,2); /* seeks on pipes don't work !"$"$!! */ } fail() { unlink(fname); kill(0, 15); /* shut off other script processes */ done(); } done() { chmod(tty, mode); chmod(fname, 0664); exit(); } #ifndef V7ENV #ifndef CC char *ttyname(i) int i; { char *string; string = "/dev/ttyx"; string[8] = ttyn(fd); if (string[8] == 'x') return((char *) (-1)); else return(string); } #endif #endif check(n) int n; { /* checks the result of a write call, if neg assume ran out of disk space & die */ if (n < 0) { write(1,"Disk quota exceeded - script quits\n",35); kill(0,15); done(); } } ------------------------------------------------------------------ If you don't have this line, you're missing something.
bbanerje@sjuvax.UUCP (B. Banerjee) (11/15/83)
Here is the manual page for script . Again, I hope that I am not violating anyones copyright. --------------------------------------------------------------- .TH SCRIPT 1 .UC 4 .SH NAME script \- make typescript of terminal session .SH SYNOPSIS .B script [ .B \-a ] [ .B \-q ] [ .B \-S shell ] [ file ] .SH DESCRIPTION .I Script makes a typescript of everything printed on your terminal. The typescript is saved in a file, and can be sent to the line printer later with .I lpr. If a file name is given, the typescript is saved there. If not, the typescript is saved in the file .I typescript. .PP To exit script, type control D. This sends an end of file to all processes you have started up, and causes script to exit. For this reason, control D behaves as though you had typed an infinite number of control D's. .PP This program is useful when using a crt and a hard-copy record of the dialog is desired, as for a student handing in a program that was developed on a crt when hard-copy terminals are in short supply. .PP .B \-S lets you specify the shell to use. The default depends on the system: If the variable SHELL is set in the environment, it is used if possible. .PP The .B \-q flag asks for ``quiet mode'', where the ``script started'' and ``script done'' messages are turned off. The .B \-a flag causes script to append to the typescript file instead of creating a new file. .SH AUTHOR Mark Horton .SH BUGS Since UNIX has no way to write an end-of-file down a pipe without closing the pipe, there is no way to simulate a single control D without ending script. .PP The new shell has its standard input coming from a pipe rather than a tty, so stty will not work, and neither will ttyname. In particular, this means that screen editors such as .IR vi (1) and the job control facilities of .IR csh (1) are inoperative. .PP When the user interrupts a printing process, .I script attempts to flush the output backed up in the pipe for better response. Usually the next prompt also gets flushed. -------------------------------------------------------------- If you don't have this line, you're probably missing something.