minor touchups
[unix-history] / usr / src / usr.bin / telnet / telnet.c
index fe524fc..186ebc4 100644 (file)
@@ -11,7 +11,7 @@ char copyright[] =
 #endif not lint
 
 #ifndef lint
 #endif not lint
 
 #ifndef lint
-static char sccsid[] = "@(#)telnet.c   5.8 (Berkeley) %G%";
+static char sccsid[] = "@(#)telnet.c   5.14 (Berkeley) %G%";
 #endif not lint
 
 /*
 #endif not lint
 
 /*
@@ -20,11 +20,13 @@ static char sccsid[] = "@(#)telnet.c        5.8 (Berkeley) %G%";
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <sys/time.h>
 
 #include <netinet/in.h>
 
 #define        TELOPTS
 #include <arpa/telnet.h>
 
 #include <netinet/in.h>
 
 #define        TELOPTS
 #include <arpa/telnet.h>
+#include <arpa/inet.h>
 
 #include <stdio.h>
 #include <ctype.h>
 
 #include <stdio.h>
 #include <ctype.h>
@@ -32,18 +34,50 @@ static char sccsid[] = "@(#)telnet.c        5.8 (Berkeley) %G%";
 #include <signal.h>
 #include <setjmp.h>
 #include <netdb.h>
 #include <signal.h>
 #include <setjmp.h>
 #include <netdb.h>
+#include <strings.h>
 
 
-#define        strip(x)        ((x)&0177)
 
 
-char   ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
-#define        TTYADD(c)       { if (!flushing) { *tfrontp++ = c; } }
 
 
-char   netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
+#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)
+
+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)
+
+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        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)
 char   *neturg = 0;            /* one past last byte of urgent data */
 
 char   *neturg = 0;            /* one past last byte of urgent data */
 
+char   subbuffer[100], *subpointer, *subend;   /* 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); \
+                       }
+
 char   hisopts[256];
 char   myopts[256];
 
 char   hisopts[256];
 char   myopts[256];
 
@@ -64,24 +98,24 @@ int connected;
 int    net;
 int    tout;
 int    showoptions = 0;
 int    net;
 int    tout;
 int    showoptions = 0;
-int    options;
 int    debug = 0;
 int    crmod = 0;
 int    netdata = 0;
 static FILE    *NetTrace;
 int    telnetport = 1;
 
 int    debug = 0;
 int    crmod = 0;
 int    netdata = 0;
 static FILE    *NetTrace;
 int    telnetport = 1;
 
-int    flushing = 0;           /* are we in TELNET SYNCH mode? */
 
 char   *prompt;
 char   escape = CTRL(]);
 char   echoc = CTRL(E);
 
 char   *prompt;
 char   escape = CTRL(]);
 char   echoc = CTRL(E);
-int    flushout = 0;
-int    localsigs = 0;
-int    donelclsigs = 0;
-int    linemode;
-int    doechocharrecognition = 0;
-int    dontlecho = 0;          /* do we do local echoing right now? */
+
+int    SYNCHing = 0;           /* we are in TELNET SYNCH mode */
+int    flushout = 0;           /* flush output */
+int    autoflush = 0;          /* flush output when interrupting? */
+int    autosynch = 0;          /* send interrupt characters with SYNCH? */
+int    localchars = 0;         /* we recognize interrupt/quit */
+int    donelclchars = 0;       /* the user has set "localchars" */
+int    dontlecho = 0;          /* do we suppress local echoing right now? */
 
 char   line[200];
 int    margc;
 
 char   line[200];
 int    margc;
@@ -95,13 +129,11 @@ extern     int errno;
 
 struct sockaddr_in sin;
 
 
 struct sockaddr_in sin;
 
-int    intr(), deadpeer(), doescape();
-char   *control();
 struct cmd *getcmd();
 struct servent *sp;
 
 struct tchars otc, ntc;
 struct cmd *getcmd();
 struct servent *sp;
 
 struct tchars otc, ntc;
-struct ltchars oltc;
+struct ltchars oltc, nltc;
 struct sgttyb ottyb, nttyb;
 int    globalmode = 0;
 int    flushline = 1;
 struct sgttyb ottyb, nttyb;
 int    globalmode = 0;
 int    flushline = 1;
@@ -111,69 +143,28 @@ char      hnamebuf[32];
 
 /*
  * The following are some clocks used to decide how to interpret
 
 /*
  * The following are some clocks used to decide how to interpret
- * the relationship between various varibles.
+ * the relationship between various variables.
  */
 
 struct {
     int
        system,                 /* what the current time is */
        echotoggle,             /* last time user entered echo character */
  */
 
 struct {
     int
        system,                 /* what the current time is */
        echotoggle,             /* last time user entered echo character */
-       modenegotiated;         /* last time operating mode negotiated */
-} times;
-
-#define        settimer(x)     times.x = times.system++
+       modenegotiated,         /* last time operating mode negotiated */
+       didnetreceive,          /* last time we read data from network */
+       gotDM;                  /* when did we last see a data mark */
+} clocks;
 
 
-
-main(argc, argv)
-       int argc;
-       char *argv[];
-{
-       sp = getservbyname("telnet", "tcp");
-       if (sp == 0) {
-               fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
-               exit(1);
-       }
-       NetTrace = stdout;
-       ioctl(0, TIOCGETP, (char *)&ottyb);
-       ioctl(0, TIOCGETC, (char *)&otc);
-       ioctl(0, TIOCGLTC, (char *)&oltc);
-       ntc = otc;
-       ntc.t_eofc = -1;                /* we don't want to use EOF */
-       nttyb = ottyb;
-       setbuf(stdin, 0);
-       setbuf(stdout, 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 (argc != 1) {
-               if (setjmp(toplevel) != 0)
-                       exit(0);
-               tn(argc, argv);
-       }
-       setjmp(toplevel);
-       for (;;)
-               command(1);
-}
+#define        settimer(x)     clocks.x = clocks.system++
 \f
 /*
  * Various utility routines.
  */
 
 \f
 /*
  * Various utility routines.
  */
 
+char *ambiguous;               /* special return value */
+#define Ambiguous(t)   ((t)&ambiguous)
+
+
 char **
 genget(name, table, next)
 char   *name;          /* name to match */
 char **
 genget(name, table, next)
 char   *name;          /* name to match */
@@ -201,14 +192,14 @@ char      **(*next)();    /* routine to return next entry in table */
                }
        }
        if (nmatches > 1)
                }
        }
        if (nmatches > 1)
-               return ((char **)-1);
+               return Ambiguous(char **);
        return (found);
 }
 
 /*
  * Make a character string into a number.
  *
        return (found);
 }
 
 /*
  * Make a character string into a number.
  *
- * Todo:  1.  Could take random integers (123, 0x123, 0123, 0b123).
+ * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
  */
 
 special(s)
  */
 
 special(s)
@@ -221,7 +212,7 @@ register char *s;
        case '^':
                b = *++s;
                if (b == '?') {
        case '^':
                b = *++s;
                if (b == '?') {
-                   c = b | 0x80;               /* DEL */
+                   c = b | 0x40;               /* DEL */
                } else {
                    c = b & 0x1f;
                }
                } else {
                    c = b & 0x1f;
                }
