Changes for pyramid from sbanner1@cheops (S. John Banner).
[unix-history] / usr / src / usr.bin / tn3270 / telnet.c
index 0a9cd71..b116356 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- *     Copyright (c) 1984, 1985, 1986 by the Regents of the
+ *     Copyright (c) 1984-1987 by the Regents of the
  *     University of California and by Gregory Glenn Minshall.
  *
  *     Permission to use, copy, modify, and distribute these
  *     University of California and by Gregory Glenn Minshall.
  *
  *     Permission to use, copy, modify, and distribute these
 
 #ifndef lint
 static char copyright[] =
 
 #ifndef lint
 static char copyright[] =
-"@(#) Copyright (c) 1984, 1985, 1986 Regents of the University of California.\n\
+"@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)telnet.c   3.1  10/29/86";
+static char sccsid[] = "@(#)telnet.c   6.6 (Berkeley) %G%";
 #endif /* not lint */
 
 /*
 #endif /* not lint */
 
 /*
@@ -43,8 +43,6 @@ static char sccsid[] = "@(#)telnet.c  3.1  10/29/86";
  *
  *     TN3270          -       This is to be linked with tn3270.
  *
  *
  *     TN3270          -       This is to be linked with tn3270.
  *
- *     DEBUG           -       Allow for some extra debugging operations.
- *
  *     NOT43           -       Allows the program to compile and run on
  *                             a 4.2BSD system.
  *
  *     NOT43           -       Allows the program to compile and run on
  *                             a 4.2BSD system.
  *
@@ -81,6 +79,11 @@ void setcommandmode(), command();    /* forward declarations */
 #endif /* !defined(TN3270) */
 
 #include <sys/types.h>
 #endif /* !defined(TN3270) */
 
 #include <sys/types.h>
+
+#if     defined(pyr)
+#define fd_set fdset_t
+#endif  /* defined(pyr) */
 #include <sys/socket.h>
 
 #include <netinet/in.h>
 #include <sys/socket.h>
 
 #include <netinet/in.h>
@@ -116,14 +119,16 @@ extern char       *inet_ntoa();
 #endif /* defined(unix) */
 
 #if    defined(TN3270)
 #endif /* defined(unix) */
 
 #if    defined(TN3270)
+#include "ascii/termin.ext"
 #include "ctlr/screen.h"
 #include "ctlr/screen.h"
-#include "system/globals.h"
-#include "telnet.ext"
+#include "ctlr/oia.h"
 #include "ctlr/options.ext"
 #include "ctlr/outbound.ext"
 #include "ctlr/options.ext"
 #include "ctlr/outbound.ext"
-#include "keyboard/termin.ext"
+#include "general/globals.h"
+#include "telnet.ext"
 #endif /* defined(TN3270) */
 #endif /* defined(TN3270) */
-#include "general.h"
+
+#include "general/general.h"
 
 
 
 
 
 
@@ -211,7 +216,9 @@ static int
        ISend,          /* trying to send network data in */
        debug = 0,
        crmod,
        ISend,          /* trying to send network data in */
        debug = 0,
        crmod,
-       netdata,
+       netdata,        /* Print out network data flow */
+       crlf,           /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+       noasynch = 0,   /* User specified "-noasynch" on command line */
        askedSGA = 0,   /* We have talked about suppress go ahead */
        telnetport = 1;
 
        askedSGA = 0,   /* We have talked about suppress go ahead */
        telnetport = 1;
 
@@ -230,7 +237,8 @@ static int
        autoflush = 0,          /* flush output when interrupting? */
        autosynch,              /* send interrupt characters with SYNCH? */
        localchars,             /* we recognize interrupt/quit */
        autoflush = 0,          /* flush output when interrupting? */
        autosynch,              /* send interrupt characters with SYNCH? */
        localchars,             /* we recognize interrupt/quit */
-       donelclchars,   /* the user has set "localchars" */
+       donelclchars,           /* the user has set "localchars" */
+       donebinarytoggle,       /* the user has put us in binary */
        dontlecho,              /* do we suppress local echoing right now? */
        globalmode;
 
        dontlecho,              /* do we suppress local echoing right now? */
        globalmode;
 
