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