@@ -232,6 +223,260 @@ register char *s;
        }
        return c;
 }
        }
        return c;
 }
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+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.
+ */
+
+void
+upcase(argument)
+register char *argument;
+{
+    register int c;
+
+    while (c = *argument) {
+       if (islower(c)) {
+           *argument = toupper(c);
+       }
+       argument++;
+    }
+}
+\f
+/*
+ * Check to see if any out-of-band data exists on a socket (for
+ * Telnet "synch" processing).
+ */
+
+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.
+ */
+
+
+netflush(fd)
+{
+    int n;
+
+    if ((n = nfrontp - nbackp) > 0) {
+       if (!neturg) {
+           n = write(fd, 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(fd, nbackp, n-1, 0);   /* send URGENT all by itself */
+           } else {
+               n = send(fd, nbackp, n, MSG_OOB);       /* URGENT data */
+           }
+       }
+    }
+    if (n < 0) {
+       if (errno != ENOBUFS && errno != EWOULDBLOCK) {
+           setcommandmode();
+           perror(hostname);
+           close(fd);
+           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;
+    }
+}
+\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.
+ */
+
+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.
+ */
+
+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
+/*
+ * Send as much data as possible to the terminal.
+ */
+
+
+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) {
+       return;
+    }
+    tbackp += n;
+    if (tbackp == tfrontp) {
+       tbackp = tfrontp = ttyobuf;
+    }
+}
 \f
 /*
  * Various signal handling routines.
 \f
 /*
  * Various signal handling routines.
@@ -245,7 +490,7 @@ deadpeer()
 
 intr()
 {
 
 intr()
 {
-    if (localsigs) {
+    if (localchars) {
        intp();
        return;
     }
        intp();
        return;
     }
@@ -255,7 +500,7 @@ intr()
 
 intr2()
 {
 
 intr2()
 {
-    if (localsigs) {
+    if (localchars) {
        sendbrk();
        return;
     }
        sendbrk();
        return;
     }
@@ -266,14 +511,77 @@ doescape()
     command(0);
 }
 \f
     command(0);
 }
 \f
+/*
+ * The following are routines used to print out debugging information.
+ */
+
+
+static
+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*/
+printoption(direction, fmt, option, what)
+       char *direction, *fmt;
+       int option, what;
+{
+       if (!showoptions)
+               return;
+       printf("%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]))
+               printf("%s %s", fmt, telopts[option]);
+       else
+               printf("%s %d", fmt, option);
+       if (*direction == '<') {
+               printf("\r\n");
+               return;
+       }
+       printf(" (%s)\r\n", what ? "reply" : "don't reply");
+}
+\f
 /*
  * Mode - set up terminal to a specific mode.
  */
 
 /*
  * Mode - set up terminal to a specific mode.
  */
 
-struct tchars notc =   { -1, -1, -1, -1, -1, -1 };
-struct tchars notc2;
-struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
-struct ltchars noltc2;
 
 mode(f)
        register int f;
 
 mode(f)
        register int f;
@@ -283,10 +591,14 @@ mode(f)
        struct ltchars *ltc;
        struct sgttyb sb;
        int onoff, old;
        struct ltchars *ltc;
        struct sgttyb sb;
        int onoff, old;
+       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)
 
        globalmode = f;
        if (prevmode == f)
-               return (f);
+               return;
        old = prevmode;
        prevmode = f;
        sb = nttyb;
        old = prevmode;
        prevmode = f;
        sb = nttyb;
@@ -311,19 +623,18 @@ mode(f)
                 * If user hasn't specified one way or the other,
                 * then default to not trapping signals.
                 */
                 * If user hasn't specified one way or the other,
                 * then default to not trapping signals.
                 */
-               if (!donelclsigs)
-                       localsigs = 0;
-               if (localsigs) {
+               if (!donelclchars) {
+                       localchars = 0;
+               }
+               if (localchars) {
                        notc2 = notc;
                        notc2.t_intrc = ntc.t_intrc;
                        notc2.t_quitc = ntc.t_quitc;
                        notc2 = notc;
                        notc2.t_intrc = ntc.t_intrc;
                        notc2.t_quitc = ntc.t_quitc;
-                       notc2.t_eofc = ntc.t_eofc;
                        tc = &notc2;
                } else
                        tc = &notc;
                ltc = &noltc;
                onoff = 1;
                        tc = &notc2;
                } else
                        tc = &notc;
                ltc = &noltc;
                onoff = 1;
-               linemode = 0;
                break;
        case 3:         /* local character processing, remote echo */
        case 4:         /* local character processing, local echo */
                break;
        case 3:         /* local character processing, remote echo */
        case 4:         /* local character processing, local echo */
@@ -334,25 +645,26 @@ mode(f)
                        sb.sg_flags |= ECHO;
                else
                        sb.sg_flags &= ~ECHO;
                        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 user hasn't specified one way or the other,
                 * then default to trapping signals.
                 */
-               if (!donelclsigs)
-                       localsigs = 1;
-               if (localsigs)
-                       tc = &ntc;
-               else {
-                       notc2 = ntc;
+               if (!donelclchars) {
+                       localchars = 1;
+               }
+               if (localchars) {
+                       notc2.t_brkc = nltc.t_flushc;
+                       noltc2.t_flushc = -1;
+               } else {
                        notc2.t_intrc = notc2.t_quitc = -1;
                        notc2.t_intrc = notc2.t_quitc = -1;
-                       tc = &notc2;
                }
                }
-               noltc2 = oltc;
                noltc2.t_suspc = escape;
                noltc2.t_dsuspc = -1;
                noltc2.t_suspc = escape;
                noltc2.t_dsuspc = -1;
-               ltc = &noltc2;
                onoff = 1;
                onoff = 1;
-               linemode = 1;
                break;
 
        default:
                break;
 
        default:
@@ -361,37 +673,50 @@ mode(f)
        ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
        ioctl(fileno(stdin), TIOCSETC, (char *)tc);
        ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
        ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
        ioctl(fileno(stdin), TIOCSETC, (char *)tc);
        ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
-       ioctl(fileno(stdin), FIONBIO, &onoff);
-       ioctl(fileno(stdout), FIONBIO, &onoff);
+       ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
+       ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
        if (f >= 3)
                signal(SIGTSTP, doescape);
        else if (old >= 3) {
                signal(SIGTSTP, SIG_DFL);
                sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
        }
        if (f >= 3)
                signal(SIGTSTP, doescape);
        else if (old >= 3) {
                signal(SIGTSTP, SIG_DFL);
                sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
        }
-       return (old);
 }
 }
-
+\f
 /*
  * These routines decides on what the mode should be (based on the values
  * of various global variables).
  */
 
 /*
  * These routines decides on what the mode should be (based on the values
  * of various global variables).
  */
 
-setconnmode()
+char *modedescriptions[] = {
+       "telnet command mode",                                  /* 0 */
+       "character-at-a-time mode",                             /* 1 */
+       "character-at-a-time mode (local echo)",                /* 2 */
+       "line-by-line mode (remote echo)",                      /* 3 */
+       "line-by-line mode",                                    /* 4 */
+       "line-by-line mode (local echoing suppressed)",         /* 5 */
+};
+
+getconnmode()
 {
     static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
 {
     static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
-    int index = 0;
+    int modeindex = 0;
 
     if (hisopts[TELOPT_ECHO]) {
 
     if (hisopts[TELOPT_ECHO]) {
-       index += 2;
+       modeindex += 2;
     }
     if (hisopts[TELOPT_SGA]) {
     }
     if (hisopts[TELOPT_SGA]) {
-       index += 4;
+       modeindex += 4;
     }
     }
-    if (dontlecho && (times.echotoggle > times.modenegotiated)) {
-       index += 1;
+    if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
+       modeindex += 1;
     }
     }
-    mode(newmode[index]);
+    return newmode[modeindex];
+}
+
+setconnmode()
+{
+    mode(getconnmode());
 }
 
 
 }
 
 
@@ -404,6 +729,7 @@ char        sibuf[BUFSIZ], *sbp;
 char   tibuf[BUFSIZ], *tbp;
 int    scc, tcc;
 
 char   tibuf[BUFSIZ], *tbp;
 int    scc, tcc;
 
+
 /*
  * Select from tty and network...
  */
 /*
  * Select from tty and network...
  */
@@ -412,38 +738,48 @@ telnet()
        register int c;
        int tin = fileno(stdin);
        int on = 1;
        register int c;
        int tin = fileno(stdin);
        int on = 1;
+       fd_set ibits, obits, xbits;
 
        tout = fileno(stdout);
        setconnmode();
 
        tout = fileno(stdout);
        setconnmode();