@@ -550,9 +558,11 @@ register int f;
     ioctl(tin, FIONBIO, (char *)&onoff);
     ioctl(tout, FIONBIO, (char *)&onoff);
 #endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
     ioctl(tin, FIONBIO, (char *)&onoff);
     ioctl(tout, FIONBIO, (char *)&onoff);
 #endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
-#if    defined(TN3270) && !defined(DEBUG)
-    ioctl(tin, FIOASYNC, (char *)&onoff);
-#endif /* defined(TN3270) && !defined(DEBUG) */
+#if    defined(TN3270)
+    if (noasynch == 0) {
+       ioctl(tin, FIOASYNC, (char *)&onoff);
+    }
+#endif /* defined(TN3270) */
 
     if (MODE_LINE(f)) {
        void doescape();
 
     if (MODE_LINE(f)) {
        void doescape();
@@ -610,6 +620,10 @@ int fd;
 #if    defined(MSDOS)
 #include <time.h>
 #include <signal.h>
 #if    defined(MSDOS)
 #include <time.h>
 #include <signal.h>
+#include <process.h>
+#include <fcntl.h>
+#include <io.h>
+#include <dos.h>
 
 #if    !defined(SO_OOBINLINE)
 #define        SO_OOBINLINE
 
 #if    !defined(SO_OOBINLINE)
 #define        SO_OOBINLINE
@@ -625,6 +639,8 @@ static char
     termLiteralNextChar,
     termQuitChar;
 
     termLiteralNextChar,
     termQuitChar;
 
+static char
+    savedInState, savedOutState;
 \f
 /*
  * MSDOS doesn't have anyway of deciding whether a full-edited line
 \f
 /*
  * MSDOS doesn't have anyway of deciding whether a full-edited line
@@ -787,11 +803,20 @@ inputExists()
     if (lineend) {
        return 1;
     }
     if (lineend) {
        return 1;
     }
-    if (!kbhit()) {
+#if    1       /* For BIOS variety of calls */
+    if (kbhit() == 0) {
        return lineend;
     }
     input = getch();                   /* MSC - get console character */
        return lineend;
     }
     input = getch();                   /* MSC - get console character */
-#if    0       /* For BIOS variety of calls */
+    if ((input&0xff) == 0) {
+       DoNextChar(0x01);               /* ^A */
+    } else {
+       DoNextChar(input&0xff);
+    }
+#else  /* 0 */
+    if ((input = dirconio()) == -1) {
+       return lineend;
+    }
     if ((input&0xff) == 0) {
        if ((input&0xff00) == 0x0300) {         /* Null */
            DoNextChar(0);
     if ((input&0xff) == 0) {
        if ((input&0xff00) == 0x0300) {         /* Null */
            DoNextChar(0);
@@ -808,11 +833,6 @@ inputExists()
        DoNextChar(input&0xff);
     }
 #endif /* 0 */
        DoNextChar(input&0xff);
     }
 #endif /* 0 */
-    if ((input&0xff) == 0) {
-       DoNextChar(0x01);               /* ^A */
-    } else {
-       DoNextChar(input&0xff);
-    }
     return lineend;
 }
 
     return lineend;
 }
 
@@ -821,13 +841,48 @@ void
 CtrlCInterrupt()
 {
     if (!MODE_COMMAND_LINE(globalmode)) {
 CtrlCInterrupt()
 {
     if (!MODE_COMMAND_LINE(globalmode)) {
+       char far *Bios_Break = (char far *) (((long)0x40<<16)|0x71);
+
+       *Bios_Break = 0;
        ctrlCCount++;           /* XXX */
        signal(SIGINT, CtrlCInterrupt);
     } else {
        closeallsockets();
        ctrlCCount++;           /* XXX */
        signal(SIGINT, CtrlCInterrupt);
     } else {
        closeallsockets();
-       exit();
+       exit(1);
     }
 }
     }
 }
+
+int
+dosbinary(fd, onoff)
+int    fd;
+int    onoff;
+{
+    union REGS regs;
+    int oldstate;
+
+    /* Get old stuff */
+    regs.h.ah = 0x44;
+    regs.h.al = 0;
+    regs.x.bx = fd;
+    intdos(&regs, &regs);
+    oldstate = regs.h.dl&(1<<5);               /* Save state */
+
+    /* Set correct bits in new mode */
+    regs.h.dh = 0;
+    if (onoff) {
+       regs.h.dl |= 1<<5;
+    } else {
+       regs.h.dl &= ~(1<<5);
+    }
+
+    /* Set in new mode */
+    regs.h.ah = 0x44;
+    regs.h.al = 1;
+    regs.x.bx = fd;
+    intdos(&regs, &regs);
+
+    return oldstate;
+}
 \f
 /*
  * The MSDOS routines, called from elsewhere.
 \f
 /*
  * The MSDOS routines, called from elsewhere.
@@ -861,8 +916,66 @@ static void
 TerminalNewMode(f)                             /* MSDOS */
 register int f;
 {
 TerminalNewMode(f)                             /* MSDOS */
 register int f;
 {
+    union REGS inregs;
+    struct SREGS segregs;
+    static old_1b_offset = 0, old_1b_segment = 0;
+
     globalmode = f;
     globalmode = f;
-    signal(SIGINT, CtrlCInterrupt);
+    if (MODE_COMMAND_LINE(f)) {
+       signal(SIGINT, SIG_DFL);
+       if (old_1b_segment|old_1b_offset) {
+           inregs.h.ah = 0x25;
+           inregs.h.al = 0x1b;
+           inregs.x.dx = old_1b_offset;
+           segregs.ds = old_1b_segment;
+           intdosx(&inregs, &inregs, &segregs);
+           old_1b_segment = old_1b_offset = 0;
+       }
+       if (setmode(fileno(stdout), O_TEXT) == -1) {
+           ExitPerror("setmode (text)", 1);
+       }
+       (void) dosbinary(fileno(stdout), 0);
+       if (setmode(fileno(stdin), O_TEXT) == -1) {
+           ExitPerror("setmode (text)", 1);
+       }
+       (void) dosbinary(fileno(stdin), 0);
+    } else {
+       signal(SIGINT, CtrlCInterrupt);
+       if ((old_1b_segment|old_1b_offset) == 0) {
+           extern void iret_subr();
+           void (far *foo_subr)() = iret_subr;
+
+           inregs.h.ah = 0x35;
+           inregs.h.al = 0x1b;
+           intdosx(&inregs, &inregs, &segregs);
+           old_1b_segment = segregs.es;
+           old_1b_offset = inregs.x.bx;
+           inregs.h.ah = 0x25;
+           inregs.h.al = 0x1b;
+           inregs.x.dx = FP_OFF(foo_subr);
+           segregs.ds = FP_SEG(foo_subr);
+           intdosx(&inregs, &inregs, &segregs);
+       }
+       if (MODE_LOCAL_CHARS(f)) {
+           if (setmode(fileno(stdout), O_TEXT) == -1) {
+               ExitPerror("setmode (text)", 1);
+           }
+           (void) dosbinary(fileno(stdout), 0);
+           if (setmode(fileno(stdin), O_TEXT) == -1) {
+               ExitPerror("setmode (text)", 1);
+           }
+           (void) dosbinary(fileno(stdin), 0);
+       } else {
+           if (setmode(fileno(stdout), O_BINARY) == -1) {
+               ExitPerror("setmode (binary)", 1);
+           }
+           (void) dosbinary(fileno(stdout), 1);
+           if (setmode(fileno(stdin), O_BINARY) == -1) {
+               ExitPerror("setmode (binary)", 1);
+           }
+           (void) dosbinary(fileno(stdin), 1);
+       }
+    }
 }
 
 
 }
 
 
@@ -892,6 +1005,8 @@ int        count;
 static void
 TerminalSaveState()                            /* MSDOS */
 {
 static void
 TerminalSaveState()                            /* MSDOS */
 {
+    savedInState = dosbinary(fileno(stdin), 0);
+    savedOutState = dosbinary(fileno(stdout), 0);
 }
 
 int
 }
 
 int
@@ -904,6 +1019,8 @@ TerminalSpecialChars(c)                    /* MSDOS */
 static void
 TerminalRestoreState()                         /* MSDOS */
 {
 static void
 TerminalRestoreState()                         /* MSDOS */
 {
+    (void) dosbinary(fileno(stdin), savedInState);
+    (void) dosbinary(fileno(stdout), savedOutState);
 }
 
 
 }
 
 
@@ -972,7 +1089,7 @@ tninit()
     sbp = sibuf;
     tbp = tibuf;
 
     sbp = sibuf;
     tbp = tibuf;
 
-    connected = net = scc = tcc = In3270 = ISend = 0;
+    connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0;
     telnetport = 0;
 #if    defined(unix)
     HaveInput = 0;
     telnetport = 0;
 #if    defined(unix)
     HaveInput = 0;
@@ -1011,24 +1128,30 @@ tninit()
 static void
 makeargv()
 {
 static void
 makeargv()
 {
-       register char *cp;
-       register char **argp = margv;
+    register char *cp;
+    register char **argp = margv;
 
 
-       margc = 0;
-       for (cp = line; *cp;) {
-               while (isspace(*cp))
-                       cp++;
-               if (*cp == '\0')
-                       break;
-               *argp++ = cp;
-               margc += 1;
-               while (*cp != '\0' && !isspace(*cp))
-                       cp++;
-               if (*cp == '\0')
-                       break;
-               *cp++ = '\0';
-       }
-       *argp++ = 0;
+    margc = 0;
+    cp = line;
+    if (*cp == '!') {          /* Special case shell escape */
+       *argp++ = "!";          /* No room in string to get this */
+       margc++;
+       cp++;
+    }
+    while (*cp) {
+       while (isspace(*cp))
+           cp++;
+       if (*cp == '\0')
+           break;
+       *argp++ = cp;
+       margc += 1;
+       while (*cp != '\0' && !isspace(*cp))
+           cp++;
+       if (*cp == '\0')
+           break;
+       *cp++ = '\0';
+    }
+    *argp++ = 0;
 }
 
 static char *ambiguous;                /* special return value */
 }
 
 static char *ambiguous;                /* special return value */
@@ -1045,6 +1168,9 @@ char      **(*next)();    /* routine to return next entry in table */
        register char **c, **found;
        register int nmatches, longest;
 
        register char **c, **found;
        register int nmatches, longest;
 
+       if (name == 0) {
+           return 0;
+       }
        longest = 0;
        nmatches = 0;
        found = 0;
        longest = 0;
        nmatches = 0;
        found = 0;
@@ -1873,7 +1999,7 @@ static void
 SetIn3270()
 {
     if (Sent3270TerminalType && myopts[TELOPT_BINARY]
 SetIn3270()
 {
     if (Sent3270TerminalType && myopts[TELOPT_BINARY]
-                                       && hisopts[TELOPT_BINARY]) {
+                           && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
        if (!In3270) {
            In3270 = 1;
            Init3270();         /* Initialize 3270 functions */
        if (!In3270) {
            In3270 = 1;
            Init3270();         /* Initialize 3270 functions */
@@ -1885,6 +2011,7 @@ SetIn3270()
        if (In3270) {
            StopScreen(1);
            In3270 = 0;
        if (In3270) {
            StopScreen(1);
            In3270 = 0;
+           Stop3270();         /* Tell 3270 we aren't here anymore */
            setconnmode();
        }
     }
            setconnmode();
        }
     }
@@ -1947,7 +2074,7 @@ telrcv()
                     * \n; since we must turn off CRMOD to get proper
                     * input, the mapping is done here (sigh).
                     */
                     * \n; since we must turn off CRMOD to get proper
                     * input, the mapping is done here (sigh).
                     */
-           if (c == '\r') {
+           if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
                if (scc > 0) {
                    c = *sbp&0xff;
                    if (c == 0) {
                if (scc > 0) {
                    c = *sbp&0xff;
                    if (c == 0) {
@@ -2283,6 +2410,9 @@ static void
 Finish3270()
 {
     while (Push3270() || !DoTerminalOutput()) {
 Finish3270()
 {
     while (Push3270() || !DoTerminalOutput()) {
+#if    defined(unix)
+       HaveInput = 0;
+#endif /* defined(unix) */
        ;
     }
 }
        ;
     }
 }
@@ -2369,7 +2499,17 @@ int returnCode;
 
 #endif /* defined(TN3270) */
 \f
 
 #endif /* defined(TN3270) */
 \f
-static
+/*
+ * Scheduler()
+ *
+ * Try to do something.
+ *
+ * If we do something useful, return 1; else return 0.
+ *
+ */
+
+
+int
 Scheduler(block)
 int    block;                  /* should we block in the select ? */
 {
 Scheduler(block)
 int    block;                  /* should we block in the select ? */
 {
@@ -2393,9 +2533,15 @@ int      block;                  /* should we block in the select ? */
     if (TTYBYTES()) {
        FD_SET(tout, &obits);
     }
     if (TTYBYTES()) {
        FD_SET(tout, &obits);
     }
+#if    defined(TN3270)
+    if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
+       FD_SET(tin, &ibits);
+    }
+#else  /* defined(TN3270) */
     if ((tcc == 0) && NETROOM()) {
        FD_SET(tin, &ibits);
     }
     if ((tcc == 0) && NETROOM()) {
        FD_SET(tin, &ibits);
     }
+#endif /* defined(TN3270) */
 #endif /* !defined(MSDOS) */
 #   if !defined(TN3270)
     if (TTYROOM()) {
 #endif /* !defined(MSDOS) */
 #   if !defined(TN3270)
     if (TTYROOM()) {
@@ -2545,7 +2691,7 @@ int       block;                  /* should we block in the select ? */
      * Something to read from the tty...
      */
 #if    defined(MSDOS)
      * Something to read from the tty...
      */
 #if    defined(MSDOS)
-    if ((tcc == 0) && NETROOM() && TerminalCanRead())
+    if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
 #else  /* defined(MSDOS) */
     if (FD_ISSET(tin, &ibits))
 #endif /* defined(MSDOS) */
 #else  /* defined(MSDOS) */
     if (FD_ISSET(tin, &ibits))
 #endif /* defined(MSDOS) */
@@ -2618,29 +2764,39 @@ int     block;                  /* should we block in the select ? */
                        break;
                    }
                }
                        break;
                    }
                }
-               switch (c) {
-               case '\n':
-                       /*
-                        * If we are in CRMOD mode (\r ==> \n)
-                        * on our local machine, then probably
-                        * a newline (unix) is CRLF (TELNET).
-                        */
-                   if (MODE_LOCAL_CHARS(globalmode)) {
-                       NETADD('\r');
+               if (!myopts[TELOPT_BINARY]) {
+                   switch (c) {
+                   case '\n':
+                           /*
+                            * If we are in CRMOD mode (\r ==> \n)
+                            * on our local machine, then probably
+                            * a newline (unix) is CRLF (TELNET).
+                            */
+                       if (MODE_LOCAL_CHARS(globalmode)) {
+                           NETADD('\r');
+                       }
+                       NETADD('\n');
+                       flushline = 1;
+                       break;
+                   case '\r':
+                       if (!crlf) {
+                           NET2ADD('\r', '\0');
+                       } else {
+                           NET2ADD('\r', '\n');
+                       }
+                       flushline = 1;
+                       break;
+                   case IAC:
+                       NET2ADD(IAC, IAC);
+                       break;
+                   default:
+                       NETADD(c);
+                       break;
                    }
                    }
-                   NETADD('\n');
-                   flushline = 1;
-                   break;
-               case '\r':
-                   NET2ADD('\r', '\0');
-                   flushline = 1;
-                   break;
-               case IAC:
+               } else if (c == IAC) {
                    NET2ADD(IAC, IAC);
                    NET2ADD(IAC, IAC);
-                   break;
-               default:
+               } else {
                    NETADD(c);
                    NETADD(c);
-                   break;
                }
            }
 #   if defined(TN3270)
                }
            }
 #   if defined(TN3270)
@@ -2648,7 +2804,7 @@ int       block;                  /* should we block in the select ? */
     }
 #   endif /* defined(TN3270) */
 
     }
 #   endif /* defined(TN3270) */
 
-    if ((!MODE_LINE(globalmode) || flushline) &&
+    if ((!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]) &&
        FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
        FD_CLR(net, &obits);
        returnValue = netflush();
        FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
        FD_CLR(net, &obits);
        returnValue = netflush();
@@ -2701,10 +2857,9 @@ telnet()
     NetNonblockingIO(net, 1);
 
 #if    defined(TN3270)
     NetNonblockingIO(net, 1);
 
 #if    defined(TN3270)
-#if    !defined(DEBUG)         /* DBX can't handle! */
-    NetSigIO(net, 1);
-#endif /* !defined(DEBUG) */
-
+    if (noasynch == 0) {                       /* DBX can't handle! */
+       NetSigIO(net, 1);
+    }
     NetSetPgrp(net);
 #endif /* defined(TN3270) */
 
     NetSetPgrp(net);
 #endif /* defined(TN3270) */
 
@@ -2735,7 +2890,7 @@ telnet()
     for (;;) {
        int schedValue;
 
     for (;;) {
        int schedValue;
 
-       while (!In3270) {
+       while (!In3270 && !shell_active) {
            if (Scheduler(SCHED_BLOCK) == -1) {
                setcommandmode();
                return;
            if (Scheduler(SCHED_BLOCK) == -1) {
                setcommandmode();
                return;
@@ -2754,9 +2909,15 @@ telnet()
        if (tfrontp-tbackp) {
            schedValue = 1;
        } else {
        if (tfrontp-tbackp) {
            schedValue = 1;
        } else {
-           schedValue = DoTerminalOutput();
+           if (shell_active) {
+               if (shell_continue() == 0) {
+                   ConnectScreen();
+               }
+           } else if (In3270) {
+               schedValue = DoTerminalOutput();
+           }
        }
        }
-       if (schedValue) {
+       if (schedValue && (shell_active == 0)) {
            if (Scheduler(SCHED_BLOCK) == -1) {
                setcommandmode();
                return;
            if (Scheduler(SCHED_BLOCK) == -1) {
                setcommandmode();
                return;
@@ -2962,6 +3123,46 @@ togdebug()
 }
 
 
 }
 
 
+static int
+togcrlf()
+{
+    if (crlf) {
+       printf("Will send carriage returns as telnet <CR><LF>.\n");
+    } else {
+       printf("Will send carriage returns as telnet <CR><NUL>.\n");
+    }
+    return 1;
+}
+
+
+static int
+togbinary()
+{
+    donebinarytoggle = 1;
+
+    if (myopts[TELOPT_BINARY] == 0) {  /* Go into binary mode */
+       NET2ADD(IAC, DO);
+       NETADD(TELOPT_BINARY);
+       printoption("<SENT", doopt, TELOPT_BINARY, 0);
+       NET2ADD(IAC, WILL);
+       NETADD(TELOPT_BINARY);
+       printoption("<SENT", doopt, TELOPT_BINARY, 0);
+       hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
+       printf("Negotiating binary mode with remote host.\n");
+    } else {                           /* Turn off binary mode */
+       NET2ADD(IAC, DONT);
+       NETADD(TELOPT_BINARY);
+       printoption("<SENT", dont, TELOPT_BINARY, 0);
+       NET2ADD(IAC, DONT);
+       NETADD(TELOPT_BINARY);
+       printoption("<SENT", dont, TELOPT_BINARY, 0);
+       hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
+       printf("Negotiating network ascii mode with remote host.\n");
+    }
+    return 1;
+}
+
+
 
 extern int togglehelp();
 
 
 extern int togglehelp();
 
@@ -2987,6 +3188,18 @@ static struct togglelist Togglelist[] = {
                1,
                    &autosynch,
                        "send interrupt characters in urgent mode" },
                1,
                    &autosynch,
                        "send interrupt characters in urgent mode" },
+    { "binary",
+       "toggle sending and receiving of binary data",
+           togbinary,
+               1,
+                   0,
+                       0 },
+    { "crlf",
+       "toggle sending carriage returns as telnet <CR><LF>",
+           togcrlf,
+               1,
+                   &crlf,
+                       0 },
     { "crmod",
        "toggle mapping of received carriage returns",
            0,
     { "crmod",
        "toggle mapping of received carriage returns",
            0,
@@ -3090,8 +3303,10 @@ char     *argv[];
        } else {
            if (c->variable) {
                *c->variable = !*c->variable;           /* invert it */
        } else {
            if (c->variable) {
                *c->variable = !*c->variable;           /* invert it */
-               printf("%s %s.\n", *c->variable? "Will" : "Won't",
+               if (c->actionexplanation) {
+                   printf("%s %s.\n", *c->variable? "Will" : "Won't",
                                                        c->actionexplanation);
                                                        c->actionexplanation);
+               }
            }
            if (c->handler) {
                retval &= (*c->handler)(c);
            }
            if (c->handler) {
                retval &= (*c->handler)(c);
@@ -3386,9 +3601,10 @@ suspend()
        setcommandmode();
 #if    defined(unix)
        kill(0, SIGTSTP);
        setcommandmode();
 #if    defined(unix)
        kill(0, SIGTSTP);
+#endif /* defined(unix) */
        /* reget parameters in case they were changed */
        TerminalSaveState();
        /* reget parameters in case they were changed */
        TerminalSaveState();
-#endif /* defined(unix) */
+       setconnmode();
        return 1;
 }
 
        return 1;
 }
 
@@ -3493,6 +3709,7 @@ settranscom(argc, argv)
 #endif /* defined(TN3270) && defined(unix) */
 
 
 #endif /* defined(TN3270) && defined(unix) */
 
 
+
 static
 tn(argc, argv)
        int argc;
 static
 tn(argc, argv)
        int argc;
@@ -3515,7 +3732,7 @@ tn(argc, argv)
        argc = margc;
        argv = margv;
     }
        argc = margc;
        argv = margv;
     }
-    if (argc > 3) {
+    if ((argc < 2) || (argc > 3)) {
        printf("usage: %s host-name [port]\n", argv[0]);
        return 0;
     }
        printf("usage: %s host-name [port]\n", argv[0]);
        return 0;
     }
@@ -3624,7 +3841,6 @@ static char
        openhelp[] =    "connect to a site",
        closehelp[] =   "close current connection",
        quithelp[] =    "exit telnet",
        openhelp[] =    "connect to a site",
        closehelp[] =   "close current connection",
        quithelp[] =    "exit telnet",
-       zhelp[] =       "suspend telnet",
        statushelp[] =  "print status information",
        helphelp[] =    "print help information",
        sendhelp[] =    "transmit special characters ('send ?' for more)",
        statushelp[] =  "print status information",
        helphelp[] =    "print help information",
        sendhelp[] =    "transmit special characters ('send ?' for more)",
@@ -3634,9 +3850,15 @@ static char
 #if    defined(TN3270) && defined(unix)
        transcomhelp[] = "specify Unix command for transparent mode pipe",
 #endif /* defined(TN3270) && defined(unix) */
 #if    defined(TN3270) && defined(unix)
        transcomhelp[] = "specify Unix command for transparent mode pipe",
 #endif /* defined(TN3270) && defined(unix) */
+#if    defined(unix)
+       zhelp[] =       "suspend telnet",
+#endif /* defined(unix */
+#if    defined(TN3270)
+       shellhelp[] =   "invoke a subshell",
+#endif /* defined(TN3270) */
        modehelp[] = "try to enter line-by-line or character-at-a-time mode";
 
        modehelp[] = "try to enter line-by-line or character-at-a-time mode";
 
-extern int     help();
+extern int     help(), shell();
 
 static struct cmd cmdtab[] = {
        { "close",      closehelp,      bye,            1, 1 },
 
 static struct cmd cmdtab[] = {
        { "close",      closehelp,      bye,            1, 1 },
@@ -3651,7 +3873,12 @@ static struct cmd cmdtab[] = {
 #if    defined(TN3270) && defined(unix)
        { "transcom",   transcomhelp,   settranscom,    1, 0 },
 #endif /* defined(TN3270) && defined(unix) */
 #if    defined(TN3270) && defined(unix)
        { "transcom",   transcomhelp,   settranscom,    1, 0 },
 #endif /* defined(TN3270) && defined(unix) */
+#if    defined(unix)
        { "z",          zhelp,          suspend,        1, 0 },
        { "z",          zhelp,          suspend,        1, 0 },
+#endif /* defined(unix) */
+#if    defined(TN3270)
+       { "!",          shellhelp,      shell,          1, 1 },
+#endif /* defined(TN3270) */
        { "?",          helphelp,       help,           1, 0 },
        0
 };
        { "?",          helphelp,       help,           1, 0 },
        0
 };
@@ -3709,51 +3936,57 @@ void
 command(top)
        int top;
 {
 command(top)
        int top;
 {
-       register struct cmd *c;
+    register struct cmd *c;
 
 
-       setcommandmode();
-       if (!top) {
-               putchar('\n');
-       } else {
+    setcommandmode();
+    if (!top) {
+       putchar('\n');
+    } else {
 #if    defined(unix)
 #if    defined(unix)
-               signal(SIGINT, SIG_DFL);
-               signal(SIGQUIT, SIG_DFL);
+       signal(SIGINT, SIG_DFL);
+       signal(SIGQUIT, SIG_DFL);
 #endif /* defined(unix) */
 #endif /* defined(unix) */
+    }
+    for (;;) {
+       printf("%s> ", prompt);
+       if (gets(line) == NULL) {
+           if (feof(stdin) || ferror(stdin))
+               quit();
+           break;
        }
        }
-       for (;;) {
-               printf("%s> ", prompt);
-               if (gets(line) == NULL) {
-                       if (feof(stdin) || ferror(stdin))
-                               quit();
-                       break;
-               }
-               if (line[0] == 0)
-                       break;
-               makeargv();
-               c = getcmd(margv[0]);
-               if (c == Ambiguous(struct cmd *)) {
-                       printf("?Ambiguous command\n");
-                       continue;
-               }
-               if (c == 0) {
-                       printf("?Invalid command\n");
-                       continue;
-               }
-               if (c->needconnect && !connected) {
-                       printf("?Need to be connected first.\n");
-                       continue;
-               }
-               if ((*c->handler)(margc, margv)) {
-                       break;
-               }
+       if (line[0] == 0)
+           break;
+       makeargv();
+       c = getcmd(margv[0]);
+       if (c == Ambiguous(struct cmd *)) {
+           printf("?Ambiguous command\n");
+           continue;
        }
        }
-       if (!top) {
-               if (!connected) {
-                       longjmp(toplevel, 1);
-                       /*NOTREACHED*/
-               }
-               setconnmode();
+       if (c == 0) {
+           printf("?Invalid command\n");
+           continue;
+       }
+       if (c->needconnect && !connected) {
+           printf("?Need to be connected first.\n");
+           continue;
+       }
+       if ((*c->handler)(margc, margv)) {
+           break;
+       }
+    }
+    if (!top) {
+       if (!connected) {
+           longjmp(toplevel, 1);
+           /*NOTREACHED*/
+       }
+#if    defined(TN3270)
+       if (shell_active == 0) {
+           setconnmode();
        }
        }
+#else  /* defined(TN3270) */
+       setconnmode();
+#endif /* defined(TN3270) */
+    }
 }
 \f
 /*
 }
 \f
 /*
@@ -3806,41 +4039,55 @@ main(argc, argv)
     autoflush = TerminalAutoFlush();
 
     prompt = argv[0];
     autoflush = TerminalAutoFlush();
 
     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;
+    while ((argc > 1) && (argv[1][0] == '-')) {
+       if (!strcmp(argv[1], "-d")) {
+           debug = 1;
+       } else if (!strcmp(argv[1], "-n")) {
+           if ((argc > 1) && (argv[2][0] != '-')) {    /* get file name */
+               NetTrace = fopen(argv[2], "w");
+               argv++;
+               argc--;
+               if (NetTrace == NULL) {
+                   NetTrace = stdout;
+               }
            }
            }
-       }
-    }
+       } else {
 #if    defined(TN3270) && defined(unix)
 #if    defined(TN3270) && defined(unix)
-    if (argc > 1 && !strcmp(argv[1], "-t")) {
-       argv++;
-       argc--;
-       if (argc > 1) {         /* get command name */
-           transcom = tline;
-           (void) strcpy(transcom, argv[1]);
-           argv++;
-           argc--;
+           if (!strcmp(argv[1], "-t")) {
+               if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
+                   transcom = tline;
+                   (void) strcpy(transcom, argv[1]);
+                   argv++;
+                   argc--;
+               }
+           } else if (!strcmp(argv[1], "-noasynch")) {
+               noasynch = 1;
+           } else
+#endif /* defined(TN3270) && defined(unix) */
+           if (argv[1][1] != '\0') {
+               fprintf(stderr, "Unknown option *%s*.\n", argv[1]);
+           }
        }
        }
+       argc--;
+       argv++;
     }
     }
-#endif /* defined(TN3270) && defined(unix) */
     if (argc != 1) {
        if (setjmp(toplevel) != 0)
            Exit(0);
        tn(argc, argv);
     }
     setjmp(toplevel);
     if (argc != 1) {
        if (setjmp(toplevel) != 0)
            Exit(0);
        tn(argc, argv);
     }
     setjmp(toplevel);
-    for (;;)
+    for (;;) {
+#if    !defined(TN3270)
        command(1);
        command(1);
+#else  /* !defined(TN3270) */
+       if (!shell_active) {
+           command(1);
+       } else {
+#if    defined(TN3270)
+           shell_continue();
+#endif /* defined(TN3270) */
+       }
+#endif /* !defined(TN3270) */
+    }
 }
 }