home *** CD-ROM | disk | FTP | other *** search
/ Carousel Volume 2 #1 / carousel.iso / mactosh / unix / uw_term.sha / uw.c < prev    next >
C/C++ Source or Header  |  1985-11-12  |  18KB  |  768 lines

  1. #
  2.  
  3. /*
  4.  *    uw - UNIX windows program for the Macintosh (VAX end)
  5.  *
  6.  * Copyright 1985 by John D. Bruner.  All rights reserved.  Permission to
  7.  * copy this program is given provided that the copy is not sold and that
  8.  * this copyright notice is included.
  9.  */
  10.  
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <sys/socket.h>
  14. #include <sys/ioctl.h>
  15. #include <sys/wait.h>
  16. #include <sys/time.h>
  17. #include <sys/resource.h>
  18. #include <sys/uio.h>
  19. #include <signal.h>
  20. #include <setjmp.h>
  21. #include <errno.h>
  22. #include <fcntl.h>
  23. #include <strings.h>
  24. #include <stdio.h>
  25. #include "uw.h"
  26. #include "openpty.h"
  27.  
  28. #define    MAXENV    128            /* maximum environment size */
  29.  
  30. #define    W_IN    0
  31. #define    W_OUT    1
  32.  
  33. #define    XON    0021            /* ASCII XON */
  34. #define    XOFF    0023            /* ASCII XOFF */
  35. #define    RUB    0177            /* ASCII RUBOUT */
  36.  
  37. #define    META    0200            /* "meta" bit for whatever it's worth */
  38.  
  39. #define    RCV_OK    (-1)            /* see recvcmd() */
  40. #define    RCV_META (-2)            /* see recvcmd() */
  41.  
  42. #ifndef FD_SET
  43. #define    FD_SET(n,p)    ((p)->fds_bits[0] |= (1 << (n)))
  44. #define    FD_CLR(n,p)    ((p)->fds_bits[0] &= ~(1 << (n)))
  45. #define    FD_ISSET(n,p)    ((p)->fds_bits[0] & (1 << (n)))
  46. #define    FD_ZERO(p)    ((p)->fds_bits[0] = 0)
  47. #endif
  48.  
  49. typedef int fildes_t;            /* file descriptor data type */
  50. typedef int nwin_t;            /* window index data type */
  51.  
  52. struct window {
  53.     fildes_t    w_fd;
  54.     char        w_tty[32];
  55. };
  56.  
  57. struct window window[NWINDOW];        /* window data structures */
  58. struct window **fdmap;            /* mapping from fd's to windows */
  59. struct window *curwin[2];        /* current input and output windows */
  60. fildes_t nfds;                /* number of file descriptors */
  61.  
  62. char portal[] = "/tmp/uwXXXXXX";    /* UNIX-domain network address */
  63.  
  64.  
  65. /* The following are added to the environment of all child processes */
  66.  
  67. char env_uwin[64] = "UWIN=";
  68. char *env[] = { 
  69.     env_uwin,
  70.     "TERM=adm31",
  71.     "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:",
  72.     NULL
  73. };
  74.  
  75. int ctlch[] = { -1, IAC, XON, XOFF, -1, -1, -1, -1 };    /* CTL char mapping */
  76.  
  77. struct selmask {
  78.     struct fd_set    sm_rd;
  79.     struct fd_set    sm_wt;
  80.     struct fd_set    sm_ex;
  81. } selmask[2];
  82.  
  83. extern char *mktemp();
  84. extern char *getenv();
  85. extern char *malloc();
  86. extern done(), cwait();
  87. extern int errno;
  88.  
  89. main(argc, argv)
  90. char **argv;
  91. {
  92.     register fildes_t fd, sd;
  93.     register struct window *w;
  94.     struct sockaddr sa;
  95.  
  96.     /*
  97.      * Make sure we don't accidentally try to run this inside itself.
  98.      */
  99.     if (getenv("UWIN")) {
  100.         fprintf(stderr, "%s is already running\n", *argv);
  101.         exit(1);
  102.     }
  103.  
  104.     /*
  105.      * Close all file descriptors except 0, 1, and 2.
  106.      */
  107.     nfds = getdtablesize();
  108.     for (fd=3; fd < nfds; fd++)
  109.         (void)close(fd);
  110.  
  111.     /*
  112.      * Allocate "fdmap".  We can't do this at compile time because
  113.      * we get the number of file descriptors from getdtablesize().
  114.      */
  115.     if (!(fdmap=(struct window **)malloc(nfds*sizeof(struct window *)))) {
  116.         fprintf(stderr, "cannot allocate 'fdmap' array\n");
  117.         exit(1);
  118.     }
  119.  
  120.     /*
  121.      * Mark all windows closed.
  122.      */
  123.  
  124.     for (w=window; w < window+NWINDOW; w++)
  125.         w->w_fd = -1;
  126.  
  127.  
  128.     /*
  129.      * Create a UNIX-domain network address, and put its name into
  130.      * the environment so that descendents can contact us with new
  131.      * window requests.
  132.      */
  133.  
  134.     (void)strncat(env_uwin, mktemp(portal), sizeof env_uwin - 1);
  135.     setenv(env);
  136.  
  137.     if ((sd=socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
  138.         perror("socket");
  139.         exit(1);
  140.     }
  141.     sa.sa_family = AF_UNIX;
  142.     (void)strncpy(sa.sa_data, portal, sizeof sa.sa_data-1);
  143.     sa.sa_data[sizeof sa.sa_data-1] = '\0';
  144.     if (bind(sd, &sa, sizeof sa.sa_family + strlen(sa.sa_data)) < 0) {
  145.         perror("bind");
  146.         exit(1);
  147.     }
  148.     if (fcntl(sd, F_SETFL, FNDELAY))
  149.         perror("fcntl(sd, F_SETFL, FNDELAY)");
  150.  
  151.  
  152.     /*
  153.      * Ignore interrupts, quits, and terminal stops.  Clean up and exit
  154.      * if a hangup or termination is received.  Also catch changes in
  155.      * child status (so that we can wait for them).  Set up the terminal
  156.      * modes.
  157.      */
  158.  
  159.     (void)signal(SIGHUP, done);
  160.     (void)signal(SIGINT, SIG_IGN);
  161.     (void)signal(SIGQUIT, SIG_IGN);
  162.     (void)signal(SIGTERM, done);
  163.     (void)signal(SIGTSTP, SIG_IGN);
  164.     (void)signal(SIGCHLD, cwait);
  165.  
  166.     tmode(1);
  167.  
  168.  
  169.     /*
  170.      * Tell the Macintosh to initialize.  Initialize the current input
  171.      * and output windows to NULL.
  172.      */
  173.  
  174.     xmitcmd(CB_FN_MAINT|CB_MF_ENTRY);
  175.     curwin[W_IN] = curwin[W_OUT] = NULL;
  176.  
  177.     
  178.     /*
  179.      * Initialize the "select" masks, create window 1 (to start
  180.      * things off) and wait for input.  When input is available,
  181.      * process it.
  182.      */
  183.  
  184.     FD_ZERO(&selmask[0].sm_rd);
  185.     FD_ZERO(&selmask[0].sm_wt);
  186.     FD_ZERO(&selmask[0].sm_ex);
  187.  
  188.     FD_SET(0, &selmask[0].sm_rd);
  189.     FD_SET(sd, &selmask[0].sm_rd);
  190.  
  191.     if (newwind(window) == 0)
  192.         xmitcmd(CB_FN_NEWW|1);
  193.  
  194.     while (1) {
  195.         selmask[1] = selmask[0];
  196.         if (select(nfds, &selmask[1].sm_rd, &selmask[1].sm_wt,
  197.             &selmask[1].sm_ex, (struct timeval *)0) < 0) {
  198.             if (errno == EINTR)
  199.                 continue;
  200.             perror("select");
  201.             done(1);    /* for now -- fix this! */
  202.         }
  203.         for (fd=0; fd < nfds; fd++) {
  204.             if (FD_ISSET(fd, &selmask[1].sm_rd)) {
  205.                 if (fd < 2)
  206.                     mrecv();
  207.                 else if (fd == sd)
  208.                     netrecv(sd);
  209.                 else
  210.                     mxmit(fd);
  211.             }
  212.         }
  213.     }
  214. }
  215.  
  216. mrecv()
  217. {
  218.     register int len;
  219.     register char *cp, *cq;
  220.     auto int nready;
  221.     char ibuf[512], obuf[512];
  222.     static int seen_iac, seen_meta;
  223.  
  224.     /*
  225.      * The received bytestream is examined.  Non-command bytes are
  226.      * written to the file descriptor corresponding to the current
  227.      * "input" window (relative to the Macintosh -- the window the
  228.      * user types input to).
  229.      */
  230.  
  231.     if (ioctl(0, (int)FIONREAD, (char *)&nready) < 0) {
  232.         perror("FIONREAD");
  233.         return;
  234.     }
  235.  
  236.     cq = obuf;
  237.     while (nready > 0) {
  238.         if (nready > sizeof ibuf)
  239.             len = read(0, ibuf, sizeof ibuf);
  240.         else
  241.             len = read(0, ibuf, nready);
  242.         if (len <= 0) {
  243.             perror("read");
  244.             return;
  245.         }
  246.         for (cp=ibuf; cp < ibuf+len; cp++) {
  247.             if (seen_iac) {
  248.                 if ((*cp&CB_DIR) == CB_DIR_MTOH) {
  249.                     if (cq > obuf) {
  250.                         (void)write(curwin[W_IN]->w_fd,
  251.                                 obuf, cq-obuf);
  252.                         cq = obuf;
  253.                     }
  254.                     switch (*cq = recvcmd(*cp)) {
  255.                     case RCV_OK:
  256.                         break;
  257.                     case RCV_META:
  258.                         seen_meta = 1;
  259.                         break;
  260.                     default:
  261.                         if (seen_meta) {
  262.                             seen_meta = 0;
  263.                             *cq |= META;
  264.                         }
  265.                         if (curwin[W_IN])
  266.                             cq++;
  267.                         break;
  268.                     }
  269.                 }
  270.                 seen_iac = 0;
  271.             } else if (*cp == IAC)
  272.                 seen_iac++;
  273.             else if (curwin[W_IN]) {
  274.                 if (seen_meta) {
  275.                     seen_meta = 0;
  276.                     *cq++ = *cp|META;
  277.                 } else
  278.                     *cq++ = *cp;
  279.                 if (cq >= obuf+sizeof obuf) {
  280.                     (void)write(curwin[W_IN]->w_fd,
  281.                             obuf, cq-obuf);
  282.                     cq = obuf;
  283.                 }
  284.             }
  285.         }
  286.         nready -= len;
  287.     }
  288.     if (cq > obuf)
  289.         (void)write(curwin[W_IN]->w_fd, obuf, cq-obuf);
  290. }
  291.  
  292. recvcmd(cmd)
  293. char cmd;
  294. {
  295.     register int nwin, fn;
  296.     register struct window *w;
  297.  
  298.     /*
  299.      * Perform the function the Mac is asking for.  There are three
  300.      * broad categories of these functions: RCV_META, which tells
  301.      * the caller that the next character is a "meta" character;
  302.      * an ASCII data character, which is passed back to the caller
  303.      * for proper handling; and RCV_OK, which means that this routine
  304.      * has done everything which was required to process the command.
  305.      */
  306.  
  307.     fn = cmd&CB_FN;
  308.     switch (fn) {
  309.     case CB_FN_NEWW:
  310.     case CB_FN_KILLW:
  311.     case CB_FN_ISELW:
  312.         nwin = cmd&CB_WINDOW;
  313.         if (!nwin)
  314.             break;
  315.         w = &window[nwin-1];
  316.  
  317.         switch (fn) {
  318.         case CB_FN_NEWW:
  319.             if (w->w_fd < 0 && newwind(w) < 0)
  320.                 xmitcmd(CB_FN_KILLW|nwin);
  321.             break;
  322.  
  323.         case CB_FN_KILLW:
  324.             killwind(w, 0);
  325.             break;
  326.  
  327.         case CB_FN_ISELW:
  328.             if (w->w_fd >= 0)
  329.                 curwin[W_IN] = w;
  330.             break;
  331.         }
  332.         break;
  333.  
  334.     case CB_FN_META:
  335.         return(RCV_META);
  336.  
  337.     case CB_FN_CTLCH:
  338.         return(ctlch[cmd&CB_CC]);
  339.  
  340.     case CB_FN_MAINT:
  341.         if ((cmd&CB_MF) == CB_MF_EXIT)
  342.             done(0);
  343.         /*NOTREACHED*/
  344.     }
  345.     return(RCV_OK);
  346. }
  347.  
  348. xmitcmd(cmd)
  349. char cmd;
  350. {
  351.     static char cmdbuf[2] = { IAC, '\0' };
  352.  
  353.     /*
  354.      * Transmit the command "cmd" to the Macintosh.  The byte is ORed
  355.      * with the host-to-Mac direction indicator.
  356.      */
  357.  
  358.     cmdbuf[1] = cmd|CB_DIR_HTOM;
  359.     (void)write(1, cmdbuf, sizeof cmdbuf);
  360. }
  361.  
  362. netrecv(sd)
  363. register fildes_t sd;
  364. {
  365.     register struct window *w;
  366.     register int cnt;
  367.     struct msghdr msg;
  368.     auto int fd;
  369.     struct iovec iov;
  370.     struct stat st1, st2;
  371.     static int unity = 1;
  372.     char buf[256];
  373.  
  374.  
  375.     /*
  376.      * main() calls this routine when there is a message waiting on
  377.      * the UNIX-domain socket.  The message's access rights are
  378.      * expected to contain the file descriptor for the "master" side
  379.      * of a pseudo-tty.  The message contains the name of the pty.
  380.      * The sender is expected to start up a process on the slave side
  381.      * of the pty.  This allows the host end to create windows which
  382.      * run something other than the shell.
  383.      */
  384.  
  385.     fd = -1;
  386.  
  387.     iov.iov_base = (caddr_t)buf;
  388.     iov.iov_len = sizeof buf - 1;
  389.  
  390.     msg.msg_name = (caddr_t)0;
  391.     msg.msg_namelen = 0;
  392.     msg.msg_iov = &iov;
  393.     msg.msg_iovlen = 1;
  394.     msg.msg_accrights = (caddr_t)&fd;
  395.     msg.msg_accrightslen = sizeof fd;
  396.  
  397.     if ((cnt=recvmsg(sd, &msg, 0)) < 0)
  398.         perror("recvmsg");
  399.  
  400.     if (msg.msg_accrightslen > 0 && fd >= 0) {
  401.         /*
  402.          * We can't trust the process which connected to us, so
  403.          * we verify that it really passed us a pseudo-tty's
  404.          * file descriptor by checking the device name and its
  405.          * inode number.  [Of course, if someone else wants to
  406.          * hand us a terminal session running under their uid....]
  407.          */
  408.         buf[cnt] = 0;
  409.         if (strncmp(buf, "/dev/pty", sizeof "/dev/pty" - 1) ||
  410.             fstat(fd, &st1) < 0 || stat(buf, &st2) < 0 ||
  411.             st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
  412.             (void)close(fd);
  413.             return;
  414.         }
  415.         /*
  416.          * OK, we believe the sender.  We allocate a window and
  417.          * tell the Macintosh to create that window on its end.
  418.          */
  419.         buf[5] = 't';        /* switch to "/dev/ttyp?" */
  420.         for (w=window; w < window+NWINDOW; w++) {
  421.             if (w->w_fd < 0) {
  422.                 w->w_fd = fd;
  423.                 fdmap[fd] = w;
  424.                 FD_SET(fd, &selmask[0].sm_rd);
  425.                 (void)strncpy(w->w_tty, buf, sizeof w->w_tty-1);
  426.                 xmitcmd(CB_FN_NEWW|(w-window+1));
  427.                 break;
  428.             }
  429.         }
  430.         /*
  431.          * If we have no free windows, then we close the file
  432.          * descriptor (which will terminate the slave process).
  433.          */
  434.         if (w == window+NWINDOW)
  435.             (void)close(fd);
  436.         /*
  437.          * Set non-blocking I/O on the master side.
  438.          */
  439.         (void)ioctl(fd, (int)FIONBIO, (char *)&unity);
  440.     }
  441. }
  442.  
  443. mxmit(fd)
  444. register fildes_t fd;
  445. {
  446.     register char *cp, *cq, i;
  447.     register int len;
  448.     char ibuf[32], obuf[32];
  449.  
  450.     /*
  451.      * Copy input from file "fd" to the Macintosh.  Be sure to convert
  452.      * any embedded IAC characters.
  453.      *
  454.      * Note that the input/output buffers should NOT be very large.
  455.      * It is undesirable to perform large reads and effectively
  456.      * "lock out" all other file descriptors.  The chosen size
  457.      * should preserve a reasonable amount of efficiency.
  458.      */
  459.  
  460.     if (fdmap[fd]) {
  461.         curwin[W_OUT] = fdmap[fd];
  462.         xmitcmd(CB_FN_OSELW|(fdmap[fd]-window+1));
  463.         cq = obuf;
  464.         if ((len = read(fd, ibuf, sizeof ibuf)) < 0 &&
  465.             errno != EWOULDBLOCK) {
  466.             killwind(fdmap[fd], 1);
  467.             return;
  468.         }
  469.         for (cp=ibuf; cp < ibuf+len; cp++) {
  470.             if (*cp&META) {
  471.                 if (cq > obuf) {
  472.                     (void)write(1, obuf, cq-obuf);
  473.                     cq = obuf;
  474.                 }
  475.                 xmitcmd(CB_FN_META);
  476.                 *cp &= ~META;
  477.             }
  478.             i = -1;
  479.             if (*cp == RUB || *cp < ' ') {
  480.                 i = sizeof ctlch;
  481.                 while (i >= 0 && ctlch[i] != *cp)
  482.                     i--;
  483.             }
  484.             if (i >= 0) {
  485.                 if (cq > obuf) {
  486.                     (void)write(1, obuf, cq-obuf);
  487.                     cq = obuf;
  488.                 }
  489.                 xmitcmd(CB_FN_CTLCH|i);
  490.             } else {
  491.                 *cq++ = *cp;
  492.                 if (cq >= obuf+sizeof obuf) {
  493.                     (void)write(1, obuf, cq-obuf);
  494.                     cq = obuf;
  495.                 }
  496.             }
  497.         }
  498.     } else
  499.         (void)read(fd, ibuf, sizeof ibuf);
  500.     if (cq > obuf)
  501.         (void)write(1, obuf, cq-obuf);
  502. }
  503.  
  504. killwind(w, notify)
  505. register struct window *w;
  506. {
  507.     /*
  508.      * Kill window "w".  Notify the Macintosh that it is gone if
  509.      * "notify" is nonzero.
  510.      */
  511.  
  512.     FD_CLR(w->w_fd, &selmask[0].sm_rd);
  513.     FD_CLR(w->w_fd, &selmask[0].sm_wt);
  514.     FD_CLR(w->w_fd, &selmask[0].sm_ex);
  515.     (void)close(w->w_fd);
  516.     fdmap[w->w_fd] = NULL;
  517.     w->w_fd = -1;
  518.     if (curwin[W_IN] == w)
  519.         curwin[W_IN] = NULL;
  520.     if (curwin[W_OUT] == w)
  521.         curwin[W_OUT] = NULL;
  522.     if (notify)
  523.         xmitcmd(CB_FN_KILLW|(w-window+1));
  524. }
  525.  
  526. newwind(w)
  527. register struct window *w;
  528. {
  529.     register int pid;
  530.     register char *shell;
  531.     register int fd;
  532.     auto struct ptydesc pt;
  533.     static int unity = 1;
  534.     extern char *getenv();
  535.  
  536.     /*
  537.      * Create a new window using "w".
  538.      */
  539.  
  540.     if (openpty(&pt) < 0)
  541.         return(-1);    /* better recovery is needed */
  542.  
  543.     (void)ioctl(pt.pt_pfd, (int)FIONBIO, (char *)&unity);
  544.     fdmap[pt.pt_pfd] = w;
  545.     w->w_fd = pt.pt_pfd;
  546.     FD_SET(pt.pt_pfd, &selmask[0].sm_rd);
  547.     (void)strncpy(w->w_tty, pt.pt_tname, sizeof w->w_tty);
  548.  
  549.     /*
  550.      * We must spawn a new process with fork(), not vfork(), because
  551.      * some implementations (e.g. Sun UNIX) manipulate static
  552.      * variables within the signal() subroutine.  If we vfork()
  553.      * the parent will die when the first SIGCHLD arrives.  The
  554.      * data+bss size is fairly small, so this isn't too expensive.
  555.      */
  556.     while ((pid=fork()) < 0)
  557.         sleep(5);
  558.     if (!pid) {
  559.         (void)signal(SIGHUP, SIG_DFL);
  560.         (void)signal(SIGINT, SIG_DFL);
  561.         (void)signal(SIGQUIT, SIG_DFL);
  562.         (void)signal(SIGTERM, SIG_DFL);
  563.         (void)signal(SIGTSTP, SIG_IGN);
  564.         (void)signal(SIGCHLD, SIG_DFL);
  565.         (void)ioctl(open("/dev/tty",O_RDWR), (int)TIOCNOTTY, (char *)0);
  566.         (void)close(open(pt.pt_tname, O_RDONLY)); /* set new ctrl tty */
  567.         (void)setuid(getuid());        /* shouldn't need this */
  568.         if (!(shell = getenv("SHELL")))
  569.             shell = "/bin/sh";
  570.         (void)dup2(pt.pt_tfd, 0);
  571.         (void)dup2(0, 1);
  572.         (void)dup2(0, 2);
  573.         for (fd=3; fd < nfds; fd++)
  574.             (void)close(fd);
  575.         tmode(0);    /* HACK! */
  576.         execl(shell, shell, (char *)0);
  577.         _exit(1);
  578.     }
  579.  
  580.     (void)close(pt.pt_tfd);
  581.     return(0);
  582. }
  583.  
  584. tmode(f)
  585. {
  586.     static struct sgttyb ostty, nstty;
  587.     static struct tchars otchars, ntchars;
  588.     static struct ltchars oltchars, nltchars;
  589.     static int olmode, nlmode;
  590.     static saved;
  591.  
  592.     /*
  593.      * This routine either saves the current terminal modes and then
  594.      * sets up the terminal line or resets the terminal modes (depending
  595.      * upon the value of "f").  The terminal line is used in "cbreak"
  596.      * mode with all special characters except XON/XOFF disabled.  The
  597.      * hated (by me) LDECCTQ mode is required for the Macintosh to
  598.      * handle flow control properly.
  599.      */
  600.  
  601.     if (f == 1) {
  602.         if (ioctl(0, (int)TIOCGETP, (char *)&ostty) < 0) {
  603.             perror("ioctl((int)TIOCGETP)");
  604.             done(1);
  605.         }
  606.         if (ioctl(0, (int)TIOCGETC, (char *)&otchars) < 0) {
  607.             perror("ioctl((int)TIOCGETC)");
  608.             done(1);
  609.         }
  610.         if (ioctl(0, (int)TIOCGLTC, (char *)&oltchars) < 0) {
  611.             perror("ioctl((int)TIOCGLTC)");
  612.             done(1);
  613.         }
  614.         if (ioctl(0, (int)TIOCLGET, (char *)&olmode) < 0) {
  615.             perror("ioctl((int)TIOCLGET)");
  616.             done(1);
  617.         }
  618.         nstty = ostty;
  619.         nstty.sg_erase = nstty.sg_kill = -1;
  620.         nstty.sg_flags |= CBREAK|TANDEM;
  621.         nstty.sg_flags &= ~(RAW|CRMOD|ECHO|LCASE|XTABS);
  622.         ntchars.t_intrc = ntchars.t_quitc = -1;
  623.         ntchars.t_eofc = ntchars.t_brkc = -1;
  624.         ntchars.t_startc = XON;
  625.         ntchars.t_stopc = XOFF;
  626.         nltchars.t_suspc = nltchars.t_dsuspc = -1;
  627.         nltchars.t_rprntc = nltchars.t_flushc = -1;
  628.         nltchars.t_werasc = nltchars.t_lnextc = -1;
  629.         nlmode = olmode | LDECCTQ;
  630.         if (ioctl(0, (int)TIOCSETN, (char *)&nstty) < 0) {
  631.             perror("ioctl((int)TIOCSETN)");
  632.             done(1);
  633.         }
  634.         if (ioctl(0, (int)TIOCSETC, (char *)&ntchars) < 0) {
  635.             perror("ioctl((int)TIOCSETC");
  636.             done(1);
  637.         }
  638.         if (ioctl(0, (int)TIOCSLTC, (char *)&nltchars) < 0) {
  639.             perror("ioctl((int)TIOCSLTC");
  640.             done(1);
  641.         }
  642.         if (ioctl(0, (int)TIOCLSET, (char *)&nlmode) < 0) {
  643.             perror("ioctl((int)TIOCLSET)");
  644.             done(1);
  645.         }
  646.         saved = 1;
  647.     } else if (saved) {
  648.         (void)ioctl(0, (int)TIOCSETP, (char *)&ostty);
  649.         (void)ioctl(0, (int)TIOCSETC, (char *)&otchars);
  650.         (void)ioctl(0, (int)TIOCSLTC, (char *)&oltchars);
  651.         (void)ioctl(0, (int)TIOCLSET, (char *)&olmode);
  652.     }
  653. }
  654.  
  655. done(s)
  656. {
  657.     register fildes_t fd;
  658.  
  659.     /*
  660.      * Clean up and exit.  It is overkill to close all of the file
  661.      * descriptors, but it causes no harm.  After we are sure that
  662.      * our UNIX-domain network connection is closed we remove the
  663.      * entry that it created (as a side effect) in the filesystem.
  664.      *
  665.      * We also restore the terminal modes.
  666.      */
  667.     
  668.     /*xmitcmd(CB_FN_MAINT|CB_MF_EXIT);*/
  669.     for (fd=3; fd < nfds; fd++)
  670.         (void)close(fd);
  671.     (void)unlink(portal);
  672.     tmode(0);
  673.     exit(s);
  674. }
  675.  
  676. cwait()
  677. {
  678.     union wait status;
  679.     struct rusage rusage;
  680.  
  681.     /*
  682.      * Collect dead children.  We don't use the information that
  683.      * wait3() returns.  (Someday we might.)
  684.      */
  685.     
  686.     while (wait3(&status, WNOHANG, &rusage) > 0)
  687.         ;
  688. }
  689.  
  690.  
  691. static char *earray[MAXENV+1];
  692.  
  693. setenv(env)
  694. char **env;
  695. {
  696.     register char **ep1, **ep2, *cp;
  697.     char **ep3;
  698.     extern char **environ;
  699.  
  700.  
  701.     /*
  702.      * Merge the set of environment strings in "env" into the
  703.      * environment.
  704.      */
  705.  
  706.     /*
  707.      * The first time through, copy the environment from its
  708.      * original location to the array "earray".  This makes it a
  709.      * little easier to change things.
  710.      */
  711.  
  712.     if (environ != earray){
  713.         ep1=environ;
  714.         ep2=earray;
  715.         while(*ep1 && ep2 <= earray+MAXENV)
  716.             *ep2++ = *ep1++;
  717.         *ep2++ = NULL;
  718.         environ = earray;
  719.     }
  720.  
  721.  
  722.     /*
  723.      * If "env" is non-NULL, it points to a list of new items to
  724.      * be added to the environment.  These replace existing items
  725.      * with the same name.
  726.      */
  727.  
  728.     if (env){
  729.         for(ep1=env; *ep1; ep1++){
  730.             for(ep2=environ; *ep2; ep2++)
  731.                 if (!envcmp(*ep1, *ep2))
  732.                     break;
  733.             if (ep2 < earray+MAXENV) {
  734.                 if (!*ep2)
  735.                     ep2[1] = NULL;
  736.                 *ep2 = *ep1;
  737.             }
  738.         }
  739.     }
  740.  
  741.  
  742.     /* Finally, use an insertion sort to put things in order. */
  743.  
  744.     for(ep1=environ+1; cp = *ep1; ep1++){
  745.         for(ep2=environ; ep2 < ep1; ep2++)
  746.             if (envcmp(*ep1, *ep2) < 0)
  747.                 break;
  748.         ep3 = ep2;
  749.         for(ep2=ep1; ep2 > ep3; ep2--)
  750.             ep2[0] = ep2[-1];
  751.         *ep2 = cp;
  752.     }
  753. }
  754.  
  755.  
  756. static
  757. envcmp(e1, e2)
  758. register char *e1, *e2;
  759. {
  760.     register d;
  761.  
  762.     do {
  763.         if (d = *e1 - *e2++)
  764.             return(d);
  765.     } while(*e1 && *e1++ != '=');
  766.     return(0);
  767. }
  768.