-       ioctl(net, FIONBIO, &on);
-       if (telnetport && !hisopts[TELOPT_SGA]) {
-               willoption(TELOPT_SGA);
+       scc = 0;
+       tcc = 0;
+       FD_ZERO(&ibits);
+       FD_ZERO(&obits);
+       FD_ZERO(&xbits);
+
+       ioctl(net, FIONBIO, (char *)&on);
+#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 (!myopts[TELOPT_TTYPE]) {
+               dooption(TELOPT_TTYPE, 0);
+           }
        }
        for (;;) {
        }
        for (;;) {
-               fd_set ibits, obits, xbits;
-
                if (scc < 0 && tcc < 0) {
                        break;
                }
 
                if (scc < 0 && tcc < 0) {
                        break;
                }
 
-               FD_ZERO(&ibits);
-               FD_ZERO(&obits);
-               FD_ZERO(&xbits);
-
-               if (((globalmode < 4) || flushline) && (nfrontp - nbackp)) {
+               if (((globalmode < 4) || flushline) && NETBYTES()) {
                        FD_SET(net, &obits);
                } else {
                        FD_SET(tin, &ibits);
                }
                        FD_SET(net, &obits);
                } else {
                        FD_SET(tin, &ibits);
                }
-               if (tfrontp - tbackp) {
+               if (TTYBYTES()) {
                        FD_SET(tout, &obits);
                } else {
                        FD_SET(net, &ibits);
                }
                        FD_SET(tout, &obits);
                } else {
                        FD_SET(net, &ibits);
                }
-               if (!flushing) {
+               if (!SYNCHing) {
                        FD_SET(net, &xbits);
                }
                        FD_SET(net, &xbits);
                }
-               if ((c = select(16, &ibits, &obits, &xbits, 0)) < 1) {
+               if ((c = select(16, &ibits, &obits, &xbits,
+                                               (struct timeval *)0)) < 1) {
                        if (c == -1) {
                                /*
                                 * we can get EINTR if we are in line mode,
                        if (c == -1) {
                                /*
                                 * we can get EINTR if we are in line mode,
@@ -462,7 +798,8 @@ telnet()
                 * Any urgent data?
                 */
                if (FD_ISSET(net, &xbits)) {
                 * Any urgent data?
                 */
                if (FD_ISSET(net, &xbits)) {
-                   flushing = 1;
+                   FD_CLR(net, &xbits);
+                   SYNCHing = 1;
                    ttyflush(); /* flush already enqueued data */
                }
 
                    ttyflush(); /* flush already enqueued data */
                }
 
@@ -470,48 +807,121 @@ telnet()
                 * Something to read from the network...
                 */
                if (FD_ISSET(net, &ibits)) {
                 * Something to read from the network...
                 */
                if (FD_ISSET(net, &ibits)) {
-                       scc = read(net, sibuf, sizeof (sibuf));
-                       if (scc < 0 && errno == EWOULDBLOCK)
-                               scc = 0;
-                       else {
-                               if (scc <= 0)
-                                       break;
-                               sbp = sibuf;
-                               if (netdata) {
-                                       Dump('<', sbp, scc);
+                       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) {
+                       break;
+                   }
+                   if (netdata) {
+                       Dump('<', sbp, c);
+                   }
+                   scc += c;
                }
 
                /*
                 * Something to read from the tty...
                 */
                if (FD_ISSET(tin, &ibits)) {
                }
 
                /*
                 * Something to read from the tty...
                 */
                if (FD_ISSET(tin, &ibits)) {
-                       tcc = read(tin, tibuf, sizeof (tibuf));
-                       if (tcc < 0 && errno == EWOULDBLOCK)
-                               tcc = 0;
-                       else {
-                               if (tcc <= 0)
+                       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 && globalmode >= 3) {
+                                       /* must be an EOF... */
+                                       *tbp = ntc.t_eofc;
+                                       c = 1;
+                               }
+                               if (c <= 0) {
+                                       tcc = c;
                                        break;
                                        break;
-                               tbp = tibuf;
+                               }
                        }
                        }
+                       tcc += c;
                }
 
                while (tcc > 0) {
                }
 
                while (tcc > 0) {
-                       register int c;
+                       register int sc;
 
 
-                       if ((&netobuf[BUFSIZ] - nfrontp) < 2) {
+                       if (NETROOM() < 2) {
                                flushline = 1;
                                break;
                        }
                                flushline = 1;
                                break;
                        }
-                       c = *tbp++ & 0377, tcc--;
-                       if (strip(c) == escape) {
+                       c = *tbp++ & 0xff, sc = strip(c), tcc--;
+                       if (sc == escape) {
                                command(0);
                                tcc = 0;
                                flushline = 1;
                                break;
                                command(0);
                                tcc = 0;
                                flushline = 1;
                                break;
-                       } else if ((globalmode >= 4) && doechocharrecognition &&
-                                                       (strip(c) == echoc)) {
+                       } else if ((globalmode >= 4) && (sc == echoc)) {
                                if (tcc > 0 && strip(*tbp) == echoc) {
                                        tbp++;
                                        tcc--;
                                if (tcc > 0 && strip(*tbp) == echoc) {
                                        tbp++;
                                        tcc--;
@@ -524,19 +934,25 @@ telnet()
                                        break;
                                }
                        }
                                        break;
                                }
                        }
-                       if (localsigs) {
-                               if (c == ntc.t_intrc) {
+                       if (localchars) {
+                               if (sc == ntc.t_intrc) {
                                        intp();
                                        break;
                                        intp();
                                        break;
-                               } else if (c == ntc.t_quitc) {
+                               } else if (sc == ntc.t_quitc) {
                                        sendbrk();
                                        break;
                                        sendbrk();
                                        break;
+                               } else if (sc == nltc.t_flushc) {
+                                       NET2ADD(IAC, AO);
+                                       if (autoflush) {
+                                           doflush();
+                                       }
+                                       break;
                                } else if (globalmode > 2) {
                                        ;
                                } else if (globalmode > 2) {
                                        ;
-                               } else if (c == nttyb.sg_kill) {
+                               } else if (sc == nttyb.sg_kill) {
                                        NET2ADD(IAC, EL);
                                        break;
                                        NET2ADD(IAC, EL);
                                        break;
-                               } else if (c == nttyb.sg_erase) {
+                               } else if (sc == nttyb.sg_erase) {
                                        NET2ADD(IAC, EC);
                                        break;
                                }
                                        NET2ADD(IAC, EC);
                                        break;
                                }
@@ -566,13 +982,16 @@ telnet()
                        }
                }
                if (((globalmode < 4) || flushline) &&
                        }
                }
                if (((globalmode < 4) || flushline) &&
-                   (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)) {
+                   FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
+                       FD_CLR(net, &obits);
                        netflush(net);
                }
                if (scc > 0)
                        telrcv();
                        netflush(net);
                }
                if (scc > 0)
                        telrcv();
-               if (FD_ISSET(tout, &obits) && (tfrontp - tbackp) > 0)
+               if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
+                       FD_CLR(tout, &obits);
                        ttyflush();
                        ttyflush();
+               }
        }
        setcommandmode();
 }
        }
        setcommandmode();
 }
