text: /* SUPDUP Server
   *
   *	Written Jan. 84 by David Bridgham.  The organization and some of
   *  the code was taken from the telnet server written by Berkeley for 4.2.
   */
  
  /* Hacked by Mly to clarify ITS ITP July 1987
   * Hacked by Mly 29-Aug-87 to nuke stupid auto_right_margin lossage
   * Hacked by Mly 2-Sep-87 to send params as "4;" and "146;" rather than
   *  "\004" and "\222" to avoid stupid un*x 8-bit-and-control-d non-transparency
   * Hacked by wesommer@athena.mit.edu 25-Jan-88 to use winning 4.3BSD
   *  -p flag to /bin/login and to do TIOCSWINSZ stuff.
   *  #ifdef TTYLOC around ttyloc-hacking parts
   */
  
  /* The magic number #o176 which appears in this file is the difference
   * between the real Supdup TDxxx codes and the kludge codes which appear
   * in the termcap/terminfo descriptions (which are needed because bloody
   * un*x deals incompetently with chars with the #o200 bit on
   * (as well as with #o004.  Cretins.)
   * Thus, for example, we have %TDMV0 = #o217, with "cm=\177\021%d;%d;:"
   * in the termcap entry; (- #o 217 #o021) => #o176
   */
  
  /* #define TERMINFO 1 */	/* Define if want terminfo support. */
  				/* there should be a TERMCAP too */
  
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <sys/wait.h>
  #include <sys/file.h>
  
  #include <netinet/in.h>
  
  /* #include <arpa/telnet.h> */
  #include "supdup.h"
  
  #include <stdio.h>
  #include <signal.h>
  #include <errno.h>
  #include <sgtty.h>
  #include <netdb.h>
  
  #ifdef TERMINFO
  # include <term.h>
  # undef CUR
  # define CUR
  #endif /* TERMINFO */
  
  #ifndef BANNER
  #define BANNER	"MIT Artificial Intelligence Laboratory 4.2 BSD UNIX (%s)\207%s"
  #endif
  
  #ifndef SBANNER
  #define	SBANNER	"%s SUPDUP from %s"
  #endif
  
  /* * I/O data buffers, pointers, and counters. */
  unsigned char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
  unsigned char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
  unsigned char netibuf[BUFSIZ], *netip = netibuf;
  unsigned char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
  int pcc, ncc;
  
  /* Filedesc for stream connected to local un*x pty. */
  int pty;
  /* Filedesc for stream connected to user (via internet) */
  int net;
  struct	sockaddr_in sin = { AF_INET };
  int reapchild ();
  
  char pty_name[] = "/dev/ptyq0";
  #ifdef TTYLOC
  char ttyloc[64];
  #endif /* TTYLOC */
  
  extern char **environ;
  extern int errno;
  
  
  /* Current cursor position */
  int currcol, currline;
  
  #ifndef TERMINFO /* terminfo defines these itself */
  /* Number of columns, lines of virtual terminal */
  int columns, lines;
  #endif
  
  
  #ifndef DEBUG
  #define DPRINTF(a, b, c) {}
  #else /* DEBUG */
  
  #define DPRINTF(a, b, c) \
    if (debug_output) \
       { fprintf (debug_output, (a), (b), (c)); fflush (debug_output); }
  
  FILE *debug_output = 0;
  void
  open_debug_output ()
  {
    debug_output = fopen ("/supdupd.debug", "w");
    if (debug_output < 0)
      {
        fprintf (stderr, "Couldn't open \"/supdupd.debug\"\n");
        debug_output = 0;
      }
  
    DPRINTF ("Starting up supdupd...\n",0,0);
  }
  #endif /* DEBUG */
  
  #if 0
  /* this routine is used for debugging only */
  echo_char (c)
       unsigned char c;
  {
    static int count = 0;
  
    if (count++ > 16)
      {
        printf ("\n");
        count = 0;
      }
    if (c >= '\177') printf ("\\%o ", c);
    else if (c < '\041') printf ("\\%o ", c);
    else printf ("%c ", c);
    fflush (stdout);
  }
  #endif /* 0 */
  
  
  #ifdef DAEMON
  main (argc, argv)
       int argc;
       char *argv[];
  {
    int s, pid, options;
    struct servent *sp;
  
  #ifdef DEBUG
    open_debug_output ();
  #endif /* DEBUG */
  
    sp = getservbyname ("supdup", "tcp");
    if (sp == 0)
      {
        fprintf (stderr, "supdupd: tcp/supdup: unknown service\n");
        exit (1);
      }
    sin.sin_port = sp->s_port;
    argc--, argv++;
    if (argc > 0 && !strcmp (*argv, "-d"))
      {
        options |= SO_DEBUG;
        argc--, argv++;
      }
    if (argc > 0)
      {
        sin.sin_port = atoi (*argv);
        if (sin.sin_port <= 0)
          {
            fprintf (stderr, "supdupd: %s: bad port #\n", *argv);
            exit (1);
          }
        sin.sin_port = htons ((u_short)sin.sin_port);
      }
  #ifndef DEBUG
    if (fork ())
      exit (0);
    for (s = 0; s < 10; s++)
      (void) close (s);
    (void) open ("/", 0);
    (void) dup2 (0, 1);
    (void) dup2 (0, 2);
    {
      int tt = open ("/dev/tty", 2);
      if (tt > 0)
        {
          ioctl (tt, TIOCNOTTY, 0);
          close (tt);
        }
    }
  #endif /* DEBUG */
  again:
    s = socket (AF_INET, SOCK_STREAM, 0, 0);
    if (s < 0)
      {
        perror ("supdupd: socket");;
        sleep (5);
        goto again;
      }
    if (options & SO_DEBUG)
      if (setsockopt (s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
        perror ("telnetd: setsockopt (SO_DEBUG)");
    if (setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
      perror ("supdupd: setsockopt (SO_KEEPALIVE)");
    while (bind (s, (caddr_t) &sin, sizeof (sin), 0) < 0)
      {
        perror ("supdupd: bind");
        sleep (5);
      }
    signal (SIGCHLD, reapchild);
    listen (s, 10);
    for (;;)
      {
        struct sockaddr_in from;
        int s2, fromlen = sizeof (from);
  
        s2 = accept (s, (caddr_t)&from, &fromlen);
        if (s2 < 0)
          {
            if (errno == EINTR)
              continue;
            perror ("supdupd: accept");
            sleep (1);
            continue;
          }
        if ((pid = fork ()) < 0)
          printf ("Out of processes\n");
        else if (pid == 0)
          {
            signal (SIGCHLD, SIG_DFL);
            doit (s2, &from);
          }
        close (s2);
      }
    /*NOTREACHED*/
  }
  
  #else /* not DAEMON */
  main (argc, argv)
       int argc;
       char *argv[];
  {
    struct sockaddr_in from;
    int fromlen;
  
  #ifdef DEBUG
    open_debug_output ();
  #endif /* DEBUG */
    fromlen = sizeof (from);
    if (getpeername (0, &from, &fromlen) < 0)
      {
        fprintf (stderr, "%s: ", argv[0]);
        perror ("getpeername");
        _exit (1);
      }
  
  /* MIT */
  #ifdef KEEPALIVE
    if (setsockopt (0, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
      {
        fprintf (stderr, "%s: ", argv[0]);
        perror ("setsockopt (SO_KEEPALIVE)");
      }
  #endif /* KEEPALIVE */
  /* MIT */
    doit (0, &from);
  }
  #endif /* DAEMON */
  
  
  reapchild ()
  {
    union wait status;
  
    while (wait3 (&status, WNOHANG, 0) > 0)
      ;
  }
  
  /* #ifdef TERMCAP */
  char termcap[1024];
  /* #endif /* TERMCAP */
  
  #ifdef TERMINFO
  char terminfo[64];
  #endif /* TERMINFO */
  
  char *envinit[] =
    {
      "TERM=supdup",
  /* #ifdef TERMCAP */
      termcap,
  /* #endif /* TERMCAP */
  #ifdef TERMINFO
      terminfo,
  #endif /* TERMINFO */
      0
    };
  
  int cleanup ();
  
  char *host;
  #ifdef TIOCSWINSZ
  struct winsize ws;
  #endif
  
  /*
   * Get a pty, scan input lines.
   */
  doit (f, who)
       int f;
       struct sockaddr_in *who;
  {
    char *cp = pty_name, *ntoa ();
    int i, p, cc, t;
    struct sgttyb b;
    struct hostent *hp;
  
    for (i = 0; i < 16; i++)
      {
        cp[strlen ("/dev/ptyq")] = "0123456789abcdef"[i];
        p = open (cp, O_RDWR, 0);
        if (p > 0)
          goto gotpty;
      }
    fatal (f, "All network ports in use");
    /*NOTREACHED*/
  gotpty:
    dup2 (f, 0);
    cp[strlen ("/dev/")] = 't';
  #ifdef TTYLOC
    sprintf (ttyloc, "/tmp/ttyq%c.ttyloc", cp[strlen ("/dev/ttyq")]);
    unlink (ttyloc);
  #endif /* TTYLOC */
    t = open ("/dev/tty", O_RDWR, 0);
    if (t >= 0)
      {
        ioctl (t, TIOCNOTTY, 0);
        close (t);
      }
    t = open (cp, O_RDWR, 0);
    if (t < 0)
      fatalperror (f, cp, errno);
    ioctl (t, TIOCGETP, &b);
    /* MIT */
    b.sg_ispeed = B9600;
    b.sg_ospeed = B9600;
    /* MIT */
    b.sg_flags = XTABS | ANYP;      /* punted CRMOD */
    ioctl (t, TIOCSETP, &b);
    ioctl (p, TIOCGETP, &b);
    b.sg_flags &= ~ECHO;
    ioctl (p, TIOCSETP, &b);
    sup_options (f);
  #ifdef TIOCSWINSZ
    ioctl (p, TIOCSWINSZ, &ws);
  #endif /* TIOCSWINSZ */
    hp = gethostbyaddr (&who->sin_addr, sizeof (struct in_addr),
                        who->sin_family);
    if (hp)
      host = hp->h_name;
    else
      host = ntoa (who->sin_addr);
    if ((i = fork ()) < 0)
      fatalperror (f, "fork", errno);
    if (i)
      supdup (f, p);
  
    close (f);
    close (p);
    dup2 (t, 0);
    dup2 (t, 1);
    dup2 (t, 2);
    close (t);
  #ifdef BSD4_3
    /* Winning -p switch passes through environment */
    execle ("/bin/login", "login", "-p", "-h", host, 0, envinit);
  #else
  #ifdef MIT
    /* Local /bin/login at ai.mit.edu machines hacked to pass TERM and TERMCAP */
    execle ("/bin/login", "login", "-h", host, 0, envinit);
    fatalperror (2, "/bin/login", errno);
  #else
    /* Can't use /bin/login as that nukes TERM and TERMCAP.  Cretins! */
    execle ("/usr/etc/supdup-login", "login", "-h", host, 0, envinit);
    fatalperror (2, "/usr/etc/supdup-login", errno);
  #endif /* MIT */
  #endif /* not BSD4_3 */
    /*NOTREACHED*/
  }
  
  fatal (f, msg)
       int f;
       char *msg;
  {
    char buf[BUFSIZ];
  
    (void) sprintf (buf, "supdupd: %s.\r\n", msg);
    (void) write (f, buf, strlen (buf));
    exit (1);
  }
  
  fatalperror (f, msg, errno)
       int f;
       char *msg;
       int errno;
  {
    char buf[BUFSIZ];
    extern char *sys_errlist[];
  
    (void) sprintf (buf, "%s: %s", msg, sys_errlist[errno]);
    fatal (f, buf);
  }
  
  /*
   * Main loop.  Select from pty and network, and
   * hand data to supdup receiver finite state machine.
   */
  supdup (f, p)
  {
    int on = 1;
    char hostname[256];
  
    net = f, pty = p;
    ioctl (f, FIONBIO, &on);
    ioctl (p, FIONBIO, &on);
    signal (SIGTSTP, SIG_IGN);
    signal (SIGCHLD, cleanup);
    mode (ECHO|CRMOD, 0);
  
    /*
     * Print supdup banner.
     */
    gethostname (hostname, sizeof (hostname));
    sprintf (nfrontp, SBANNER, hostname, host);
    nfrontp += strlen (nfrontp);
    *nfrontp++ = TDNOP;
    netflush ();
    sleep (2);
  	
    /*
     * Show banner that getty never gave.
     */
  
    *nfrontp++ = TDCLR;
    sprintf (nfrontp, BANNER, hostname, "");
    currline = 1;
    currcol = 0;
    nfrontp += strlen (nfrontp);
    for (;;)
      {
        int ibits = 0, obits = 0;
        register int c;
  
        /*
         * Never look for input if there's still
         * stuff in the corresponding output buffer
         */
        if ((nfrontp - nbackp) || pcc > 0)
          obits |= (1 << f);
        else
          ibits |= (1 << p);
        if ((pfrontp - pbackp) || ncc > 0)
          obits |= (1 << p);
        else
          ibits |= (1 << f);
        if (ncc < 0 && pcc < 0)
          break;
        select (16, &ibits, &obits, 0, 0);
        if (ibits == 0 && obits == 0)
          {
            sleep (5);
            continue;
          }
  
        /*
         * Something to read from the network...
         */
        if (ibits & (1 << f))
          {
            ncc = read (f, netibuf, BUFSIZ);
            if (ncc < 0 && errno == EWOULDBLOCK)
              ncc = 0;
            else
              {
                if (ncc <= 0)
                  break;
                netip = netibuf;
              }
          }
  
        /*
         * Something to read from the pty...
         */
        if (ibits & (1 << p))
          {
            pcc = read (p, ptyibuf, BUFSIZ);
            if (pcc < 0 && errno == EWOULDBLOCK)
              pcc = 0;
            else
              {
                if (pcc <= 0)
                  break;
                ptyip = ptyibuf;
              }
          }
  
        if (pcc > 0)
          supxmit ();
        if ((obits & (1 << f)) && (nfrontp - nbackp) > 0)
          netflush ();
        if (ncc > 0)
          suprcv ();
        if ((obits & (1 << p)) && (pfrontp - pbackp) > 0)
          ptyflush ();
      }
    cleanup ();
  }
  	
  /* State for xmit fsm */
  #define	XS_DATA		0	/* base state */
  #define	XS_ESCAPE	1	/* supdup commands are escaped in TERMCAP */
  #define	XS_MV0_V	2	/* getting vertical position for TDMV0 */
  #define	XS_MV0_H	4	/* getting horizontal position for TDMV0 */
  #define	XS_CRLF		6	/* got \r looking for \n */
  #define XS_LFCR         7       /* got \n looking for \r */
  #if (TDILP < 8) || (TDDLP < 8) || (TDICP < 8) || (TDDCP < 8)
   you lose!
  #endif
  #define	XS_ILINE	TDILP	/* waiting for number of lines to insert */
  #define	XS_DLINE	TDDLP	/* waiting for number of lines to delete */
  #define	XS_ICHAR	TDICP	/* waiting for number of chars to insert */
  #define	XS_DCHAR	TDDCP	/* waiting for number of chars to delete */
  
  /* Because of Un*x Brain Death(tm)
   * characters with the 200 bit don't go through the terminal driver reliably
   * and \004 won't go through a un*x at all pty (what sort of bozos wrote
   * this sh*t?)
   */
  
  supxmit ()
  {
    static int state = XS_DATA;
    register int c;
    static int piece_o_state, piece_o_state_v;
  
    while (pcc > 0)
      {
        if ((&netobuf[BUFSIZ] - nfrontp) < 4)
          /* Caller will flush pending output */
          return;
        c = *ptyip++ & 0377; pcc--;
        switch (state)
          {
          case XS_DATA:
            switch (c)
              {
              case ITP_ESCAPE:
                *nfrontp++ = c;
                *nfrontp++ = c;
                break;
  
              case KLUDGE_ESCAPE:
                state = XS_ESCAPE;
                break;
  
              case '\007':
                *nfrontp++ = TDBEL;
                break;
  
              case '\t':
                currcol = (currcol + 8) & ~7;
                if (currcol >= columns)
                  currcol = columns;
                *nfrontp++ = TDMV0;
                *nfrontp++ = currline;
                *nfrontp++ = currcol;
                break;
  
              case '\b':
                currcol--;
                if (currcol < 0)
                  currcol = 0;
                *nfrontp++ = TDMV0;
                *nfrontp++ = currline;
                *nfrontp++ = currcol;
                break;
  
              case '\r':
                DPRINTF ("\n<<Got a return>>",0,0);
                state = XS_CRLF;
                break;
  
              case '\n':
                DPRINTF ("\n<<Got a newline>>",0,0);
                state = XS_LFCR;
                break;
  
              case '\0':
                /* throw away nulls. Something is being
                 * a twit and padding even though it
                 * is not wanted! */
                /* (Of course, \0 is really a printable MIT Ascii character) */
                break;
  
              default:
                if (currcol < columns)
                  {
                    currcol++;
                    *nfrontp++ = c;
                  }
                else
                  {
                    /* unread-char and do newline */
                    ptyip--; pcc++;
                    do_crlf ();
                  }
                break;
              }
            break;
  
          case XS_ESCAPE:
            c += 0176;
            switch (c)
              {
              case TDCRL:
                state = XS_DATA;
                do_crlf ();
                break;
  
              case TDFS:
                currcol++;
                state = XS_DATA;
                *nfrontp++ = c;
                break;
  
              case TDMV0:
                state = XS_MV0_V;
                piece_o_state = 0;
                piece_o_state_v = 0;
                break;
  
              case TDCLR:
                DPRINTF ("\n<<In TDCLR handler>>",0,0);
                currcol = 0; currline = 0;
                state = XS_DATA;
                *nfrontp++ = c;
                break;
  
              case TDLF:
                /* this is not in the SUPDUP spec */
                /* Move down one row vertically */
                state = XS_DATA;
                if (currline < (lines - 1))
                  currline++;
                *nfrontp++ = TDMV0;
                *nfrontp++ = currline;
                *nfrontp++ = currcol;
                break;
  
              case TDUP:
                state = XS_DATA;
                if (currline > 0) currline--;
                *nfrontp++ = TDMV0;
                *nfrontp++ = currline;
                *nfrontp++ = currcol;
                break;
  
              case TDILP:
              case TDDLP:
              case TDICP:
              case TDDCP:
                state = c;
                piece_o_state = 0;
                break;
  				
              case KLUDGE_ESCAPE:
                /* Really shouldn't get two */
                /* of these but it's happening */
                break;
  
              default:
                state = XS_DATA;
                *nfrontp++ = c;
                break;
  
              }
            break;
  
  
            /* The newline algorithm is as follows:
             *	A \r causes the cursor to be sent to the
             * beginning of the line unless there is a \n
             * immediately following on the input.
             *	If the next character is a \n then a
             * %TDCRL is sent.
             *	A lone \n causes the cursor to be moved
             * straight down one line wrapping if necessary
             * or doing a %TDCRL if at the bottom of the screen
             * and it is a scrolling terminal.
             *
             * The CR and NL handling is way too complicated...
             */
          case XS_LFCR:
            {
              DPRINTF ("\n<<In XS_LFCR handler>>",0,0);
              if (c == '\r')
                do_crlf ();
              else
                {
                  /* Got a \012 followed by something other than a \015 */
                  /* Treat the \012 as `move down down one row */
                  if (currline < (lines - 1))
                    {
                      *nfrontp++ = TDMV0;
                      *nfrontp++ = ++currline;
                      *nfrontp++ = currcol;
                    }
                  else if (TOROL)
                    {
                      /* this is not the right thing, but
                       * what else can I do? */
                      currcol = 0;
                      *nfrontp++ = TDCRL;
                    }
                  else
                    {
                      currline = 0;
                      *nfrontp++ = TDMV0;
                      *nfrontp++ = 0;
                      *nfrontp++ = currcol;
                      *nfrontp++ = TDEOL;
                    }
                  ptyip--; pcc++; /* unread-char */
                  DPRINTF ("\n<<LFCR: unreading %c>>", *ptyip,0);
                }
              state = XS_DATA;
              break;
            }
  
          case XS_CRLF:
            {
              DPRINTF ("\n<<In XS_CRLF handler>>",0,0);
              if (c == '\n')
                do_crlf ();
              else
                {
                  /* \013 followed by something other than \010 */
                  /* Treat the \010 as a request to move to start of row */
                  currcol = 0;
                  *nfrontp++ = TDMV0;
                  *nfrontp++ = currline;
                  *nfrontp++ = 0;
                  ptyip--; pcc++; /* unread-char */
                  DPRINTF ("\n<<CRLF: unreading %c>>", *ptyip,0);
                }
              state = XS_DATA;
              break;
            }
  
          case XS_MV0_V:
          case XS_MV0_H:
          case TDILP:
          case TDDLP:
          case TDICP:
          case TDDCP:
            {
              DPRINTF ("\n<<arg:%d+%c>>", piece_o_state, c);
              if (c >= '0' && c <= '9')
                {
                  piece_o_state = piece_o_state * 10 + c - '0';
                  if (piece_o_state >= lines && piece_o_state >= columns)
                    {
                      state = XS_DATA;
                      break;
                    }
                }
              else if (c != ';')
                {
                  state = XS_DATA;
                  break;
                }
              else switch (state)
                {
                case XS_MV0_V:
                  piece_o_state_v = piece_o_state;
                  piece_o_state = 0;
                  state = XS_MV0_H;
                  break;
                case XS_MV0_H:
                  currline = piece_o_state_v;
                  currcol = piece_o_state;
                  state = XS_DATA;
                  *nfrontp++ = TDMV0;
                  *nfrontp++ = currline;
                  *nfrontp++ = currcol;
                  break;
                default: /* TDILP, TDDLP, RDICP, TDDCP */
                  while (pcc > 4 && (ptyip[0] & 0377) == KLUDGE_ESCAPE &&
                         (ptyip[1] & 0377) == state - 0176 &&
                         (ptyip[2] & 0377) == '1' &&
                         (ptyip[3] & 0377) == ';')
                    /* Merge successive single insert/delete-line/char requests
                       (pander to clients who don't understand TERMCAP
                       AL/DL (and less-importantly IC/DC) capabilities) */
                    {
                      piece_o_state++;
                      pcc -= 4;
                      ptyip += 4;
                    }
                  *nfrontp++ = state;
                  *nfrontp++ = piece_o_state;
                  if (state == TDILP || state == TDDLP)
                    {
                      /* Just to be on the safe side */
                      currcol = 0;
                      *nfrontp++ = TDMV0;
                      *nfrontp++ = currline;
                      *nfrontp++ = currcol;
                    }
                  state = XS_DATA;
                  break;
                }
              DPRINTF ("\n<<now:%d,%d>>", piece_o_state_v, piece_o_state);
              break;
            }
  
          default:
            state = XS_DATA;
            break;
          }
      }
  }
  
  do_crlf ()
  {
    currcol = 0;
    if (currline < (lines - 1))
      {
        *nfrontp++ = TDCRL;
        currline++;
      }
    else if (TOROL)
      *nfrontp++ = TDCRL;
    else
      {
        currline = 0;
        *nfrontp++ = TDMV0;
        *nfrontp++ = 0;
        *nfrontp++ = 0;
        *nfrontp++ = TDEOL;
      }
  }
  
  
  /*
   * State for recv fsm
   */
  
  /* base state */
  #define RS_DATA		0
  
  /* recieved ITP_ESCAPE (034) Waiting for `m' */
  #define	RS_ITP_ESCAPE	1
  /* received ITP_ESCAPE, `m'>#o100
     Waiting for `n'.
     Char will be (+ (* (- `m' #o100) #o200) `n') */
  #define RS_BUCKY	2
  
  /* Recived ITP_ESCAPE, ITP_FLOW_CONTROL_INCREASE
     Ignore next char, since un*x can't hack real, winning, flow control */
  #define RS_FLOW_CONTROL_INCREASE 3
  
  /* Recived ITP_ESCAPE, ITP_CURSORPOS
     Waiting for `row' */
  #define RS_CURSORPOS_1	4
  /* Recived ITP_ESCAPE, ITP_CURSORPOS, `row'
     Waiting for `column' */
  #define RS_CURSORPOS_2	5
  
  /* received SUPDUP_ESCAPE (#o300)
     Waiting for `m' */
  #define	RS_SUPDUP_ESCAPE 6
  
  /* received SUPDUP_ESCAPE, SUPDUP_LOCATION.
     Now receiving 000-terminated
     location string */
  #define	RS_LOCATION	7
  
  suprcv ()
  {
    register int	c;
  
    static int state = RS_DATA;
    static int piece_o_state = 69;
  
    while ((ncc > 0) &&
           /* Caller must flush pending output */
           ((&ptyobuf[BUFSIZ] - pfrontp) > 3))
      {
        c = *netip++ & 0377; ncc--;
        switch (state)
          {
          case RS_DATA:
            {
              if (c == SUPDUP_ESCAPE)
                state = RS_SUPDUP_ESCAPE;
              else if (c == ITP_ESCAPE)
                state = RS_ITP_ESCAPE;
              else
                *pfrontp++ = c;
              break;
            }
  
          case RS_ITP_ESCAPE:
            {
              if (c & 0100)
                {
                  piece_o_state = c;
                  state = RS_BUCKY;
                }
              else switch (c)
                {
                case ITP_ESCAPE:
                  *pfrontp++ = c;
                  state = RS_DATA;
                  break;
                case ITP_PIATY:
                  /* %piaty --- user's screen is munged.
                     Cretinous unix provides no way to use this.
                     Conceivably we could send through a c-L character (barf!)
                     */
                case ITP_STOP_OUTPUT:
                case ITP_RESTART_OUTPUT:
                case ITP_FLOW_CONTROL_START:
                case ITP_FLOW_CONTROL_END:
                  /* ignore it */
                  state = RS_DATA;
                  break;
                case ITP_FLOW_CONTROL_INCREASE:
                  state = RS_FLOW_CONTROL_INCREASE;
                  break;
                case ITP_CURSORPOS:
                  /* read cursorpos -- v then h */
                  state = RS_CURSORPOS_1;
                  break;
                default:          /* huh?! */
                  state = RS_DATA;
                  break;
                }
            break;
            }
  
          case RS_BUCKY:
            {
              register int bucky;
  
              bucky = ITP_CHAR (piece_o_state, c);
              /* unix supdup server uses 0237 (CTL_PREFIX) as a control escape.
               * c-a	001
               * m-a	341
               * c-m-a	201
               * c-1	237 061
               * m-1	261
               * c-m-1	237 261
               * c-m-_	237 237
               */
  
              switch (bucky)
                {
                case SUPDUP_HELP_KEY:
                  c = 'h' & 037; /* hack for Help key */
                  break;
                case SUPDUP_ESCAPE_KEY:
                  c = '\033';
                  break;
                case SUPDUP_SUSPEND_KEY:
                  c = 'z' & 037;
                  break;
                case SUPDUP_CLEAR_KEY:
                  c = -1;
                  break;
                default:
                  c = bucky & 0177;
                  if (bucky & ITP_TOP)
                    c &= 037;	/* This is pretty random -- Mly */
                  if (bucky & ITP_CTL)
                    {
                      if ((c >= 'a' && c <= 'z') ||
                          (c >= '@' && c <= 'Z') ||
                          (c >= '\\' && c <= '_'))
                        c &= 037;
                      else
                        *pfrontp++ = CTL_PREFIX;
                    }
                  if (bucky & ITP_MTA)
                    c |= 0200;
                  if (c == CTL_PREFIX)
                    /* Double control prefix to send 0237 (200 + ^_)
                       through pseudo-transparently */
                    *pfrontp++ = c;
                }
              if (c >= 0)
                *pfrontp++ = c;
              state = RS_DATA;
              break;
            }
  
          case RS_FLOW_CONTROL_INCREASE:
            /* Ignore it -- we can't hack this */
            state = RS_DATA;
            break;
  
          case RS_CURSORPOS_1:
            piece_o_state = c;
            state = RS_CURSORPOS_2;
            break;
  
          case RS_CURSORPOS_2:
            {
              /* read cursorpos -- v then h */
              if ((piece_o_state < lines) && (c < columns))
                {
                  currcol = piece_o_state;
                  currline = c;
                }
              state = RS_DATA;
            }
  
  
          case RS_SUPDUP_ESCAPE:
            switch (c)
              {
              case SUPDUP_LOGOUT:
                cleanup ();
  
              case SUPDUP_LOCATION:
                state = RS_LOCATION;
                break;
  
              default:            /* WTF? */
                state = RS_DATA;
                break;
              }
            break;
  
          case RS_LOCATION:
            {
              static char buf[BUFSIZ];
              static char *p = &buf[0];
              int f;
  
              if (c != '\000')
                {
                  if (p - buf < BUFSIZ)
                    *p++ = c;
                }
              else
                {
  #ifdef TTYLOC
                  f = creat (ttyloc, 0644);
                  if (f > 0)
                    {
                      (void) write (f, buf, p - buf);
                      (void) close (f);
                    }
  #endif /* TTYLOC */
                  state = RS_DATA;
                }
            }
            break;
          }
      }
  }
  
  mode (on, off)
       int on, off;
  {
    struct sgttyb b;
  
    ptyflush ();
    ioctl (pty, TIOCGETP, &b);
    b.sg_flags |= on;
    b.sg_flags &= ~off;
    ioctl (pty, TIOCSETP, &b);
  }
  
  ptyflush ()
  {
    int n;
  
    if ((n = pfrontp - pbackp) > 0)
      n = write (pty, pbackp, n);
    if (n < 0)
      return;
    pbackp += n;
    if (pbackp == pfrontp)
      pbackp = pfrontp = ptyobuf;
  }
  
  netflush ()
  {
    int n;
  
    if ((n = nfrontp - nbackp) > 0)
      {
        n = write (net, nbackp, n);
  #ifdef DEBUG
        if (debug_output)
          {
            fflush (debug_output);
            (void) write (fileno (debug_output), nbackp, n);
          }
  #endif /* DEBUG */
      }
    if (n < 0)
      {
        if (errno == EWOULDBLOCK)
          return;
        /* should blow this guy away... */
        return;
      }
    nbackp += n;
    if (nbackp == nfrontp)
      nbackp = nfrontp = netobuf;
  }
  
  cleanup ()
  {
  #ifdef TERMINFO
    clean_terminfo ();
  #endif /* TERMINFO */
  	
    rmut ();
  #ifdef TTYLOC
    unlink (ttyloc);
  #endif /* TTYLOC */
    vhangup ();                   /* XXX */
    shutdown (net, 2);
    kill (0, SIGKILL);
    exit (1);
  }
  
  #ifdef	TERMINFO
  /* Cleans up the files created for the TERMINFO stuff.
   */
  clean_terminfo ()
  {
    char	dir[128];
    int	pid;
  
    pid = getpid ();
    sprintf (dir, "/tmp/%d/s/supdup", pid);
    unlink (dir);
    sprintf (dir, "/tmp/%d/s", pid);
    rmdir (dir);
    sprintf (dir, "/tmp/%d", pid);
    rmdir (dir);
  }
  
  #endif /* TERMINFO */
  
  
  #include <utmp.h>
  
  struct	utmp wtmp;
  char	wtmpf[]	= "/usr/adm/wtmp";
  char	utmp[] = "/etc/utmp";
  #define SCPYN(a, b)	strncpy (a, b, sizeof (a))
  #define SCMPN(a, b)	strncmp (a, b, sizeof (a))
  
  rmut ()
  {
    register f;
    int found = 0;
  
    f = open (utmp, O_RDWR, 0);
    if (f >= 0)
      {
        while (sizeof (wtmp) == read (f, (char *) &wtmp, sizeof (wtmp)))
          {
            if (SCMPN (wtmp.ut_line, pty_name + strlen ("/dev/")) ||
                wtmp.ut_name[0] == 0)
              continue;
            lseek (f, - (long)sizeof (wtmp), 1);
            SCPYN (wtmp.ut_name, "");
            SCPYN (wtmp.ut_host, "");
            time (&wtmp.ut_time);
            write (f, (char *) &wtmp, sizeof (wtmp));
            found++;
          }
        close (f);
      }
    if (found)
      {
        f = open (wtmpf, O_WRONLY, 0);
        if (f >= 0)
          {
            SCPYN (wtmp.ut_line, pty_name + strlen ("/dev/"));
            SCPYN (wtmp.ut_name, "");
            SCPYN (wtmp.ut_host, "");
            time (&wtmp.ut_time);
            lseek (f, (long) 0, 2);
            write (f, (char *) &wtmp, sizeof (wtmp));
            close (f);
          }
      }
    chmod (pty_name, 0666);
    chown (pty_name, 0, 0);
    pty_name[strlen ("/dev/")] = 'p';
    chmod (pty_name, 0666);
    chown (pty_name, 0, 0);
  }
  
  /*
   * Convert network-format internet address
   * to base 256 d.d.d.d representation.
   */
  char *
  ntoa (in)
       struct in_addr in;
  {
    static char b[18];
    register char *p;
  
    p = (char *) &in;
  #define	UC(b)	(((int)b)&0xff)
    sprintf (b, "%d.%d.%d.%d", UC (p[0]), UC (p[1]), UC (p[2]), UC (p[3]));
    return (b);
  }
  
  /* Read the 36 bit options from the net setting variables and create
   * the TERMCAP environment variable.
   */
  sup_options (net)
       int net;
  {
    char temp[6];
    int count;
    int tcmxh, tcmxv;
  
    read (net, temp, 6);          /* Read count */
    count = -((-1 << 6) | temp[2]);
    if (count--)
      {
        read (net, temp, 6);      /* Discard TCTYP */
        if (count--)
          {
            read (net, ttyopt, 6);
            if (count--)
              {
                read (net, temp, 6);
                tcmxv = (temp[5] & 0377) | ((temp[4] & 0377) << 6);
  #if 0 /* No longer needed, since we now encode positions using %d */
                /* Un*x braindeath (tm) */
                if (tcmxv >= 128 - 5) tcmxv = 128 - 5 - 1;
  #endif 0
                if (count--)
                  {
                    read (net, temp, 6);
                    /* The 1 + is because supdup spec transmits 1 - columns */
                    tcmxh = 1 + (temp[5] & 0377) | ((temp[4] & 0377) << 6);
  #if 0
                    /* Un*x braindeath (tm) */
                    if (tcmxh >= 128 - 5) tcmxh = 128 - 5 - 1;
  #endif 0
                    if (count--)
                      {
                        read (net, temp, 6);
                        ttyrol = temp[5] & 077;
                        while (count--)
                          read (net, temp, 6);
                      }
                  }
              }
          }
      }
  
    lines = tcmxv;
    columns = tcmxh;
  
  #ifdef TIOCSWINSZ
    ws.ws_row = lines;
    ws.ws_col = columns;
    ws.ws_xpixel = columns * 6;
    ws.ws_ypixel = lines * 10;
  #endif /* TIOCSWINSZ */
  
    sprintf (termcap, "TERMCAP=SD|supdup|SUPDUP virtual terminal:co#%d:li#%d:",
             columns, lines);
    strcat (termcap, "vb=\\177\\023:nd=\\177\\020:MT:");
    strcat (termcap, "cl=\\177\\022:so=\\177\\031:se=\\177\\032:pt:");
    if (TOERS)
      strcat (termcap, "ce=\\177\\005:ec=\\177\\006:cd=\\177\\004:");
    if (TOMVB)
      strcat (termcap, "bs:");
    if (TOOVR)
      strcat (termcap, "os:");
    if (TOMVU)
      {
        strcat (termcap, "up=\\177\\041:cm=\\177\\021%d;%d;:");
        strcat (termcap, "do=\\177\\014:nl=\\177\\014:");
      }
    if (TOLID)
      {
        strcat (termcap, "al=\\177\\0251;:dl=\\177\\0261;:");
        strcat (termcap, "AL=\\177\\025%d;:DL=\\177\\026%d;:");
      }
    if (TOCID)
      {
        strcat (termcap, "mi:im=:ei=:dm=:ed=:");
        strcat (termcap, "ic=\\177\\0271;:dc=\\177\\0301;:");
        strcat (termcap, "IC=\\177\\027%d;:DC=\\177\\030%d;:");
      }
    if (!TOROL)
      strcat (termcap, "ns:");
  /*if (TOFCI) -- I don't think that always doing this can hurt much */
      strcat (termcap, "km:");
  
    /* We only wrap if a character is written when we are
       -past- the last column.  This way we avoid brain-dead unwanted
       scrolling, whilst still allowing un*x' completely brain lack of
       terminal-handling (which assumes that directly sending an
       arbitrary-length string containing no newlines to the terminal
       will result in the user seeing all of it.
       As PGS says, ``filthy damned unix pinheads.'' */
    /* strcat (termcap, "am:"); We don't lose in this way any more. */
    /* Not-widely-known flag, which says that writing in the last column of
       the last line doesn't cause idiotic scrolling */
    strcat (termcap, "LP:");
    /* Not-widely-known flag, which says not to use idiotic
       c-s/c-q so-called ``flow control'' under any circumstances. */
    strcat (termcap, "NF:");
  
  #ifdef TERMINFO
    init_terminfo ();
  #endif /* TERMINFO */
  }
  
  
  #ifdef TERMINFO
  
  char names[] = "supdup|sd|supdup virtual terminal";
  #define	MAGIC_NUM	0432
  
  struct head
    {
      short	magic;
      short	name_len;
      short	bools_len;
      short	nums_len;
      short	str_len;
      short	strtab_len;
    } header;
  
  unsigned char Booleans[BOOLCOUNT + 1];
  short	Numbers[NUMCOUNT];
  short	Strings[STRCOUNT];
  char	String_Table[] = {
  #define	FLASH_SCREEN	0
  	'\177', '\023', '\0',
  #define	CURSOR_RIGHT FLASH_SCREEN + 3
  	'\177', '\020', '\0',
  #define	CLEAR_SCREEN CURSOR_RIGHT + 3
  	'\177', '\022', '\0',
  #define	ENTER_STANDOUT_MODE CLEAR_SCREEN + 3
  	'\177', '\031', '\0',
  #define	EXIT_STANDOUT_MODE ENTER_STANDOUT_MODE + 3
  	'\177', '\032', '\0',
  #define	CLR_EOL	EXIT_STANDOUT_MODE + 3
  	'\177', '\005', '\0',
  #define	CLR_EOS	CLR_EOL + 3
  	'\177', '\004', '\0',
  #define	CURSOR_UP CLR_EOS + 3
  	'\177', '\041', '\0',
  #define	CURSOR_DOWN CURSOR_UP + 3
  	'\177', '\014', '\0',
  #define	NEWLINE	CURSOR_DOWN + 3
  	'\177', '\011', '\0',	/* this does cursor_down (\014) in termcap */
  #define	CURSOR_ADDRESS NEWLINE + 3
          /* %p1%d;%p2%d; */
  	'\177', '\021',
          '%', 'p', '1', '%', 'd', ';', '%', 'p', '2', '%', 'd', ';', '\0',
  #define	INSERT_LINE CURSOR_ADDRESS + 15
  	'\177', '\025', '1', ';', '\0',
  #define	DELETE_LINE INSERT_LINE + 5
  	'\177', '\026', '1', ';', '\0',
  #define	PARM_INSERT_LINE DELETE_LINE + 5
  	'\177', '\025', '%', 'p', '1', '%', 'd', ';', '\0',
  #define	PARM_DELETE_LINE PARM_INSERT_LINE + 9
  	'\177', '\026', '%', 'p', '1', '%', 'd', ';', '\0',
  #define	CURSOR_LEFT PARM_DELETE_LINE + 9
          /* >> BARF!! */
  	'', '\0',
  #define	DELETE_CHARACTER CURSOR_LEFT + 2
  	'\177', '\030', '1', ';', '\0',
  #define	INSERT_CHARACTER DELETE_CHARACTER + 5
  	'\177', '\027', '1', ';', '\0',
  #define	PARM_DCH INSERT_CHARACTER + 5
  	'\177', '\030', '%', 'p', '1', '%', 'd', ';', '\0',
  #define	PARM_ICH PARM_DCH + 9
  	'\177', '\027', '%', 'p', '1', '%', 'd', ';', '\0',
  #define	STRTABLEN PARM_ICH + 9 + 10
  };
  
  
  /* This routine sets up the files and environment variables for using the
   * TERMINFO data base.
   */
  init_terminfo ()
  {
    register int i;
    int	file;
    int	pid;
    char	directory[128];
  
    pid = getpid ();
    sprintf (directory, "/tmp/%d", pid);
    mkdir (directory, 0777);
    sprintf (terminfo, "TERMINFO=%s", directory);
    strcat (directory, "/s");
    mkdir (directory, 0777);
    strcat (directory, "/supdup");
  
    file = open (directory, O_WRONLY | O_CREAT, 0777);
    if (file <= 0) cleanup ();
  
    for (i = 0; i < BOOLCOUNT + 1; i++)
      Booleans[i] = 0;
    for (i = 0; i < NUMCOUNT; i++)
      Numbers[i] = -1;
    for (i = 0; i < STRCOUNT; i++)
      Strings[i] = -1;
  
    header.magic = MAGIC_NUM;
    header.name_len = strlen (names) + 1;
    header.bools_len = BOOLCOUNT;
    header.nums_len = NUMCOUNT;
    header.str_len = STRCOUNT;
    header.strtab_len = STRTABLEN;
  
    lines = tcmxv;
    columns = tcmxh;
    auto_right_margin = 0;
    flash_screen = FLASH_SCREEN;
    cursor_right = CURSOR_RIGHT;
    clear_screen = CLEAR_SCREEN;
    enter_standout_mode = ENTER_STANDOUT_MODE;
    exit_standout_mode = EXIT_STANDOUT_MODE;
    init_tabs = 8;
    if (TOERS)
      {
        clr_eol = CLR_EOL;
        clr_eos = CLR_EOS;
      }
    if (TOMVB)
      cursor_left = CURSOR_LEFT;
    if (TOOVR)
      over_strike = 1;
    if (TOMVU)
      {
        cursor_up = CURSOR_UP;
        cursor_down = CURSOR_DOWN;
        newline = NEWLINE;
        cursor_address = CURSOR_ADDRESS;
      }
    if (TOLID)
      {
        insert_line = INSERT_LINE;
        delete_line = DELETE_LINE;
        parm_insert_line = PARM_INSERT_LINE;
        parm_delete_line = PARM_DELETE_LINE;
      }
    if (TOCID)
      {
        insert_character = INSERT_CHARACTER;
        delete_character = DELETE_CHARACTER;
        parm_dch = PARM_DCH;
        parm_ich = PARM_ICH;
      }
  /*if (TOFCI) -- I don't think that always doing this can hurt much */
      has_meta_key = 1;
  
    write (file, &header, sizeof (struct head));
    write (file, names, header.name_len);
    write (file, Booleans, (header.name_len + header.bools_len) & 1
  			  ? header.bools_len + 1
  			  : header.bools_len);
    write (file, Numbers, 2 * header.nums_len);
    write (file, Strings, 2 * header.str_len);
    write (file, String_Table, header.strtab_len + 5);
  	
  }
  
  #endif /* TERMINFO */
  