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