@@ -587,53 +1006,65 @@ telnet()
 #define        TS_DO           4
 #define        TS_DONT         5
 #define        TS_CR           6
 #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 */
 
 telrcv()
 {
        register int c;
        static int state = TS_DATA;
 
 
 telrcv()
 {
        register int c;
        static int state = TS_DATA;
 
-       while (scc > 0) {
-               c = *sbp++ & 0377, scc--;
+       while ((scc > 0) && (TTYROOM() > 2)) {
+               c = *sbp++ & 0xff, scc--;
                switch (state) {
 
                case TS_CR:
                        state = TS_DATA;
                switch (state) {
 
                case TS_CR:
                        state = TS_DATA;
-                       if ((c == '\0') || (c == '\n')) {
-                           break;      /* by now, we ignore \n */
+                       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;
                        }
 
                case TS_DATA:
                        if (c == IAC) {
                                state = TS_IAC;
                                continue;
                        }
+                           /*
+                            * 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) {
                        if (c == '\r') {
                                if (scc > 0) {
-                                       c = *sbp&0377;
+                                       c = *sbp&0xff;
                                        if (c == 0) {
                                                sbp++, scc--;
                                        if (c == 0) {
                                                sbp++, scc--;
+                                               /* a "true" CR */
                                                TTYADD('\r');
                                                TTYADD('\r');
-                               /*
-                                * The following hack 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 (crmod) {
-                                                       TTYADD('\n');
-                                               }
                                        } else if (!hisopts[TELOPT_ECHO] &&
                                                                (c == '\n')) {
                                                sbp++, scc--;
                                                TTYADD('\n');
                                        } else {
                                                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');
                                        }
                                } else {
                                        state = TS_CR;
                                        TTYADD('\r');
+                                       if (crmod) {
+                                               TTYADD('\n');
+                                       }
                                }
                        } else {
                                TTYADD(c);
                                }
                        } else {
                                TTYADD(c);
@@ -665,15 +1096,21 @@ telrcv()
                                 * so make sure we flush whatever is in the
                                 * buffer currently.
                                 */
                                 * so make sure we flush whatever is in the
                                 * buffer currently.
                                 */
-                               flushing = 1;
+                               SYNCHing = 1;
                                ttyflush();
                                ttyflush();
-                               flushing = stilloob(net);
+                               SYNCHing = stilloob(net);
+                               settimer(gotDM);
                                break;
 
                        case NOP:
                        case GA:
                                break;
 
                                break;
 
                        case NOP:
                        case GA:
                                break;
 
+                       case SB:
+                               SB_CLEAR();
+                               state = TS_SB;
+                               continue;
+
                        default:
                                break;
                        }
                        default:
                                break;
                        }
@@ -681,54 +1118,74 @@ telrcv()
                        continue;
 
                case TS_WILL:
                        continue;
 
                case TS_WILL:
-                       printoption("RCVD", will, c, !hisopts[c]);
+                       printoption(">RCVD", will, c, !hisopts[c]);
                        if (c == TELOPT_TM) {
                                if (flushout) {
                        if (c == TELOPT_TM) {
                                if (flushout) {
-                                       flushout = 1;
+                                       flushout = 0;
                                }
                        } else if (!hisopts[c]) {
                                }
                        } else if (!hisopts[c]) {
-                               willoption(c);
+                               willoption(c, 1);
                        }
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
                        }
                        state = TS_DATA;
                        continue;
 
                case TS_WONT:
-                       printoption("RCVD", wont, c, hisopts[c]);
+                       printoption(">RCVD", wont, c, hisopts[c]);
                        if (c == TELOPT_TM) {
                                if (flushout) {
                        if (c == TELOPT_TM) {
                                if (flushout) {
-                                       flushout = 1;
+                                       flushout = 0;
                                }
                        } else if (hisopts[c]) {
                                }
                        } else if (hisopts[c]) {
-                               wontoption(c);
+                               wontoption(c, 1);
                        }
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
                        }
                        state = TS_DATA;
                        continue;
 
                case TS_DO:
-                       printoption("RCVD", doopt, c, !myopts[c]);
+                       printoption(">RCVD", doopt, c, !myopts[c]);
                        if (!myopts[c])
                                dooption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
                        if (!myopts[c])
                                dooption(c);
                        state = TS_DATA;
                        continue;
 
                case TS_DONT:
-                       printoption("RCVD", dont, c, myopts[c]);
+                       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) */
                        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);
+                               printoption(">SENT", wont, c);
                        }
                        state = TS_DATA;
                        continue;
                        }
                        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 */
+                               state = TS_DATA;
+                       }
                }
        }
 }
 \f
                }
        }
 }
 \f
-willoption(option)
-       int option;
+willoption(option, reply)
+       int option, reply;
 {
        char *fmt;
 
 {
        char *fmt;
 
@@ -751,11 +1208,14 @@ willoption(option)
        }
        sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (dont) - 2;
        }
        sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (dont) - 2;
-       printoption("SENT", fmt, option);
+       if (reply)
+               printoption(">SENT", fmt, option);
+       else
+               printoption("<SENT", fmt, option);
 }
 
 }
 
-wontoption(option)
-       int option;
+wontoption(option, reply)
+       int option, reply;
 {
        char *fmt;
 
 {
        char *fmt;
 
@@ -777,7 +1237,10 @@ wontoption(option)
        }
        sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (doopt) - 2;
        }
        sprintf(nfrontp, fmt, option);
        nfrontp += sizeof (doopt) - 2;
-       printoption("SENT", fmt, option);
+       if (reply)
+               printoption(">SENT", fmt, option);
+       else
+               printoption("<SENT", fmt, option);
 }
 
 dooption(option)
 }
 
 dooption(option)
@@ -791,182 +1254,61 @@ dooption(option)
                fmt = will;
                break;
 
                fmt = will;
                break;
 
+       case TELOPT_TTYPE:              /* terminal type option */
        case TELOPT_SGA:                /* no big deal */
                fmt = will;
                myopts[option] = 1;
        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);
-}
-\f
-/*
- * Check to see if any out-of-band data exists on a socket (for
- * Telnet "synch" processing).
- */
-
-int
-stilloob(s)
-int    s;              /* socket number */
-{
-    struct timeval *timeout = { 0 };
-    long       excepts = (1<<s);
-
-    if (select(s+1, 0, 0, &excepts, timeout) < 0) {
-       perror("select");
-       quit();
-    }
-    if (excepts) {
-       return 1;
-    } else {
-       return 0;
-    }
-}
-
-/*
- * Construct a control character sequence
- * for a special character.
- */
-char *
-control(c)
-       register int c;
-{
-       static char buf[3];
-
-       if (c == 0177)
-               return ("^?");
-       if (c >= 040) {
-               buf[0] = c;
-               buf[1] = 0;
-       } else {
-               buf[0] = '^';
-               buf[1] = '@'+c;
-               buf[2] = 0;
-       }
-       return (buf);
-}
-
-ttyflush()
-{
-    int n;
-
-    if ((n = tfrontp - tbackp) > 0) {
-       if (!flushing) {
-           n = write(tout, tbackp, n);
-       } else {
-           ioctl(fileno(stdout), TIOCFLUSH, 0);
-       }
-    }
-    if (n < 0) {
-       return;
-    }
-    tbackp += n;
-    if (tbackp == tfrontp) {
-       tbackp = tfrontp = ttyobuf;
-    }
-}
-
-netflush(fd)
-{
-    int n;
-
-    if ((n = nfrontp - nbackp) > 0) {
-       if (!neturg) {
-           n = write(fd, nbackp, n);   /* normal write */
-       } else {
-           n = neturg - nbackp;
-           n = send(fd, nbackp, n, MSG_OOB);   /* URGENT data (SYNCH) */
-       }
-    }
-    if (n < 0) {
-       if (errno != ENOBUFS && errno != EWOULDBLOCK) {
-           setcommandmode();
-           perror(hostname);
-           close(fd);
-           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;
-    }
-}
-
-static
-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;
+               break;
+
+       case TELOPT_ECHO:               /* We're never going to echo... */
+       default:
+               fmt = wont;
+               break;
        }
        }
-       /* find next unique line */
-    }
+       sprintf(nfrontp, fmt, option);
+       nfrontp += sizeof (doopt) - 2;
+       printoption(">SENT", fmt, option);
 }
 
 }
 
+/*
+ * suboption()
+ *
+ *     Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ *     Currently we recognize:
+ *
+ *             Terminal type, send request.
+ */
 
 
-/*VARARGS*/
-printoption(direction, fmt, option, what)
-       char *direction, *fmt;
-       int option, what;
+suboption()
 {
 {
-       if (!showoptions)
-               return;
-       printf("%s ", direction);
-       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 < TELOPT_SUPDUP)
-               printf("%s %s", fmt, telopts[option]);
-       else
-               printf("%s %d", fmt, option);
-       if (*direction == '<') {
-               printf("\r\n");
-               return;
+    switch (subbuffer[0]&0xff) {
+    case TELOPT_TTYPE:
+       if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
+           ;
+       } else {
+           char *name;
+           char namebuf[41];
+           char *getenv();
+           int len;
+
+           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);
+               nfrontp += 4+strlen(namebuf)+2;
+           }
        }
        }
