date and time created 86/11/16 20:44:49 by minshall
authorGregory Minshall <minshall@ucbvax.Berkeley.EDU>
Mon, 17 Nov 1986 12:44:49 +0000 (04:44 -0800)
committerGregory Minshall <minshall@ucbvax.Berkeley.EDU>
Mon, 17 Nov 1986 12:44:49 +0000 (04:44 -0800)
SCCS-vsn: usr.bin/tn3270/telnet.c 1.1

usr/src/usr.bin/tn3270/telnet.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/tn3270/telnet.c b/usr/src/usr.bin/tn3270/telnet.c
new file mode 100644 (file)
index 0000000..457ab49
--- /dev/null
@@ -0,0 +1,3246 @@
+/*
+ *     Copyright (c) 1984, 1985, 1986 by the Regents of the
+ *     University of California and by Gregory Glenn Minshall.
+ *
+ *     Permission to use, copy, modify, and distribute these
+ *     programs and their documentation for any purpose and
+ *     without fee is hereby granted, provided that this
+ *     copyright and permission appear on all copies and
+ *     supporting documentation, the name of the Regents of
+ *     the University of California not be used in advertising
+ *     or publicity pertaining to distribution of the programs
+ *     without specific prior permission, and notice be given in
+ *     supporting documentation that copying and distribution is
+ *     by permission of the Regents of the University of California
+ *     and by Gregory Glenn Minshall.  Neither the Regents of the
+ *     University of California nor Gregory Glenn Minshall make
+ *     representations about the suitability of this software
+ *     for any purpose.  It is provided "as is" without
+ *     express or implied warranty.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1984, 1985, 1986 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)telnet.c   3.1  10/29/86";
+#endif not lint
+
+/*
+ * User telnet program, modified for use by tn3270.c.
+ *
+ * Many of the FUNCTIONAL changes in this newest version of TELNET
+ * were suggested by Dave Borman of Cray Research, Inc.
+ *
+ * Other changes in the tn3270 side come from Alan Crosswell (Columbia),
+ * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley).
+ *
+ * This code is common between telnet(1c) and tn3270(1c).  There are the
+ * following defines used to generate the various versions:
+ *
+ *     TN3270          -       This is to be linked with tn3270.
+ *
+ *     DEBUG           -       Allow for some extra debugging operations.
+ *
+ *     NOT43           -       Allows the program to compile and run on
+ *                             a 4.2BSD system.
+ *
+ *     PUTCHAR         -       Within tn3270, on a NOT43 system,
+ *                             allows the use of the 4.3 curses
+ *                             (greater speed updating the screen).
+ *                             You need the 4.3 curses for this to work.
+ *
+ *     FD_SETSIZE      -       On whichever system, if this isn't defined,
+ *                             we patch over the FD_SET, etc., macros with
+ *                             some homebrewed ones.
+ *
+ *     SO_OOBINLINE    -       This is a socket option which we would like
+ *                             to set to allow TCP urgent data to come
+ *                             to us "inline".  This is NECESSARY for
+ *                             CORRECT operation, and desireable for
+ *                             simpler operation.
+ *
+ *     LNOFLSH         -       Detects the presence of the LNOFLSH bit
+ *                             in the tty structure.
+ *
+ *     unix            -       Compiles in unix specific stuff.
+ *
+ *     msdos           -       Compiles in msdos specific stuff.
+ *
+ */
+
+#if    !defined(TN3270)
+#define        ExitString(f,s,r)       { fprintf(f, s); exit(r); }
+#define        Exit(x)                 exit(x)
+#define        SetIn3270()
+
+void   setcommandmode(), command();    /* forward declarations */
+#endif /* !defined(TN3270) */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+
+#include <curses.h>
+
+#define        TELOPTS
+#include <arpa/telnet.h>
+
+#if    !defined(NOT43)
+#include <arpa/inet.h>
+#else  /* !defined(NOT43) */
+extern unsigned long inet_addr();
+extern char    *inet_ntoa();
+#endif /* !defined(NOT43) */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <strings.h>
+
+#if    defined(TN3270)
+#include "ctlr/screen.h"
+#include "system/globals.h"
+#include "telnet.ext"
+#include "ctlr/options.ext"
+#include "ctlr/outbound.ext"
+#include "keyboard/termin.ext"
+#endif /* defined(TN3270) */
+
+
+
+#ifndef        FD_SETSIZE
+/*
+ * The following is defined just in case someone should want to run
+ * this telnet on a 4.2 system.
+ *
+ */
+
+#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
+\f
+#define        strip(x)        ((x)&0x7f)
+#define min(x,y)       ((x<y)? x:y)
+
+#if    defined(TN3270)
+static char    Ibuf[8*BUFSIZ], *Ifrontp = Ibuf, *Ibackp = Ibuf;
+#endif /* defined(TN3270) */
+
+static char    ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
+#define        TTYADD(c)       { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
+#define        TTYLOC()        (tfrontp)
+#define        TTYMAX()        (ttyobuf+sizeof ttyobuf-1)
+#define        TTYMIN()        (netobuf)
+#define        TTYBYTES()      (tfrontp-tbackp)
+#define        TTYROOM()       (TTYMAX()-TTYLOC()+1)
+
+static char    netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
+#define        NETADD(c)       { *nfrontp++ = c; }
+#define        NET2ADD(c1,c2)  { NETADD(c1); NETADD(c2); }
+#define NETLOC()       (nfrontp)
+#define        NETMAX()        (netobuf+sizeof netobuf-1)
+#define        NETBYTES()      (nfrontp-nbackp)
+#define        NETROOM()       (NETMAX()-NETLOC()+1)
+static char    *neturg = 0;            /* one past last byte of urgent data */
+
+static char    subbuffer[100] = 0,
+               *subpointer, *subend = 0;        /* buffer for sub-options */
+#define        SB_CLEAR()      subpointer = subbuffer;
+#define        SB_TERM()       subend = subpointer;
+#define        SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { \
+                               *subpointer++ = (c); \
+                       }
+
+static char    sb_terminal[] = { IAC, SB,
+                       TELOPT_TTYPE, TELQUAL_IS,
+                       'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
+                       IAC, SE };
+#define        SBTERMMODEL     13
+
+
+static char    hisopts[256] = 0;
+static char    myopts[256] = 0;
+
+static char    doopt[] = { IAC, DO, '%', 'c', 0 };
+static char    dont[] = { IAC, DONT, '%', 'c', 0 };
+static char    will[] = { IAC, WILL, '%', 'c', 0 };
+static char    wont[] = { IAC, WONT, '%', 'c', 0 };
+
+struct cmd {
+       char    *name;          /* command name */
+       char    *help;          /* help string */
+       int     (*handler)();   /* routine which executes command */
+       int     dohelp;         /* Should we give general help information? */
+       int     needconnect;    /* Do we need to be connected to execute? */
+};
+
+static char    sibuf[BUFSIZ], *sbp = 0;
+static char    tibuf[BUFSIZ], *tbp;
+static fd_set ibits, obits, xbits;
+
+
+static int
+       connected = 0,
+       net = 0,
+       scc = 0,
+       tcc = 0,
+       showoptions = 0,
+       In3270 = 0,             /* Are we in 3270 mode? */
+       ISend = 0,              /* trying to send network data in */
+       debug = 0,
+       crmod = 0,
+       netdata = 0,
+       telnetport = 1;
+
+static FILE    *NetTrace = 0;
+
+#define        CONTROL(x)      ((x)&0x1f)              /* CTRL(x) is not portable */
+
+static char
+       *prompt = 0,
+       escape = CONTROL(']'),
+       echoc = CONTROL('E');
+
+static int
+       SYNCHing = 0,           /* we are in TELNET SYNCH mode */
+       flushout = 0,           /* flush output */
+       autoflush = 0,          /* flush output when interrupting? */
+       autosynch = 0,          /* send interrupt characters with SYNCH? */
+       localchars = 0,         /* we recognize interrupt/quit */
+       donelclchars = 0,       /* the user has set "localchars" */
+       dontlecho = 0;          /* do we suppress local echoing right now? */
+
+/*     The following are some tn3270 specific flags */
+#if    defined(TN3270)
+
+static int
+       Sent3270TerminalType = 0;       /* Have we said we are a 3270? */
+
+/* Some real, live, globals. */
+int
+#if    defined(unix)
+       HaveInput = 0,          /* There is input available to scan */
+#endif /* defined(unix) */
+       tout = 0,                       /* Output file descriptor */
+       tin = 0;                        /* Input file descriptor */
+#if    defined(unix)
+char   *transcom = 0;  /* transparent mode command (default: none) */
+#endif /* defined(unix) */
+
+#else  /* defined(TN3270) */
+static int tin = 0, tout = 0;          /* file descriptors */
+#endif /* defined(TN3270) */
+
+static char    line[200];
+#if    defined(TN3270) && defined(unix)
+static char    tline[200];
+#endif /* defined(TN3270) && defined(unix) */
+static int     margc = 0;
+static char    *margv[20];
+
+static jmp_buf toplevel;
+static jmp_buf peerdied;
+
+extern int errno;
+
+
+static struct sockaddr_in sin;
+
+static struct  servent *sp = 0;
+
+static struct  tchars otc = { 0 }, ntc = { 0 };
+static struct  ltchars oltc = { 0 }, nltc = { 0 };
+static struct  sgttyb ottyb = { 0 }, nttyb = { 0 };
+static int     flushline = 1;
+
+static char    *hostname = 0;
+static char    hnamebuf[32];
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+static struct {
+    int
+       system,                 /* what the current time is */
+       echotoggle,             /* last time user entered echo character */
+       modenegotiated,         /* last time operating mode negotiated */
+       didnetreceive,          /* last time we read data from network */
+       gotDM;                  /* when did we last see a data mark */
+} clocks = { 0 };
+
+#define        settimer(x)     clocks.x = clocks.system++
+\f
+/*
+ * Various utility routines.
+ */
+
+static void
+makeargv()
+{
+       register char *cp;
+       register char **argp = margv;
+
+       margc = 0;
+       for (cp = line; *cp;) {
+               while (isspace(*cp))
+                       cp++;
+               if (*cp == '\0')
+                       break;
+               *argp++ = cp;
+               margc += 1;
+               while (*cp != '\0' && !isspace(*cp))
+                       cp++;
+               if (*cp == '\0')
+                       break;
+               *cp++ = '\0';
+       }
+       *argp++ = 0;
+}
+
+static char *ambiguous;                /* special return value */
+#define Ambiguous(t)   ((t)&ambiguous)
+
+
+static char **
+genget(name, table, next)
+char   *name;          /* name to match */
+char   **table;                /* name entry in table */
+char   **(*next)();    /* routine to return next entry in table */
+{
+       register char *p, *q;
+       register char **c, **found;
+       register int nmatches, longest;
+
+       longest = 0;
+       nmatches = 0;
+       found = 0;
+       for (c = table; (p = *c) != 0; c = (*next)(c)) {
+               for (q = name;
+                   (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
+                       if (*q == 0)            /* exact match? */
+                               return (c);
+               if (!*q) {                      /* the name was a prefix */
+                       if (q - name > longest) {
+                               longest = q - name;
+                               nmatches = 1;
+                               found = c;
+                       } else if (q - name == longest)
+                               nmatches++;
+               }
+       }
+       if (nmatches > 1)
+               return Ambiguous(char **);
+       return (found);
+}
+
+/*
+ * Make a character string into a number.
+ *
+ * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
+ */
+
+static
+special(s)
+register char *s;
+{
+       register char c;
+       char b;
+
+       switch (*s) {
+       case '^':
+               b = *++s;
+               if (b == '?') {
+                   c = b | 0x40;               /* DEL */
+               } else {
+                   c = b & 0x1f;
+               }
+               break;
+       default:
+               c = *s;
+               break;
+       }
+       return c;
+}
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+static char *
+control(c)
+       register int c;
+{
+       static char buf[3];
+
+       if (c == 0x7f)
+               return ("^?");
+       if (c == '\377') {
+               return "off";
+       }
+       if (c >= 0x20) {
+               buf[0] = c;
+               buf[1] = 0;
+       } else {
+               buf[0] = '^';
+               buf[1] = '@'+c;
+               buf[2] = 0;
+       }
+       return (buf);
+}
+
+
+/*
+ * upcase()
+ *
+ *     Upcase (in place) the argument.
+ */
+
+static void
+upcase(argument)
+register char *argument;
+{
+    register int c;
+
+    while ((c = *argument) != 0) {
+       if (islower(c)) {
+           *argument = toupper(c);
+       }
+       argument++;
+    }
+}
+\f
+/*
+ * The following are routines used to print out debugging information.
+ */
+
+
+static void
+Dump(direction, buffer, length)
+char   direction;
+char   *buffer;
+int    length;
+{
+#   define BYTES_PER_LINE      32
+#   define min(x,y)    ((x<y)? x:y)
+    char *pThis;
+    int offset;
+
+    offset = 0;
+
+    while (length) {
+       /* print one line */
+       fprintf(NetTrace, "%c 0x%x\t", direction, offset);
+       pThis = buffer;
+       buffer = buffer+min(length, BYTES_PER_LINE);
+       while (pThis < buffer) {
+           fprintf(NetTrace, "%.2x", (*pThis)&0xff);
+           pThis++;
+       }
+       fprintf(NetTrace, "\n");
+       length -= BYTES_PER_LINE;
+       offset += BYTES_PER_LINE;
+       if (length < 0) {
+           return;
+       }
+       /* find next unique line */
+    }
+}
+
+
+/*VARARGS*/
+static void
+printoption(direction, fmt, option, what)
+       char *direction, *fmt;
+       int option, what;
+{
+       if (!showoptions)
+               return;
+       fprintf(NetTrace, "%s ", direction+1);
+       if (fmt == doopt)
+               fmt = "do";
+       else if (fmt == dont)
+               fmt = "dont";
+       else if (fmt == will)
+               fmt = "will";
+       else if (fmt == wont)
+               fmt = "wont";
+       else
+               fmt = "???";
+       if (option < (sizeof telopts/sizeof telopts[0]))
+               fprintf(NetTrace, "%s %s", fmt, telopts[option]);
+       else
+               fprintf(NetTrace, "%s %d", fmt, option);
+       if (*direction == '<') {
+               fprintf(NetTrace, "\r\n");
+               return;
+       }
+       fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
+}
+
+static void
+printsub(direction, pointer, length)
+char   *direction,             /* "<" or ">" */
+       *pointer;               /* where suboption data sits */
+int    length;                 /* length of suboption data */
+{
+    if (showoptions) {
+       fprintf(NetTrace, "%s suboption ",
+                               (direction[0] == '<')? "Received":"Sent");
+       switch (pointer[0]) {
+       case TELOPT_TTYPE:
+           fprintf(NetTrace, "Terminal type ");
+           switch (pointer[1]) {
+           case TELQUAL_IS:
+               {
+                   char tmpbuf[sizeof subbuffer];
+                   int minlen = min(length, sizeof tmpbuf);
+
+                   bcopy(pointer+2, tmpbuf, minlen);
+                   tmpbuf[minlen-1] = 0;
+                   fprintf(NetTrace, "is %s.\n", tmpbuf);
+               }
+               break;
+           case TELQUAL_SEND:
+               fprintf(NetTrace, "- request to send.\n");
+               break;
+           default:
+               fprintf(NetTrace,
+                               "- unknown qualifier %d (0x%x).\n", pointer[1]);
+           }
+           break;
+       default:
+           fprintf(NetTrace, "Unknown option %d (0x%x)\n",
+                                       pointer[0], pointer[0]);
+       }
+    }
+}
+\f
+/*
+ * Check to see if any out-of-band data exists on a socket (for
+ * Telnet "synch" processing).
+ */
+
+static int
+stilloob(s)
+int    s;              /* socket number */
+{
+    static struct timeval timeout = { 0 };
+    fd_set     excepts;
+    int value;
+
+    do {
+       FD_ZERO(&excepts);
+       FD_SET(s, &excepts);
+       value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
+    } while ((value == -1) && (errno == EINTR));
+
+    if (value < 0) {
+       perror("select");
+       quit();
+    }
+    if (FD_ISSET(s, &excepts)) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+
+/*
+ *  netflush
+ *             Send as much data as possible to the network,
+ *     handling requests for urgent data.
+ *
+ *             The return value indicates whether we did any
+ *     useful work.
+ */
+
+
+int
+netflush()
+{
+    int n;
+
+    if ((n = nfrontp - nbackp) > 0) {
+       if (!neturg) {
+           n = write(net, nbackp, n);  /* normal write */
+       } else {
+           n = neturg - nbackp;
+           /*
+            * In 4.2 (and 4.3) systems, there is some question about
+            * what byte in a sendOOB operation is the "OOB" data.
+            * To make ourselves compatible, we only send ONE byte
+            * out of band, the one WE THINK should be OOB (though
+            * we really have more the TCP philosophy of urgent data
+            * rather than the Unix philosophy of OOB data).
+            */
+           if (n > 1) {
+               n = send(net, nbackp, n-1, 0);  /* send URGENT all by itself */
+           } else {
+               n = send(net, nbackp, n, MSG_OOB);      /* URGENT data */
+           }
+       }
+    }
+    if (n < 0) {
+       if (errno != ENOBUFS && errno != EWOULDBLOCK) {
+           setcommandmode();
+           perror(hostname);
+           close(net);
+           neturg = 0;
+           longjmp(peerdied, -1);
+           /*NOTREACHED*/
+       }
+       n = 0;
+    }
+    if (netdata && n) {
+       Dump('>', nbackp, n);
+    }
+    nbackp += n;
+    if (nbackp >= neturg) {
+       neturg = 0;
+    }
+    if (nbackp == nfrontp) {
+       nbackp = nfrontp = netobuf;
+    }
+    return n > 0;
+}
+\f
+/*
+ * nextitem()
+ *
+ *     Return the address of the next "item" in the TELNET data
+ * stream.  This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+
+static char *
+nextitem(current)
+char   *current;
+{
+    if ((*current&0xff) != IAC) {
+       return current+1;
+    }
+    switch (*(current+1)&0xff) {
+    case DO:
+    case DONT:
+    case WILL:
+    case WONT:
+       return current+3;
+    case SB:           /* loop forever looking for the SE */
+       {
+           register char *look = current+2;
+
+           for (;;) {
+               if ((*look++&0xff) == IAC) {
+                   if ((*look++&0xff) == SE) {
+                       return look;
+                   }
+               }
+           }
+       }
+    default:
+       return current+2;
+    }
+}
+/*
+ * netclear()
+ *
+ *     We are about to do a TELNET SYNCH operation.  Clear
+ * the path to the network.
+ *
+ *     Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ *     A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer.  The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+
+static void
+netclear()
+{
+    register char *thisitem, *next;
+    char *good;
+#define        wewant(p)       ((nfrontp > p) && ((*p&0xff) == IAC) && \
+                               ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+    thisitem = netobuf;
+
+    while ((next = nextitem(thisitem)) <= nbackp) {
+       thisitem = next;
+    }
+
+    /* Now, thisitem is first before/at boundary. */
+
+    good = netobuf;    /* where the good bytes go */
+
+    while (nfrontp > thisitem) {
+       if (wewant(thisitem)) {
+           int length;
+
+           next = thisitem;
+           do {
+               next = nextitem(next);
+           } while (wewant(next) && (nfrontp > next));
+           length = next-thisitem;
+           bcopy(thisitem, good, length);
+           good += length;
+           thisitem = next;
+       } else {
+           thisitem = nextitem(thisitem);
+       }
+    }
+
+    nbackp = netobuf;
+    nfrontp = good;            /* next byte to be sent */
+    neturg = 0;
+}
+\f
+/*
+ * These routines add various telnet commands to the data stream.
+ */
+
+#if    defined(NOT43)
+static int
+#else  /* defined(NOT43) */
+static void
+#endif /* defined(NOT43) */
+dosynch()
+{
+    netclear();                        /* clear the path to the network */
+    NET2ADD(IAC, DM);
+    neturg = NETLOC()-1;       /* Some systems are off by one XXX */
+
+#if    defined(NOT43)
+    return 0;
+#endif /* defined(NOT43) */
+}
+
+static void
+doflush()
+{
+    NET2ADD(IAC, DO);
+    NETADD(TELOPT_TM);
+    flushline = 1;
+    flushout = 1;
+    ttyflush();
+    /* do printoption AFTER flush, otherwise the output gets tossed... */
+    printoption("<SENT", doopt, TELOPT_TM, 0);
+}
+
+static void
+intp()
+{
+    NET2ADD(IAC, IP);
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
+}
+
+static void
+sendbrk()
+{
+    NET2ADD(IAC, BREAK);
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
+}
+\f
+/*
+ *             Send as much data as possible to the terminal.
+ *
+ *             The return value indicates whether we did any
+ *     useful work.
+ */
+
+
+static int
+ttyflush()
+{
+    int n;
+
+    if ((n = tfrontp - tbackp) > 0) {
+       if (!(SYNCHing||flushout)) {
+           n = write(tout, tbackp, n);
+       } else {
+           ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
+           /* we leave 'n' alone! */
+       }
+    }
+    if (n >= 0) {
+       tbackp += n;
+       if (tbackp == tfrontp) {
+           tbackp = tfrontp = ttyobuf;
+       }
+    }
+    return n > 0;
+}
+
+#if    defined(TN3270)
+
+#if    defined(unix)
+static void
+inputAvailable()
+{
+    HaveInput = 1;
+}
+#endif /* defined(unix) */
+
+void
+outputPurge()
+{
+    int tmp = flushout;
+
+    flushout = 1;
+
+    ttyflush();
+
+    flushout = tmp;
+}
+
+#endif /* defined(TN3270) */
+\f
+#if    defined(unix)
+/*
+ * Various signal handling routines.
+ */
+
+static void
+deadpeer()
+{
+       setcommandmode();
+       longjmp(peerdied, -1);
+}
+
+static void
+intr()
+{
+    if (localchars) {
+       intp();
+       return;
+    }
+    setcommandmode();
+    longjmp(toplevel, -1);
+}
+
+static void
+intr2()
+{
+    if (localchars) {
+       sendbrk();
+       return;
+    }
+}
+
+static void
+doescape()
+{
+    command(0);
+}
+#endif /* defined(unix) */
+\f
+static int     globalmode = 0;
+/*     Various modes */
+#define        MODE_LINE(m)    (modelist[m].modetype & LINE)
+#define        MODE_LOCAL_CHARS(m)     (modelist[m].modetype &  LOCAL_CHARS)
+
+#define        LOCAL_CHARS     0x01            /* Characters processed locally */
+#define        LINE            0x02            /* Line-by-line mode of operation */
+
+static struct {
+    char *modedescriptions;
+    char modetype;
+} modelist[] = {
+       { "telnet command mode", 0 },
+       { "character-at-a-time mode", 0 },
+       { "character-at-a-time mode (local echo)", LOCAL_CHARS },
+       { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
+       { "line-by-line mode", LINE | LOCAL_CHARS },
+       { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
+       { "3270 mode", 0 },
+};
+/*
+ * Mode - set up terminal to a specific mode.
+ */
+
+
+static void
+mode(f)
+       register int f;
+{
+    static int prevmode = 0;
+    struct tchars *tc;
+    struct tchars tc3;
+    struct ltchars *ltc;
+    struct sgttyb sb;
+    int onoff;
+#if    defined(unix)
+    int old;
+#endif /* defined(unix) */
+    struct     tchars notc2;
+    struct     ltchars noltc2;
+    static struct      tchars notc =   { -1, -1, -1, -1, -1, -1 };
+    static struct      ltchars noltc = { -1, -1, -1, -1, -1, -1 };
+
+    globalmode = f;
+    if (prevmode == f)
+       return;
+#if    defined(unix)
+    old = prevmode;
+#endif /* defined(unix) */
+    prevmode = f;
+    sb = nttyb;
+
+    switch (f) {
+
+    case 0:
+       onoff = 0;
+       tc = &otc;
+       ltc = &oltc;
+       break;
+
+    case 1:            /* remote character processing, remote echo */
+    case 2:            /* remote character processing, local echo */
+    case 6:            /* 3270 mode - like 1, but with xon/xoff local */
+                   /* (might be nice to have "6" in telnet also...) */
+           sb.sg_flags |= CBREAK;
+           if ((f == 1) || (f == 6)) {
+               sb.sg_flags &= ~(ECHO|CRMOD);
+           } else {
+               sb.sg_flags |= ECHO|CRMOD;
+           }
+           sb.sg_erase = sb.sg_kill = -1;
+           if (f == 6) {
+               tc = &tc3;
+               tc3 = notc;
+                   /* get XON, XOFF characters */
+               tc3.t_startc = otc.t_startc;
+               tc3.t_stopc = otc.t_stopc;
+           } else {
+               /*
+                * If user hasn't specified one way or the other,
+                * then default to not trapping signals.
+                */
+               if (!donelclchars) {
+                   localchars = 0;
+               }
+               if (localchars) {
+                   notc2 = notc;
+                   notc2.t_intrc = ntc.t_intrc;
+                   notc2.t_quitc = ntc.t_quitc;
+                   tc = &notc2;
+               } else {
+                   tc = &notc;
+               }
+           }
+           ltc = &noltc;
+           onoff = 1;
+           break;
+    case 3:            /* local character processing, remote echo */
+    case 4:            /* local character processing, local echo */
+    case 5:            /* local character processing, no echo */
+           sb.sg_flags &= ~CBREAK;
+           sb.sg_flags |= CRMOD;
+           if (f == 4)
+               sb.sg_flags |= ECHO;
+           else
+               sb.sg_flags &= ~ECHO;
+           notc2 = ntc;
+           tc = &notc2;
+           noltc2 = oltc;
+           ltc = &noltc2;
+           /*
+            * If user hasn't specified one way or the other,
+            * then default to trapping signals.
+            */
+           if (!donelclchars) {
+               localchars = 1;
+           }
+           if (localchars) {
+               notc2.t_brkc = nltc.t_flushc;
+               noltc2.t_flushc = -1;
+           } else {
+               notc2.t_intrc = notc2.t_quitc = -1;
+           }
+           noltc2.t_suspc = escape;
+           noltc2.t_dsuspc = -1;
+           onoff = 1;
+           break;
+
+    default:
+           return;
+    }
+    ioctl(tin, TIOCSLTC, (char *)ltc);
+    ioctl(tin, TIOCSETC, (char *)tc);
+    ioctl(tin, TIOCSETP, (char *)&sb);
+#if    (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
+    ioctl(tin, FIONBIO, (char *)&onoff);
+    ioctl(tout, FIONBIO, (char *)&onoff);
+#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
+#if    defined(TN3270) && !defined(DEBUG)
+    ioctl(tin, FIOASYNC, (char *)&onoff);
+#endif /* defined(TN3270) && !defined(DEBUG) */
+
+#if    defined(unix)
+    if (MODE_LINE(f)) {
+       signal(SIGTSTP, doescape);
+    } else if (MODE_LINE(old)) {
+       signal(SIGTSTP, SIG_DFL);
+       sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
+    }
+#endif /* defined(unix) */
+}
+\f
+/*
+ * These routines decides on what the mode should be (based on the values
+ * of various global variables).
+ */
+
+
+static
+getconnmode()
+{
+    static char newmode[16] =
+                       { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
+    int modeindex = 0;
+
+    if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
+       modeindex += 1;
+    }
+    if (hisopts[TELOPT_ECHO]) {
+       modeindex += 2;
+    }
+    if (hisopts[TELOPT_SGA]) {
+       modeindex += 4;
+    }
+    if (In3270) {
+       modeindex += 8;
+    }
+    return newmode[modeindex];
+}
+
+void
+setconnmode()
+{
+    mode(getconnmode());
+}
+
+
+void
+setcommandmode()
+{
+    mode(0);
+}
+\f
+static void
+willoption(option, reply)
+       int option, reply;
+{
+       char *fmt;
+
+       switch (option) {
+
+#      if defined(TN3270)
+       case TELOPT_EOR:
+       case TELOPT_BINARY:
+#endif /* defined(TN3270) */
+       case TELOPT_ECHO:
+       case TELOPT_SGA:
+               settimer(modenegotiated);
+               hisopts[option] = 1;
+               fmt = doopt;
+               setconnmode();          /* possibly set new tty mode */
+               break;
+
+       case TELOPT_TM:
+               return;                 /* Never reply to TM will's/wont's */
+
+       default:
+               fmt = dont;
+               break;
+       }
+       sprintf(nfrontp, fmt, option);
+       nfrontp += sizeof (dont) - 2;
+       if (reply)
+               printoption(">SENT", fmt, option, reply);
+       else
+               printoption("<SENT", fmt, option, reply);
+}
+
+static void
+wontoption(option, reply)
+       int option, reply;
+{
+       char *fmt;
+
+       switch (option) {
+
+       case TELOPT_ECHO:
+       case TELOPT_SGA:
+               settimer(modenegotiated);
+               hisopts[option] = 0;
+               fmt = dont;
+               setconnmode();                  /* Set new tty mode */
+               break;
+
+       case TELOPT_TM:
+               return;         /* Never reply to TM will's/wont's */
+
+       default:
+               fmt = dont;
+       }
+       sprintf(nfrontp, fmt, option);
+       nfrontp += sizeof (doopt) - 2;
+       if (reply)
+               printoption(">SENT", fmt, option, reply);
+       else
+               printoption("<SENT", fmt, option, reply);
+}
+
+static void
+dooption(option)
+       int option;
+{
+       char *fmt;
+
+       switch (option) {
+
+       case TELOPT_TM:
+               fmt = will;
+               break;
+
+#      if defined(TN3270)
+       case TELOPT_EOR:
+       case TELOPT_BINARY:
+#      endif   /* defined(TN3270) */
+       case TELOPT_TTYPE:              /* terminal type option */
+       case TELOPT_SGA:                /* no big deal */
+               fmt = will;
+               myopts[option] = 1;
+               break;
+
+       case TELOPT_ECHO:               /* We're never going to echo... */
+       default:
+               fmt = wont;
+               break;
+       }
+       sprintf(nfrontp, fmt, option);
+       nfrontp += sizeof (doopt) - 2;
+       printoption(">SENT", fmt, option, 0);
+}
+
+/*
+ * suboption()
+ *
+ *     Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ *     Currently we recognize:
+ *
+ *             Terminal type, send request.
+ */
+
+static void
+suboption()
+{
+    printsub("<", subbuffer, subend-subbuffer+1);
+    switch (subbuffer[0]&0xff) {
+    case TELOPT_TTYPE:
+       if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
+           ;
+       } else {
+           char *name;
+           char namebuf[41];
+           extern char *getenv();
+           int len;
+
+#if    defined(TN3270)
+           /*
+            * Try to send a 3270 type terminal name.  Decide which one base
+            * on the format of our screen, and (in the future) color
+            * capaiblities.
+            */
+           if ((initscr() != ERR) &&   /* Initialize curses to get line size */
+               (LINES >= 24) && (COLS >= 80)) {
+               Sent3270TerminalType = 1;
+               if ((LINES >= 27) && (COLS >= 132)) {
+                   MaxNumberLines = 27;
+                   MaxNumberColumns = 132;
+                   sb_terminal[SBTERMMODEL] = '5';
+               } else if (LINES >= 43) {
+                   MaxNumberLines = 43;
+                   MaxNumberColumns = 80;
+                   sb_terminal[SBTERMMODEL] = '4';
+               } else if (LINES >= 32) {
+                   MaxNumberLines = 32;
+                   MaxNumberColumns = 80;
+                   sb_terminal[SBTERMMODEL] = '3';
+               } else {
+                   MaxNumberLines = 24;
+                   MaxNumberColumns = 80;
+                   sb_terminal[SBTERMMODEL] = '2';
+               }
+               NumberLines = 24;               /* before we start out... */
+               NumberColumns = 80;
+               ScreenSize = NumberLines*NumberColumns;
+               if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
+                   ExitString(stderr,
+                       "Programming error:  MAXSCREENSIZE too small.\n", 1);
+                   /*NOTREACHED*/
+               }
+               bcopy(sb_terminal, nfrontp, sizeof sb_terminal);
+               printsub(">", nfrontp+2, sizeof sb_terminal-2);
+               nfrontp += sizeof sb_terminal;
+               return;
+           }
+#endif /* defined(TN3270) */
+
+           name = getenv("TERM");
+           if ((name == 0) || ((len = strlen(name)) > 40)) {
+               name = "UNKNOWN";
+           }
+           if ((len + 4+2) < NETROOM()) {
+               strcpy(namebuf, name);
+               upcase(namebuf);
+               sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
+                                   TELQUAL_IS, namebuf, IAC, SE);
+               printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
+               nfrontp += 4+strlen(namebuf)+2;
+           } else {
+               ExitString(stderr, "No room in buffer for terminal type.\n",
+                                                       1);
+               /*NOTREACHED*/
+           }
+       }
+
+    default:
+       break;
+    }
+}
+
+#if    defined(TN3270)
+static void
+SetIn3270()
+{
+    if (Sent3270TerminalType && myopts[TELOPT_BINARY]
+                                       && hisopts[TELOPT_BINARY]) {
+       if (!In3270) {
+           In3270 = 1;
+           OptInit();          /* initialize mappings */
+           /* initialize terminal key mapping */
+           (void) DataFromTerminal(ttyobuf, 0);
+           StartScreen();      /* Start terminal going */
+           setconnmode();
+       }
+    } else {
+       if (In3270) {
+           StopScreen(1);
+           In3270 = 0;
+           setconnmode();
+       }
+    }
+}
+#endif /* defined(TN3270) */
+\f
+/*
+ * Telnet receiver states for fsm
+ */
+#define        TS_DATA         0
+#define        TS_IAC          1
+#define        TS_WILL         2
+#define        TS_WONT         3
+#define        TS_DO           4
+#define        TS_DONT         5
+#define        TS_CR           6
+#define        TS_SB           7               /* sub-option collection */
+#define        TS_SE           8               /* looking for sub-option end */
+
+static void
+telrcv()
+{
+    register int c;
+    static int state = TS_DATA;
+#   if defined(TN3270)
+    register int Scc;
+    register char *Sbp;
+#   endif /* defined(TN3270) */
+
+    while ((scc > 0) && (TTYROOM() > 2)) {
+       c = *sbp++ & 0xff, scc--;
+       switch (state) {
+
+       case TS_CR:
+           state = TS_DATA;
+           if (c == '\0') {
+               break;  /* Ignore \0 after CR */
+           } else if (c == '\n') {
+               if (hisopts[TELOPT_ECHO] && !crmod) {
+                   TTYADD(c);
+               }
+               break;
+           }
+           /* Else, fall through */
+
+       case TS_DATA:
+           if (c == IAC) {
+               state = TS_IAC;
+               continue;
+           }
+#          if defined(TN3270)
+           if (In3270) {
+               *Ifrontp++ = c;
+               Sbp = sbp;
+               Scc = scc;
+               while (Scc > 0) {
+                   c = *Sbp++ & 0377, Scc--;
+                   if (c == IAC) {
+                       state = TS_IAC;
+                       break;
+                   }
+                   *Ifrontp++ = c;
+               }
+               sbp = Sbp;
+               scc = Scc;
+           } else
+#          endif /* defined(TN3270) */
+                   /*
+                    * The 'crmod' hack (see following) is needed
+                    * since we can't * set CRMOD on output only.
+                    * Machines like MULTICS like to send \r without
+                    * \n; since we must turn off CRMOD to get proper
+                    * input, the mapping is done here (sigh).
+                    */
+           if (c == '\r') {
+               if (scc > 0) {
+                   c = *sbp&0xff;
+                   if (c == 0) {
+                       sbp++, scc--;
+                       /* a "true" CR */
+                       TTYADD('\r');
+                   } else if (!hisopts[TELOPT_ECHO] &&
+                                       (c == '\n')) {
+                       sbp++, scc--;
+                       TTYADD('\n');
+                   } else {
+                       TTYADD('\r');
+                       if (crmod) {
+                               TTYADD('\n');
+                       }
+                   }
+               } else {
+                   state = TS_CR;
+                   TTYADD('\r');
+                   if (crmod) {
+                           TTYADD('\n');
+                   }
+               }
+           } else {
+               TTYADD(c);
+           }
+           continue;
+
+       case TS_IAC:
+           switch (c) {
+           
+           case WILL:
+               state = TS_WILL;
+               continue;
+
+           case WONT:
+               state = TS_WONT;
+               continue;
+
+           case DO:
+               state = TS_DO;
+               continue;
+
+           case DONT:
+               state = TS_DONT;
+               continue;
+
+           case DM:
+                   /*
+                    * We may have missed an urgent notification,
+                    * so make sure we flush whatever is in the
+                    * buffer currently.
+                    */
+               SYNCHing = 1;
+               ttyflush();
+               SYNCHing = stilloob(net);
+               settimer(gotDM);
+               break;
+
+           case NOP:
+           case GA:
+               break;
+
+           case SB:
+               SB_CLEAR();
+               state = TS_SB;
+               continue;
+
+#          if defined(TN3270)
+           case EOR:
+               if (In3270) {
+                   Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
+                   if (Ibackp == Ifrontp) {
+                       Ibackp = Ifrontp = Ibuf;
+                       ISend = 0;      /* should have been! */
+                   } else {
+                       ISend = 1;
+                   }
+               }
+               break;
+#          endif /* defined(TN3270) */
+
+           case IAC:
+#          if !defined(TN3270)
+               TTYADD(IAC);
+#          else /* !defined(TN3270) */
+               if (In3270) {
+                   *Ifrontp++ = IAC;
+               } else {
+                   TTYADD(IAC);
+               }
+#          endif /* !defined(TN3270) */
+               break;
+
+           default:
+               break;
+           }
+           state = TS_DATA;
+           continue;
+
+       case TS_WILL:
+           printoption(">RCVD", will, c, !hisopts[c]);
+           if (c == TELOPT_TM) {
+               if (flushout) {
+                   flushout = 0;
+               }
+           } else if (!hisopts[c]) {
+               willoption(c, 1);
+           }
+           SetIn3270();
+           state = TS_DATA;
+           continue;
+
+       case TS_WONT:
+           printoption(">RCVD", wont, c, hisopts[c]);
+           if (c == TELOPT_TM) {
+               if (flushout) {
+                   flushout = 0;
+               }
+           } else if (hisopts[c]) {
+               wontoption(c, 1);
+           }
+           SetIn3270();
+           state = TS_DATA;
+           continue;
+
+       case TS_DO:
+           printoption(">RCVD", doopt, c, !myopts[c]);
+           if (!myopts[c])
+               dooption(c);
+           SetIn3270();
+           state = TS_DATA;
+           continue;
+
+       case TS_DONT:
+           printoption(">RCVD", dont, c, myopts[c]);
+           if (myopts[c]) {
+               myopts[c] = 0;
+               sprintf(nfrontp, wont, c);
+               nfrontp += sizeof (wont) - 2;
+               flushline = 1;
+               setconnmode();  /* set new tty mode (maybe) */
+               printoption(">SENT", wont, c, 0);
+           }
+           SetIn3270();
+           state = TS_DATA;
+           continue;
+
+       case TS_SB:
+           if (c == IAC) {
+               state = TS_SE;
+           } else {
+               SB_ACCUM(c);
+           }
+           continue;
+
+       case TS_SE:
+           if (c != SE) {
+               if (c != IAC) {
+                   SB_ACCUM(IAC);
+               }
+               SB_ACCUM(c);
+               state = TS_SB;
+           } else {
+               SB_TERM();
+               suboption();    /* handle sub-option */
+               SetIn3270();
+               state = TS_DATA;
+           }
+       }
+    }
+}
+\f
+#if    defined(TN3270)
+
+/*
+ * The following routines are places where the various tn3270
+ * routines make calls into telnet.c.
+ */
+
+/* TtyChars() - returns the number of characters in the TTY buffer */
+TtyChars()
+{
+    return(tfrontp-tbackp);
+}
+
+/*
+ * DataToNetwork - queue up some data to go to network.  If "done" is set,
+ * then when last byte is queued, we add on an IAC EOR sequence (so,
+ * don't call us with "done" until you want that done...)
+ *
+ * We actually do send all the data to the network buffer, since our
+ * only client needs for us to do that.
+ */
+
+int
+DataToNetwork(buffer, count, done)
+register char  *buffer;        /* where the data is */
+register int   count;          /* how much to send */
+int            done;           /* is this the last of a logical block */
+{
+    register int c;
+    int origCount;
+    fd_set o;
+
+    origCount = count;
+    FD_ZERO(&o);
+
+    while (count) {
+       if ((netobuf+sizeof netobuf - nfrontp) < 6) {
+           netflush();
+           while ((netobuf+sizeof netobuf - nfrontp) < 6) {
+               FD_SET(net, &o);
+               (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
+                                               (struct timeval *) 0);
+               netflush();
+           }
+       }
+       c = *buffer++;
+       count--;
+       if (c == IAC) {
+           *nfrontp++ = IAC;
+           *nfrontp++ = IAC;
+       } else {
+           *nfrontp++ = c;
+       }
+    }
+
+    if (done && !count) {
+       *nfrontp++ = IAC;
+       *nfrontp++ = EOR;
+       netflush();             /* try to move along as quickly as ... */
+    }
+    return(origCount - count);
+}
+
+/* DataToTerminal - queue up some data to go to terminal. */
+
+int
+DataToTerminal(buffer, count)
+register char  *buffer;                /* where the data is */
+register int   count;                  /* how much to send */
+{
+    int origCount;
+    fd_set     o;
+
+    origCount = count;
+    FD_ZERO(&o);
+
+    while (count) {
+       if (tfrontp >= ttyobuf+sizeof ttyobuf) {
+           ttyflush();
+           while (tfrontp >= ttyobuf+sizeof ttyobuf) {
+               FD_SET(tout, &o);
+               (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
+                                               (struct timeval *) 0);
+               ttyflush();
+           }
+       }
+       *tfrontp++ = *buffer++;
+       count--;
+    }
+    return(origCount - count);
+}
+
+/* EmptyTerminal - called to make sure that the terminal buffer is empty.
+ *                     Note that we consider the buffer to run all the
+ *                     way to the kernel (thus the select).
+ */
+
+void
+EmptyTerminal()
+{
+    fd_set     o;
+
+    FD_ZERO(&o);
+
+    if (tfrontp == tbackp) {
+       FD_SET(tout, &o);
+       (void) select(tout+1, (int *) 0, &o, (int *) 0,
+                       (struct timeval *) 0);  /* wait for TTLOWAT */
+    } else {
+       while (tfrontp != tbackp) {
+           ttyflush();
+           FD_SET(tout, &o);
+           (void) select(tout+1, (int *) 0, &o, (int *) 0,
+                               (struct timeval *) 0);  /* wait for TTLOWAT */
+       }
+    }
+}
+
+/*
+ * Push3270 - Try to send data along the 3270 output (to screen) direction.
+ */
+
+static int
+Push3270()
+{
+    int save = scc;
+
+    if (scc) {
+       if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
+           if (Ibackp != Ibuf) {
+               bcopy(Ibackp, Ibuf, Ifrontp-Ibackp);
+               Ifrontp -= (Ibackp-Ibuf);
+               Ibackp = Ibuf;
+           }
+       }
+       if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
+           telrcv();
+       }
+    }
+    return save != scc;
+}
+
+
+/*
+ * Finish3270 - get the last dregs of 3270 data out to the terminal
+ *             before quitting.
+ */
+
+static void
+Finish3270()
+{
+    while (Push3270() || !DoTerminalOutput()) {
+       ;
+    }
+}
+
+
+
+/* StringToTerminal - output a null terminated string to the terminal */
+
+void
+StringToTerminal(s)
+char *s;
+{
+    int count;
+
+    count = strlen(s);
+    if (count) {
+       (void) DataToTerminal(s, count);        /* we know it always goes... */
+    }
+}
+
+
+#if    defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
+/* _putchar - output a single character to the terminal.  This name is so that
+ *     curses(3x) can call us to send out data.
+ */
+
+void
+_putchar(c)
+char c;
+{
+    if (tfrontp >= ttyobuf+sizeof ttyobuf) {
+       (void) DataToTerminal(&c, 1);
+    } else {
+       *tfrontp++ = c;         /* optimize if possible. */
+    }
+}
+#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
+\f
+static void
+SetForExit()
+{
+    setconnmode();
+    if (In3270) {
+       Finish3270();
+    }
+    setcommandmode();
+    fflush(stdout);
+    fflush(stderr);
+    if (In3270) {
+       StopScreen(1);
+    }
+    setconnmode();
+    setcommandmode();
+}
+
+static void
+Exit(returnCode)
+int returnCode;
+{
+    SetForExit();
+    exit(returnCode);
+}
+
+void
+ExitString(file, string, returnCode)
+FILE *file;
+char *string;
+int returnCode;
+{
+    SetForExit();
+    fwrite(string, 1, strlen(string), file);
+    exit(returnCode);
+}
+
+void
+ExitPerror(string, returnCode)
+char *string;
+int returnCode;
+{
+    SetForExit();
+    perror(string);
+    exit(returnCode);
+}
+
+#endif /* defined(TN3270) */
+\f
+static
+Scheduler(block)
+int    block;                  /* should we block in the select ? */
+{
+    register int c;
+               /* One wants to be a bit careful about setting returnValue
+                * to one, since a one implies we did some useful work,
+                * and therefore probably won't be called to block next
+                * time (TN3270 mode only).
+                */
+    int returnValue = 0;
+    static struct timeval TimeValue = { 0 };
+
+    if (scc < 0 && tcc < 0) {
+       return -1;
+    }
+
+    if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
+       FD_SET(net, &obits);
+    } 
+    if (TTYBYTES()) {
+       FD_SET(tout, &obits);
+    }
+    if ((tcc == 0) && NETROOM()) {
+       FD_SET(tin, &ibits);
+    }
+#   if !defined(TN3270)
+    if (TTYROOM()) {
+       FD_SET(net, &ibits);
+    }
+#   else /* !defined(TN3270) */
+    if (!ISend && TTYROOM()) {
+       FD_SET(net, &ibits);
+    }
+#   endif /* !defined(TN3270) */
+    if (!SYNCHing) {
+       FD_SET(net, &xbits);
+    }
+#   if defined(TN3270) && defined(unix)
+    if (HaveInput) {
+       HaveInput = 0;
+       signal(SIGIO, inputAvailable);
+    }
+#endif /* defined(TN3270) && defined(unix) */
+    if ((c = select(16, &ibits, &obits, &xbits,
+                       block? (struct timeval *)0 : &TimeValue)) < 1) {
+       if (c == -1) {
+                   /*
+                    * we can get EINTR if we are in line mode,
+                    * and the user does an escape (TSTP), or
+                    * some other signal generator.
+                    */
+           if (errno == EINTR) {
+               return 0;
+           }
+#          if defined(TN3270)
+                   /*
+                    * we can get EBADF if we were in transparent
+                    * mode, and the transcom process died.
+                   */
+           if (errno == EBADF) {
+                       /*
+                        * zero the bits (even though kernel does it)
+                        * to make sure we are selecting on the right
+                        * ones.
+                       */
+               FD_ZERO(&ibits);
+               FD_ZERO(&obits);
+               FD_ZERO(&xbits);
+               return 0;
+           }
+#          endif /* defined(TN3270) */
+                   /* I don't like this, does it ever happen? */
+           printf("sleep(5) from telnet, after select\r\n");
+#if    defined(unix)
+           sleep(5);
+#endif /* defined(unix) */
+       }
+       return 0;
+    }
+
+    /*
+     * Any urgent data?
+     */
+    if (FD_ISSET(net, &xbits)) {
+       FD_CLR(net, &xbits);
+       SYNCHing = 1;
+       ttyflush();     /* flush already enqueued data */
+    }
+
+    /*
+     * Something to read from the network...
+     */
+    if (FD_ISSET(net, &ibits)) {
+       int canread;
+
+       FD_CLR(net, &ibits);
+       if (scc == 0) {
+           sbp = sibuf;
+       }
+       canread = sibuf + sizeof sibuf - sbp;
+#if    !defined(SO_OOBINLINE)
+           /*
+            * In 4.2 (and some early 4.3) systems, the
+            * OOB indication and data handling in the kernel
+            * is such that if two separate TCP Urgent requests
+            * come in, one byte of TCP data will be overlaid.
+            * This is fatal for Telnet, but we try to live
+            * with it.
+            *
+            * In addition, in 4.2 (and...), a special protocol
+            * is needed to pick up the TCP Urgent data in
+            * the correct sequence.
+            *
+            * What we do is:  if we think we are in urgent
+            * mode, we look to see if we are "at the mark".
+            * If we are, we do an OOB receive.  If we run
+            * this twice, we will do the OOB receive twice,
+            * but the second will fail, since the second
+            * time we were "at the mark", but there wasn't
+            * any data there (the kernel doesn't reset
+            * "at the mark" until we do a normal read).
+            * Once we've read the OOB data, we go ahead
+            * and do normal reads.
+            *
+            * There is also another problem, which is that
+            * since the OOB byte we read doesn't put us
+            * out of OOB state, and since that byte is most
+            * likely the TELNET DM (data mark), we would
+            * stay in the TELNET SYNCH (SYNCHing) state.
+            * So, clocks to the rescue.  If we've "just"
+            * received a DM, then we test for the
+            * presence of OOB data when the receive OOB
+            * fails (and AFTER we did the normal mode read
+            * to clear "at the mark").
+            */
+       if (SYNCHing) {
+           int atmark;
+
+           ioctl(net, SIOCATMARK, (char *)&atmark);
+           if (atmark) {
+               c = recv(net, sibuf, canread, MSG_OOB);
+               if ((c == -1) && (errno == EINVAL)) {
+                   c = read(net, sibuf, canread);
+                   if (clocks.didnetreceive < clocks.gotDM) {
+                       SYNCHing = stilloob(net);
+                   }
+               }
+           } else {
+               c = read(net, sibuf, canread);
+           }
+       } else {
+           c = read(net, sibuf, canread);
+       }
+       settimer(didnetreceive);
+#else  /* !defined(SO_OOBINLINE) */
+       c = read(net, sbp, canread);
+#endif /* !defined(SO_OOBINLINE) */
+       if (c < 0 && errno == EWOULDBLOCK) {
+           c = 0;
+       } else if (c <= 0) {
+           return -1;
+       }
+       if (netdata) {
+           Dump('<', sbp, c);
+       }
+       scc += c;
+       returnValue = 1;
+    }
+
+    /*
+     * Something to read from the tty...
+     */
+    if (FD_ISSET(tin, &ibits)) {
+       FD_CLR(tin, &ibits);
+       if (tcc == 0) {
+           tbp = tibuf;        /* nothing left, reset */
+       }
+       c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
+       if (c < 0 && errno == EWOULDBLOCK) {
+           c = 0;
+       } else {
+           /* EOF detection for line mode!!!! */
+           if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
+                       /* must be an EOF... */
+               *tbp = ntc.t_eofc;
+               c = 1;
+           }
+           if (c <= 0) {
+               tcc = c;
+               return -1;
+           }
+       }
+       tcc += c;
+       returnValue = 1;                /* did something useful */
+    }
+
+#   if defined(TN3270)
+    if (tcc > 0) {
+       if (In3270) {
+           c = DataFromTerminal(tbp, tcc);
+           if (c) {
+               returnValue = 1;
+           }
+           tcc -= c;
+           tbp += c;
+       } else {
+#   endif defined(TN3270)
+           returnValue = 1;
+           while (tcc > 0) {
+               register int sc;
+
+               if (NETROOM() < 2) {
+                   flushline = 1;
+                   break;
+               }
+               c = *tbp++ & 0xff, sc = strip(c), tcc--;
+               if (sc == escape) {
+                   command(0);
+                   tcc = 0;
+                   flushline = 1;
+                   break;
+               } else if (MODE_LINE(globalmode) && (sc == echoc)) {
+                   if (tcc > 0 && strip(*tbp) == echoc) {
+                       tbp++;
+                       tcc--;
+                   } else {
+                       dontlecho = !dontlecho;
+                       settimer(echotoggle);
+                       setconnmode();
+                       tcc = 0;
+                       flushline = 1;
+                       break;
+                   }
+               }
+               if (localchars) {
+                   if (sc == ntc.t_intrc) {
+                       intp();
+                       break;
+                   } else if (sc == ntc.t_quitc) {
+                       sendbrk();
+                       break;
+                   } else if (sc == nltc.t_flushc) {
+                       NET2ADD(IAC, AO);
+                       if (autoflush) {
+                           doflush();
+                       }
+                       break;
+                   } else if (MODE_LOCAL_CHARS(globalmode)) {
+                       ;
+                   } else if (sc == nttyb.sg_kill) {
+                       NET2ADD(IAC, EL);
+                       break;
+                   } else if (sc == nttyb.sg_erase) {
+                       NET2ADD(IAC, EC);
+                       break;
+                   }
+               }
+               switch (c) {
+               case '\n':
+                       /*
+                        * If we are in CRMOD mode (\r ==> \n)
+                        * on our local machine, then probably
+                        * a newline (unix) is CRLF (TELNET).
+                        */
+                   if (MODE_LOCAL_CHARS(globalmode)) {
+                       NETADD('\r');
+                   }
+                   NETADD('\n');
+                   flushline = 1;
+                   break;
+               case '\r':
+                   NET2ADD('\r', '\0');
+                   flushline = 1;
+                   break;
+               case IAC:
+                   NET2ADD(IAC, IAC);
+                   break;
+               default:
+                   NETADD(c);
+                   break;
+               }
+           }
+#   if defined(TN3270)
+       }
+    }
+#   endif /* defined(TN3270) */
+
+    if ((!MODE_LINE(globalmode) || flushline) &&
+       FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
+       FD_CLR(net, &obits);
+       returnValue = netflush();
+    }
+    if (scc > 0) {
+#      if !defined(TN3270)
+       telrcv();
+       returnValue = 1;
+#      else /* !defined(TN3270) */
+       returnValue = Push3270();
+#      endif /* !defined(TN3270) */
+    }
+    if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
+       FD_CLR(tout, &obits);
+       returnValue = ttyflush();
+    }
+    return returnValue;
+}
+\f
+/*
+ * Select from tty and network...
+ */
+static void
+telnet()
+{
+    int on = 1;
+#if    defined(TN3270) && defined(unix)
+    int myPid;
+#endif /* defined(TN3270) */
+
+    tout = fileno(stdout);
+    tin = fileno(stdin);
+    setconnmode();
+    scc = 0;
+    tcc = 0;
+    FD_ZERO(&ibits);
+    FD_ZERO(&obits);
+    FD_ZERO(&xbits);
+
+    ioctl(net, FIONBIO, (char *)&on);
+
+#if    defined(TN3270)
+#if    !defined(DEBUG)         /* DBX can't handle! */
+    ioctl(net, FIOASYNC, (char *)&on); /* hear about input */
+#endif /* !defined(DEBUG) */
+
+#if    defined(unix)
+    myPid = getpid();
+#if    defined(NOT43)
+    myPid = -myPid;
+#endif /* defined(NOT43) */
+    ioctl(net, SIOCSPGRP, (char *)&myPid);     /* set my pid */
+#endif /* defined(unix) */
+
+#endif /* defined(TN3270) */
+
+#if    defined(SO_OOBINLINE)
+    setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* defined(SO_OOBINLINE) */
+
+    if (telnetport) {
+       if (!hisopts[TELOPT_SGA]) {
+           willoption(TELOPT_SGA, 0);
+       }
+#   if !defined(TN3270)
+       if (!myopts[TELOPT_TTYPE]) {
+           dooption(TELOPT_TTYPE, 0);
+       }
+#   endif !defined(TN3270)
+    }
+
+#   if !defined(TN3270)
+    for (;;) {
+       if (Scheduler(1) == -1) {
+           setcommandmode();
+           return;
+       }
+    }
+#   else /* !defined(TN3270) */
+    for (;;) {
+       int schedValue;
+
+       while (!In3270) {
+           if (Scheduler(1) == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+
+       while ((schedValue = Scheduler(0)) != 0) {
+           if (schedValue == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+               /* If there is data waiting to go out to terminal, don't
+                * schedule any more data for the terminal.
+                */
+       if (tfrontp-tbackp) {
+           schedValue = 1;
+       } else {
+           schedValue = DoTerminalOutput();
+       }
+       if (schedValue) {
+           if (Scheduler(1) == -1) {
+               setcommandmode();
+               return;
+           }
+       }
+    }
+#   endif /* !defined(TN3270) */
+}
+\f
+/*
+ *     The following are data structures and routines for
+ *     the "send" command.
+ *
+ */
+struct sendlist {
+    char       *name;          /* How user refers to it (case independent) */
+    int                what;           /* Character to be sent (<0 ==> special) */
+    char       *help;          /* Help information (0 ==> no help) */
+#if    defined(NOT43)
+    int                (*routine)();   /* Routine to perform (for special ops) */
+#else  /* defined(NOT43) */
+    void       (*routine)();   /* Routine to perform (for special ops) */
+#endif /* defined(NOT43) */
+};
+\f
+#define        SENDQUESTION    -1
+#define        SENDESCAPE      -3
+
+static struct sendlist Sendlist[] = {
+    { "ao", AO, "Send Telnet Abort output" },
+    { "ayt", AYT, "Send Telnet 'Are You There'" },
+    { "brk", BREAK, "Send Telnet Break" },
+    { "ec", EC, "Send Telnet Erase Character" },
+    { "el", EL, "Send Telnet Erase Line" },
+    { "escape", SENDESCAPE, "Send current escape character" },
+    { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
+    { "ip", IP, "Send Telnet Interrupt Process" },
+    { "nop", NOP, "Send Telnet 'No operation'" },
+    { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
+    { "?", SENDQUESTION, "Display send options" },
+    { 0 }
+};
+
+static struct sendlist Sendlist2[] = {         /* some synonyms */
+       { "break", BREAK, 0 },
+
+       { "intp", IP, 0 },
+       { "interrupt", IP, 0 },
+       { "intr", IP, 0 },
+
+       { "help", SENDQUESTION, 0 },
+
+       { 0 }
+};
+
+static char **
+getnextsend(name)
+char *name;
+{
+    struct sendlist *c = (struct sendlist *) name;
+
+    return (char **) (c+1);
+}
+
+static struct sendlist *
+getsend(name)
+char *name;
+{
+    struct sendlist *sl;
+
+    if ((sl = (struct sendlist *)
+                       genget(name, (char **) Sendlist, getnextsend)) != 0) {
+       return sl;
+    } else {
+       return (struct sendlist *)
+                               genget(name, (char **) Sendlist2, getnextsend);
+    }
+}
+
+static
+sendcmd(argc, argv)
+int    argc;
+char   **argv;
+{
+    int what;          /* what we are sending this time */
+    int count;         /* how many bytes we are going to need to send */
+    int i;
+    int question = 0;  /* was at least one argument a question */
+    struct sendlist *s;        /* pointer to current command */
+
+    if (argc < 2) {
+       printf("need at least one argument for 'send' command\n");
+       printf("'send ?' for help\n");
+       return 0;
+    }
+    /*
+     * First, validate all the send arguments.
+     * In addition, we see how much space we are going to need, and
+     * whether or not we will be doing a "SYNCH" operation (which
+     * flushes the network queue).
+     */
+    count = 0;
+    for (i = 1; i < argc; i++) {
+       s = getsend(argv[i]);
+       if (s == 0) {
+           printf("Unknown send argument '%s'\n'send ?' for help.\n",
+                       argv[i]);
+           return 0;
+       } else if (s == Ambiguous(struct sendlist *)) {
+           printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
+                       argv[i]);
+           return 0;
+       }
+       switch (s->what) {
+       case SENDQUESTION:
+           break;
+       case SENDESCAPE:
+           count += 1;
+           break;
+       case SYNCH:
+           count += 2;
+           break;
+       default:
+           count += 2;
+           break;
+       }
+    }
+    /* Now, do we have enough room? */
+    if (NETROOM() < count) {
+       printf("There is not enough room in the buffer TO the network\n");
+       printf("to process your request.  Nothing will be done.\n");
+       printf("('send synch' will throw away most data in the network\n");
+       printf("buffer, if this might help.)\n");
+       return 0;
+    }
+    /* OK, they are all OK, now go through again and actually send */
+    for (i = 1; i < argc; i++) {
+       if ((s = getsend(argv[i])) == 0) {
+           fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
+           quit();
+           /*NOTREACHED*/
+       }
+       if (s->routine) {
+           (*s->routine)(s);
+       } else {
+           switch (what = s->what) {
+           case SYNCH:
+               dosynch();
+               break;
+           case SENDQUESTION:
+               for (s = Sendlist; s->name; s++) {
+                   if (s->help) {
+                       printf(s->name);
+                       if (s->help) {
+                           printf("\t%s", s->help);
+                       }
+                       printf("\n");
+                   }
+               }
+               question = 1;
+               break;
+           case SENDESCAPE:
+               NETADD(escape);
+               break;
+           default:
+               NET2ADD(IAC, what);
+               break;
+           }
+       }
+    }
+    return !question;
+}
+\f
+/*
+ * The following are the routines and data structures referred
+ * to by the arguments to the "toggle" command.
+ */
+
+static
+lclchars()
+{
+    donelclchars = 1;
+    return 1;
+}
+
+static
+togdebug()
+{
+#ifndef        NOT43
+    if (net > 0 &&
+       setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
+                                                                       < 0) {
+           perror("setsockopt (SO_DEBUG)");
+    }
+#else  NOT43
+    if (debug) {
+       if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
+           perror("setsockopt (SO_DEBUG)");
+    } else
+       printf("Cannot turn off socket debugging\n");
+#endif NOT43
+    return 1;
+}
+
+
+
+extern int togglehelp();
+
+struct togglelist {
+    char       *name;          /* name of toggle */
+    char       *help;          /* help message */
+    int                (*handler)();   /* routine to do actual setting */
+    int                dohelp;         /* should we display help information */
+    int                *variable;
+    char       *actionexplanation;
+};
+
+static struct togglelist Togglelist[] = {
+    { "autoflush",
+       "toggle flushing of output when sending interrupt characters",
+           0,
+               1,
+                   &autoflush,
+                       "flush output when sending interrupt characters" },
+    { "autosynch",
+       "toggle automatic sending of interrupt characters in urgent mode",
+           0,
+               1,
+                   &autosynch,
+                       "send interrupt characters in urgent mode" },
+    { "crmod",
+       "toggle mapping of received carriage returns",
+           0,
+               1,
+                   &crmod,
+                       "map carriage return on output" },
+    { "localchars",
+       "toggle local recognition of certain control characters",
+           lclchars,
+               1,
+                   &localchars,
+                       "recognize certain control characters" },
+    { " ", "", 0, 1 },         /* empty line */
+    { "debug",
+       "(debugging) toggle debugging",
+           togdebug,
+               1,
+                   &debug,
+                       "turn on socket level debugging" },
+    { "netdata",
+       "(debugging) toggle printing of hexadecimal network data",
+           0,
+               1,
+                   &netdata,
+                       "print hexadecimal representation of network traffic" },
+    { "options",
+       "(debugging) toggle viewing of options processing",
+           0,
+               1,
+                   &showoptions,
+                       "show option processing" },
+    { " ", "", 0, 1 },         /* empty line */
+    { "?",
+       "display help information",
+           togglehelp,
+               1 },
+    { "help",
+       "display help information",
+           togglehelp,
+               0 },
+    { 0 }
+};
+
+static
+togglehelp()
+{
+    struct togglelist *c;
+
+    for (c = Togglelist; c->name; c++) {
+       if (c->dohelp) {
+           printf("%s\t%s\n", c->name, c->help);
+       }
+    }
+    return 0;
+}
+
+static char **
+getnexttoggle(name)
+char *name;
+{
+    struct togglelist *c = (struct togglelist *) name;
+
+    return (char **) (c+1);
+}
+
+static struct togglelist *
+gettoggle(name)
+char *name;
+{
+    return (struct togglelist *)
+                       genget(name, (char **) Togglelist, getnexttoggle);
+}
+
+static
+toggle(argc, argv)
+int    argc;
+char   *argv[];
+{
+    int retval = 1;
+    char *name;
+    struct togglelist *c;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
+       return 0;
+    }
+    argc--;
+    argv++;
+    while (argc--) {
+       name = *argv++;
+       c = gettoggle(name);
+       if (c == Ambiguous(struct togglelist *)) {
+           fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
+                                       name);
+           return 0;
+       } else if (c == 0) {
+           fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
+                                       name);
+           return 0;
+       } else {
+           if (c->variable) {
+               *c->variable = !*c->variable;           /* invert it */
+               printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+           }
+           if (c->handler) {
+               retval &= (*c->handler)(c);
+           }
+       }
+    }
+    return retval;
+}
+\f
+/*
+ * The following perform the "set" command.
+ */
+
+struct setlist {
+    char *name;                                /* name */
+    char *help;                                /* help information */
+    char *charp;                       /* where it is located at */
+};
+
+static struct setlist Setlist[] = {
+    { "echo",  "character to toggle local echoing on/off", &echoc },
+    { "escape",        "character to escape back to telnet command mode", &escape },
+    { " ", "" },
+    { " ", "The following need 'localchars' to be toggled true", 0 },
+    { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
+    { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
+    { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
+    { "kill",  "character to cause an Erase Line", &nttyb.sg_kill },
+    { "quit",  "character to cause a Break", &ntc.t_quitc },
+    { "eof",   "character to cause an EOF ", &ntc.t_eofc },
+    { 0 }
+};
+
+static char **
+getnextset(name)
+char *name;
+{
+    struct setlist *c = (struct setlist *)name;
+
+    return (char **) (c+1);
+}
+
+static struct setlist *
+getset(name)
+char *name;
+{
+    return (struct setlist *) genget(name, (char **) Setlist, getnextset);
+}
+
+static
+setcmd(argc, argv)
+int    argc;
+char   *argv[];
+{
+    int value;
+    struct setlist *ct;
+
+    /* XXX back we go... sigh */
+    if (argc != 3) {
+       if ((argc == 2) &&
+                   ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
+           for (ct = Setlist; ct->name; ct++) {
+               printf("%s\t%s\n", ct->name, ct->help);
+           }
+           printf("?\tdisplay help information\n");
+       } else {
+           printf("Format is 'set Name Value'\n'set ?' for help.\n");
+       }
+       return 0;
+    }
+
+    ct = getset(argv[1]);
+    if (ct == 0) {
+       fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
+                       argv[1]);
+       return 0;
+    } else if (ct == Ambiguous(struct setlist *)) {
+       fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
+                       argv[1]);
+       return 0;
+    } else {
+       if (strcmp("off", argv[2])) {
+           value = special(argv[2]);
+       } else {
+           value = -1;
+       }
+       *(ct->charp) = value;
+       printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+    }
+    return 1;
+}
+\f
+/*
+ * The following are the data structures and routines for the
+ * 'mode' command.
+ */
+
+static
+dolinemode()
+{
+    if (hisopts[TELOPT_SGA]) {
+       wontoption(TELOPT_SGA, 0);
+    }
+    if (hisopts[TELOPT_ECHO]) {
+       wontoption(TELOPT_ECHO, 0);
+    }
+    return 1;
+}
+
+static
+docharmode()
+{
+    if (!hisopts[TELOPT_SGA]) {
+       willoption(TELOPT_SGA, 0);
+    }
+    if (!hisopts[TELOPT_ECHO]) {
+       willoption(TELOPT_ECHO, 0);
+    }
+    return 1;
+}
+
+static struct cmd Modelist[] = {
+    { "character",     "character-at-a-time mode",     docharmode, 1, 1 },
+    { "line",          "line-by-line mode",            dolinemode, 1, 1 },
+    { 0 },
+};
+
+static char **
+getnextmode(name)
+char *name;
+{
+    struct cmd *c = (struct cmd *) name;
+
+    return (char **) (c+1);
+}
+
+static struct cmd *
+getmodecmd(name)
+char *name;
+{
+    return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
+}
+
+static
+modecmd(argc, argv)
+int    argc;
+char   *argv[];
+{
+    struct cmd *mt;
+
+    if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
+       printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
+       for (mt = Modelist; mt->name; mt++) {
+           printf("%s\t%s\n", mt->name, mt->help);
+       }
+       return 0;
+    }
+    mt = getmodecmd(argv[1]);
+    if (mt == 0) {
+       fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
+       return 0;
+    } else if (mt == Ambiguous(struct cmd *)) {
+       fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
+       return 0;
+    } else {
+       (*mt->handler)();
+    }
+    return 1;
+}
+\f
+/*
+ * The following data structures and routines implement the
+ * "display" command.
+ */
+
+static
+display(argc, argv)
+int    argc;
+char   *argv[];
+{
+#define        dotog(tl)       if (tl->variable && tl->actionexplanation) { \
+                           if (*tl->variable) { \
+                               printf("will"); \
+                           } else { \
+                               printf("won't"); \
+                           } \
+                           printf(" %s.\n", tl->actionexplanation); \
+                       }
+
+#define        doset(sl)   if (sl->name && *sl->name != ' ') { \
+                       printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
+                   }
+
+    struct togglelist *tl;
+    struct setlist *sl;
+
+    if (argc == 1) {
+       for (tl = Togglelist; tl->name; tl++) {
+           dotog(tl);
+       }
+       printf("\n");
+       for (sl = Setlist; sl->name; sl++) {
+           doset(sl);
+       }
+    } else {
+       int i;
+
+       for (i = 1; i < argc; i++) {
+           sl = getset(argv[i]);
+           tl = gettoggle(argv[i]);
+           if ((sl == Ambiguous(struct setlist *)) ||
+                               (tl == Ambiguous(struct togglelist *))) {
+               printf("?Ambiguous argument '%s'.\n", argv[i]);
+               return 0;
+           } else if (!sl && !tl) {
+               printf("?Unknown argument '%s'.\n", argv[i]);
+               return 0;
+           } else {
+               if (tl) {
+                   dotog(tl);
+               }
+               if (sl) {
+                   doset(sl);
+               }
+           }
+       }
+    }
+    return 1;
+#undef doset
+#undef dotog
+}
+\f
+/*
+ * The following are the data structures, and many of the routines,
+ * relating to command processing.
+ */
+
+/*
+ * Set the escape character.
+ */
+static
+setescape(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register char *arg;
+       char buf[50];
+
+       printf(
+           "Deprecated usage - please use 'set escape%s%s' in the future.\n",
+                               (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
+       if (argc > 2)
+               arg = argv[1];
+       else {
+               printf("new escape character: ");
+               gets(buf);
+               arg = buf;
+       }
+       if (arg[0] != '\0')
+               escape = arg[0];
+       if (!In3270) {
+               printf("Escape character is '%s'.\n", control(escape));
+       }
+       fflush(stdout);
+       return 1;
+}
+
+/*VARARGS*/
+static
+togcrmod()
+{
+    crmod = !crmod;
+    printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
+    printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
+    fflush(stdout);
+    return 1;
+}
+
+/*VARARGS*/
+suspend()
+{
+       setcommandmode();
+#if    defined(unix)
+       kill(0, SIGTSTP);
+#endif /* defined(unix) */
+       /* reget parameters in case they were changed */
+       ioctl(0, TIOCGETP, (char *)&ottyb);
+       ioctl(0, TIOCGETC, (char *)&otc);
+       ioctl(0, TIOCGLTC, (char *)&oltc);
+       return 1;
+}
+
+/*VARARGS*/
+static
+bye()
+{
+    if (connected) {
+       shutdown(net, 2);
+       printf("Connection closed.\n");
+       close(net);
+       connected = 0;
+       /* reset options */
+       bzero((char *)hisopts, sizeof hisopts);
+       bzero((char *)myopts, sizeof myopts);
+       SYNCHing = flushout = 0;
+       flushline = 1;
+#if    defined(TN3270)
+               /*
+                * The problem is that we were called from command() which
+                * was called from DataFrom3270() which was called from
+                * DataFromTerminal() which was called from...
+                *
+                * So, just quit.
+                */
+       if (In3270) {
+           Exit(0);
+       }
+#endif /* defined(TN3270) */
+    }
+    return 1;
+}
+
+/*VARARGS*/
+quit()
+{
+       (void) call(bye, "bye", 0);
+       Exit(0);
+       /*NOTREACHED*/
+       return 1;                       /* just to keep lint happy */
+}
+
+/*
+ * Print status about the connection.
+ */
+static
+status(argc, argv)
+int    argc;
+char   *argv[];
+{
+    if (connected) {
+       printf("Connected to %s.\n", hostname);
+       if (argc < 2) {
+           printf("Operating in %s.\n",
+                               modelist[getconnmode()].modedescriptions);
+           if (localchars) {
+               printf("Catching signals locally.\n");
+           }
+       }
+    } else {
+       printf("No connection.\n");
+    }
+#   if !defined(TN3270)
+    printf("Escape character is '%s'.\n", control(escape));
+    fflush(stdout);
+#   else /* !defined(TN3270) */
+    if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
+       printf("Escape character is '%s'.\n", control(escape));
+    }
+#   if defined(unix)
+    if (In3270 && transcom) {
+       printf("Transparent mode command is '%s'.\n", transcom);
+    }
+#   endif /* defined(unix) */
+    fflush(stdout);
+    if (In3270) {
+       return 0;
+    }
+#   endif /* defined(TN3270) */
+    return 1;
+}
+
+#if    defined(TN3270) && defined(unix)
+static
+settranscom(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int i, len = 0;
+       char *strcpy(), *strcat();
+
+       if (argc == 1 && transcom) {
+          transcom = 0;
+       }
+       if (argc == 1) {
+          return;
+       }
+       for (i = 1; i < argc; ++i) {
+           len += 1 + strlen(argv[1]);
+       }
+       transcom = tline;
+       (void) strcpy(transcom, argv[1]);
+       for (i = 2; i < argc; ++i) {
+           (void) strcat(transcom, " ");
+           (void) strcat(transcom, argv[i]);
+       }
+}
+#endif /* defined(TN3270) && defined(unix) */
+
+
+static
+tn(argc, argv)
+       int argc;
+       char *argv[];
+{
+    register struct hostent *host = 0;
+#if defined(msdos)
+    char *cp;
+#endif /* defined(msdos) */
+
+    if (connected) {
+       printf("?Already connected to %s\n", hostname);
+       return 0;
+    }
+    if (argc < 2) {
+       (void) strcpy(line, "Connect ");
+       printf("(to) ");
+       gets(&line[strlen(line)]);
+       makeargv();
+       argc = margc;
+       argv = margv;
+    }
+    if (argc > 3) {
+       printf("usage: %s host-name [port]\n", argv[0]);
+       return 0;
+    }
+#if    defined(msdos)
+    for (cp = argv[1]; *cp; cp++) {
+       if (isupper(*cp)) {
+           *cp = tolower(*cp);
+       }
+    }
+#endif /* defined(msdos) */
+    sin.sin_addr.s_addr = inet_addr(argv[1]);
+    if (sin.sin_addr.s_addr != -1) {
+       sin.sin_family = AF_INET;
+       (void) strcpy(hnamebuf, argv[1]);
+       hostname = hnamebuf;
+    } else {
+       host = gethostbyname(argv[1]);
+       if (host) {
+           sin.sin_family = host->h_addrtype;
+#if    defined(h_addr)         /* In 4.3, this is a #define */
+           bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr, host->h_length);
+#else  /* defined(h_addr) */
+           bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
+#endif /* defined(h_addr) */
+           hostname = host->h_name;
+       } else {
+           printf("%s: unknown host\n", argv[1]);
+           return 0;
+       }
+    }
+    sin.sin_port = sp->s_port;
+    if (argc == 3) {
+       sin.sin_port = atoi(argv[2]);
+       if (sin.sin_port == 0) {
+           sp = getservbyname(argv[2], "tcp");
+           if (sp)
+               sin.sin_port = sp->s_port;
+           else {
+               printf("%s: bad port number\n", argv[2]);
+               return 0;
+           }
+       } else {
+           sin.sin_port = atoi(argv[2]);
+           sin.sin_port = htons(sin.sin_port);
+       }
+       telnetport = 0;
+    } else {
+       telnetport = 1;
+    }
+#if    defined(unix)
+    signal(SIGINT, intr);
+    signal(SIGQUIT, intr2);
+    signal(SIGPIPE, deadpeer);
+#endif /* defined(unix) */
+    printf("Trying...\n");
+    do {
+       net = socket(AF_INET, SOCK_STREAM, 0);
+       if (net < 0) {
+           perror("telnet: socket");
+           return 0;
+       }
+#ifndef        NOT43
+       if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG,
+                               (char *)&debug, sizeof(debug)) < 0)
+#else  NOT43
+       if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
+#endif NOT43
+               perror("setsockopt (SO_DEBUG)");
+
+       if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+#if    defined(h_addr)         /* In 4.3, this is a #define */
+           if (host && host->h_addr_list[1]) {
+               int oerrno = errno;
+
+               fprintf(stderr, "telnet: connect to address %s: ",
+                                               inet_ntoa(sin.sin_addr));
+               errno = oerrno;
+               perror((char *)0);
+               host->h_addr_list++;
+               bcopy(host->h_addr_list[0],
+                   (caddr_t)&sin.sin_addr, host->h_length);
+               fprintf(stderr, "Trying %s...\n",
+                       inet_ntoa(sin.sin_addr));
+               (void) close(net);
+               continue;
+           }
+#endif /* defined(h_addr) */
+           perror("telnet: Unable to connect to remote host");
+#if defined(unix)
+           signal(SIGINT, SIG_DFL);
+           signal(SIGQUIT, SIG_DFL);
+#endif /* defined(unix) */
+           return 0;
+           }
+       connected++;
+    } while (connected == 0);
+    call(status, "status", "notmuch", 0);
+    if (setjmp(peerdied) == 0)
+       telnet();
+    ExitString(stderr, "Connection closed by foreign host.\n",1);
+    /*NOTREACHED*/
+}
+
+
+#define HELPINDENT (sizeof ("connect"))
+
+static char
+       openhelp[] =    "connect to a site",
+       closehelp[] =   "close current connection",
+       quithelp[] =    "exit telnet",
+       zhelp[] =       "suspend telnet",
+       statushelp[] =  "print status information",
+       helphelp[] =    "print help information",
+       sendhelp[] =    "transmit special characters ('send ?' for more)",
+       sethelp[] =     "set operating parameters ('set ?' for more)",
+       togglestring[] ="toggle operating parameters ('toggle ?' for more)",
+       displayhelp[] = "display operating parameters",
+#if    defined(TN3270) && defined(unix)
+       transcomhelp[] = "specify Unix command for transparent mode pipe",
+#endif /* defined(TN3270) && defined(unix) */
+       modehelp[] = "try to enter line-by-line or character-at-a-time mode";
+
+extern int     help();
+
+static struct cmd cmdtab[] = {
+       { "close",      closehelp,      bye,            1, 1 },
+       { "display",    displayhelp,    display,        1, 0 },
+       { "mode",       modehelp,       modecmd,        1, 1 },
+       { "open",       openhelp,       tn,             1, 0 },
+       { "quit",       quithelp,       quit,           1, 0 },
+       { "send",       sendhelp,       sendcmd,        1, 1 },
+       { "set",        sethelp,        setcmd,         1, 0 },
+       { "status",     statushelp,     status,         1, 0 },
+       { "toggle",     togglestring,   toggle,         1, 0 },
+#if    defined(TN3270) && defined(unix)
+       { "transcom",   transcomhelp,   settranscom,    1, 0 },
+#endif /* defined(TN3270) && defined(unix) */
+       { "z",          zhelp,          suspend,        1, 0 },
+       { "?",          helphelp,       help,           1, 0 },
+       0
+};
+
+static char    crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
+static char    escapehelp[] =  "deprecated command -- use 'set escape' instead";
+
+static struct cmd cmdtab2[] = {
+       { "help",       helphelp,       help,           0, 0 },
+       { "escape",     escapehelp,     setescape,      1, 0 },
+       { "crmod",      crmodhelp,      togcrmod,       1, 0 },
+       0
+};
+
+/*
+ * Call routine with argc, argv set from args (terminated by 0).
+ * VARARGS2
+ */
+static
+call(routine, args)
+       int (*routine)();
+       char *args;
+{
+       register char **argp;
+       register int argc;
+
+       for (argc = 0, argp = &args; *argp++ != 0; argc++)
+               ;
+       return (*routine)(argc, &args);
+}
+
+static char **
+getnextcmd(name)
+char *name;
+{
+    struct cmd *c = (struct cmd *) name;
+
+    return (char **) (c+1);
+}
+
+static struct cmd *
+getcmd(name)
+char *name;
+{
+    struct cmd *cm;
+
+    if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
+       return cm;
+    } else {
+       return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
+    }
+}
+
+void
+command(top)
+       int top;
+{
+       register struct cmd *c;
+
+       setcommandmode();
+       if (!top) {
+               putchar('\n');
+       } else {
+#if    defined(unix)
+               signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+#endif /* defined(unix) */
+       }
+       for (;;) {
+               printf("%s> ", prompt);
+               if (gets(line) == NULL) {
+                       if (feof(stdin) || ferror(stdin))
+                               quit();
+                       break;
+               }
+               if (line[0] == 0)
+                       break;
+               makeargv();
+               c = getcmd(margv[0]);
+               if (c == Ambiguous(struct cmd *)) {
+                       printf("?Ambiguous command\n");
+                       continue;
+               }
+               if (c == 0) {
+                       printf("?Invalid command\n");
+                       continue;
+               }
+               if (c->needconnect && !connected) {
+                       printf("?Need to be connected first.\n");
+                       continue;
+               }
+               if ((*c->handler)(margc, margv)) {
+                       break;
+               }
+       }
+       if (!top) {
+               if (!connected) {
+                       longjmp(toplevel, 1);
+                       /*NOTREACHED*/
+               }
+               setconnmode();
+       }
+}
+\f
+/*
+ * Help command.
+ */
+static
+help(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register struct cmd *c;
+
+       if (argc == 1) {
+               printf("Commands may be abbreviated.  Commands are:\n\n");
+               for (c = cmdtab; c->name; c++)
+                       if (c->dohelp) {
+                               printf("%-*s\t%s\n", HELPINDENT, c->name,
+                                                                   c->help);
+                       }
+               return 0;
+       }
+       while (--argc > 0) {
+               register char *arg;
+               arg = *++argv;
+               c = getcmd(arg);
+               if (c == Ambiguous(struct cmd *))
+                       printf("?Ambiguous help command %s\n", arg);
+               else if (c == (struct cmd *)0)
+                       printf("?Invalid help command %s\n", arg);
+               else
+                       printf("%s\n", c->help);
+       }
+       return 0;
+}
+\f
+/*
+ * main.  Parse arguments, invoke the protocol or command parser.
+ */
+
+
+void
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+    sp = getservbyname("telnet", "tcp");
+    if (sp == 0) {
+       ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1);
+       /*NOTREACHED*/
+    }
+    NetTrace = stdout;
+    ioctl(0, TIOCGETP, (char *)&ottyb);
+    ioctl(0, TIOCGETC, (char *)&otc);
+    ioctl(0, TIOCGLTC, (char *)&oltc);
+#if    defined(LNOFLSH)
+    ioctl(0, TIOCLGET, (char *)&autoflush);
+    autoflush = !(autoflush&LNOFLSH);  /* if LNOFLSH, no autoflush */
+#else  /* LNOFLSH */
+    autoflush = 1;
+#endif /* LNOFLSH */
+    ntc = otc;
+    nltc = oltc;
+    nttyb = ottyb;
+    setbuf(stdin, (char *)0);
+    setbuf(stdout, (char *)0);
+    prompt = argv[0];
+    if (argc > 1 && !strcmp(argv[1], "-d")) {
+       debug = 1;
+       argv++;
+       argc--;
+    }
+    if (argc > 1 && !strcmp(argv[1], "-n")) {
+       argv++;
+       argc--;
+       if (argc > 1) {         /* get file name */
+           NetTrace = fopen(argv[1], "w");
+           argv++;
+           argc--;
+           if (NetTrace == NULL) {
+               NetTrace = stdout;
+           }
+       }
+    }
+#if    defined(TN3270) && defined(unix)
+    if (argc > 1 && !strcmp(argv[1], "-t")) {
+       argv++;
+       argc--;
+       if (argc > 1) {         /* get command name */
+           transcom = tline;
+           (void) strcpy(transcom, argv[1]);
+           argv++;
+           argc--;
+       }
+    }
+#endif /* defined(TN3270) && defined(unix) */
+    if (argc != 1) {
+       if (setjmp(toplevel) != 0)
+           Exit(0);
+       tn(argc, argv);
+    }
+    setjmp(toplevel);
+    for (;;)
+       command(1);
+}