home *** CD-ROM | disk | FTP | other *** search
- From: maart@cs.vu.nl (Maarten Litmaath)
- Newsgroups: alt.sources
- Subject: mtty - mimic a tty - a public domain pseudo tty utility
- Message-ID: <7171@star.cs.vu.nl>
- Date: 24 Jul 90 20:50:38 GMT
-
- : This is a shar archive. Extract with sh, not csh.
- : This archive ends with exit, so do not worry about trailing junk.
- : --------------------------- cut here --------------------------
- PATH=/bin:/usr/bin:/usr/ucb
- echo Extracting 'data_flow.c'
- sed 's/^X//' > 'data_flow.c' << '+ END-OF-FILE ''data_flow.c'
- X#include "extern.h"
- X
- X
- Xvoid data_flow(pty, tty, death)
- Xint pty, tty, *death;
- X{
- X fd_set rfds, wfds;
- X int width, flags, left = 0, status = 0;
- X
- X
- X width = getdtablesize();
- X (void) signal(SIGPIPE, SIG_IGN);
- X
- X flags = fcntl(pty, F_GETFL, 0);
- X flags |= FNDELAY;
- X fcntl(pty, F_SETFL, flags);
- X
- X for (;;) {
- X FD_ZERO(&rfds);
- X FD_ZERO(&wfds);
- X FD_SET(death[0], &rfds);
- X if (Do_stdin) {
- X /*
- X * If there is nothing left from a previous read from
- X * stdin, wait for data to arrive on stdin, else wait
- X * till the pty becomes writable again.
- X * left < 0 denotes an error has occurred.
- X */
- X if (left == 0)
- X FD_SET(0, &rfds);
- X else if (left > 0)
- X FD_SET(pty, &wfds);
- X }
- X if (Do_stdout && status == 0)
- X FD_SET(pty, &rfds);
- X if (select(width, &rfds, &wfds, (fd_set *) 0,
- X (struct timeval *) 0) < 0) {
- X /*
- X * `impossible'
- X */
- X fprintf(stderr, "%s: select: %s\n", Prog,
- X strerror(errno));
- X exit(1);
- X }
- X /*
- X * If our parent has detected the death of the other child,
- X * death[0] will be ready for reading.
- X */
- X if (FD_ISSET(death[0], &rfds))
- X status = do_stdout(pty, 1);
- X if (FD_ISSET(pty, &rfds))
- X status = do_stdout(pty, 0);
- X if (FD_ISSET(pty, &wfds) || FD_ISSET(0, &rfds))
- X left = do_stdin(pty, tty);
- X }
- X}
- + END-OF-FILE data_flow.c
- chmod 'u=rw,g=r,o=r' 'data_flow.c'
- set `wc -c 'data_flow.c'`
- count=$1
- case $count in
- 1255) :;;
- *) echo 'Bad character count in ''data_flow.c' >&2
- echo 'Count should be 1255' >&2
- esac
- echo Extracting 'do_child.c'
- sed 's/^X//' > 'do_child.c' << '+ END-OF-FILE ''do_child.c'
- X#include "extern.h"
- X
- X
- Xvoid do_child(pty, tty, name, message, argp)
- Xint pty, tty, *message;
- Xchar *name, **argp;
- X{
- X int fd;
- X char c;
- X
- X
- X /*
- X * Wait for a message that all is well, i.e. our parent has been
- X * able to create another pipe and to fork again.
- X */
- X close(message[1]);
- X if (read(message[0], &c, 1) != 1)
- X exit(1);
- X close(message[0]);
- X
- X close(pty);
- X if (Do_stdin)
- X dup2(tty, 0);
- X if (Do_stdout)
- X dup2(tty, 1);
- X if (Do_stderr)
- X dup2(tty, 2);
- X close(tty);
- X
- X if (Mode == INTERACTIVE) {
- X if ((fd = open("/dev/tty", 1)) >= 0) {
- X /*
- X * Relinquish controlling tty.
- X */
- X ioctl(fd, TIOCNOTTY, (caddr_t) 0);
- X close(fd);
- X } else
- X setpgrp(0, 0);
- X /*
- X * Acquire new controlling tty.
- X * Our process group has been set to 0, either implicitly
- X * by the TIOCNOTTY ioctl(), or explicitly by the setpgrp().
- X * When we reopen the pseudo terminal, it will become our
- X * controlling tty and our process group gets set to the tty
- X * process group. If the latter had not been set yet, it
- X * will have been set to our process id first.
- X */
- X if ((fd = open(name, 2)) < 0) {
- X fprintf(stderr, "%s: open %s failed: %s\n",
- X Prog, name, strerror(errno));
- X exit(1);
- X }
- X close(fd);
- X }
- X
- X execvp(*argp, argp);
- X fprintf(stderr, "%s: cannot exec %s: %s\n",
- X Prog, *argp, strerror(errno));
- X exit(1);
- X}
- + END-OF-FILE do_child.c
- chmod 'u=rw,g=r,o=r' 'do_child.c'
- set `wc -c 'do_child.c'`
- count=$1
- case $count in
- 1323) :;;
- *) echo 'Bad character count in ''do_child.c' >&2
- echo 'Count should be 1323' >&2
- esac
- echo Extracting 'do_parent.c'
- sed 's/^X//' > 'do_parent.c' << '+ END-OF-FILE ''do_parent.c'
- X#include "extern.h"
- X
- X
- Xvoid do_parent(pty, tty, message)
- Xint pty, tty, *message;
- X{
- X union wait w, dummy;
- X int child, n, death[2];
- X char c = '!';
- X
- X
- X close(message[0]);
- X
- X if (pipe(death) < 0) {
- X fprintf(stderr, "%s: cannot create a pipe: %s\n",
- X Prog, strerror(errno));
- X exit(1);
- X }
- X
- X switch (child = fork()) {
- X case -1:
- X fprintf(stderr, "%s: cannot fork: %s\n",
- X Prog, strerror(errno));
- X exit(1);
- X case 0:
- X close(message[1]);
- X close(death[1]);
- X data_flow(pty, tty, death);
- X }
- X
- X /*
- X * Send a message to the other child, indicating all is well.
- X */
- X write(message[1], &c, 1);
- X close(message[1]);
- X close(death[0]);
- X
- X close(pty);
- X close(tty);
- X
- X while ((n = wait(&w)) != Pid)
- X if (n == child)
- X child = -1;
- X /*
- X * To signal the child that the process has died, we close our side
- X * of the pipe. A read() in the child will return 0 immediately.
- X * Then wait for the child to finish the IO.
- X */
- X if (child != -1) {
- X close(death[1]);
- X while (wait(&dummy) != child)
- X ;
- X }
- X
- X exit(w.w_termsig ? w.w_termsig + 0200 : w.w_retcode);
- X}
- + END-OF-FILE do_parent.c
- chmod 'u=rw,g=r,o=r' 'do_parent.c'
- set `wc -c 'do_parent.c'`
- count=$1
- case $count in
- 1043) :;;
- *) echo 'Bad character count in ''do_parent.c' >&2
- echo 'Count should be 1043' >&2
- esac
- echo Extracting 'do_stdin.c'
- sed 's/^X//' > 'do_stdin.c' << '+ END-OF-FILE ''do_stdin.c'
- X#include "extern.h"
- X
- X
- Xint do_stdin(pty, tty)
- Xint pty, tty;
- X{
- X static char buf[MAXBUF], *leftbuf;
- X static int left = 0, first = 1;
- X static void reset_tty();
- X int pass2 = 0;
- X
- X
- X for (;;) {
- X if (left < 0 || left > 0
- X && (left = to_pty(pty, &leftbuf, left)) != 0 || pass2)
- X return left;
- X
- X leftbuf = buf;
- X if ((left = read(0, buf, MAXBUF)) < 0)
- X fprintf(stderr, "%s: read stdin: %s\n",
- X Prog, strerror(errno));
- X else if (left == 0) {
- X if (Mode == INTERACTIVE && first) {
- X reset_tty(tty);
- X first = 0;
- X }
- X *buf = Tc[Mode].t_eofc;
- X left = 1;
- X }
- X pass2 = 1;
- X }
- X}
- X
- X
- Xstatic void reset_tty(tty)
- Xint tty;
- X{
- X ioctl(tty, TIOCGETP, (caddr_t) &Sg[INTERACTIVE]);
- X if (Sg[INTERACTIVE].sg_flags & (RAW | CBREAK)) {
- X Sg[INTERACTIVE].sg_flags &= ~(RAW | CBREAK);
- X ioctl(tty, TIOCSETN, (caddr_t) &Sg[INTERACTIVE]);
- X }
- X ioctl(tty, TIOCGETC, (caddr_t) &Tc[INTERACTIVE]);
- X if (Tc[INTERACTIVE].t_eofc == -1 || Tc[INTERACTIVE].t_eofc == 0) {
- X Tc[INTERACTIVE].t_eofc = Eofc;
- X ioctl(tty, TIOCSETC, (caddr_t) &Tc[INTERACTIVE]);
- X }
- X}
- + END-OF-FILE do_stdin.c
- chmod 'u=rw,g=r,o=r' 'do_stdin.c'
- set `wc -c 'do_stdin.c'`
- count=$1
- case $count in
- 1034) :;;
- *) echo 'Bad character count in ''do_stdin.c' >&2
- echo 'Count should be 1034' >&2
- esac
- echo Extracting 'do_stdout.c'
- sed 's/^X//' > 'do_stdout.c' << '+ END-OF-FILE ''do_stdout.c'
- X#include "extern.h"
- X
- X
- Xint do_stdout(pty, child_died)
- Xint pty, child_died;
- X{
- X char buf[MAXBUF];
- X int n;
- X
- X
- X for (;;) {
- X if ((n = read(pty, buf, (int) sizeof buf)) <= 0)
- X exit(!child_died);
- X if (write(1, buf, n) < 0) {
- X if (errno == EPIPE) {
- X if (child_died)
- X exit(1);
- X kill(Pid, SIGPIPE);
- X } else
- X fprintf(stderr, "%s: write stdout: %s\n",
- X Prog, strerror(errno));
- X return -1;
- X }
- X if (!child_died)
- X break;
- X }
- X return 0;
- X}
- + END-OF-FILE do_stdout.c
- chmod 'u=rw,g=r,o=r' 'do_stdout.c'
- set `wc -c 'do_stdout.c'`
- count=$1
- case $count in
- 451) :;;
- *) echo 'Bad character count in ''do_stdout.c' >&2
- echo 'Count should be 451' >&2
- esac
- echo Extracting 'extern.c'
- sed 's/^X//' > 'extern.c' << '+ END-OF-FILE ''extern.c'
- X#include "extern.h"
- X
- X#undef CTRL
- X#define CTRL(x) ((x) & 037)
- X#undef DEL
- X#define DEL 0177
- X
- Xint Lw[] = {
- X (LLITOUT | LPASS8),
- X (LCRTERA | LCRTKIL | LCTLECH)
- X};
- Xstruct sgttyb Sg[] = {
- X { B9600, B9600, -1, -1, 0 },
- X { B9600, B9600, CTRL('H'), CTRL('U'), ECHO | CRMOD | ANYP | XTABS }
- X};
- Xstruct tchars Tc[] = {
- X { -1, -1, -1, -1, CTRL('D'), -1 },
- X { DEL, CTRL('\\'), CTRL('S'), CTRL('Q'), CTRL('D'), -1 }
- X};
- Xstruct ltchars Ltc[] = {
- X { -1, -1, -1, -1, -1, -1 },
- X { CTRL('Z'), CTRL('Y'), CTRL('R'), CTRL('O'), CTRL('W'), CTRL('V') }
- X};
- Xstruct winsize Win = {
- X 24, 80, 0, 0
- X};
- Xchar Eofc = -1;
- Xint Mode = !INTERACTIVE;
- Xint Do_stdin = 0, Do_stdout = 0, Do_stderr = 0;
- Xint Pid;
- Xchar *Prog;
- + END-OF-FILE extern.c
- chmod 'u=rw,g=r,o=r' 'extern.c'
- set `wc -c 'extern.c'`
- count=$1
- case $count in
- 684) :;;
- *) echo 'Bad character count in ''extern.c' >&2
- echo 'Count should be 684' >&2
- esac
- echo Extracting 'main.c'
- sed 's/^X//' > 'main.c' << '+ END-OF-FILE ''main.c'
- X#include "extern.h"
- X
- X
- Xstatic char Usage[] =
- X "Usage: %s [-i] [-012] [-E \\ooo] [-V] [--] command\n";
- X
- X
- Xstatic void usage()
- X{
- X fprintf(stderr, Usage, Prog);
- X exit(2);
- X}
- X
- X
- Xstatic void geteofc(p)
- Xchar *p;
- X{
- X int i;
- X
- X
- X if (!p || sscanf(p, "\\%o", &i) != 1)
- X usage();
- X if (i == -1 || i == 0) {
- X fprintf(stderr, "%s: the EOF character cannot be -1 or 0\n",
- X Prog);
- X exit(2);
- X }
- X Eofc = i;
- X}
- X
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int stop = 0, pty, tty, message[2];
- X char name[PTYMAXNAME], **argp, *p, c;
- X
- X
- X Prog = argv[0];
- X
- X for (argp = &argv[1]; !stop && *argp && **argp == '-'; ++argp)
- X for (p = *argp + 1; c = *p; ++p)
- X switch (c) {
- X case 'E':
- X geteofc(*++argp);
- X break;
- X case 'V':
- X printf("%s\n", Version);
- X exit(0);
- X case 'i':
- X Mode = INTERACTIVE;
- X break;
- X case '0':
- X Do_stdin = 1;
- X break;
- X case '1':
- X Do_stdout = 1;
- X break;
- X case '2':
- X Do_stderr = 1;
- X break;
- X case '-':
- X stop = 1;
- X break;
- X default:
- X usage();
- X }
- X
- X if (!(Do_stdin | Do_stdout | Do_stderr)) {
- X Do_stdin = Do_stdout = 1; /* default */
- X if (Mode == INTERACTIVE)
- X Do_stderr = 1;
- X }
- X
- X if (!*argp)
- X usage();
- X
- X if ((pty = getpty(&tty, name)) < 0) {
- X fprintf(stderr, "%s: cannot allocate a pty. Exit.\n", Prog);
- X exit(1);
- X }
- X
- X set_modes(tty);
- X
- X if (pipe(message) < 0) {
- X fprintf(stderr, "%s: cannot create a pipe: %s\n",
- X Prog, strerror(errno));
- X exit(1);
- X }
- X
- X switch (Pid = fork()) {
- X case -1:
- X fprintf(stderr, "%s: cannot fork: %s\n",
- X Prog, strerror(errno));
- X exit(1);
- X case 0:
- X do_child(pty, tty, name, message, argp);
- X }
- X
- X do_parent(pty, tty, message);
- X}
- + END-OF-FILE main.c
- chmod 'u=rw,g=r,o=r' 'main.c'
- set `wc -c 'main.c'`
- count=$1
- case $count in
- 1614) :;;
- *) echo 'Bad character count in ''main.c' >&2
- echo 'Count should be 1614' >&2
- esac
- echo Extracting 'set_modes.c'
- sed 's/^X//' > 'set_modes.c' << '+ END-OF-FILE ''set_modes.c'
- X#include "extern.h"
- X
- X
- Xvoid set_modes(tty)
- Xint tty;
- X{
- X int fd, devtty;
- X
- X
- X if (Mode == INTERACTIVE) {
- X if ((fd = devtty = open("/dev/tty", 0)) < 0)
- X fd = Do_stdin ? Do_stdout ? 2 : 1 : 0;
- X if (ioctl(fd, TIOCLGET, (caddr_t) &Lw[INTERACTIVE]) == 0) {
- X ioctl(fd, TIOCGETC, (caddr_t) &Tc[INTERACTIVE]);
- X ioctl(fd, TIOCGLTC, (caddr_t) &Ltc[INTERACTIVE]);
- X ioctl(fd, TIOCGETP, (caddr_t) &Sg[INTERACTIVE]);
- X Sg[INTERACTIVE].sg_flags |= ECHO;
- X Sg[INTERACTIVE].sg_flags &= ~(RAW | CBREAK);
- X ioctl(fd, TIOCGWINSZ, (caddr_t) &Win);
- X ioctl(tty, TIOCSWINSZ, (caddr_t) &Win);
- X if (devtty >= 0)
- X close(devtty);
- X }
- X } else
- X ioctl(tty, TIOCEXCL, (caddr_t) 0);
- X /*
- X * Put the tty into exclusive use. There is a race
- X * condition, but this ioctl() is better than nothing,
- X * I guess. Opening the master should set the access mode
- X * of the slave (via the third argument to open(2))!
- X * Furthermore opening the tty side before the pty side
- X * should be impossible.
- X */
- X
- X if (Eofc != -1)
- X Tc[Mode].t_eofc = Eofc;
- X else
- X Eofc = Tc[Mode].t_eofc;
- X
- X ioctl(tty, TIOCLSET, (caddr_t) &Lw[Mode]);
- X ioctl(tty, TIOCSETC, (caddr_t) &Tc[Mode]);
- X ioctl(tty, TIOCSLTC, (caddr_t) &Ltc[Mode]);
- X ioctl(tty, TIOCSETP, (caddr_t) &Sg[Mode]);
- X}
- + END-OF-FILE set_modes.c
- chmod 'u=rw,g=r,o=r' 'set_modes.c'
- set `wc -c 'set_modes.c'`
- count=$1
- case $count in
- 1245) :;;
- *) echo 'Bad character count in ''set_modes.c' >&2
- echo 'Count should be 1245' >&2
- esac
- echo Extracting 'to_pty.c'
- sed 's/^X//' > 'to_pty.c' << '+ END-OF-FILE ''to_pty.c'
- X#include "extern.h"
- X
- X
- X/*
- X * Try to write `n' bytes to `pty', starting at `*buf'.
- X * Increment `*buf' with the number of bytes actually written.
- X * Return the number of bytes still to be written, -1 on error.
- X */
- Xint to_pty(pty, buf, n)
- Xint pty, n;
- Xchar **buf;
- X{
- X register char *p;
- X register int m;
- X char *q;
- X int backslash;
- X
- X
- X for (backslash = 0; n > 0 && !backslash; *buf += m, n -= m) {
- X /*
- X * Write 1 line at a time.
- X */
- X for (p = *buf, m = n; m > 0 && *p++ != '\n'; m--)
- X ;
- X if (*--p == '\\') {
- X for (q = p; p > *buf && *--p == '\\'; )
- X ;
- X m = q - p;
- X if (*p == '\\')
- X m++;
- X /*
- X * Only if the number of backslashes ending the
- X * block is uneven, we have to watch out: the tty
- X * driver translates a backslash-EOF sequence to a
- X * literal EOF character.
- X */
- X p = (m & 1) ? q : q + 1;
- X backslash = 1;
- X } else
- X p++;
- X if ((m = write(pty, *buf, p - *buf)) < 0)
- X return (errno == EWOULDBLOCK) ? n : -1;
- X }
- X
- X /*
- X * If the last chunk didn't end in a `break',
- X * write an EOF character if we're not interactive.
- X */
- X if (*--p != '\n' && *p != Eofc && Mode != INTERACTIVE)
- X if (write(pty, &Eofc, 1) < 0)
- X return (errno == EWOULDBLOCK) ? n : -1;
- X
- X /*
- X * Write the final backslash.
- X */
- X if (backslash) {
- X if (write(pty, *buf, 1) < 0)
- X return (errno == EWOULDBLOCK) ? n : -1;
- X *buf += 1;
- X n -= 1;
- X }
- X
- X return n;
- X}
- + END-OF-FILE to_pty.c
- chmod 'u=rw,g=r,o=r' 'to_pty.c'
- set `wc -c 'to_pty.c'`
- count=$1
- case $count in
- 1366) :;;
- *) echo 'Bad character count in ''to_pty.c' >&2
- echo 'Count should be 1366' >&2
- esac
- echo Extracting 'version.c'
- sed 's/^X//' > 'version.c' << '+ END-OF-FILE ''version.c'
- Xchar Version[] =
- X "@(#)mtty 1.1 90/07/24 Maarten Litmaath @ VU Dept. of CS, Amsterdam";
- + END-OF-FILE version.c
- chmod 'u=rw,g=r,o=r' 'version.c'
- set `wc -c 'version.c'`
- count=$1
- case $count in
- 88) :;;
- *) echo 'Bad character count in ''version.c' >&2
- echo 'Count should be 88' >&2
- esac
- echo Extracting 'strerror.c'
- sed 's/^X//' > 'strerror.c' << '+ END-OF-FILE ''strerror.c'
- Xchar *strerror(n)
- Xint n;
- X{
- X static char buf[32];
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X
- X
- X if ((unsigned) n < sys_nerr)
- X return sys_errlist[n];
- X (void) sprintf(buf, "Unknown error %d", n);
- X return buf;
- X}
- + END-OF-FILE strerror.c
- chmod 'u=rw,g=r,o=r' 'strerror.c'
- set `wc -c 'strerror.c'`
- count=$1
- case $count in
- 217) :;;
- *) echo 'Bad character count in ''strerror.c' >&2
- echo 'Count should be 217' >&2
- esac
- echo Extracting 'getpty.c'
- sed 's/^X//' > 'getpty.c' << '+ END-OF-FILE ''getpty.c'
- X#include <sys/file.h>
- X#include <errno.h>
- X#include <stdio.h>
- X
- Xstatic char pty_name[] = "/dev/ptyPQ";
- X
- X#define T_INDEX (sizeof pty_name - 6)
- X#define P_INDEX (sizeof pty_name - 3)
- X#define Q_INDEX (sizeof pty_name - 2)
- X
- Xstruct range {
- X char lower, upper;
- X};
- Xstatic struct range P_range[] = {
- X { 'p', 'z' },
- X { 'P', 'Z' },
- X { 'a', 'o' },
- X { 'A', 'O' },
- X};
- Xstatic struct range Q_range[] = {
- X { '0', '9' },
- X { 'a', 'f' },
- X { 'A', 'F' },
- X { 'g', 'z' },
- X { 'G', 'Z' },
- X};
- Xextern int errno;
- X
- X
- X#define SIZE(a) (sizeof(a) / sizeof((a)[0]))
- X
- X#ifdef DEBUG
- X#define FPRINTF(x) fprintf x
- X#else /* DEBUG */
- X#define FPRINTF(x)
- X#endif /* DEBUG */
- X#if defined(DEBUG) || defined(LIST)
- Xextern char *strerror();
- X#endif /* defined(DEBUG) || defined(LIST) */
- X
- X
- Xint getpty(tty, s)
- Xint *tty;
- Xchar *s;
- X{
- X register char P, P_max;
- X register struct range *pr;
- X int pty;
- X extern int try_pty();
- X
- X
- X strcpy(s, pty_name);
- X for (pr = P_range; pr < &P_range[SIZE(P_range)]; ++pr) {
- X for (P = pr->lower, P_max = pr->upper; P <= P_max; ++P) {
- X s[P_INDEX] = P;
- X switch (pty = try_pty(tty, s)) {
- X case -1:
- X continue;
- X case -2:
- X break;
- X default:
- X return pty;
- X }
- X break;
- X }
- X }
- X return -1;
- X}
- X
- X
- Xstatic int try_pty(tty, s)
- Xint *tty;
- Xchar *s;
- X{
- X extern int errno;
- X register char Q, Q_max;
- X register struct range *qr;
- X int pty;
- X
- X
- X for (qr = Q_range; qr < &Q_range[SIZE(Q_range)]; ++qr) {
- X for (Q = qr->lower, Q_max = qr->upper; Q <= Q_max; ++Q) {
- X s[Q_INDEX] = Q;
- X#ifdef LIST
- X fprintf(stderr, "%s", s);
- X if (access(s, 0) == 0) {
- X fprintf(stderr, "\n");
- X continue;
- X }
- X fprintf(stderr, ": %s\n", strerror(errno));
- X if (Q == qr->lower && qr == Q_range)
- X return -2;
- X break;
- X#else /* LIST */
- X if ((pty = open(s, 2)) >= 0) {
- X
- X FPRINTF((stderr, "opening %s succeeded\n", s));
- X
- X s[T_INDEX] = 't';
- X if (access(s, R_OK | W_OK) == 0
- X && (*tty = open(s, 2)) >= 0) {
- X FPRINTF((stderr,
- X "opening %s succeeded\n", s));
- X return pty;
- X }
- X
- X FPRINTF((stderr, "opening %s failed: %s\n",
- X s, strerror(errno)));
- X
- X close(pty);
- X s[T_INDEX] = 'p';
- X continue;
- X }
- X FPRINTF((stderr, "opening %s failed: %s\n",
- X s, strerror(errno)));
- X if (errno == ENOENT) {
- X if (Q == qr->lower && qr == Q_range)
- X return -2;
- X break;
- X }
- X#endif /* LIST */
- X }
- X }
- X return -1;
- X}
- X
- X
- X#if defined(DEBUG) || defined(LIST)
- X
- Xmain()
- X{
- X char buf[sizeof pty_name];
- X int pty, tty;
- X
- X pty = getpty(&tty, buf);
- X}
- X
- X#endif /* defined(DEBUG) || defined(LIST) */
- X
- X#ifdef STRERROR
- X
- Xchar *strerror(errno)
- X{
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X static char buf[32];
- X
- X if ((unsigned) errno < sys_nerr)
- X return sys_errlist[errno];
- X sprintf(buf, "Unknown error %d", errno);
- X return buf;
- X}
- X
- X#endif /* STRERROR */
- + END-OF-FILE getpty.c
- chmod 'u=rw,g=r,o=r' 'getpty.c'
- set `wc -c 'getpty.c'`
- count=$1
- case $count in
- 2730) :;;
- *) echo 'Bad character count in ''getpty.c' >&2
- echo 'Count should be 2730' >&2
- esac
- echo Extracting 'extern.h'
- sed 's/^X//' > 'extern.h' << '+ END-OF-FILE ''extern.h'
- X#include <sys/param.h>
- X#include <sys/time.h>
- X#include <sys/wait.h>
- X#include <sys/file.h>
- X#include <errno.h>
- X#include <signal.h>
- X#include <sys/ioctl.h>
- X#include <stdio.h>
- X
- X
- X#ifndef CANBSIZ
- X#define CANBSIZ 256
- X#endif /* !CANBSIZ */
- X#define MAXBUF (CANBSIZ - 1)
- X#define PTYMAXNAME 32
- X#define INTERACTIVE 1
- X
- X
- Xextern int Lw[];
- Xextern struct sgttyb Sg[];
- Xextern struct tchars Tc[];
- Xextern struct ltchars Ltc[];
- Xextern struct winsize Win;
- Xextern char Eofc;
- Xextern int Mode;
- Xextern int Do_stdin, Do_stdout, Do_stderr;
- Xextern int Pid;
- Xextern char *Prog, Version[];
- Xextern int errno;
- X
- Xextern char *strerror();
- Xextern int getpty(), do_stdin(), do_stdout(), to_pty();
- Xextern void set_modes(), do_child(), do_parent(), data_flow();
- + END-OF-FILE extern.h
- chmod 'u=rw,g=r,o=r' 'extern.h'
- set `wc -c 'extern.h'`
- count=$1
- case $count in
- 725) :;;
- *) echo 'Bad character count in ''extern.h' >&2
- echo 'Count should be 725' >&2
- esac
- echo Extracting 'notty.c'
- sed 's/^X//' > 'notty.c' << '+ END-OF-FILE ''notty.c'
- X#include <sys/types.h>
- X#include <sys/ioctl.h>
- X#include <stdio.h>
- X
- X/*
- X * Relinquish controlling tty. Exec args.
- X */
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int fd;
- X
- X
- X if (argc < 2) {
- X fprintf(stderr, "Usage: %s command\n", argv[0]);
- X exit(2);
- X }
- X if ((fd = open("/dev/tty", 1)) >= 0) {
- X ioctl(fd, TIOCNOTTY, (caddr_t) 0);
- X close(fd);
- X /*
- X * Now prevent our process group getting set to that of
- X * the first tty opened by the command.
- X */
- X setpgrp(0, getpid());
- X }
- X execvp(argv[1], &argv[1]);
- X perror(argv[1]);
- X exit(1);
- X}
- + END-OF-FILE notty.c
- chmod 'u=rw,g=r,o=r' 'notty.c'
- set `wc -c 'notty.c'`
- count=$1
- case $count in
- 538) :;;
- *) echo 'Bad character count in ''notty.c' >&2
- echo 'Count should be 538' >&2
- esac
- echo Extracting 'frontend.c'
- sed 's/^X//' > 'frontend.c' << '+ END-OF-FILE ''frontend.c'
- X#include <sys/types.h>
- X#include <signal.h>
- X#include <sys/ioctl.h>
- X#include <stdio.h>
- X
- X
- Xchar *Prog;
- X
- X
- Xvoid usage()
- X{
- X fprintf(stderr, "Usage: %s [-ec]\n", Prog);
- X exit(1);
- X}
- X
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X register char esc = '~', susp, eof, killc, intr, quit;
- X char c, prev = '\n';
- X int save = 0;
- X struct sgttyb osg, nsg;
- X struct tchars tc;
- X struct ltchars ltc;
- X
- X
- X Prog = argv[0];
- X
- X if (argc > 2)
- X usage();
- X if (argc == 2 && (argv[1][0] != '-' || argv[1][1] != 'e'
- X || !(esc = argv[1][2]) || argv[1][3]))
- X usage();
- X
- X ioctl(0, TIOCGETP, (caddr_t) &osg);
- X killc = osg.sg_kill;
- X nsg = osg;
- X nsg.sg_flags |= RAW;
- X nsg.sg_flags &= ~ECHO;
- X ioctl(0, TIOCSETN, (caddr_t) &nsg);
- X ioctl(0, TIOCGETC, (caddr_t) &tc);
- X intr = tc.t_intrc;
- X quit = tc.t_quitc;
- X eof = tc.t_eofc;
- X ioctl(0, TIOCGLTC, (caddr_t) <c);
- X susp = ltc.t_suspc;
- X
- X signal(SIGPIPE, SIG_IGN);
- X
- X#define BOL(c) (c == '\n' || c == '\r' || c == killc \
- X || c == eof || c == intr || c == quit \
- X || c == susp)
- X
- X while (read(0, &c, 1) == 1) {
- X if (c == '.' || c == susp) {
- X if (save) {
- X if (c == '.')
- X break;
- X ioctl(0, TIOCSETN, (caddr_t) &osg);
- X kill(0, SIGTSTP);
- X ioctl(0, TIOCSETN, (caddr_t) &nsg);
- X prev = '\n';
- X continue;
- X }
- X } else if (c == esc) {
- X if (BOL(prev)) {
- X prev = esc;
- X save = 1;
- X continue;
- X }
- X }
- X if (save) {
- X if (write(1, &prev, 1) != 1)
- X break;
- X save = 0;
- X }
- X if (write(1, &c, 1) != 1)
- X break;
- X prev = c;
- X }
- X
- X ioctl(0, TIOCSETN, (caddr_t) &osg);
- X}
- + END-OF-FILE frontend.c
- chmod 'u=rw,g=r,o=r' 'frontend.c'
- set `wc -c 'frontend.c'`
- count=$1
- case $count in
- 1497) :;;
- *) echo 'Bad character count in ''frontend.c' >&2
- echo 'Count should be 1497' >&2
- esac
- echo Extracting 'irsh'
- sed 's/^X//' > 'irsh' << '+ END-OF-FILE ''irsh'
- X#!/bin/sh
- X
- Xtest $# != 1 && {
- X echo "Usage: `basename $0` machine" >&2
- X exit 2
- X}
- X
- Xcase ${SHELL-sh} in
- X*csh)
- X term="set term=$TERM"
- X ;;
- X*)
- X term="TERM=$TERM; export TERM"
- Xesac
- X
- Xfrontend | rsh $1 "$term;" exec mtty -i ${SHELL-sh}
- X
- X# `rsh' may exit first, making `frontend' a child of init;
- X# if so, it will be killed when it attempts to read from or reset the
- X# tty while the job control shell already has taken it over
- X
- Xstty -raw echo
- + END-OF-FILE irsh
- chmod 'u=rwx,g=rx,o=rx' 'irsh'
- set `wc -c 'irsh'`
- count=$1
- case $count in
- 433) :;;
- *) echo 'Bad character count in ''irsh' >&2
- echo 'Count should be 433' >&2
- esac
- echo Extracting 'myscript'
- sed 's/^X//' > 'myscript' << '+ END-OF-FILE ''myscript'
- X#!/bin/sh
- X
- Xopen='>'
- X
- Xcase $1 in
- X-a)
- X open='>>'
- X shift
- Xesac
- X
- Xfile=${1-myscript.out}
- X
- Xeval exec 3$open '$file'
- X
- Xecho "Script started, file is $file"
- X
- X(echo -n 'Script started on '; date) >&3
- Xexec 3>&-
- X
- Xstty raw -echo
- Xmtty -i ${SHELL-sh} | tee -a $file
- Xstty -raw echo
- X
- X(echo ''; echo -n 'Script done on '; date) >> $file
- X
- Xecho "Script done, file is $file"
- + END-OF-FILE myscript
- chmod 'u=rwx,g=rx,o=rx' 'myscript'
- set `wc -c 'myscript'`
- count=$1
- case $count in
- 353) :;;
- *) echo 'Bad character count in ''myscript' >&2
- echo 'Count should be 353' >&2
- esac
- echo Extracting 'Makefile'
- sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
- X# Makefile for mtty and notty
- X
- X# if your system doesn't have a strerror(3) function,
- X# uncomment the next 2 lines
- XSTRERROR = strerror.o
- XDSTRERROR = -DSTRERROR
- X
- XSRCS = data_flow.c do_child.c do_parent.c do_stdin.c do_stdout.c \
- X extern.c main.c set_modes.c to_pty.c version.c strerror.c \
- X getpty.c extern.h notty.c frontend.c irsh myscript Makefile \
- X mtty.1 Implementation Sketch README notty.1
- X
- XOBJS = data_flow.o do_child.o do_parent.o do_stdin.o do_stdout.o \
- X extern.o main.o set_modes.o to_pty.o version.o $(STRERROR) \
- X getpty.o
- X
- Xmtty: $(OBJS)
- X $(CC) $(OBJS) -o mtty
- X
- Xfrontend:
- X $(CC) frontend.c -o frontend
- X
- Xptylist: getpty.c
- X $(CC) -DLIST $(DSTRERROR) getpty.c -o ptylist
- X
- Xptytest: getpty.c
- X $(CC) -DDEBUG $(DSTRERROR) getpty.c -o ptytest
- X
- Xnotty:
- X $(CC) notty.c -o notty
- X
- Xshar:
- X shar $(SRCS) > mtty.sh
- X
- Xtar:
- X tar cvf mtty.t $(SRCS)
- X
- Xdata_flow.o: extern.h
- Xdo_child.o: extern.h
- Xdo_parent.o: extern.h
- Xdo_stdout.o: extern.h
- Xdo_stdin.o: extern.h
- Xextern.o: extern.h
- Xmain.o: extern.h
- Xset_modes.o: extern.h
- Xto_pty.o: extern.h
- + END-OF-FILE Makefile
- chmod 'u=rw,g=r,o=r' 'Makefile'
- set `wc -c 'Makefile'`
- count=$1
- case $count in
- 1043) :;;
- *) echo 'Bad character count in ''Makefile' >&2
- echo 'Count should be 1043' >&2
- esac
- echo Extracting 'mtty.1'
- sed 's/^X//' > 'mtty.1' << '+ END-OF-FILE ''mtty.1'
- X.\" Uses -man.
- X.TH MTTY 1 Jul\ 24\ 1990
- X.SH NAME
- Xmtty \- mimic a tty and execute a command
- X.SH SYNOPSIS
- X.B mtty
- X[
- X.B \-i
- X] [
- X.B \-012
- X] [
- X.BI "\-E \e" ooo
- X] [
- X.B \-V
- X] [
- X.B \-\-
- X]
- X.I command
- X.SH DESCRIPTION
- X.I Mtty
- Xallocates a \fIpseudo terminal\fR (see \fIpty\fR(4)) and executes the given
- X.I command
- Xwith (a subset of) its standard input, output and error output connected to
- Xthe pseudo terminal: default \fIstdin\fR and \fIstdout\fR, and under
- Xthe \-\fIi\fR option \fIstderr\fR as well. \fIMtty\fR then waits
- Xfor \fIcommand\fR to finish and reports
- Xthe exit status in its own exit status. If \fIcommand\fR died due to a signal,
- Xthe exit status of \fImtty\fR will be the signal number plus 0200.
- X.PP
- XDefault the pseudo terminal operates in non-interactive mode. In this mode
- Xall special processing of input and output characters is turned off, except
- Xfor the EOF character on input. Although the pseudo tty actually has been put
- Xinto \fIcanonical\fR mode, it behaves as if it were in \fIraw\fR mode, in the
- Xsense that every character written on the standard input of \fImtty\fR is
- Xmade available to \fIcommand\fR immediately. Non-interactive operation is
- Xtypically used in pipelines in which \fIfully\fR (\fIblock\fR-) \fIbuffered
- XIO\fR is undesirable: \fImtty\fR
- Xtries to lure the application into using \fIline-buffered\fR output. Normally
- Xthis scheme will be successful, because the \fIstdio\fR(3) library
- Xautomatically selects line-buffering when the standard output is a terminal.
- X.SH OPTIONS
- X.TP
- X.B \-i
- XSelect interactive mode: the pseudo terminal will be made the \fIcontrolling
- Xterminal\fR (see \fItty\fR(4)) for \fIcommand\fR. The terminal modes,
- Xincluding for example the special characters like interrupt and quit, will be
- Xcopied from the controlling tty of \fImtty\fR itself (if possible), with the
- Xfollowing exception: the pseudo tty will always be put into canonical
- Xecho mode, i.e. its \fBRAW\fR and \fBCBREAK\fR bits will be turned off and
- Xits \fBECHO\fR bit will be turned on.
- X.TP
- X.B \-012
- XConnect only the specified \fIfile descriptors\fR to the pseudo tty.
- X.TP
- X.BI "\-E \e" ooo
- XSet the EOF character of the pseudo tty to the byte whose octal representation
- Xis `\fIooo\fR' (default ^D).
- X.TP
- X.B \-V
- XPrint the version number and exit.
- X.SH EXAMPLES
- XThe following Bourne shell command will hang without producing any output at
- Xall:
- X.sp
- X.RS
- X.nf
- Xtail -f /etc/passwd | tr a A |
- Xwhile read line
- Xdo
- X echo $line
- Xdone
- X.fi
- X.RE
- X.sp
- XExplanation:
- X.IP \-
- Xbecause the output of `\fItr\fR' is a pipe, it will be block-buffered
- X.IP \-
- Xthe last 10 lines of /etc/passwd together contain fewer bytes than the
- Xblocksize used by the \fIstdio\fR(3) library
- X.IP \-
- Xnow the \fIwhile\fR loop must wait until the output buffer of `\fItr\fR' fills
- Xup, which will not happen unless `\fItr\fR' receives enough extra input (or
- XEOF), which cannot happen while `\fItail\fR' is waiting for /etc/passwd to
- Xgrow.
- X.PP
- XTo lure `\fItr\fR' into using line-buffered output we can use \fImtty\fR:
- X.sp
- X.RS
- X.nf
- Xtail -f /etc/passwd | mtty tr a A |
- Xwhile read line
- Xdo
- X echo $line
- Xdone
- X.fi
- X.RE
- X.sp
- XSince only the output of `\fItr\fR' needs to be connected to a pseudo tty,
- Xwe could have invoked \fImtty\fR with the \-\fI1\fR option.
- X.PP
- XHow \fImtty\fR can be used interactively is shown in the next example.
- XThe following commands could be the heart of an alternative implementation
- Xof \fIscript\fR(1):
- X.sp
- X.RS
- X.nf
- Xstty raw \-echo
- Xmtty \-i ${SHELL\-sh} | tee typescript
- Xstty \-raw echo
- X.fi
- X.RE
- X.SH BUGS
- XBecause of problems too complicated to discuss here, \fImtty\fR will close the
- Xpseudo tty channel as soon as \fIcommand\fR has finished, i.e. a \fIbackground
- Xprocess\fR started by \fIcommand\fR will not be able to use the pseudo tty any
- Xlonger.
- X.PP
- XThough in non-interactive operation the pseudo tty will be put into
- X\fIexclusive use\fR by means of a \fBTIOCEXCL\fR \fIioctl\fR(), there is
- Xcurrently no guarantee that the channel will be exclusive indeed: there is a
- Xrace condition and the tty might have been open already before \fImtty\fR
- Xhad even started.
- X.SH AUTHOR
- XMaarten Litmaath @ VU Dept. of CS, Amsterdam
- + END-OF-FILE mtty.1
- chmod 'u=rw,g=r,o=r' 'mtty.1'
- set `wc -c 'mtty.1'`
- count=$1
- case $count in
- 4096) :;;
- *) echo 'Bad character count in ''mtty.1' >&2
- echo 'Count should be 4096' >&2
- esac
- echo Extracting 'Implementation'
- sed 's/^X//' > 'Implementation' << '+ END-OF-FILE ''Implementation'
- XSome notes on the implementation of mtty.
- X-----------------------------------------
- XAs a read() from the pty (master) side of a pty-tty pair will return -1 with
- Xerrno set to EIO, as soon as the last file descriptor to the tty (slave) side
- Xhas been closed, the master has to keep the tty side open himself all the
- Xtime! But that means he cannot detect the slave side closing its last fd to
- Xthe tty: as the master still has the tty open, a read() from the pty will not
- Xreturn. My solution is to let the master first fork() off the command, then
- Xfork() off the IO handler, and wait() for both of them itself. De IO handler
- Xselect()s on 3 file descriptors (at most): stdin, the pty and a pipe set up
- Xbetween the master and itself. If stdin is selected, data are read from fd 0
- Xand written to the pty to become input for the command. If the pty is
- Xselected for reading, data read from it will be written on stdout: the output
- Xfrom the command is passed on. If the pipe is selected for reading, it is a
- Xsignal from the master that the command has finished, so the IO handler can
- Xread any remaining data from the pty and write them to stdout.
- XWe cannot let the master handle the IO, informed of the command's death by
- Xa SIGCHLD: if stdout is a `slow' device and the master would just be writing
- Xto it when the signal arrives, output would be lost; to `hold' the signal
- Xbefore each write() and releasing it immediately afterward would be gross.
- XBy letting the IO handler take care of all IO (including stdin), the master
- Xcan step out of the way: it can get swapped out while wait()ing for its two
- Xchildren to finish.
- XOne more thing: if there should be no activity (when stdin is empty, or the
- Xpty has been filled up with data yet unread by the command, and when there is
- Xno unread output from the command), the IO handler will not take up cpu cycles,
- Xbut instead will hang on the select(). This fact is not so trivial as it
- Xseems; it involves non-blocking IO.
- X
- XBugs in the pty(4) driver.
- X--------------------------
- XThere's no distinction between the tty side being opened for reading and
- Xit being opened for writing: only the fact that it is open counts when
- Xdetermining if a read or write on the pty side should fail.
- X
- XWhen the last tty file descriptor is closed any unread output is lost!
- XThe next read() on the pty fd should return the remaining output instead
- Xof an IO error!
- X
- XOne should be able to open the pty side for reading and writing separately.
- X
- XAccording to pty(4):
- X
- X Input flow control is automatically performed; a process
- X that attempts to write to the controller device will be
- X blocked if too much unconsumed data is buffered on the slave
- X device.
- X
- XThis does not work if the slave device is in canonical mode: extra input
- Xcharacters are thrown away.
- X
- XTIOCSETN on the tty slave flushes input!
- X
- XAnyone can open the tty slave; the mode of the tty side should be set
- Xaccording to the third argument of open(2)!
- + END-OF-FILE Implementation
- chmod 'u=rw,g=r,o=r' 'Implementation'
- set `wc -c 'Implementation'`
- count=$1
- case $count in
- 2949) :;;
- *) echo 'Bad character count in ''Implementation' >&2
- echo 'Count should be 2949' >&2
- esac
- echo Extracting 'Sketch'
- sed 's/^X//' > 'Sketch' << '+ END-OF-FILE ''Sketch'
- X
- X +------------------+
- X | |
- X | v
- X +------+ | +--------+ +------+
- X | | <-- | | <-- | | <-- real stdin
- X | proc | |tty pty| |master|
- X | | --> | | --> | | --> real stdout
- X +------+ ^ +--------+ +------+
- X | |
- X | |
- X +------------------+
- X
- X1) If the process dies, we must deselect stdin and drain the output from the
- X process, present on the read side of the pty.
- X
- X2) If EOF is detected on stdin, we write the tty's EOF character to the write
- X side of the pty to signal EOF to the process.
- + END-OF-FILE Sketch
- chmod 'u=rw,g=r,o=r' 'Sketch'
- set `wc -c 'Sketch'`
- count=$1
- case $count in
- 627) :;;
- *) echo 'Bad character count in ''Sketch' >&2
- echo 'Count should be 627' >&2
- esac
- echo Extracting 'README'
- sed 's/^X//' > 'README' << '+ END-OF-FILE ''README'
- XCurrently mtty is BSD-oriented. I have tested it on SunOS 4.0.3c.
- X
- XCheck the Makefile regarding strerror(3).
- X
- XYou should check if on your system ptys are allocated `the BSD way'.
- XTry `make ptylist ptytest' and run the 2 resulting programs.
- X
- XI have included 2 examples of how mtty can be used: `myscript' is an
- Xalternative implementation of `script'; `irsh' starts an `interactive rsh',
- Xi.e. it is if you rlogin to the remote machine, only you will not get logged
- Xin the utmp file (interesting!), and a `.rhosts' file is required.
- X`frontend.c' is the source of `frontend' (how unexpected!), used by `irsh'.
- X
- X`notty.c' is included as a bonus! See the manual.
- + END-OF-FILE README
- chmod 'u=rw,g=r,o=r' 'README'
- set `wc -c 'README'`
- count=$1
- case $count in
- 659) :;;
- *) echo 'Bad character count in ''README' >&2
- echo 'Count should be 659' >&2
- esac
- echo Extracting 'notty.1'
- sed 's/^X//' > 'notty.1' << '+ END-OF-FILE ''notty.1'
- X.\" Uses -man.
- X.TH NOTTY 1 Jul\ 24\ 1990
- X.SH NAME
- Xnotty \- detach /dev/tty and execute a command
- X.SH SYNOPSIS
- X.B notty
- X.I command
- X.SH DESCRIPTION
- X.I Notty
- Xdetaches the \fIcontrolling tty\fR (see \fItty\fR(4)) and executes the
- X\fIcommand\fR specified.
- X.SH EXAMPLE
- X.RS
- X.nf
- X# the file `pw' contains the password of `joe'
- X# the file `cmds' contains shell commands,
- X# to be executed by `joe'
- Xnotty su joe cmds < pw
- X.fi
- X.RE
- X.PP
- XThe following form is wrong:
- X.sp
- X.RS
- Xcat pw cmds | notty su joe
- X.RE
- X.sp
- XAs `\fIsu\fR' uses the \fIstdio\fR(3) library to read the password from
- Xstandard input, and as stdin is a pipe, it will be \fIblock-buffered\fR,
- Xand more than 1 line will be read, so part of `\fIcmds\fR' is consumed
- Xby `\fIsu\fR'.
- X.PP
- XThe \fImtty\fR(1) utility cannot help here: if `\fIsu\fR' discovers
- Xstdin is a tty, it will put it into non-echo mode using a \fBTIOCSETP\fR
- X\fIioctl\fR(), which may flush unread input.
- X.SH AUTHOR
- XMaarten Litmaath @ VU Dept. of CS, Amsterdam
- + END-OF-FILE notty.1
- chmod 'u=rw,g=r,o=r' 'notty.1'
- set `wc -c 'notty.1'`
- count=$1
- case $count in
- 971) :;;
- *) echo 'Bad character count in ''notty.1' >&2
- echo 'Count should be 971' >&2
- esac
- exit 0
- --
- "and with a sudden plop it lands on usenet. what is it? omigosh, it must[...]
- be a new user! quick kill it before it multiplies!" (Loren J. Miller)
-