-       printf(" (%s)\r\n", what ? "reply" : "don't reply");
+
+    default:
+       break;
+    }
 }
 \f
 /*
 }
 \f
 /*
@@ -982,71 +1324,79 @@ struct sendlist {
     int                (*routine)();   /* Routine to perform (for special ops) */
 };
 
     int                (*routine)();   /* Routine to perform (for special ops) */
 };
 
+/*ARGSUSED*/
 dosynch(s)
 struct sendlist *s;
 {
 dosynch(s)
 struct sendlist *s;
 {
-    /* XXX We really should purge the buffer to the network */
+    netclear();                        /* clear the path to the network */
     NET2ADD(IAC, DM);
     NET2ADD(IAC, DM);
-    neturg = NETLOC();
-}
-
-sendesc()
-{
-    NETADD(escape);
+    neturg = NETLOC()-1;       /* Some systems are off by one XXX */
 }
 
 }
 
-ayt()
+doflush()
 {
 {
-    NET2ADD(IAC, AYT);
+    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);
 }
 
 intp()
 {
     NET2ADD(IAC, IP);
 }
 
 intp()
 {
     NET2ADD(IAC, IP);
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
 }
 
 sendbrk()
 {
 }
 
 sendbrk()
 {
-    *nfrontp++ = IAC;
-    *nfrontp++ = DO;
-    *nfrontp++ = TELOPT_TM;
-    flushout = 1;
-    *nfrontp++ = IAC;
-    *nfrontp++ = BREAK;
-    flushline = 1;
-    printoption("SENT", doopt, TELOPT_TM);
+    NET2ADD(IAC, BREAK);
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
 }
 
 
 #define        SENDQUESTION    -1
 }
 
 
 #define        SENDQUESTION    -1
-#define        SEND2QUESTION   -2
 #define        SENDESCAPE      -3
 
 struct sendlist Sendlist[] = {
 #define        SENDESCAPE      -3
 
 struct sendlist Sendlist[] = {
-    { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
-    { "brk", BREAK, "Send Telnet Break" },
-       { "break", BREAK, 0 },
-    { "ip", IP, "Send Telnet Interrupt Process" },
-       { "intp", IP, 0 },
-       { "interrupt", IP, 0 },
-       { "intr", IP, 0 },
     { "ao", AO, "Send Telnet Abort output" },
     { "ao", AO, "Send Telnet Abort output" },
-       { "abort", AO, 0 },
     { "ayt", AYT, "Send Telnet 'Are You There'" },
     { "ayt", AYT, "Send Telnet 'Are You There'" },
-       { "are", AYT, 0 },
-       { "hello", AYT, 0 },
+    { "brk", BREAK, "Send Telnet Break" },
     { "ec", EC, "Send Telnet Erase Character" },
     { "el", EL, "Send Telnet Erase Line" },
     { "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" },
     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
-       { "go", GA, 0 },
+    { "ip", IP, "Send Telnet Interrupt Process" },
     { "nop", NOP, "Send Telnet 'No operation'" },
     { "nop", NOP, "Send Telnet 'No operation'" },
-    { "escape", SENDESCAPE, "Send current escape character" },
+    { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
     { "?", SENDQUESTION, "Display send options" },
     { "?", SENDQUESTION, "Display send options" },
-       { "help", SENDQUESTION, 0 },
-    { "??", SEND2QUESTION, "Display all send options (including aliases)" },
     { 0 }
 };
 
     { 0 }
 };
 
+struct sendlist Sendlist2[] = {                /* some synonyms */
+       { "break", BREAK, 0 },
+
+       { "intp", IP, 0 },
+       { "interrupt", IP, 0 },
+       { "intr", IP, 0 },
+
+       { "help", SENDQUESTION, 0 },
+
+       { 0 }
+};
+
 char **
 getnextsend(name)
 char *name;
 char **
 getnextsend(name)
 char *name;
@@ -1060,7 +1410,15 @@ struct sendlist *
 getsend(name)
 char *name;
 {
 getsend(name)
 char *name;
 {
-    return (struct sendlist *) genget(name, (char **) Sendlist, getnextsend);
+    struct sendlist *sl;
+
+    if (sl = (struct sendlist *)
+                               genget(name, (char **) Sendlist, getnextsend)) {
+       return sl;
+    } else {
+       return (struct sendlist *)
+                               genget(name, (char **) Sendlist2, getnextsend);
+    }
 }
 
 sendcmd(argc, argv)
 }
 
 sendcmd(argc, argv)
@@ -1071,12 +1429,13 @@ char    **argv;
     int count;         /* how many bytes we are going to need to send */
     int hadsynch;      /* are we going to process a "synch"? */
     int i;
     int count;         /* how many bytes we are going to need to send */
     int hadsynch;      /* are we going to process a "synch"? */
     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");
     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;
+       return 0;
     }
     /*
      * First, validate all the send arguments.
     }
     /*
      * First, validate all the send arguments.
@@ -1091,15 +1450,14 @@ char    **argv;
        if (s == 0) {
            printf("Unknown send argument '%s'\n'send ?' for help.\n",
                        argv[i]);
        if (s == 0) {
            printf("Unknown send argument '%s'\n'send ?' for help.\n",
                        argv[i]);
-           return;
-       } else if (s == (struct sendlist *) -1) {
+           return 0;
+       } else if (s == Ambiguous(struct sendlist *)) {
            printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
                        argv[i]);
            printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
                        argv[i]);
-           return;
+           return 0;
        }
        switch (s->what) {
        case SENDQUESTION:
        }
        switch (s->what) {
        case SENDQUESTION:
-       case SEND2QUESTION:
            break;
        case SENDESCAPE:
            count += 1;
            break;
        case SENDESCAPE:
            count += 1;
@@ -1114,12 +1472,12 @@ char    **argv;
        }
     }
     /* Now, do we have enough room? */
        }
     }
     /* Now, do we have enough room? */
-    if (netobuf+sizeof netobuf-nfrontp-1 < count) {
+    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");
        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;
+       return 0;
     }
     /* OK, they are all OK, now go through again and actually send */
     for (i = 1; i < argc; i++) {
     }
     /* OK, they are all OK, now go through again and actually send */
     for (i = 1; i < argc; i++) {
@@ -1132,10 +1490,12 @@ char    **argv;
            (*s->routine)(s);
        } else {
            switch (what = s->what) {
            (*s->routine)(s);
        } else {
            switch (what = s->what) {
+           case SYNCH:
+               dosynch();
+               break;
            case SENDQUESTION:
            case SENDQUESTION:
-           case SEND2QUESTION:
                for (s = Sendlist; s->name; s++) {
                for (s = Sendlist; s->name; s++) {
-                   if (s->help || (what == SEND2QUESTION)) {
+                   if (s->help) {
                        printf(s->name);
                        if (s->help) {
                            printf("\t%s", s->help);
                        printf(s->name);
                        if (s->help) {
                            printf("\t%s", s->help);
@@ -1143,6 +1503,7 @@ char      **argv;
                        printf("\n");
                    }
                }
                        printf("\n");
                    }
                }
+               question = 1;
                break;
            case SENDESCAPE:
                NETADD(escape);
                break;
            case SENDESCAPE:
                NETADD(escape);
@@ -1153,6 +1514,7 @@ char      **argv;
            }
        }
     }
            }
        }
     }
+    return !question;
 }
 \f
 /*
 }
 \f
 /*
@@ -1160,171 +1522,206 @@ char  **argv;
  * to by the arguments to the "toggle" command.
  */
 
  * to by the arguments to the "toggle" command.
  */
 
