[alt.sources] Writing to multiple terminals

rsalz@bbn.com (Rich Salz) (12/08/89)

Submitted-by: rsalz@bbn.com (Rich Salz)
Posting-id: 891207.1802
Posting-number: Volume TEST, Number TEST
Archive-name: tutor: write to multiple terminals at the same time

[This is an experimental alt.sources re-posting from the
newsgroup(s) comp.unix.questions,comp.sources.wanted.
No attempt has been made to edit, clean, modify, or otherwise
change the contents of the original posting, or to contact the
author.  Please consider cross-posting all sources postings to
alt.sources as a matter of course.]

[Comments on this service to emv@math.lsa.umich.edu (Edward Vielmetti)]

Someone wanted a program that would write to multiple terminals at the
same time.  I had hacked the BSD "script" program to do that; here's the
source.  Compile with -DTUTOR.  Usage:
	tutor /dev/tty3 tty4 /dev/pty2 ...
(If you don't put / or . in front of the pathname, /dev is assumed.)

It's a useful hack for when you want to run through a demo and show
everyone in a class what's going on.
	/r$

#! /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:  script.c
# Wrapped by rsalz@bbn.com on Thu Dec  7 12:08:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'script.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'script.c'\"
else
echo shar: Extracting \"'script.c'\" \(7226 characters\)
sed "s/^X//" >'script.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifdef	TUTOR
X/*
X**  Hacked on by Rich $alz <rsalz@bbn.com> so that it writes to multiple
X**  devices at the same time.  Compile with -DTUTOR to get that effect.
X**	cc -DTUTOR -o tutor script.c
X**  Usage:
X**	tutor console tty3 /dev/ttyp2 ...
X*/
X#endif	/* TUTOR */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)script.c	5.6 (Berkeley) 6/29/88";
X#endif /* not lint */
X
X/*
X * script
X */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <stdio.h>
X#include <signal.h>
X
Xchar	*shell;
X#ifndef	TUTOR
XFILE	*fscript;
X#else	/* TUTOR */
Xint ttys[50];
Xint count;
X#endif	/* TUTOR */
Xint	master;
Xint	slave;
Xint	child;
Xint	subchild;
X#ifndef	TUTOR
Xchar	*fname;
X#endif	/* TUTOR */
X
Xstruct	sgttyb b;
Xstruct	tchars tc;
Xstruct	ltchars lc;
Xstruct	winsize win;
Xint	lb;
Xint	l;
Xchar	*line = "/dev/ptyXX";
Xint	aflg;
X
Xmain(argc, argv)
X	int argc;
X	char *argv[];
X{
X	extern char *optarg;
X	extern int optind;
X	int ch;
X	int finish();
X	char *getenv();
X
X	while ((ch = getopt(argc, argv, "a")) != EOF)
X		switch((char)ch) {
X		case 'a':
X			aflg++;
X			break;
X		case '?':
X		default:
X#ifndef	TUTOR
X			fprintf(stderr, "usage: script [-a] [file]\n");
X#else	/* TUTOR */
X			fprintf(stderr, "usage: script [-a] ttys...\n");
X#endif	/* TUTOR */
X			exit(1);
X		}
X	argc -= optind;
X	argv += optind;
X
X#ifndef	TUTOR
X	if (argc > 0)
X		fname = argv[0];
X	else
X		fname = "typescript";
X	if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
X		perror(fname);
X		fail();
X#else	/* TUTOR */
X	if (argc == 0) {
X		fprintf(stderr, "usage: script [-a] ttys...\n");
X		exit(1);
X	}
X	openthem(argv);
X#endif	/* TUTOR */
X
X	shell = getenv("SHELL");
X	if (shell == NULL)
X		shell = "/bin/sh";
X
X	getmaster();
X#ifndef	TUTOR
X	printf("Script started, file is %s\n", fname);
X#else
X	printf("Script started.\n");
X#endif	/* TUTOR */
X	fixtty();
X
X	(void) signal(SIGCHLD, finish);
X	child = fork();
X	if (child < 0) {
X		perror("fork");
X		fail();
X	}
X	if (child == 0) {
X		subchild = child = fork();
X		if (child < 0) {
X			perror("fork");
X			fail();
X		}
X		if (child)
X			dooutput();
X		else
X			doshell();
X	}
X	doinput();
X}
X
Xdoinput()
X{
X	register int cc;
X	char ibuf[BUFSIZ];
X
X#ifndef	TUTOR
X	(void) fclose(fscript);
X#else	/* TUTOR */
X	closethem();
X#endif	/* TUTOR */
X	while ((cc = read(0, ibuf, BUFSIZ)) > 0)
X		(void) write(master, ibuf, cc);
X	done();
X}
X
X#include <sys/wait.h>
X
Xfinish()
X{
X	union wait status;
X	register int pid;
X	register int die = 0;
X
X	while ((pid = wait3(&status, WNOHANG, 0)) > 0)
X		if (pid == child)
X			die = 1;
X
X	if (die)
X		done();
X}
X
Xdooutput()
X{
X	register int cc;
X	time_t tvec, time();
X	char obuf[BUFSIZ], *ctime();
X
X	(void) close(0);
X	tvec = time((time_t *)NULL);
X#ifndef	TUTOR
X	fprintf(fscript, "Script started on %s", ctime(&tvec));
X#else	/* TUTOR */
X	(void) sprintf(obuf, "Script started on %s", ctime(&tvec));
X	writethem(obuf, strlen(obuf));
X#endif	/* TUTOR */
X	for (;;) {
X		cc = read(master, obuf, sizeof (obuf));
X		if (cc <= 0)
X			break;
X		(void) write(1, obuf, cc);
X#ifndef	TUTOR
X		(void) fwrite(obuf, 1, cc, fscript);
X#else	/* TUTOR */
X		writethem(obuf, cc);
X#endif	/* TUTOR */
X	}
X	done();
X}
X
Xdoshell()
X{
X	int t;
X
X	t = open("/dev/tty", O_RDWR);
X	if (t >= 0) {
X		(void) ioctl(t, TIOCNOTTY, (char *)0);
X		(void) close(t);
X	}
X	getslave();
X	(void) close(master);
X#ifndef	TUTOR
X	(void) fclose(fscript);
X#else	/* TUTOR */
X	closethem();
X#endif	/* TUTOR */
X	(void) dup2(slave, 0);
X	(void) dup2(slave, 1);
X	(void) dup2(slave, 2);
X	(void) close(slave);
X	execl(shell, "sh", "-i", 0);
X	perror(shell);
X	fail();
X}
X
Xfixtty()
X{
X	struct sgttyb sbuf;
X
X	sbuf = b;
X	sbuf.sg_flags |= RAW;
X	sbuf.sg_flags &= ~ECHO;
X	(void) ioctl(0, TIOCSETP, (char *)&sbuf);
X}
X
Xfail()
X{
X
X	(void) kill(0, SIGTERM);
X	done();
X}
X
Xdone()
X{
X	time_t tvec, time();
X#ifndef	TUTOR
X	char *ctime();
X#else	/* TUTOR */
X	char *ctime(), buff[128];
X#endif	/* TUTOR */
X
X	if (subchild) {
X		tvec = time((time_t *)NULL);
X#ifndef	TUTOR
X		fprintf(fscript,"\nscript done on %s", ctime(&tvec));
X		(void) fclose(fscript);
X#else	/* TUTOR */
X		(void) sprintf(buff,"\nscript done on %s", ctime(&tvec));
X		writethem(buff, strlen(buff));
X		closethem();
X#endif	/* TUTOR */
X		(void) close(master);
X	} else {
X		(void) ioctl(0, TIOCSETP, (char *)&b);
X#ifndef	TUTOR
X		printf("Script done, file is %s\n", fname);
X#else	/* TUTOR */
X		printf("Script done.\n");
X#endif	/* TUTOR */
X	}
X	exit(0);
X}
X
Xgetmaster()
X{
X	char *pty, *bank, *cp;
X	struct stat stb;
X
X	pty = &line[strlen("/dev/ptyp")];
X	for (bank = "pqrs"; *bank; bank++) {
X		line[strlen("/dev/pty")] = *bank;
X		*pty = '0';
X		if (stat(line, &stb) < 0)
X			break;
X		for (cp = "0123456789abcdef"; *cp; cp++) {
X			*pty = *cp;
X			master = open(line, O_RDWR);
X			if (master >= 0) {
X				char *tp = &line[strlen("/dev/")];
X				int ok;
X
X				/* verify slave side is usable */
X				*tp = 't';
X				ok = access(line, R_OK|W_OK) == 0;
X				*tp = 'p';
X				if (ok) {
X				    (void) ioctl(0, TIOCGETP, (char *)&b);
X				    (void) ioctl(0, TIOCGETC, (char *)&tc);
X				    (void) ioctl(0, TIOCGETD, (char *)&l);
X				    (void) ioctl(0, TIOCGLTC, (char *)&lc);
X				    (void) ioctl(0, TIOCLGET, (char *)&lb);
X				    (void) ioctl(0, TIOCGWINSZ, (char *)&win);
X					return;
X				}
X				(void) close(master);
X			}
X		}
X	}
X	fprintf(stderr, "Out of pty's\n");
X	fail();
X}
X
Xgetslave()
X{
X
X	line[strlen("/dev/")] = 't';
X	slave = open(line, O_RDWR);
X	if (slave < 0) {
X		perror(line);
X		fail();
X	}
X	(void) ioctl(slave, TIOCSETP, (char *)&b);
X	(void) ioctl(slave, TIOCSETC, (char *)&tc);
X	(void) ioctl(slave, TIOCSLTC, (char *)&lc);
X	(void) ioctl(slave, TIOCLSET, (char *)&lb);
X	(void) ioctl(slave, TIOCSETD, (char *)&l);
X	(void) ioctl(slave, TIOCSWINSZ, (char *)&win);
X}
X
X
X#ifdef	TUTOR
Xopenthem(argv)
X	char **argv;
X{
X	char buff[BUFSIZ];
X
X	for ( ; *argv; argv++) {
X		/* If path doesn't start with . or / assume it's in /dev */
X		if (**argv != '.' && **argv != '/') {
X			(void) sprintf(buff, "/dev/%s", *argv);
X			*argv = buff;
X		}
X		ttys[count] = open(*argv, O_WRONLY | O_NDELAY);
X		if (ttys[count] < 0)
X			perror(*argv);
X		else
X			count++;
X	}
X
X	if (count == 0) {
X		fprintf(stderr, "No devices open.\n");
X		exit(1);
X	}
X}
X
Xwritethem(p, i)
X	char *p;
X	int i;
X{
X	int j;
X
X	for (j = 0; j < count; j++)
X		(void) write(ttys[j], p, i);
X}
X
Xclosethem()
X{
X	while (--count >= 0)
X		(void)close(ttys[count]);
X}
X#endif	/* TUTOR */
END_OF_FILE
if test 7226 -ne `wc -c <'script.c'`; then
    echo shar: \"'script.c'\" unpacked with wrong size!
fi
# end of 'script.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.