jdb@mordor.UUCP (John Bruner) (11/10/85)
: This is a shar archive. Extract with sh, not csh.
cat << '!Funky!Stuff!'
This is the UW distribution, part 3 of 3. If you do not receive
all three parts, DO NOT post to "net.sources.mac". Instead, please
send mail to me (John Bruner) at one of the following addresses:
jdb@s1-c.ARPA
seismo!mordor!jdb
ucbvax!dual!mordor!jdb
decvax!decwrl!mordor!jdb
!Funky!Stuff!
echo x - Makefile
cat > Makefile << '!Funky!Stuff!'
CC = /bin/cc
CFLAGS = -O
all: uw uwtool
uw: uw.o openpty.o
$(CC) -o uw uw.o openpty.o
uwtool: uwtool.o openpty.o
$(CC) -o uwtool uwtool.o openpty.o
!Funky!Stuff!
echo x - openpty.h
cat > openpty.h << '!Funky!Stuff!'
/*
* This file defines the "ptydesc" structure which is returned
* by the routine "openpty".
*/
struct ptydesc {
int pt_pfd; /* file descriptor of master side */
int pt_tfd; /* file descriptor of slave side */
char *pt_pname; /* master device name */
char *pt_tname; /* slave device name */
};
!Funky!Stuff!
echo x - uw.h
cat > uw.h << '!Funky!Stuff!'
/*
* uw command bytes
*
* Copyright 1985 by John D. Bruner. All rights reserved. Permission to
* copy this program is given provided that the copy is not sold and that
* this copyright notice is included.
*
*
* Two types of information are exchanged through the 7-bit serial line:
* ordinary data and command bytes. Command bytes are preceeded by
* an IAC byte. IAC bytes and literal XON/XOFF characters (those which
* are not used for flow control) are sent by a CB_FN_CTLCH command.
* Characters with the eighth bit set (the "meta" bit) are prefixed with
* a CB_FN_META function.
*
* The next most-significant bit in the byte specifies the sender and
* recipient of the command. If this bit is clear (0), the command byte
* was sent from the host computer to the Macintosh; if it is set (1)
* the command byte was sent from the Macintosh to the host computer.
* This prevents confusion in the event that the host computer
* (incorrectly) echos a command back to the Macintosh.
*
* The remaining six bits are partitioned into two fields. The low-order
* three bits specify a window number from 1-7 (window 0 is reserved for
* other uses) or another type of command-dependent parameter. The next
* three bits specify the operation to be performed by the recipient of
* the command byte.
*
* Note that the choice of command bytes prevents the ASCII XON (021) and
* XOFF (023) characters from being sent as commands. CB_FN_ISELW commands
* are only sent by the Macintosh (and thus are tagged with the CB_DIR_MTOH
* bit). Since XON and XOFF data characters are handled via CB_FN_CTLCH,
* this allows them to be used for flow control purposes.
*/
#define IAC 0001 /* interpret as command */
#define CB_DIR 0100 /* command direction: */
#define CB_DIR_HTOM 0000 /* from host to Mac */
#define CB_DIR_MTOH 0100 /* from Mac to host */
#define CB_FN 0070 /* function code: */
#define CB_FN_NEWW 0000 /* new window */
#define CB_FN_KILLW 0010 /* kill (delete) window */
#define CB_FN_ISELW 0020 /* select window for input */
#define CB_FN_OSELW 0030 /* select window for output */
#define CB_FN_META 0050 /* add meta to next data char */
#define CB_FN_CTLCH 0060 /* low 3 bits specify char */
#define CB_FN_MAINT 0070 /* maintenance functions */
#define CB_WINDOW 0007 /* window number mask */
#define CB_CC 0007 /* control character specifier: */
#define CB_CC_IAC 1 /* IAC */
#define CB_CC_XON 2 /* XON */
#define CB_CC_XOFF 3 /* XOFF */
#define CB_MF 0007 /* maintenance functions: */
#define CB_MF_ENTRY 0 /* beginning execution */
#define CB_MF_EXIT 7 /* execution terminating */
#define NWINDOW 7 /* maximum number of windows */
!Funky!Stuff!
echo x - openpty.c
cat > openpty.c << '!Funky!Stuff!'
/*
* openpty - open a pseudo-terminal
*
* The first time that the routine is called, the device directory is
* searched and a list of all candidate pseudo-terminals is compiled.
* Candidates are defined to be those entries in "/dev" whose names
* (1) are the same length as PTY_PROTO and (2) start with the
* initial string PTY_PREFIX. Further, the master and slave sides
* must both exist.
*
* openpty() attempts to find an unused pseudo-terminal from the list
* of candidates. If one is found, the master and slave sides are
* opened and the file descriptors and names of these two devices are
* returned in a "ptydesc" structure. (The address of this structure
* is supplied by the caller. Zero is returned if openpty() was
* successful, -1 is returned if no pty could be found.
*/
#include <sys/types.h>
#include <sys/dir.h>
#include <fcntl.h>
#include <strings.h>
#include "openpty.h"
#define DEV_DIR "/dev" /* directory where devices live */
#define PT_INDEX (sizeof DEV_DIR) /* location of 'p' in "pty" */
#define PTY_PROTO "ptyp0" /* prototype for pty names */
#define PTY_PREFIX "pty" /* prefix required for name of pty */
struct ptyinfo {
struct ptyinfo *pi_next;
char *pi_pty;
char *pi_tty;
};
static struct ptyinfo *ptylist;
extern char *malloc();
static
char *
devname(name)
char *name;
{
register char *fullname;
/*
* Construct the full name of a device in DEV_DIR. Returns
* NULL if it failed (because malloc() failed).
*/
fullname = malloc((unsigned)(sizeof DEV_DIR + 1 + strlen(name)));
if (fullname != NULL) {
(void)strcpy(fullname, DEV_DIR);
(void)strcat(fullname, "/");
(void)strcat(fullname, name);
}
return(fullname);
}
static
isapty(dp)
struct direct *dp;
{
static struct ptyinfo *pi;
/*
* We don't care about the gory details of the directory entry.
* Instead, what we really want is an array of pointers to
* device names (with DEV_DIR prepended). Therefore, we create
* this array ourselves and tell scandir() to ignore every
* directory entry.
*
* If malloc() fails, the current directory entry is ignored.
*/
if (pi == NULL &&
(pi = (struct ptyinfo *)malloc((unsigned)sizeof *pi)) == NULL)
return(0);
if (strlen(dp->d_name) == sizeof PTY_PROTO - 1 &&
strncmp(dp->d_name, PTY_PREFIX, sizeof PTY_PREFIX - 1) == 0) {
pi->pi_pty = devname(dp->d_name);
if (pi->pi_pty == NULL)
return(0);
pi->pi_tty = malloc((unsigned)(strlen(pi->pi_pty) + 1));
if (pi->pi_tty == NULL) {
free(pi->pi_pty);
return(0);
}
(void)strcpy(pi->pi_tty, pi->pi_pty);
pi->pi_tty[PT_INDEX] = 't';
if (access(pi->pi_pty, 0) == 0 && access(pi->pi_tty, 0) == 0) {
pi->pi_next = ptylist;
ptylist = pi;
pi = NULL;
} else {
free(pi->pi_pty);
free(pi->pi_tty);
}
}
return(0);
}
openpty(pt)
struct ptydesc *pt;
{
register struct ptyinfo *pi;
static int fail;
auto struct direct **dirlist;
extern char *re_comp();
extern int alphasort();
/*
* If the regular expression PTY_PROTO is bad, scandir() fails, or
* no possible pty's are found, then "fail" is set non-zero. If
* "fail" is non-zero then the routine bombs out immediately.
* Otherwise, the list of candidates is examined starting with the
* entry following the last one chosen.
*/
if (fail)
return(-1);
if (!ptylist) { /* first time */
if (scandir(DEV_DIR, &dirlist, isapty, alphasort) < 0 ||
ptylist == NULL) {
fail = 1;
return(-1);
}
for (pi=ptylist; pi->pi_next; pi=pi->pi_next)
;
pi->pi_next = ptylist; /* make the list circular */
}
pi = ptylist;
do {
if ((pt->pt_pfd = open(pi->pi_pty, O_RDWR)) >= 0) {
if ((pt->pt_tfd = open(pi->pi_tty, O_RDWR)) >= 0) {
ptylist = pi->pi_next;
pt->pt_pname = pi->pi_pty;
pt->pt_tname = pi->pi_tty;
return(0);
} else
(void)close(pt->pt_pfd);
}
pi = pi->pi_next;
} while (pi != ptylist);
return(-1);
}
!Funky!Stuff!
echo x - uw.c
cat > uw.c << '!Funky!Stuff!'
#
/*
* uw - UNIX windows program for the Macintosh (VAX end)
*
* Copyright 1985 by John D. Bruner. All rights reserved. Permission to
* copy this program is given provided that the copy is not sold and that
* this copyright notice is included.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#include <fcntl.h>
#include <strings.h>
#include <stdio.h>
#include "uw.h"
#include "openpty.h"
#define MAXENV 128 /* maximum environment size */
#define W_IN 0
#define W_OUT 1
#define XON 0021 /* ASCII XON */
#define XOFF 0023 /* ASCII XOFF */
#define RUB 0177 /* ASCII RUBOUT */
#define META 0200 /* "meta" bit for whatever it's worth */
#define RCV_OK (-1) /* see recvcmd() */
#define RCV_META (-2) /* see recvcmd() */
#ifndef FD_SET
#define FD_SET(n,p) ((p)->fds_bits[0] |= (1 << (n)))
#define FD_CLR(n,p) ((p)->fds_bits[0] &= ~(1 << (n)))
#define FD_ISSET(n,p) ((p)->fds_bits[0] & (1 << (n)))
#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
#endif
typedef int fildes_t; /* file descriptor data type */
typedef int nwin_t; /* window index data type */
struct window {
fildes_t w_fd;
char w_tty[32];
};
struct window window[NWINDOW]; /* window data structures */
struct window **fdmap; /* mapping from fd's to windows */
struct window *curwin[2]; /* current input and output windows */
fildes_t nfds; /* number of file descriptors */
char portal[] = "/tmp/uwXXXXXX"; /* UNIX-domain network address */
/* The following are added to the environment of all child processes */
char env_uwin[64] = "UWIN=";
char *env[] = {
env_uwin,
"TERM=adm31",
"TERMCAP=adm31:cr=^M:do=^J:nl=^J:al=\\EE:am:le=^H:bs:ce=\\ET:cm=\\E=%+ %+ :cl=^Z:cd=\\EY:co#80:dc=\\EW:dl=\\ER:ei=\\Er:ho=^^:im=\\Eq:li#24:mi:nd=^L:up=^K:MT:km:so=\\EG1:se=\\EG0:",
NULL
};
int ctlch[] = { -1, IAC, XON, XOFF, -1, -1, -1, -1 }; /* CTL char mapping */
struct selmask {
struct fd_set sm_rd;
struct fd_set sm_wt;
struct fd_set sm_ex;
} selmask[2];
extern char *mktemp();
extern char *getenv();
extern char *malloc();
extern done(), cwait();
extern int errno;
main(argc, argv)
char **argv;
{
register fildes_t fd, sd;
register struct window *w;
struct sockaddr sa;
/*
* Make sure we don't accidentally try to run this inside itself.
*/
if (getenv("UWIN")) {
fprintf(stderr, "%s is already running\n", *argv);
exit(1);
}
/*
* Close all file descriptors except 0, 1, and 2.
*/
nfds = getdtablesize();
for (fd=3; fd < nfds; fd++)
(void)close(fd);
/*
* Allocate "fdmap". We can't do this at compile time because
* we get the number of file descriptors from getdtablesize().
*/
if (!(fdmap=(struct window **)malloc(nfds*sizeof(struct window *)))) {
fprintf(stderr, "cannot allocate 'fdmap' array\n");
exit(1);
}
/*
* Mark all windows closed.
*/
for (w=window; w < window+NWINDOW; w++)
w->w_fd = -1;
/*
* Create a UNIX-domain network address, and put its name into
* the environment so that descendents can contact us with new
* window requests.
*/
(void)strncat(env_uwin, mktemp(portal), sizeof env_uwin - 1);
setenv(env);
if ((sd=socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
sa.sa_family = AF_UNIX;
(void)strncpy(sa.sa_data, portal, sizeof sa.sa_data-1);
sa.sa_data[sizeof sa.sa_data-1] = '\0';
if (bind(sd, &sa, sizeof sa.sa_family + strlen(sa.sa_data)) < 0) {
perror("bind");
exit(1);
}
if (fcntl(sd, F_SETFL, FNDELAY))
perror("fcntl(sd, F_SETFL, FNDELAY)");
/*
* Ignore interrupts, quits, and terminal stops. Clean up and exit
* if a hangup or termination is received. Also catch changes in
* child status (so that we can wait for them). Set up the terminal
* modes.
*/
(void)signal(SIGHUP, done);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGTERM, done);
(void)signal(SIGTSTP, SIG_IGN);
(void)signal(SIGCHLD, cwait);
tmode(1);
/*
* Tell the Macintosh to initialize. Initialize the current input
* and output windows to NULL.
*/
xmitcmd(CB_FN_MAINT|CB_MF_ENTRY);
curwin[W_IN] = curwin[W_OUT] = NULL;
/*
* Initialize the "select" masks, create window 1 (to start
* things off) and wait for input. When input is available,
* process it.
*/
FD_ZERO(&selmask[0].sm_rd);
FD_ZERO(&selmask[0].sm_wt);
FD_ZERO(&selmask[0].sm_ex);
FD_SET(0, &selmask[0].sm_rd);
FD_SET(sd, &selmask[0].sm_rd);
if (newwind(window) == 0)
xmitcmd(CB_FN_NEWW|1);
while (1) {
selmask[1] = selmask[0];
if (select(nfds, &selmask[1].sm_rd, &selmask[1].sm_wt,
&selmask[1].sm_ex, (struct timeval *)0) < 0) {
if (errno == EINTR)
continue;
perror("select");
done(1); /* for now -- fix this! */
}
for (fd=0; fd < nfds; fd++) {
if (FD_ISSET(fd, &selmask[1].sm_rd)) {
if (fd < 2)
mrecv();
else if (fd == sd)
netrecv(sd);
else
mxmit(fd);
}
}
}
}
mrecv()
{
register int len;
register char *cp, *cq;
auto int nready;
char ibuf[512], obuf[512];
static int seen_iac, seen_meta;
/*
* The received bytestream is examined. Non-command bytes are
* written to the file descriptor corresponding to the current
* "input" window (relative to the Macintosh -- the window the
* user types input to).
*/
if (ioctl(0, (int)FIONREAD, (char *)&nready) < 0) {
perror("FIONREAD");
return;
}
cq = obuf;
while (nready > 0) {
if (nready > sizeof ibuf)
len = read(0, ibuf, sizeof ibuf);
else
len = read(0, ibuf, nready);
if (len <= 0) {
perror("read");
return;
}
for (cp=ibuf; cp < ibuf+len; cp++) {
if (seen_iac) {
if ((*cp&CB_DIR) == CB_DIR_MTOH) {
if (cq > obuf) {
(void)write(curwin[W_IN]->w_fd,
obuf, cq-obuf);
cq = obuf;
}
switch (*cq = recvcmd(*cp)) {
case RCV_OK:
break;
case RCV_META:
seen_meta = 1;
break;
default:
if (seen_meta) {
seen_meta = 0;
*cq |= META;
}
if (curwin[W_IN])
cq++;
break;
}
}
seen_iac = 0;
} else if (*cp == IAC)
seen_iac++;
else if (curwin[W_IN]) {
if (seen_meta) {
seen_meta = 0;
*cq++ = *cp|META;
} else
*cq++ = *cp;
if (cq >= obuf+sizeof obuf) {
(void)write(curwin[W_IN]->w_fd,
obuf, cq-obuf);
cq = obuf;
}
}
}
nready -= len;
}
if (cq > obuf)
(void)write(curwin[W_IN]->w_fd, obuf, cq-obuf);
}
recvcmd(cmd)
char cmd;
{
register int nwin, fn;
register struct window *w;
/*
* Perform the function the Mac is asking for. There are three
* broad categories of these functions: RCV_META, which tells
* the caller that the next character is a "meta" character;
* an ASCII data character, which is passed back to the caller
* for proper handling; and RCV_OK, which means that this routine
* has done everything which was required to process the command.
*/
fn = cmd&CB_FN;
switch (fn) {
case CB_FN_NEWW:
case CB_FN_KILLW:
case CB_FN_ISELW:
nwin = cmd&CB_WINDOW;
if (!nwin)
break;
w = &window[nwin-1];
switch (fn) {
case CB_FN_NEWW:
if (w->w_fd < 0 && newwind(w) < 0)
xmitcmd(CB_FN_KILLW|nwin);
break;
case CB_FN_KILLW:
killwind(w, 0);
break;
case CB_FN_ISELW:
if (w->w_fd >= 0)
curwin[W_IN] = w;
break;
}
break;
case CB_FN_META:
return(RCV_META);
case CB_FN_CTLCH:
return(ctlch[cmd&CB_CC]);
case CB_FN_MAINT:
if ((cmd&CB_MF) == CB_MF_EXIT)
done(0);
/*NOTREACHED*/
}
return(RCV_OK);
}
xmitcmd(cmd)
char cmd;
{
static char cmdbuf[2] = { IAC, '\0' };
/*
* Transmit the command "cmd" to the Macintosh. The byte is ORed
* with the host-to-Mac direction indicator.
*/
cmdbuf[1] = cmd|CB_DIR_HTOM;
(void)write(1, cmdbuf, sizeof cmdbuf);
}
netrecv(sd)
register fildes_t sd;
{
register struct window *w;
register int cnt;
struct msghdr msg;
auto int fd;
struct iovec iov;
struct stat st1, st2;
static int unity = 1;
char buf[256];
/*
* main() calls this routine when there is a message waiting on
* the UNIX-domain socket. The message's access rights are
* expected to contain the file descriptor for the "master" side
* of a pseudo-tty. The message contains the name of the pty.
* The sender is expected to start up a process on the slave side
* of the pty. This allows the host end to create windows which
* run something other than the shell.
*/
fd = -1;
iov.iov_base = (caddr_t)buf;
iov.iov_len = sizeof buf - 1;
msg.msg_name = (caddr_t)0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_accrights = (caddr_t)&fd;
msg.msg_accrightslen = sizeof fd;
if ((cnt=recvmsg(sd, &msg, 0)) < 0)
perror("recvmsg");
if (msg.msg_accrightslen > 0 && fd >= 0) {
/*
* We can't trust the process which connected to us, so
* we verify that it really passed us a pseudo-tty's
* file descriptor by checking the device name and its
* inode number. [Of course, if someone else wants to
* hand us a terminal session running under their uid....]
*/
buf[cnt] = 0;
if (strncmp(buf, "/dev/pty", sizeof "/dev/pty" - 1) ||
fstat(fd, &st1) < 0 || stat(buf, &st2) < 0 ||
st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
(void)close(fd);
return;
}
/*
* OK, we believe the sender. We allocate a window and
* tell the Macintosh to create that window on its end.
*/
buf[5] = 't'; /* switch to "/dev/ttyp?" */
for (w=window; w < window+NWINDOW; w++) {
if (w->w_fd < 0) {
w->w_fd = fd;
fdmap[fd] = w;
FD_SET(fd, &selmask[0].sm_rd);
(void)strncpy(w->w_tty, buf, sizeof w->w_tty-1);
xmitcmd(CB_FN_NEWW|(w-window+1));
break;
}
}
/*
* If we have no free windows, then we close the file
* descriptor (which will terminate the slave process).
*/
if (w == window+NWINDOW)
(void)close(fd);
/*
* Set non-blocking I/O on the master side.
*/
(void)ioctl(fd, (int)FIONBIO, (char *)&unity);
}
}
mxmit(fd)
register fildes_t fd;
{
register char *cp, *cq, i;
register int len;
char ibuf[32], obuf[32];
/*
* Copy input from file "fd" to the Macintosh. Be sure to convert
* any embedded IAC characters.
*
* Note that the input/output buffers should NOT be very large.
* It is undesirable to perform large reads and effectively
* "lock out" all other file descriptors. The chosen size
* should preserve a reasonable amount of efficiency.
*/
if (fdmap[fd]) {
curwin[W_OUT] = fdmap[fd];
xmitcmd(CB_FN_OSELW|(fdmap[fd]-window+1));
cq = obuf;
if ((len = read(fd, ibuf, sizeof ibuf)) < 0 &&
errno != EWOULDBLOCK) {
killwind(fdmap[fd], 1);
return;
}
for (cp=ibuf; cp < ibuf+len; cp++) {
if (*cp&META) {
if (cq > obuf) {
(void)write(1, obuf, cq-obuf);
cq = obuf;
}
xmitcmd(CB_FN_META);
*cp &= ~META;
}
i = -1;
if (*cp == RUB || *cp < ' ') {
i = sizeof ctlch;
while (i >= 0 && ctlch[i] != *cp)
i--;
}
if (i >= 0) {
if (cq > obuf) {
(void)write(1, obuf, cq-obuf);
cq = obuf;
}
xmitcmd(CB_FN_CTLCH|i);
} else {
*cq++ = *cp;
if (cq >= obuf+sizeof obuf) {
(void)write(1, obuf, cq-obuf);
cq = obuf;
}
}
}
} else
(void)read(fd, ibuf, sizeof ibuf);
if (cq > obuf)
(void)write(1, obuf, cq-obuf);
}
killwind(w, notify)
register struct window *w;
{
/*
* Kill window "w". Notify the Macintosh that it is gone if
* "notify" is nonzero.
*/
FD_CLR(w->w_fd, &selmask[0].sm_rd);
FD_CLR(w->w_fd, &selmask[0].sm_wt);
FD_CLR(w->w_fd, &selmask[0].sm_ex);
(void)close(w->w_fd);
fdmap[w->w_fd] = NULL;
w->w_fd = -1;
if (curwin[W_IN] == w)
curwin[W_IN] = NULL;
if (curwin[W_OUT] == w)
curwin[W_OUT] = NULL;
if (notify)
xmitcmd(CB_FN_KILLW|(w-window+1));
}
newwind(w)
register struct window *w;
{
register int pid;
register char *shell;
register int fd;
auto struct ptydesc pt;
static int unity = 1;
extern char *getenv();
/*
* Create a new window using "w".
*/
if (openpty(&pt) < 0)
return(-1); /* better recovery is needed */
(void)ioctl(pt.pt_pfd, (int)FIONBIO, (char *)&unity);
fdmap[pt.pt_pfd] = w;
w->w_fd = pt.pt_pfd;
FD_SET(pt.pt_pfd, &selmask[0].sm_rd);
(void)strncpy(w->w_tty, pt.pt_tname, sizeof w->w_tty);
/*
* We must spawn a new process with fork(), not vfork(), because
* some implementations (e.g. Sun UNIX) manipulate static
* variables within the signal() subroutine. If we vfork()
* the parent will die when the first SIGCHLD arrives. The
* data+bss size is fairly small, so this isn't too expensive.
*/
while ((pid=fork()) < 0)
sleep(5);
if (!pid) {
(void)signal(SIGHUP, SIG_DFL);
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGTERM, SIG_DFL);
(void)signal(SIGTSTP, SIG_IGN);
(void)signal(SIGCHLD, SIG_DFL);
(void)ioctl(open("/dev/tty",O_RDWR), (int)TIOCNOTTY, (char *)0);
(void)close(open(pt.pt_tname, O_RDONLY)); /* set new ctrl tty */
(void)setuid(getuid()); /* shouldn't need this */
if (!(shell = getenv("SHELL")))
shell = "/bin/sh";
(void)dup2(pt.pt_tfd, 0);
(void)dup2(0, 1);
(void)dup2(0, 2);
for (fd=3; fd < nfds; fd++)
(void)close(fd);
tmode(0); /* HACK! */
execl(shell, shell, (char *)0);
_exit(1);
}
(void)close(pt.pt_tfd);
return(0);
}
tmode(f)
{
static struct sgttyb ostty, nstty;
static struct tchars otchars, ntchars;
static struct ltchars oltchars, nltchars;
static int olmode, nlmode;
static saved;
/*
* This routine either saves the current terminal modes and then
* sets up the terminal line or resets the terminal modes (depending
* upon the value of "f"). The terminal line is used in "cbreak"
* mode with all special characters except XON/XOFF disabled. The
* hated (by me) LDECCTQ mode is required for the Macintosh to
* handle flow control properly.
*/
if (f == 1) {
if (ioctl(0, (int)TIOCGETP, (char *)&ostty) < 0) {
perror("ioctl((int)TIOCGETP)");
done(1);
}
if (ioctl(0, (int)TIOCGETC, (char *)&otchars) < 0) {
perror("ioctl((int)TIOCGETC)");
done(1);
}
if (ioctl(0, (int)TIOCGLTC, (char *)&oltchars) < 0) {
perror("ioctl((int)TIOCGLTC)");
done(1);
}
if (ioctl(0, (int)TIOCLGET, (char *)&olmode) < 0) {
perror("ioctl((int)TIOCLGET)");
done(1);
}
nstty = ostty;
nstty.sg_erase = nstty.sg_kill = -1;
nstty.sg_flags |= CBREAK|TANDEM;
nstty.sg_flags &= ~(RAW|CRMOD|ECHO|LCASE|XTABS);
ntchars.t_intrc = ntchars.t_quitc = -1;
ntchars.t_eofc = ntchars.t_brkc = -1;
ntchars.t_startc = XON;
ntchars.t_stopc = XOFF;
nltchars.t_suspc = nltchars.t_dsuspc = -1;
nltchars.t_rprntc = nltchars.t_flushc = -1;
nltchars.t_werasc = nltchars.t_lnextc = -1;
nlmode = olmode | LDECCTQ;
if (ioctl(0, (int)TIOCSETN, (char *)&nstty) < 0) {
perror("ioctl((int)TIOCSETN)");
done(1);
}
if (ioctl(0, (int)TIOCSETC, (char *)&ntchars) < 0) {
perror("ioctl((int)TIOCSETC");
done(1);
}
if (ioctl(0, (int)TIOCSLTC, (char *)&nltchars) < 0) {
perror("ioctl((int)TIOCSLTC");
done(1);
}
if (ioctl(0, (int)TIOCLSET, (char *)&nlmode) < 0) {
perror("ioctl((int)TIOCLSET)");
done(1);
}
saved = 1;
} else if (saved) {
(void)ioctl(0, (int)TIOCSETP, (char *)&ostty);
(void)ioctl(0, (int)TIOCSETC, (char *)&otchars);
(void)ioctl(0, (int)TIOCSLTC, (char *)&oltchars);
(void)ioctl(0, (int)TIOCLSET, (char *)&olmode);
}
}
done(s)
{
register fildes_t fd;
/*
* Clean up and exit. It is overkill to close all of the file
* descriptors, but it causes no harm. After we are sure that
* our UNIX-domain network connection is closed we remove the
* entry that it created (as a side effect) in the filesystem.
*
* We also restore the terminal modes.
*/
/*xmitcmd(CB_FN_MAINT|CB_MF_EXIT);*/
for (fd=3; fd < nfds; fd++)
(void)close(fd);
(void)unlink(portal);
tmode(0);
exit(s);
}
cwait()
{
union wait status;
struct rusage rusage;
/*
* Collect dead children. We don't use the information that
* wait3() returns. (Someday we might.)
*/
while (wait3(&status, WNOHANG, &rusage) > 0)
;
}
static char *earray[MAXENV+1];
setenv(env)
char **env;
{
register char **ep1, **ep2, *cp;
char **ep3;
extern char **environ;
/*
* Merge the set of environment strings in "env" into the
* environment.
*/
/*
* The first time through, copy the environment from its
* original location to the array "earray". This makes it a
* little easier to change things.
*/
if (environ != earray){
ep1=environ;
ep2=earray;
while(*ep1 && ep2 <= earray+MAXENV)
*ep2++ = *ep1++;
*ep2++ = NULL;
environ = earray;
}
/*
* If "env" is non-NULL, it points to a list of new items to
* be added to the environment. These replace existing items
* with the same name.
*/
if (env){
for(ep1=env; *ep1; ep1++){
for(ep2=environ; *ep2; ep2++)
if (!envcmp(*ep1, *ep2))
break;
if (ep2 < earray+MAXENV) {
if (!*ep2)
ep2[1] = NULL;
*ep2 = *ep1;
}
}
}
/* Finally, use an insertion sort to put things in order. */
for(ep1=environ+1; cp = *ep1; ep1++){
for(ep2=environ; ep2 < ep1; ep2++)
if (envcmp(*ep1, *ep2) < 0)
break;
ep3 = ep2;
for(ep2=ep1; ep2 > ep3; ep2--)
ep2[0] = ep2[-1];
*ep2 = cp;
}
}
static
envcmp(e1, e2)
register char *e1, *e2;
{
register d;
do {
if (d = *e1 - *e2++)
return(d);
} while(*e1 && *e1++ != '=');
return(0);
}
!Funky!Stuff!
echo x - uwtool.c
cat > uwtool.c << '!Funky!Stuff!'
#
/*
* uwtool
*
* Copyright 1985 by John D. Bruner. All rights reserved. Permission to
* copy this program is given provided that the copy is not sold and that
* this copyright notice is included.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
#include <strings.h>
#include <signal.h>
#include <stdio.h>
#include "openpty.h"
#define NFDS 20 /* max number of file descriptors */
typedef int fildes_t;
extern char *getenv();
main(argc, argv)
char **argv;
{
register int pid;
register fildes_t sd;
auto fildes_t fd;
char *portal, *shell;
int lmode;
struct sgttyb sg;
struct tchars tc;
struct ltchars ltc;
struct iovec iov;
struct msghdr msg;
struct sockaddr sa;
auto struct ptydesc pt;
/*
* Close all file descriptors except 0, 1, and 2.
*/
for (fd=3; fd < NFDS; fd++)
(void)close(fd);
/*
* Get the terminal configuration for this tty.
*/
(void)ioctl(0, (int)TIOCGETP, (char *)&sg);
(void)ioctl(0, (int)TIOCGETC, (char *)&tc);
(void)ioctl(0, (int)TIOCGLTC, (char *)<c);
(void)ioctl(0, (int)TIOCLGET, (char *)&lmode);
/*
* Create a UNIX-domain socket.
*/
if (!(portal=getenv("UWIN"))) {
fprintf(stderr,
"You must run %s under the window manager\n", *argv);
exit(1);
}
if ((sd=socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
sa.sa_family = AF_UNIX;
(void)strncpy(sa.sa_data, portal, sizeof sa.sa_data-1);
sa.sa_data[sizeof sa.sa_data-1] = '\0';
/*
* Obtain a pseudo-tty.
*/
if (openpty(&pt) < 0) {
fprintf(stderr, "Can't obtain a pseudo-tty for a new window\n");
exit(1);
}
/*
* Fork a child process using this pseudo-tty. Initialize the
* terminal modes on the pseudo-tty to match those of the parent
* tty.
*/
while ((pid=vfork()) < 0)
sleep(5);
if (!pid) {
(void)setuid(getuid());/* in case it's setuid-root by mistake */
(void)signal(SIGTSTP, SIG_IGN);
(void)ioctl(open("/dev/tty", 2), (int)TIOCNOTTY, (char *)0);
(void)close(open(pt.pt_tname, 0)); /*set new ctrl tty */
(void)dup2(pt.pt_tfd, 0);
(void)dup2(0, 1);
(void)dup2(0, 2);
for (fd=3; fd < NFDS; fd++)
(void)close(fd);
(void)ioctl(0, (int)TIOCSETN, (char *)&sg);
(void)ioctl(0, (int)TIOCSETC, (char *)&tc);
(void)ioctl(0, (int)TIOCSLTC, (char *)<c);
(void)ioctl(0, (int)TIOCLSET, (char *)&lmode);
if (argc == 1) {
if (!(shell=getenv("SHELL")))
shell = "/bin/sh";
execl(shell, shell, (char *)0);
perror(shell);
} else {
execvp(argv[1], argv+1);
perror(argv[1]);
}
_exit(1);
}
/*
* Pass the file descriptor to the window server and exit.
*/
iov.iov_base = pt.pt_pname;
iov.iov_len = strlen(pt.pt_pname);
msg.msg_name = (caddr_t)&sa;
msg.msg_namelen = sizeof sa.sa_family + strlen(sa.sa_data);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_accrights = (caddr_t)&pt.pt_pfd;
msg.msg_accrightslen = sizeof pt.pt_pfd;
if (sendmsg(sd, &msg, 0) < 0) {
perror("sendmsg");
exit(1);
}
exit(0);
}
!Funky!Stuff!
echo x - macmouse.ml
cat > macmouse.ml << '!Funky!Stuff!'
; $Header: /c/cak/lib/mlisp/RCS/macmouse.ml,v 1.5 85/11/05 14:01:44 cak Rel $
;
; Macintosh mouse routines for use with John Bruner's uw program.
; Chris Kent, Purdue University Fri Oct 25 1985
; Copyright 1985 by Christopher A. Kent. All rights reserved.
; Permission to copy is given provided that the copy is not
; sold and this copyright notice is included.
;
; Provides a scroll bar/thumbing area in the unused scroll bar with the
; following features:
; click at line 1 does previous page
; click at line 24 does next page
; click anywhere else "thumbs" to the relative portion of the buffer.
; shift-click at line 1 scrolls one line down
; shift-click at line 24 scrolls one line up
; shift-click elsewhere moves line to top of window
; option-shift-click elsewhere moves line to bottom of window
;
; There is also basic positioning and kill-buffer support:
; click in a buffer moves dot there
; drag copies the dragged region to the kill buffer (mark is left
; at the beginning of the region.)
; shift-drag deletes the dragged region to the kill buffer
; it is possible to use the scrolling and thumbing area to make the region
; larger than a single screen; just click, scroll, release. Make sure
; that the last scroll is just a down event; the up must be in the buffer.
;
; option-click yanks from the kill buffer, doesn't affect mark.
; option-shift-click similarly yanks from a named buffer.
;
(declare-global
#mouse-last-x ; x of last event
#mouse-last-y ; y of last event
#mouse-last-b ; buttons at last event
#mouse-last-dot ; dot after last event
#mouse-last-action ; whether last was scroll (1) or edit (2)
)
(defun
(move-mac-cursor savest b x y up down lock shift option command saveb
(setq savest stack-trace-on-error)
(setq stack-trace-on-error 0)
; decode everything
(setq y (- (get-tty-character) 32))
(setq x (- (get-tty-character) 32))
(setq b (- (get-tty-character) 32))
(setq saveb b)
(setq command (% b 2))(setq b (/ b 2)) ; command key
(setq shift (% b 2))(setq b (/ b 2)) ; shift
(setq lock (% b 2))(setq b (/ b 2)) ; caps-lock
(setq option (% b 2))(setq b (/ b 2)) ; option
(setq down (% b 2))(setq b (/ b 2)) ; mouse down
(setq up (% b 2))
(if (= x 81) ; right margin -- move-dot-to-x-y is wrong
(progn
(#mouse-scroll-region)
(setq #mouse-last-action 1))
(if (error-occurred
(if (= #mouse-last-action 2) ; not if just scrolled
(setq #mouse-last-dot (dot)))
(move-dot-to-x-y x y)
(backward-character)(forward-character)
(#mouse-edit-action)
(setq #mouse-last-action 2)
)
(progn
(#mouse-scroll-region b x y)
(setq #mouse-last-action 1))
))
(setq stack-trace-on-error savest)
(if (= down 1)
(progn
(setq #mouse-last-x x)
(setq #mouse-last-y y)
(setq #mouse-last-b saveb))
(progn
(setq #mouse-last-x 0)
(setq #mouse-last-y 0)
(setq #mouse-last-b 0)))
)
(#mouse-edit-action ; marking and editing actions on buttons:
; if no movement, nothing.
; if movement, put mark at #mouse-last-dot,
; leave dot here,and edit.
; editing (on upstrokes):
; unmodified, copy to kill buffer.
; SHIFTed, delete (cut) to kill buffer.
;
; option-click yanks from kill buffer;
; shift-option-click from named buffer.
(if (= saveb 16)
(#mouse-d))
(if (= saveb 17)
(#mouse-dc))
(if (= saveb 18)
(#mouse-ds))
(if (= saveb 19)
(#mouse-dsc))
(if (= saveb 20)
(#mouse-dl))
(if (= saveb 21)
(#mouse-dlc))
(if (= saveb 22)
(#mouse-dls))
(if (= saveb 23)
(#mouse-dlsc))
(if (= saveb 24)
(#mouse-do))
(if (= saveb 25)
(#mouse-doc))
(if (= saveb 26)
(#mouse-dos))
(if (= saveb 27)
(#mouse-dosc))
(if (= saveb 28)
(#mouse-dol))
(if (= saveb 29)
(#mouse-dolc))
(if (= saveb 30)
(#mouse-dols))
(if (= saveb 31)
(#mouse-dolsc))
(if (= saveb 32)
(#mouse-u))
(if (= saveb 33)
(#mouse-uc))
(if (= saveb 34)
(#mouse-us))
(if (= saveb 35)
(#mouse-usc))
(if (= saveb 36)
(#mouse-ul))
(if (= saveb 37)
(#mouse-ulc))
(if (= saveb 38)
(#mouse-uls))
(if (= saveb 39)
(#mouse-ulsc))
(if (= saveb 40)
(#mouse-uo))
(if (= saveb 41)
(#mouse-uoc))
(if (= saveb 42)
(#mouse-uos))
(if (= saveb 43)
(#mouse-uosc))
(if (= saveb 44)
(#mouse-uol))
(if (= saveb 45)
(#mouse-uolc))
(if (= saveb 46)
(#mouse-uols))
(if (= saveb 47)
(#mouse-uolsc))
)
; individual button bindings
(#mouse-u ; up
(if (! (#mouse-click-p))
(progn
(#mouse-set-region)
(Copy-region-to-kill-buffer)
))
)
(#mouse-uc ; up/command
)
(#mouse-us ; up/shift
(if (! (#mouse-click-p))
(progn
(#mouse-set-region)
(delete-to-killbuffer)
))
)
(#mouse-usc ; up/shift/command
)
(#mouse-ul ; up/lock
)
(#mouse-ulc ; up/lock/command
)
(#mouse-uls ; up/lock/shift
)
(#mouse-ulsc ; up/lock/shift/command
)
(#mouse-uo ; up/option
(if (#mouse-click-p)
(yank-from-killbuffer)
)
)
(#mouse-uoc ; up/option/command
)
(#mouse-uos ; up/option/shift
(if (#mouse-click-p) ; click
(yank-buffer (get-tty-buffer "Insert contents of buffer: "))
)
)
(#mouse-uosc ; up/option/shift
)
(#mouse-uol ; up/option/lock
)
(#mouse-uolc ; up/option/lock
)
(#mouse-uols ; up/option/lock/shift
)
(#mouse-uolsc ; up/option/lock/shift/command
)
(#mouse-d ; down
)
(#mouse-dc ; down/command
)
(#mouse-ds ; down/shift
)
(#mouse-dsc ; down/shift/command
)
(#mouse-dl ; down/lock
)
(#mouse-dlc ; down/lock/command
)
(#mouse-dls ; down/lock/shift
)
(#mouse-dlsc ; down/lock/shift/command
)
(#mouse-do ; down/option
)
(#mouse-doc ; down/option/command
)
(#mouse-dos ; down/option/shift
)
(#mouse-dosc ; down/option/shift
)
(#mouse-dol ; down/option/lock
)
(#mouse-dolc ; down/option/lock
)
(#mouse-dols ; down/option/lock/shift
)
(#mouse-dolsc ; down/option/lock/shift/command
)
(#mouse-set-region ; set the region to be from last dot to dot.
(set-mark)
(goto-character #mouse-last-dot)
(exchange-dot-and-mark)
)
(#mouse-click-p clickp
(if (= (dot) #mouse-last-dot)
(setq clickp 1)
(setq clickp 0)
))
(#mouse-scroll-region ; out of range actions:
; left margin -- hard to generate, ignored
; right margin -- simulate scroll bar
; line 1 -- previous page
; line 24/25 -- next page
; other lines -- thumbing
; top margin -- previous page
; bottom margin -- next page
;
; if shifted, deal with lines.
; line 1 scrolls one line down
; line 24/25 scrolls one line up
; else line to top; with option to bottom.
;
; if up stroke is in same place as down
; stroke, don't do anything, so clicks in
; the scroll region don't do the action
; twice.
(if (= down 1)
(if (= shift 1)
(do-lines)
(do-pages))
)
(if (& (= up 1)
(| (!= x #mouse-last-x) (!= y #mouse-last-y)))
(if (= shift 1)
(do-lines)
(do-pages)
)
)
(#mouse-set-region)
)
(do-pages ; large motions via pages and thumbing
(if (| (= y 0) (= y 1) (= y 24) (= y 25))
(progn
(if (| (= y 0) (= y 1))
(previous-page)
(Next-Page)
))
(if (= x 81)
(goto-percent (/ (* y 100) 25))
)
))
(do-lines ; fine control over lines
(if (= x 81)
(if (| (= y 1) (= y 24) (= y 25))
(if (| (= y 0) (= y 1))
(scroll-one-line-down)
(scroll-one-line-up)
)
(progn
(move-dot-to-x-y 1 y)
(if (= option 0)
(line-to-top-of-window)
(line-to-bottom-of-window))
)
)
)
)
(line-to-bottom-of-window nlines i
(line-to-top-of-window)
(setq i 0)
(setq nlines (- (window-height) 1))
(while (< i nlines)
(scroll-one-line-down)
(setq i (+ i 1))
)
)
(goto-percent
(goto-character (/ (* (buffer-size) (arg 1)) 100))
)
)
(bind-to-key "move-mac-cursor" "\em")
!Funky!Stuff!
exit 0
: end of shell archive
--
John Bruner (S-1 Project, Lawrence Livermore National Laboratory)
MILNET: jdb@mordor [jdb@s1-c.ARPA] (415) 422-0758
UUCP: ...!ucbvax!dual!mordor!jdb ...!seismo!mordor!jdb