-lclsigs()
-{
-    localsigs = !localsigs;
-    printf("%s recognize interrupt/quit characters.\n",
-                                       localsigs ? "Will" : "Won't");
-    donelclsigs = 1;
-    fflush(stdout);
-}
-
-localecho()
-{
-    doechocharrecognition = !doechocharrecognition;
-    printf("%s recognize echo toggle character.\n",
-                               doechocharrecognition ? "Will" : "Won't");
-    fflush(stdout);
-}
-
-/*VARARGS*/
-setcrmod()
+lclchars()
 {
 {
-
-    crmod = !crmod;
-    printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
-    fflush(stdout);
+    donelclchars = 1;
+    return 1;
 }
 
 }
 
-setdebug()
+togdebug()
 {
 {
-
-    debug = debug ? 0 : 1;
-    printf("%s turn on socket level debugging.\n",
-           debug ? "Will" : "Won't");
-    fflush(stdout);
+#ifndef        NOT43
     if (net > 0 &&
     if (net > 0 &&
-       setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
+       setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
+                                                                       < 0) {
            perror("setsockopt (SO_DEBUG)");
            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;
 }
 
 }
 
-static
-setnetdata()
-{
-
-    netdata = !netdata;
-    printf("%s turn on printing of raw network traffic.\n",
-           netdata ? "Will" : "Wont");
-}
-
-setoptions()
-{
 
 
-    showoptions = !showoptions;
-    printf("%s show option processing.\n", showoptions ? "Will" : "Won't");
-    fflush(stdout);
-}
 
 int togglehelp();
 
 
 int togglehelp();
 
-char   crmodhelp[] =   "toggle mapping of received carriage returns";
+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;
+};
 
 
-struct cmd togglelist[] = {
-    { "localchars", "toggle local recognition of control characters",
-                                                               lclsigs, 1 },
-    { "echochar", "toggle recognition of echo toggle character", localecho, 1 },
-    { "crmod", crmodhelp,      setcrmod, 1, 0 },
+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 */
     { " ", "", 0, 1 },         /* empty line */
-    { "debug", "(debugging) toggle debugging", setdebug, 1 },
-    { "options", "(debugging) toggle viewing of options processing",
-                                                               setoptions, 1 },
-    { "netdata", "(debugging) toggle printing of hexadecimal network data",
-                                                               setnetdata, 1 },
-    { "?", "display help information", togglehelp, 1 },
-    { "help", "display help information", togglehelp, 0 },
+    { "?",
+       "display help information",
+           togglehelp,
+               1 },
+    { "help",
+       "display help information",
+           togglehelp,
+               0 },
     { 0 }
 };
 
 togglehelp()
 {
     { 0 }
 };
 
 togglehelp()
 {
-    struct cmd *c;
+    struct togglelist *c;
 
 
-    for (c = togglelist; c->name; c++) {
+    for (c = Togglelist; c->name; c++) {
        if (c->dohelp) {
            printf("%s\t%s\n", c->name, c->help);
        }
     }
        if (c->dohelp) {
            printf("%s\t%s\n", c->name, c->help);
        }
     }
+    return 0;
 }
 
 char **
 getnexttoggle(name)
 char *name;
 {
 }
 
 char **
 getnexttoggle(name)
 char *name;
 {
-    struct cmd *c = (struct cmd *) name;
+    struct togglelist *c = (struct togglelist *) name;
 
     return (char **) (c+1);
 }
 
 
     return (char **) (c+1);
 }
 
-struct cmd *
+struct togglelist *
 gettoggle(name)
 char *name;
 {
 gettoggle(name)
 char *name;
 {
-    return (struct cmd *) genget(name, (char **) togglelist, getnexttoggle);
+    return (struct togglelist *)
+                       genget(name, (char **) Togglelist, getnexttoggle);
 }
 
 toggle(argc, argv)
 int    argc;
 char   *argv[];
 {
 }
 
 toggle(argc, argv)
 int    argc;
 char   *argv[];
 {
+    int retval = 1;
     char *name;
     char *name;
-    struct cmd *c;
+    struct togglelist *c;
 
     if (argc < 2) {
        fprintf(stderr,
            "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
 
     if (argc < 2) {
        fprintf(stderr,
            "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
-       return;
+       return 0;
     }
     argc--;
     argv++;
     while (argc--) {
        name = *argv++;
        c = gettoggle(name);
     }
     argc--;
     argv++;
     while (argc--) {
        name = *argv++;
        c = gettoggle(name);
-       if (c == (struct cmd *) -1) {
+       if (c == Ambiguous(struct togglelist *)) {
            fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
                                        name);
            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);
        } else if (c == 0) {
            fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
                                        name);
+           return 0;
        } else {
        } else {
-           (*c->handler)(c);
+           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.
  */
 
 }
 \f
 /*
  * The following perform the "set" command.
  */
 
-struct chartab {
-    char *label;                       /* name */
+struct setlist {
+    char *name;                                /* name */
     char *help;                                /* help information */
     char *charp;                       /* where it is located at */
 };
 
     char *help;                                /* help information */
     char *charp;                       /* where it is located at */
 };
 
-struct chartab Chartab[] = {
+struct setlist Setlist[] = {
     { "echo",  "character to toggle local echoing on/off", &echoc },
     { "escape",        "character to escape back to telnet command mode", &escape },
     { "echo",  "character to toggle local echoing on/off", &echoc },
     { "escape",        "character to escape back to telnet command mode", &escape },
-    { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
-    { "quit",  "character to cause a Break", &ntc.t_quitc },
+    { " ", "" },
+    { " ", "The following need 'localchars' to be toggled true", 0 },
     { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
     { "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 },
     { "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 }
 };
 
 char **
     { 0 }
 };
 
 char **
-getnextchar(name)
+getnextset(name)
 char *name;
 {
 char *name;
 {
-    struct chartab *c = (struct chartab *)name;
+    struct setlist *c = (struct setlist *)name;
 
     return (char **) (c+1);
 }
 
 
     return (char **) (c+1);
 }
 
-struct chartab *
-getchartab(name)
+struct setlist *
+getset(name)
 char *name;
 {
 char *name;
 {
-    return (struct chartab *) genget(name, (char **) Chartab, getnextchar);
+    return (struct setlist *) genget(name, (char **) Setlist, getnextset);
 }
 
 setcmd(argc, argv)
 }
 
 setcmd(argc, argv)
@@ -1332,24 +1729,31 @@ int     argc;
 char   *argv[];
 {
     int value;
 char   *argv[];
 {
     int value;
-    struct chartab *ct;
+    struct setlist *ct;
 
     /* XXX back we go... sigh */
     if (argc != 3) {
 
     /* XXX back we go... sigh */
     if (argc != 3) {
-       printf("Format is 'set Name Value', where 'Name' is one of:\n\n");
-       for (ct = Chartab; ct->label; ct++) {
-           printf("%s\t%s\n", ct->label, ct->help);
+       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;
+       return 0;
     }
 
     }
 
-    ct = getchartab(argv[1]);
+    ct = getset(argv[1]);
     if (ct == 0) {
        fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
                        argv[1]);
     if (ct == 0) {
        fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
                        argv[1]);
-    } else if (ct == (struct chartab *) -1) {
+       return 0;
+    } else if (ct == Ambiguous(struct setlist *)) {
        fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
                        argv[1]);
        fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
                        argv[1]);
+       return 0;
     } else {
        if (strcmp("off", argv[2])) {
            value = special(argv[2]);
     } else {
        if (strcmp("off", argv[2])) {
            value = special(argv[2]);
@@ -1357,8 +1761,9 @@ char      *argv[];
            value = -1;
        }
        *(ct->charp) = value;
            value = -1;
        }
        *(ct->charp) = value;
-       printf("%s character is '%s'.\n", ct->label, control(*(ct->charp)));
+       printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
     }
     }
+    return 1;
 }
 \f
 /*
 }
 \f
 /*
@@ -1387,8 +1792,8 @@ docharmode()
 }
 
 struct cmd Modelist[] = {
 }
 
 struct cmd Modelist[] = {
-    { "line",          "line-by-line mode",            dolinemode, 1, 1 },
     { "character",     "character-at-a-time mode",     docharmode, 1, 1 },
     { "character",     "character-at-a-time mode",     docharmode, 1, 1 },
+    { "line",          "line-by-line mode",            dolinemode, 1, 1 },
     { 0 },
 };
 
     { 0 },
 };
 
@@ -1419,16 +1824,80 @@ char    *argv[];
        for (mt = Modelist; mt->name; mt++) {
            printf("%s\t%s\n", mt->name, mt->help);
        }
        for (mt = Modelist; mt->name; mt++) {
            printf("%s\t%s\n", mt->name, mt->help);
        }
-       return;
+       return 0;
     }
     mt = getmodecmd(argv[1]);
     if (mt == 0) {
        fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
     }
     mt = getmodecmd(argv[1]);
     if (mt == 0) {
        fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
-    } else if (mt == (struct cmd *) -1) {
+       return 0;
+    } else if (mt == Ambiguous(struct cmd *)) {
        fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
        fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
+       return 0;
     } else {
        (*mt->handler)();
     }
     } else {
        (*mt->handler)();
     }
+    return 1;
+}
+\f
+/*
+ * The following data structures and routines implement the
+ * "display" command.
+ */
+
+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(sl)
+#undef dotog(tl)
 }
 \f
 /*
 }
 \f
 /*
@@ -1446,6 +1915,9 @@ setescape(argc, argv)
        register char *arg;
        char buf[50];
 
        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 {
        if (argc > 2)
                arg = argv[1];
        else {
@@ -1457,6 +1929,17 @@ setescape(argc, argv)
                escape = arg[0];
        printf("Escape character is '%s'.\n", control(escape));
        fflush(stdout);
                escape = arg[0];
        printf("Escape character is '%s'.\n", control(escape));
        fflush(stdout);
+       return 1;
+}
+
+/*VARARGS*/
+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*/
 }
 
 /*VARARGS*/
@@ -1468,6 +1951,7 @@ suspend()
        ioctl(0, TIOCGETP, (char *)&ottyb);
        ioctl(0, TIOCGETC, (char *)&otc);
        ioctl(0, TIOCGLTC, (char *)&oltc);
        ioctl(0, TIOCGETP, (char *)&ottyb);
        ioctl(0, TIOCGETC, (char *)&otc);
        ioctl(0, TIOCGLTC, (char *)&oltc);
+       return 1;
 }
 
 /*VARARGS*/
 }
 
 /*VARARGS*/
@@ -1484,44 +1968,53 @@ bye()
                for (op = hisopts; op < &hisopts[256]; op++)
                        *op = 0;
        }
                for (op = hisopts; op < &hisopts[256]; op++)
                        *op = 0;
        }
+       return 1;
 }
 
 /*VARARGS*/
 quit()
 {
 }
 
 /*VARARGS*/
 quit()
 {
-       call(bye, "bye", 0);
+       (void) call(bye, "bye", 0);
        exit(0);
        exit(0);
+       /*NOTREACHED*/
 }
 
 /*
  * Print status about the connection.
  */
 }
 
 /*
  * Print status about the connection.
  */
-/*VARARGS*/
-status()
+/*ARGSUSED*/
+status(argc, argv)
+int    argc;
+char   *argv[];
 {
 {
-       if (connected) {
-               printf("Connected to %s.\n", hostname);
-               /* XXX should print out line modes, etc. */
-       } else {
-               printf("No connection.\n");
+    if (connected) {
+       printf("Connected to %s.\n", hostname);
+       if (argc < 2) {
+           printf("Operating in %s.\n", modedescriptions[getconnmode()]);
+           if (localchars) {
+               printf("Catching signals locally.\n");
+           }
        }
        }
-       printf("Escape character is '%s'.\n", control(escape));
-       fflush(stdout);
+    } else {
+       printf("No connection.\n");
+    }
+    printf("Escape character is '%s'.\n", control(escape));
+    fflush(stdout);
+    return 1;
 }
 
 tn(argc, argv)
        int argc;
        char *argv[];
 {
 }
 
 tn(argc, argv)
        int argc;
        char *argv[];
 {
-       register int c;
        register struct hostent *host = 0;
 
        if (connected) {
                printf("?Already connected to %s\n", hostname);
        register struct hostent *host = 0;
 
        if (connected) {
                printf("?Already connected to %s\n", hostname);
-               return;
+               return 0;
        }
        if (argc < 2) {
        }
        if (argc < 2) {
-               strcpy(line, "Connect ");
+               (void) strcpy(line, "Connect ");
                printf("(to) ");
                gets(&line[strlen(line)]);
                makeargv();
                printf("(to) ");
                gets(&line[strlen(line)]);
                makeargv();
@@ -1530,35 +2023,40 @@ tn(argc, argv)
        }
        if (argc > 3) {
                printf("usage: %s host-name [port]\n", argv[0]);
        }
        if (argc > 3) {
                printf("usage: %s host-name [port]\n", argv[0]);
-               return;
+               return 0;
        }
        sin.sin_addr.s_addr = inet_addr(argv[1]);
        if (sin.sin_addr.s_addr != -1) {
                sin.sin_family = AF_INET;
        }
        sin.sin_addr.s_addr = inet_addr(argv[1]);
        if (sin.sin_addr.s_addr != -1) {
                sin.sin_family = AF_INET;
-               strcpy(hnamebuf, argv[1]);
+               (void) strcpy(hnamebuf, argv[1]);
                hostname = hnamebuf;
        } else {
                host = gethostbyname(argv[1]);
                if (host) {
                        sin.sin_family = host->h_addrtype;
                hostname = hnamebuf;
        } else {
                host = gethostbyname(argv[1]);
                if (host) {
                        sin.sin_family = host->h_addrtype;
+#ifndef        NOT43
                        bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
                                host->h_length);
                        bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
                                host->h_length);
+#else  NOT43
+                       bcopy(host->h_addr, (caddr_t)&sin.sin_addr,
+                               host->h_length);
+#endif NOT43
                        hostname = host->h_name;
                } else {
                        printf("%s: unknown host\n", argv[1]);
                        hostname = host->h_name;
                } else {
                        printf("%s: unknown host\n", argv[1]);
-                       return;
+                       return 0;
                }
        }
        sin.sin_port = sp->s_port;
        if (argc == 3) {
                sin.sin_port = atoi(argv[2]);
                }
        }
        sin.sin_port = sp->s_port;
        if (argc == 3) {
                sin.sin_port = atoi(argv[2]);
-               if (sin.sin_port <= 0) {
+               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]);
                        sp = getservbyname(argv[2], "tcp");
                        if (sp)
                                sin.sin_port = sp->s_port;
                        else {
                                printf("%s: bad port number\n", argv[2]);
-                               return;
+                               return 0;
                        }
                } else {
                        sin.sin_port = atoi(argv[2]);
                        }
                } else {
                        sin.sin_port = atoi(argv[2]);
@@ -1576,12 +2074,19 @@ tn(argc, argv)
                net = socket(AF_INET, SOCK_STREAM, 0);
                if (net < 0) {
                        perror("telnet: socket");
                net = socket(AF_INET, SOCK_STREAM, 0);
                if (net < 0) {
                        perror("telnet: socket");
-                       return;
+                       return 0;
                }
                }
-               if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug,
-                   sizeof(debug)) < 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)");
                        perror("setsockopt (SO_DEBUG)");
-               if (connect(net, (caddr_t)&sin, sizeof (sin)) < 0) {
+
+               if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+#ifndef        NOT43
                        if (host && host->h_addr_list[1]) {
                                int oerrno = errno;
 
                        if (host && host->h_addr_list[1]) {
                                int oerrno = errno;
 
@@ -1589,7 +2094,7 @@ tn(argc, argv)
                                    "telnet: connect to address %s: ",
                                    inet_ntoa(sin.sin_addr));
                                errno = oerrno;
                                    "telnet: connect to address %s: ",
                                    inet_ntoa(sin.sin_addr));
                                errno = oerrno;
-                               perror(0);
+                               perror((char *)0);
                                host->h_addr_list++;
                                bcopy(host->h_addr_list[0],
                                    (caddr_t)&sin.sin_addr, host->h_length);
                                host->h_addr_list++;
                                bcopy(host->h_addr_list[0],
                                    (caddr_t)&sin.sin_addr, host->h_length);
@@ -1598,17 +2103,20 @@ tn(argc, argv)
                                (void) close(net);
                                continue;
                        }
                                (void) close(net);
                                continue;
                        }
+#endif NOT43
                        perror("telnet: connect");
                        signal(SIGINT, SIG_DFL);
                        perror("telnet: connect");
                        signal(SIGINT, SIG_DFL);
-                       return;
+                       signal(SIGQUIT, SIG_DFL);
+                       return 0;
                }
                connected++;
        } while (connected == 0);
                }
                connected++;
        } while (connected == 0);
-       call(status, "status", 0);
+       call(status, "status", "notmuch", 0);
        if (setjmp(peerdied) == 0)
                telnet();
        fprintf(stderr, "Connection closed by foreign host.\n");
        exit(1);
        if (setjmp(peerdied) == 0)
                telnet();
        fprintf(stderr, "Connection closed by foreign host.\n");
        exit(1);
+       /*NOTREACHED*/
 }
 
 
 }
 
 
@@ -1618,35 +2126,41 @@ char    openhelp[] =    "connect to a site";
 char   closehelp[] =   "close current connection";
 char   quithelp[] =    "exit telnet";
 char   zhelp[] =       "suspend telnet";
 char   closehelp[] =   "close current connection";
 char   quithelp[] =    "exit telnet";
 char   zhelp[] =       "suspend telnet";
-char   escapehelp[] =  "set escape character";
 char   statushelp[] =  "print status information";
 char   helphelp[] =    "print help information";
 char   statushelp[] =  "print status information";
 char   helphelp[] =    "print help information";
-char   togglestring[] = "toggle various options ('toggle ?' for more)";
 char   sendhelp[] =    "transmit special characters ('send ?' for more)";
 char   sendhelp[] =    "transmit special characters ('send ?' for more)";
-char   sethelp[] =     "set various special characters ('set ?' for more)";
-char   modehelp[] =    "change operating mode ('mode ?' for more)";
+char   sethelp[] =     "set operating parameters ('set ?' for more)";
+char   togglestring[] ="toggle operating parameters ('toggle ?' for more)";
+char   displayhelp[] = "display operating parameters";
+char   modehelp[] =
+               "try to enter line-by-line or character-at-a-time mode";
 
 int    help();
 
 struct cmd cmdtab[] = {
 
 int    help();
 
 struct cmd cmdtab[] = {
-       { "open",       openhelp,       tn,             1, 0 },
        { "close",      closehelp,      bye,            1, 1 },
        { "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 },
        { "quit",       quithelp,       quit,           1, 0 },
-       { "z",          zhelp,          suspend,        1, 0 },
-       { "escape",     escapehelp,     setescape,      1, 0 },
-       { "status",     statushelp,     status,         1, 0 },
-       { "crmod",      crmodhelp,      setcrmod,       1, 0 },
        { "send",       sendhelp,       sendcmd,        1, 1 },
        { "set",        sethelp,        setcmd,         1, 0 },
        { "send",       sendhelp,       sendcmd,        1, 1 },
        { "set",        sethelp,        setcmd,         1, 0 },
-       { "mode",       modehelp,       modecmd,        1, 1 },
-       { "transmit",   sendhelp,       sendcmd,        0, 1 },
-       { "xmit",       sendhelp,       sendcmd,        0, 1 },
+       { "status",     statushelp,     status,         1, 0 },
        { "toggle",     togglestring,   toggle,         1, 0 },
        { "toggle",     togglestring,   toggle,         1, 0 },
+       { "z",          zhelp,          suspend,        1, 0 },
        { "?",          helphelp,       help,           1, 0 },
        { "?",          helphelp,       help,           1, 0 },
-       { "help",       helphelp,       help,           0, 0 },
        0
 };
 
        0
 };
 
+char   crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
+char   escapehelp[] =  "deprecated command -- use 'set escape' instead";
+
+struct cmd cmdtab2[] = {
+       { "help",       helphelp,       help,           0, 0 },
+       { "escape",     escapehelp,     setescape,      1, 0 },
+       { "crmod",      crmodhelp,      togcrmod,       1, 0 },
+       0
+};
 
 /*
  * Help command.
 
 /*
  * Help command.
@@ -1664,19 +2178,20 @@ help(argc, argv)
                                printf("%-*s\t%s\n", HELPINDENT, c->name,
                                                                    c->help);
                        }
                                printf("%-*s\t%s\n", HELPINDENT, c->name,
                                                                    c->help);
                        }
-               return;
+               return 0;
        }
        while (--argc > 0) {
                register char *arg;
                arg = *++argv;
                c = getcmd(arg);
        }
        while (--argc > 0) {
                register char *arg;
                arg = *++argv;
                c = getcmd(arg);
-               if (c == (struct cmd *)-1)
+               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);
        }
                        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;
 }
 /*
  * Call routine with argc, argv set from args (terminated by 0).
 }
 /*
  * Call routine with argc, argv set from args (terminated by 0).
@@ -1684,14 +2199,14 @@ help(argc, argv)
  */
 call(routine, args)
        int (*routine)();
  */
 call(routine, args)
        int (*routine)();
-       int args;
+       char *args;
 {
 {
-       register int *argp;
+       register char **argp;
        register int argc;
 
        for (argc = 0, argp = &args; *argp++ != 0; argc++)
                ;
        register int argc;
 
        for (argc = 0, argp = &args; *argp++ != 0; argc++)
                ;
-       (*routine)(argc, &args);
+       return (*routine)(argc, &args);
 }
 
 makeargv()
 }
 
 makeargv()
@@ -1729,20 +2244,27 @@ struct cmd *
 getcmd(name)
 char *name;
 {
 getcmd(name)
 char *name;
 {
-    return (struct cmd *) genget(name, (char **) cmdtab, getnextcmd);
+    struct cmd *cm;
+
+    if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) {
+       return cm;
+    } else {
+       return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
+    }
 }
 
 command(top)
        int top;
 {
        register struct cmd *c;
 }
 
 command(top)
        int top;
 {
        register struct cmd *c;
-       int wasopen;
 
        setcommandmode();
 
        setcommandmode();
-       if (!top)
+       if (!top) {
                putchar('\n');
                putchar('\n');
-       else
+       } else {
                signal(SIGINT, SIG_DFL);
                signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+       }
        for (;;) {
                printf("%s> ", prompt);
                if (gets(line) == 0) {
        for (;;) {
                printf("%s> ", prompt);
                if (gets(line) == 0) {
@@ -1754,7 +2276,7 @@ command(top)
                        break;
                makeargv();
                c = getcmd(margv[0]);
                        break;
                makeargv();
                c = getcmd(margv[0]);
-               if (c == (struct cmd *)-1) {
+               if (c == Ambiguous(struct cmd *)) {
                        printf("?Ambiguous command\n");
                        continue;
                }
                        printf("?Ambiguous command\n");
                        continue;
                }
@@ -1766,9 +2288,9 @@ command(top)
                        printf("?Need to be connected first.\n");
                        continue;
                }
                        printf("?Need to be connected first.\n");
                        continue;
                }
-               (*c->handler)(margc, margv);
-               if (c->handler != help)
+               if ((*c->handler)(margc, margv)) {
                        break;
                        break;
+               }
        }
        if (!top) {
                if (!connected) {
        }
        if (!top) {
                if (!connected) {
@@ -1778,3 +2300,60 @@ command(top)
                setconnmode();
        }
 }
                setconnmode();
        }
 }
+\f
+/*
+ * main.  Parse arguments, invoke the protocol or command parser.
+ */
+
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       sp = getservbyname("telnet", "tcp");
+       if (sp == 0) {
+               fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
+               exit(1);
+       }
+       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 (argc != 1) {
+               if (setjmp(toplevel) != 0)
+                       exit(0);
+               tn(argc, argv);
+       }
+       setjmp(toplevel);
+       for (;;)
+               command(1);
+}