Add support for LINEMODE option
authorPaul Borman <borman@ucbvax.Berkeley.EDU>
Tue, 22 Aug 1989 05:59:03 +0000 (21:59 -0800)
committerPaul Borman <borman@ucbvax.Berkeley.EDU>
Tue, 22 Aug 1989 05:59:03 +0000 (21:59 -0800)
SCCS-vsn: usr.bin/telnet/commands.c 1.19
SCCS-vsn: usr.bin/telnet/defines.h 1.8
SCCS-vsn: usr.bin/telnet/externs.h 1.17
SCCS-vsn: usr.bin/telnet/main.c 1.11
SCCS-vsn: usr.bin/telnet/ring.c 1.11
SCCS-vsn: usr.bin/telnet/sys_bsd.c 1.19
SCCS-vsn: usr.bin/telnet/telnet.c 5.41
SCCS-vsn: usr.bin/telnet/terminal.c 1.16
SCCS-vsn: usr.bin/telnet/tn3270.c 1.19
SCCS-vsn: usr.bin/telnet/utilities.c 1.14

usr/src/usr.bin/telnet/commands.c
usr/src/usr.bin/telnet/defines.h
usr/src/usr.bin/telnet/externs.h
usr/src/usr.bin/telnet/main.c
usr/src/usr.bin/telnet/ring.c
usr/src/usr.bin/telnet/sys_bsd.c
usr/src/usr.bin/telnet/telnet.c
usr/src/usr.bin/telnet/terminal.c
usr/src/usr.bin/telnet/tn3270.c
usr/src/usr.bin/telnet/utilities.c

index a71e024..929b2e4 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)commands.c 1.18 (Berkeley) %G%";
+static char sccsid[] = "@(#)commands.c 1.19 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/types.h>
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -25,6 +25,9 @@ static char sccsid[] = "@(#)commands.c        1.18 (Berkeley) %G%";
 #endif /* defined(unix) */
 #include <sys/socket.h>
 #include <netinet/in.h>
 #endif /* defined(unix) */
 #include <sys/socket.h>
 #include <netinet/in.h>
+#ifdef CRAY
+#include <sys/fcntl.h>
+#endif CRAY
 
 #include <signal.h>
 #include <netdb.h>
 
 #include <signal.h>
 #include <netdb.h>
@@ -41,20 +44,32 @@ static char sccsid[] = "@(#)commands.c      1.18 (Berkeley) %G%";
 #include "defines.h"
 #include "types.h"
 
 #include "defines.h"
 #include "types.h"
 
+#ifdef SRCRT
+#if    !defined(CRAY)
+#include <netinet/in_systm.h>
+#if    !defined(sun)
+#include <machine/endian.h>
+#endif
+#endif
+#include <netinet/ip.h>
+#endif
+
+
 char   *hostname;
 char   *hostname;
+extern char *getenv();
 
 #define Ambiguous(s)   ((char **)s == &ambiguous)
 static char *ambiguous;                /* special return value for command routines */
 
 typedef struct {
        char    *name;          /* command name */
 
 #define Ambiguous(s)   ((char **)s == &ambiguous)
 static char *ambiguous;                /* special return value for command routines */
 
 typedef struct {
        char    *name;          /* command name */
-       char    *help;          /* help string */
+       char    *help;          /* help string (NULL for no help) */
        int     (*handler)();   /* routine which executes command */
        int     (*handler)();   /* routine which executes command */
-       int     dohelp;         /* Should we give general help information? */
        int     needconnect;    /* Do we need to be connected to execute? */
 } Command;
 
        int     needconnect;    /* Do we need to be connected to execute? */
 } Command;
 
-static char line[200];
+static char line[256];
+static char saveline[256];
 static int margc;
 static char *margv[20];
 
 static int margc;
 static char *margv[20];
 
@@ -106,6 +121,7 @@ makeargv()
     margc = 0;
     cp = line;
     if (*cp == '!') {          /* Special case shell escape */
     margc = 0;
     cp = line;
     if (*cp == '!') {          /* Special case shell escape */
+       strcpy(saveline, line); /* save for shell command */
        *argp++ = "!";          /* No room in string to get this */
        margc++;
        cp++;
        *argp++ = "!";          /* No room in string to get this */
        margc++;
        cp++;
@@ -227,43 +243,47 @@ control(c)
  
 struct sendlist {
     char       *name;          /* How user refers to it (case independent) */
  
 struct sendlist {
     char       *name;          /* How user refers to it (case independent) */
-    int                what;           /* Character to be sent (<0 ==> special) */
     char       *help;          /* Help information (0 ==> no help) */
 #if    defined(NOT43)
     char       *help;          /* Help information (0 ==> no help) */
 #if    defined(NOT43)
-    int                (*routine)();   /* Routine to perform (for special ops) */
+    int                (*handler)();   /* Routine to perform (for special ops) */
 #else  /* defined(NOT43) */
 #else  /* defined(NOT43) */
-    void       (*routine)();   /* Routine to perform (for special ops) */
+    void       (*handler)();   /* Routine to perform (for special ops) */
 #endif /* defined(NOT43) */
 #endif /* defined(NOT43) */
+    int                what;           /* Character to be sent (<0 ==> special) */
 };
 \f
 #define        SENDQUESTION    -1
 #define        SENDESCAPE      -3
 
 static struct sendlist Sendlist[] = {
 };
 \f
 #define        SENDQUESTION    -1
 #define        SENDESCAPE      -3
 
 static struct sendlist Sendlist[] = {
-    { "ao", AO, "Send Telnet Abort output" },
-    { "ayt", AYT, "Send Telnet 'Are You There'" },
-    { "brk", BREAK, "Send Telnet Break" },
-    { "ec", EC, "Send Telnet Erase Character" },
-    { "el", EL, "Send Telnet Erase Line" },
-    { "escape", SENDESCAPE, "Send current escape character" },
-    { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
-    { "ip", IP, "Send Telnet Interrupt Process" },
-    { "nop", NOP, "Send Telnet 'No operation'" },
-    { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
-    { "?", SENDQUESTION, "Display send options" },
+    { "ao",    "Send Telnet Abort output",             0,      AO },
+    { "ayt",   "Send Telnet 'Are You There'",          0,      AYT },
+    { "brk",   "Send Telnet Break",                    0,      BREAK },
+    { "ec",    "Send Telnet Erase Character",          0,      EC },
+    { "el",    "Send Telnet Erase Line",               0,      EL },
+    { "escape",        "Send current escape character",        0,      SENDESCAPE },
+    { "ga",    "Send Telnet 'Go Ahead' sequence",      0,      GA },
+    { "ip",    "Send Telnet Interrupt Process",        0,      IP },
+    { "nop",   "Send Telnet 'No operation'",           0,      NOP },
+    { "eor",   "Send Telnet 'End of Record'",          0,      EOR },
+    { "abort", "Send Telnet 'Abort Process'",          0,      ABORT },
+    { "susp",  "Send Telnet 'Suspend Process'",        0,      SUSP },
+    { "eof",   "Send Telnet End of File Character",    0,      xEOF },
+    { "synch", "Perform Telnet 'Synch operation'",     dosynch, SYNCH },
+    { "?",     "Display send options",                 0,      SENDQUESTION },
     { 0 }
 };
 
 static struct sendlist Sendlist2[] = {         /* some synonyms */
     { 0 }
 };
 
 static struct sendlist Sendlist2[] = {         /* some synonyms */
-       { "break", BREAK, 0 },
+    { "break",         0, 0, BREAK },
 
 
-       { "intp", IP, 0 },
-       { "interrupt", IP, 0 },
-       { "intr", IP, 0 },
+    { "intp",          0, 0, IP },
+    { "interrupt",     0, 0, IP },
+    { "intr",          0, 0, IP },
 
 
-       { "help", SENDQUESTION, 0 },
+    { "help",          0, 0, SENDQUESTION },
 
 
-       { 0 }
+    { 0 }
 };
 
 static char **
 };
 
 static char **
@@ -326,6 +346,7 @@ char        **argv;
        }
        switch (s->what) {
        case SENDQUESTION:
        }
        switch (s->what) {
        case SENDQUESTION:
+           question = 1;
            break;
        case SENDESCAPE:
            count += 1;
            break;
        case SENDESCAPE:
            count += 1;
@@ -338,6 +359,17 @@ char       **argv;
            break;
        }
     }
            break;
        }
     }
+    if (!connected) {
+       if (count)
+           printf("?Need to be connected first.\n");
+       if (question) {
+           for (s = Sendlist; s->name; s++)
+               if (s->help)
+                   printf("%-15s %s\n", s->name, s->help);
+       } else
+           printf("'send ?' for help\n");
+       return !question;
+    }
     /* Now, do we have enough room? */
     if (NETROOM() < count) {
        printf("There is not enough room in the buffer TO the network\n");
     /* Now, do we have enough room? */
     if (NETROOM() < count) {
        printf("There is not enough room in the buffer TO the network\n");
@@ -353,8 +385,8 @@ char        **argv;
            quit();
            /*NOTREACHED*/
        }
            quit();
            /*NOTREACHED*/
        }
-       if (s->routine) {
-           (*s->routine)(s);
+       if (s->handler) {
+           (*s->handler)(s);
        } else {
            switch (what = s->what) {
            case SYNCH:
        } else {
            switch (what = s->what) {
            case SYNCH:
@@ -362,13 +394,8 @@ char       **argv;
                break;
            case SENDQUESTION:
                for (s = Sendlist; s->name; s++) {
                break;
            case SENDQUESTION:
                for (s = Sendlist; s->name; s++) {
-                   if (s->help) {
-                       printf(s->name);
-                       if (s->help) {
-                           printf("\t%s", s->help);
-                       }
-                       printf("\n");
-                   }
+                   if (s->help)
+                       printf("%-15s %s\n", s->name, s->help);
                }
                question = 1;
                break;
                }
                question = 1;
                break;
@@ -428,17 +455,27 @@ togcrlf()
 
 
 static int
 
 
 static int
-togbinary()
+togbinary(val)
+int val;
 {
     donebinarytoggle = 1;
 
 {
     donebinarytoggle = 1;
 
-    if (did_he_say_will(TELOPT_BINARY) || did_I_say_will(TELOPT_BINARY)) {
-       /* leave binary mode */
-       printf("Negotiating network ascii mode with remote host.\n");
-       tel_leave_binary();
+    if (my_want_state_is_will(TELOPT_BINARY) ||
+       my_want_state_is_do(TELOPT_BINARY)) {
+       if (val == 1)
+           printf("Already operating in binary mode with remote host.\n");
+       else {
+           /* leave binary mode */
+           printf("Negotiating network ascii mode with remote host.\n");
+           tel_leave_binary();
+       }
     } else {                           /* Turn off binary mode */
     } else {                           /* Turn off binary mode */
-       printf("Negotiating binary mode with remote host.\n");
-       tel_enter_binary();
+       if (val == 0)
+           printf("Already in network ascii mode with remote host.\n");
+       else {
+           printf("Negotiating binary mode with remote host.\n");
+           tel_enter_binary();
+       }
     }
     return 1;
 }
     }
     return 1;
 }
@@ -446,97 +483,88 @@ togbinary()
 
 
 extern int togglehelp();
 
 
 extern int togglehelp();
+extern int slc_check();
 
 struct togglelist {
     char       *name;          /* name of toggle */
     char       *help;          /* help message */
     int                (*handler)();   /* routine to do actual setting */
 
 struct togglelist {
     char       *name;          /* name of toggle */
     char       *help;          /* help message */
     int                (*handler)();   /* routine to do actual setting */
-    int                dohelp;         /* should we display help information */
     int                *variable;
     char       *actionexplanation;
 };
 
 static struct togglelist Togglelist[] = {
     { "autoflush",
     int                *variable;
     char       *actionexplanation;
 };
 
 static struct togglelist Togglelist[] = {
     { "autoflush",
-       "toggle flushing of output when sending interrupt characters",
+       "flushing of output when sending interrupt characters",
            0,
            0,
-               1,
-                   &autoflush,
-                       "flush output when sending interrupt characters" },
+               &autoflush,
+                   "flush output when sending interrupt characters" },
     { "autosynch",
     { "autosynch",
-       "toggle automatic sending of interrupt characters in urgent mode",
+       "automatic sending of interrupt characters in urgent mode",
            0,
            0,
-               1,
-                   &autosynch,
-                       "send interrupt characters in urgent mode" },
+               &autosynch,
+                   "send interrupt characters in urgent mode" },
     { "binary",
     { "binary",
-       "toggle sending and receiving of binary data",
+       "sending and receiving of binary data",
            togbinary,
            togbinary,
-               1,
-                   0,
-                       0 },
+               0,
+                   0 },
     { "crlf",
     { "crlf",
-       "toggle sending carriage returns as telnet <CR><LF>",
+       "sending carriage returns as telnet <CR><LF>",
            togcrlf,
            togcrlf,
-               1,
-                   &crlf,
-                       0 },
+               &crlf,
+                   0 },
     { "crmod",
     { "crmod",
-       "toggle mapping of received carriage returns",
+       "mapping of received carriage returns",
            0,
            0,
-               1,
-                   &crmod,
-                       "map carriage return on output" },
+               &crmod,
+                   "map carriage return on output" },
     { "localchars",
     { "localchars",
-       "toggle local recognition of certain control characters",
+       "local recognition of certain control characters",
            lclchars,
            lclchars,
-               1,
-                   &localchars,
-                       "recognize certain control characters" },
-    { " ", "", 0, 1 },         /* empty line */
+               &localchars,
+                   "recognize certain control characters" },
+    { " ", "", 0 },            /* empty line */
 #if    defined(unix) && defined(TN3270)
     { "cursesdata",
        "(debugging) toggle printing of hexadecimal curses data",
            0,
 #if    defined(unix) && defined(TN3270)
     { "cursesdata",
        "(debugging) toggle printing of hexadecimal curses data",
            0,
-               1,
-                   &cursesdata,
-                       "print hexadecimal representation of curses data" },
+               &cursesdata,
+                   "print hexadecimal representation of curses data" },
 #endif /* defined(unix) && defined(TN3270) */
     { "debug",
 #endif /* defined(unix) && defined(TN3270) */
     { "debug",
-       "(debugging) toggle debugging",
+       "debugging",
            togdebug,
            togdebug,
-               1,
-                   &debug,
-                       "turn on socket level debugging" },
+               &debug,
+                   "turn on socket level debugging" },
     { "netdata",
     { "netdata",
-       "(debugging) toggle printing of hexadecimal network data",
+       "printing of hexadecimal network data (debugging)",
+           0,
+               &netdata,
+                   "print hexadecimal representation of network traffic" },
+    { "prettydump",
+       "output of \"netdata\" to user readable format (debugging)",
            0,
            0,
-               1,
-                   &netdata,
-                       "print hexadecimal representation of network traffic" },
+               &prettydump,
+                   "print user readable output for \"netdata\"" },
     { "options",
     { "options",
-       "(debugging) toggle viewing of options processing",
+       "viewing of options processing (debugging)",
            0,
            0,
-               1,
-                   &showoptions,
-                       "show option processing" },
+               &showoptions,
+                   "show option processing" },
 #if    defined(unix)
     { "termdata",
        "(debugging) toggle printing of hexadecimal terminal data",
            0,
 #if    defined(unix)
     { "termdata",
        "(debugging) toggle printing of hexadecimal terminal data",
            0,
-               1,
-                   &termdata,
-                       "print hexadecimal representation of terminal traffic" },
+               &termdata,
+                   "print hexadecimal representation of terminal traffic" },
 #endif /* defined(unix) */
 #endif /* defined(unix) */
-    { " ", "", 0, 1 },         /* empty line */
     { "?",
     { "?",
-       "display help information",
-           togglehelp,
-               1 },
+       0,
+           togglehelp },
     { "help",
     { "help",
-       "display help information",
-           togglehelp,
-               0 },
+       0,
+           togglehelp },
     { 0 }
 };
 
     { 0 }
 };
 
@@ -546,13 +574,35 @@ togglehelp()
     struct togglelist *c;
 
     for (c = Togglelist; c->name; c++) {
     struct togglelist *c;
 
     for (c = Togglelist; c->name; c++) {
-       if (c->dohelp) {
-           printf("%s\t%s\n", c->name, c->help);
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s toggle %s\n", c->name, c->help);
+           else
+               printf("\n");
        }
     }
        }
     }
+    printf("\n");
+    printf("%-15s %s\n", "?", "display help information");
     return 0;
 }
 
     return 0;
 }
 
+static
+settogglehelp(set)
+int set;
+{
+    struct togglelist *c;
+
+    for (c = Togglelist; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
+                                               c->help);
+           else
+               printf("\n");
+       }
+    }
+}
+
 static char **
 getnexttoggle(name)
 char *name;
 static char **
 getnexttoggle(name)
 char *name;
@@ -606,7 +656,7 @@ char        *argv[];
                }
            }
            if (c->handler) {
                }
            }
            if (c->handler) {
-               retval &= (*c->handler)(c);
+               retval &= (*c->handler)(-1);
            }
        }
     }
            }
        }
     }
@@ -617,26 +667,56 @@ char      *argv[];
  * The following perform the "set" command.
  */
 
  * The following perform the "set" command.
  */
 
+#ifdef USE_TERMIO
+struct termio new_tc = { 0 };
+#endif
+
 struct setlist {
     char *name;                                /* name */
     char *help;                                /* help information */
 struct setlist {
     char *name;                                /* name */
     char *help;                                /* help information */
+    void (*handler)();
     char *charp;                       /* where it is located at */
 };
 
 static struct setlist Setlist[] = {
     char *charp;                       /* where it is located at */
 };
 
 static struct setlist Setlist[] = {
-    { "echo",  "character to toggle local echoing on/off", &echoc },
-    { "escape",        "character to escape back to telnet command mode", &escape },
+    { "echo",  "character to toggle local echoing on/off", 0, &echoc },
+    { "escape",        "character to escape back to telnet command mode", 0, &escape },
+    { "tracefile", "file to write trace intormation to", SetNetTrace, NetTraceFile},
     { " ", "" },
     { " ", "" },
-    { " ", "The following need 'localchars' to be toggled true", 0 },
-    { "erase", "character to cause an Erase Character", &termEraseChar },
-    { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
-    { "interrupt", "character to cause an Interrupt Process", &termIntChar },
-    { "kill",  "character to cause an Erase Line", &termKillChar },
-    { "quit",  "character to cause a Break", &termQuitChar },
-    { "eof",   "character to cause an EOF ", &termEofChar },
+    { " ", "The following need 'localchars' to be toggled true", 0, 0 },
+#ifndef        CRAY
+    { "flushoutput", "character to cause an Abort Oubput", 0, termFlushCharp },
+#endif
+    { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp },
+    { "quit",  "character to cause an Abort process", 0, termQuitCharp },
+    { "eof",   "character to cause an EOF ", 0, termEofCharp },
+    { " ", "" },
+    { " ", "The following are for local editing in linemode", 0, 0 },
+    { "erase", "character to use to erase a character", 0, termEraseCharp },
+    { "kill",  "character to use to erase a line", 0, termKillCharp },
+#ifndef        CRAY
+    { "lnext", "character to use for literal next", 0, termLiteralNextCharp },
+    { "susp",  "character to cuase a Suspend Process", 0, termSuspCharp },
+    { "reprint", "character to use for line reprint", 0, termRprntCharp },
+    { "worderase", "character to use to erase a word", 0, termWerasCharp },
+    { "start", "character to use for XON", 0, termStartCharp },
+    { "stop",  "character to sue for XOFF", 0, termStopCharp },
+#endif
     { 0 }
 };
 
     { 0 }
 };
 
+#ifdef CRAY
+/* Work around compiler bug */
+_setlist_init()
+{
+       Setlist[6].charp = &termIntChar;
+       Setlist[7].charp = &termQuitChar;
+       Setlist[8].charp = &termEofChar;
+       Setlist[11].charp = &termEraseChar;
+       Setlist[12].charp = &termKillChar;
+}
+#endif CRAY
+
 static char **
 getnextset(name)
 char *name;
 static char **
 getnextset(name)
 char *name;
@@ -660,30 +740,60 @@ char      *argv[];
 {
     int value;
     struct setlist *ct;
 {
     int value;
     struct setlist *ct;
+    struct togglelist *c;
 
 
-    /* XXX back we go... sigh */
-    if (argc != 3) {
-       if ((argc == 2) &&
-                   ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
-           for (ct = Setlist; ct->name; ct++) {
-               printf("%s\t%s\n", ct->name, ct->help);
-           }
-           printf("?\tdisplay help information\n");
-       } else {
-           printf("Format is 'set Name Value'\n'set ?' for help.\n");
-       }
+    if (argc < 2 || argc > 3) {
+       printf("Format is 'set Name Value'\n'set ?' for help.\n");
+       return 0;
+    }
+    if ((argc == 2) &&
+               ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
+       for (ct = Setlist; ct->name; ct++)
+           printf("%-15s %s\n", ct->name, ct->help);
+       printf("\n");
+       settogglehelp(1);
+       printf("%-15s %s\n", "?", "display help information");
        return 0;
     }
 
     ct = getset(argv[1]);
     if (ct == 0) {
        return 0;
     }
 
     ct = getset(argv[1]);
     if (ct == 0) {
-       fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
+       c = gettoggle(argv[1]);
+       if (c == 0) {
+           fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
+                       argv[1]);
+           return 0;
+       } else if (Ambiguous(c)) {
+           fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
                        argv[1]);
                        argv[1]);
+           return 0;
+       }
+       if (c->variable) {
+           if ((argc == 2) || (strcmp("on", argv[2]) == 0))
+               *c->variable = 1;
+           else if (strcmp("off", argv[2]) == 0)
+               *c->variable = 0;
+           else {
+               printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n");
+               return 0;
+           }
+           if (c->actionexplanation) {
+               printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+           }
+       }
+       if (c->handler)
+           (*c->handler)(1);
+    } else if (argc != 3) {
+       printf("Format is 'set Name Value'\n'set ?' for help.\n");
        return 0;
     } else if (Ambiguous(ct)) {
        fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
                        argv[1]);
        return 0;
        return 0;
     } else if (Ambiguous(ct)) {
        fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
                        argv[1]);
        return 0;
+    } else if (ct->handler) {
+       (*ct->handler)(argv[2]);
+       printf("%s set to \"%s\".\n", ct->name, ct->charp);
     } else {
        if (strcmp("off", argv[2])) {
            value = special(argv[2]);
     } else {
        if (strcmp("off", argv[2])) {
            value = special(argv[2]);
@@ -693,6 +803,72 @@ char       *argv[];
        *(ct->charp) = value;
        printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
     }
        *(ct->charp) = value;
        printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
     }
+    slc_check();
+    return 1;
+}
+
+static
+unsetcmd(argc, argv)
+int    argc;
+char   *argv[];
+{
+    int value;
+    struct setlist *ct;
+    struct togglelist *c;
+    register char *name;
+
+    if (argc < 2) {
+       fprintf(stderr,
+           "Need an argument to 'unset' command.  'unset ?' for help.\n");
+       return 0;
+    }
+    if ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help"))) {
+       for (ct = Setlist; ct->name; ct++)
+           printf("%-15s %s\n", ct->name, ct->help);
+       printf("\n");
+       settogglehelp(0);
+       printf("%-15s %s\n", "?", "display help information");
+       return 0;
+    }
+
+    argc--;
+    argv++;
+    while (argc--) {
+       name = *argv++;
+       ct = getset(name);
+       if (ct == 0) {
+           c = gettoggle(name);
+           if (c == 0) {
+               fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n",
+                       name);
+               return 0;
+           } else if (Ambiguous(c)) {
+               fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+                       name);
+               return 0;
+           }
+           if (c->variable) {
+               *c->variable = 0;
+               if (c->actionexplanation) {
+                   printf("%s %s.\n", *c->variable? "Will" : "Won't",
+                                                       c->actionexplanation);
+               }
+           }
+           if (c->handler)
+               (*c->handler)(0);
+       } else if (Ambiguous(ct)) {
+           fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+                       name);
+           return 0;
+       } else if (ct->handler) {
+           (*ct->handler)(0);
+           printf("%s reset to \"%s\".\n", ct->name, ct->charp);
+       } else {
+           value = -1;
+           *(ct->charp) = -1;
+           printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+       }
+    }
     return 1;
 }
 \f
     return 1;
 }
 \f
@@ -700,34 +876,94 @@ char      *argv[];
  * The following are the data structures and routines for the
  * 'mode' command.
  */
  * The following are the data structures and routines for the
  * 'mode' command.
  */
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+#endif
 
 static
 dolinemode()
 {
 
 static
 dolinemode()
 {
-    if (did_I_say_do(TELOPT_SGA)) {
-       send_dont(TELOPT_SGA);
-    }
-    if (did_I_say_do(TELOPT_ECHO)) {
-       send_dont(TELOPT_ECHO);
-    }
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode)
+       send_dont(TELOPT_SGA, 1);
+#endif
+    send_will(TELOPT_LINEMODE, 1);
+    send_dont(TELOPT_ECHO, 1);
     return 1;
 }
 
 static
 docharmode()
 {
     return 1;
 }
 
 static
 docharmode()
 {
-    if (!did_I_say_do(TELOPT_SGA)) {
-       send_do(TELOPT_SGA);
-    }
-    if (!did_I_say_do(TELOPT_ECHO)) {
-       send_do(TELOPT_ECHO);
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode)
+       send_do(TELOPT_SGA, 1);
+    else
+#endif
+    send_wont(TELOPT_LINEMODE, 1);
+    send_do(TELOPT_ECHO, 1);
+    return 1;
+}
+
+setmode(bit)
+{
+    return dolmmode(bit, 1);
+}
+
+clearmode(bit)
+{
+    return dolmmode(bit, 0);
+}
+
+dolmmode(bit, on)
+int bit, on;
+{
+    char c;
+    extern int linemode;
+
+    if (my_want_state_is_wont(TELOPT_LINEMODE)) {
+       printf("?Need to have LINEMODE option enabled first.\n");
+       printf("'mode ?' for help.\n");
+       return 0;
     }
     }
+
+    if (on)
+       c = (linemode | bit);
+    else
+       c = (linemode & ~bit);
+    lm_mode(&c, 1, 1);
     return 1;
 }
 
     return 1;
 }
 
-static Command Mode_commands[] = {
-    { "character",     "character-at-a-time mode",     docharmode, 1, 1 },
-    { "line",          "line-by-line mode",            dolinemode, 1, 1 },
+struct modelist {
+       char    *name;          /* command name */
+       char    *help;          /* help string */
+       int     (*handler)();   /* routine which executes command */
+       int     needconnect;    /* Do we need to be connected to execute? */
+       int     arg1;
+};
+
+extern int modehelp();
+
+static struct modelist ModeList[] = {
+    { "character", "Disable LINEMODE option",  docharmode, 1 },
+#ifdef KLUDEGLINEMODE
+    { "",      "(or disable obsolete line-by-line mode)", 0 };
+#endif
+    { "line",  "Enable LINEMODE option",       dolinemode, 1 },
+#ifdef KLUDEGLINEMODE
+    { "",      "(or enable obsolete line-by-line mode)", 0 };
+#endif
+    { "", "", 0 },
+    { "",      "These require the LINEMODE option to be enabled", 0 },
+    { "isig",  "Enable signal trapping",       setmode, 1, MODE_TRAPSIG },
+    { "+isig", 0,                              setmode, 1, MODE_TRAPSIG },
+    { "-isig", "Disable signal trapping",      clearmode, 1, MODE_TRAPSIG },
+    { "edit",  "Enable character editing",     setmode, 1, MODE_EDIT },
+    { "+edit", 0,                              setmode, 1, MODE_EDIT },
+    { "-edit", "Disable character editing",    clearmode, 1, MODE_EDIT },
+    { "help",  0,                              modehelp, 0 },
+    { "?",     "Print help information",       modehelp, 0 },
     { 0 },
 };
 
     { 0 },
 };
 
@@ -735,16 +971,30 @@ static char **
 getnextmode(name)
 char *name;
 {
 getnextmode(name)
 char *name;
 {
-    Command *c = (Command *) name;
-
-    return (char **) (c+1);
+    return (char **) (((struct modelist *)name)+1);
 }
 
 }
 
-static Command *
+static struct modelist *
 getmodecmd(name)
 char *name;
 {
 getmodecmd(name)
 char *name;
 {
-    return (Command *) genget(name, (char **) Mode_commands, getnextmode);
+    return (struct modelist *) genget(name, (char **) ModeList, getnextmode);
+}
+
+modehelp()
+{
+    struct modelist *mt;
+
+    printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
+    for (mt = ModeList; mt->name; mt++) {
+       if (mt->help) {
+           if (*mt->help)
+               printf("%-15s %s\n", mt->name, mt->help);
+           else
+               printf("\n");
+       }
+    }
+    return 0;
 }
 
 static
 }
 
 static
@@ -752,26 +1002,22 @@ modecmd(argc, argv)
 int    argc;
 char   *argv[];
 {
 int    argc;
 char   *argv[];
 {
-    Command *mt;
+    struct modelist *mt;
 
 
-    if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
-       printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
-       for (mt = Mode_commands; mt->name; mt++) {
-           printf("%s\t%s\n", mt->name, mt->help);
-       }
-       return 0;
-    }
-    mt = getmodecmd(argv[1]);
-    if (mt == 0) {
+    if (argc != 2) {
+       printf("'mode' command requires an argument\n");
+       printf("'mode ?' for help.\n");
+    } else if ((mt = getmodecmd(argv[1])) == 0) {
        fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
        fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
-       return 0;
     } else if (Ambiguous(mt)) {
        fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
     } else if (Ambiguous(mt)) {
        fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
-       return 0;
-    } else {
-       (*mt->handler)();
+    } else if (mt->needconnect && !connected) {
+       printf("?Need to be connected first.\n");
+       printf("'mode ?' for help.\n");
+    } else if (mt->handler) {
+       return (*mt->handler)(mt->arg1);
     }
     }
-    return 1;
+    return 0;
 }
 \f
 /*
 }
 \f
 /*
@@ -794,7 +1040,10 @@ char      *argv[];
                        }
 
 #define        doset(sl)   if (sl->name && *sl->name != ' ') { \
                        }
 
 #define        doset(sl)   if (sl->name && *sl->name != ' ') { \
-                       printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
+                       if (sl->handler == 0) \
+                           printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \
+                       else \
+                           printf("%-15s \"%s\"\n", sl->name, sl->charp); \
                    }
 
     struct togglelist *tl;
                    }
 
     struct togglelist *tl;
@@ -830,6 +1079,7 @@ char       *argv[];
            }
        }
     }
            }
        }
     }
+/*@*/optionstatus();
     return 1;
 #undef doset
 #undef dotog
     return 1;
 #undef doset
 #undef dotog
@@ -884,8 +1134,8 @@ togcrmod()
 /*VARARGS*/
 suspend()
 {
 /*VARARGS*/
 suspend()
 {
+#ifdef SIGTSTP
     setcommandmode();
     setcommandmode();
-#if    defined(unix)
     {
        long oldrows, oldcols, newrows, newcols;
 
     {
        long oldrows, oldcols, newrows, newcols;
 
@@ -898,13 +1148,59 @@ suspend()
            }
        }
     }
            }
        }
     }
-#endif /* defined(unix) */
     /* reget parameters in case they were changed */
     TerminalSaveState();
     /* reget parameters in case they were changed */
     TerminalSaveState();
-    setconnmode();
+    setconnmode(0);
+#else
+    printf("Suspend is not supported.  Try the '!' command instead\n");
+#endif
     return 1;
 }
 
     return 1;
 }
 
+#if    !defined(TN3270)
+#ifdef CRAY
+#define        vfork   fork
+#endif
+shell(argc, argv)
+int argc;
+char *argv[];
+{
+    extern char *rindex();
+    char cmdbuf[256];
+
+    setcommandmode();
+    switch(vfork()) {
+    case -1:
+       perror("Fork failed\n");
+       break;
+
+    case 0:
+       {
+           /*
+            * Fire up the shell in the child.
+            */
+           register char *shell, *shellname;
+
+           shell = getenv("SHELL");
+           if (shell == NULL)
+               shell = "/bin/sh";
+           if ((shellname = rindex(shell, '/')) == 0)
+               shellname = shell;
+           else
+               shellname++;
+           if (argc > 1)
+               execl(shell, shellname, "-c", &saveline[1], 0);
+           else
+               execl(shell, shellname, 0);
+           perror("Execl");
+           _exit(1);
+       }
+    default:
+           wait((int *)0);     /* Wait for the shell to complete */
+    }
+}
+#endif /* !defined(TN3270) */
+
 /*VARARGS*/
 static
 bye(argc, argv)
 /*VARARGS*/
 static
 bye(argc, argv)
@@ -936,6 +1232,89 @@ quit()
        Exit(0);
        return 1;                       /* just to keep lint happy */
 }
        Exit(0);
        return 1;                       /* just to keep lint happy */
 }
+\f
+/*
+ * The SLC command.
+ */
+
+struct slclist {
+       char    *name;
+       char    *help;
+       int     (*handler)();
+       int     arg;
+};
+
+extern int slc_help();
+extern int slc_mode_export(), slc_mode_import(), slcstate();
+
+struct slclist SlcList[] = {
+    { "export",        "Use local special character definitions",
+                                               slc_mode_export,        0 },
+    { "import",        "Use remote special character definitions",
+                                               slc_mode_import,        1 },
+    { "check", "Verify remote special character definitions",
+                                               slc_mode_import,        0 },
+    { "help",  0,                              slc_help,               0 },
+    { "?",     "Print help information",       slc_help,               0 },
+    { 0 },
+};
+
+static
+slc_help()
+{
+    struct slclist *c;
+
+    for (c = SlcList; c->name; c++) {
+       if (c->help) {
+           if (*c->help)
+               printf("%-15s %s\n", c->name, c->help);
+           else
+               printf("\n");
+       }
+    }
+}
+
+static char **
+getnextslc(name)
+char *name;
+{
+    return (char **)(((struct slclist *)name)+1);
+}
+
+static struct slclist *
+getslc(name)
+char *name;
+{
+    return (struct slclist *)genget(name, (char **) SlcList, getnextslc);
+}
+
+static
+slccmd(argc, argv)
+int    argc;
+char   *argv[];
+{
+    struct slclist *c;
+
+    if (argc != 2) {
+       fprintf(stderr,
+           "Need an argument to 'slc' command.  'slc ?' for help.\n");
+       return 0;
+    }
+    c = getslc(argv[1]);
+    if (c == 0) {
+        fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n",
+                               argv[1]);
+        return 0;
+    }
+    if (Ambiguous(c)) {
+        fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n",
+                               argv[1]);
+        return 0;
+    }
+    (*c->handler)(c->arg);
+    slcstate();
+    return 1;
+}
 
 #if    defined(unix)
 /*
 
 #if    defined(unix)
 /*
@@ -1004,7 +1383,8 @@ int fd;
 {
     int res;
 
 {
     int res;
 
-    setconnmode();
+#ifdef F_GETOWN
+    setconnmode(0);
     res = fcntl(fd, F_GETOWN, 0);
     setcommandmode();
 
     res = fcntl(fd, F_GETOWN, 0);
     setcommandmode();
 
@@ -1013,8 +1393,9 @@ int fd;
        return;
     }
     printf("\tOwner is %d.\n", res);
        return;
     }
     printf("\tOwner is %d.\n", res);
+#endif
 
 
-    setconnmode();
+    setconnmode(0);
     res = fcntl(fd, F_GETFL, 0);
     setcommandmode();
 
     res = fcntl(fd, F_GETFL, 0);
     setcommandmode();
 
@@ -1040,11 +1421,26 @@ char    *argv[];
     if (connected) {
        printf("Connected to %s.\n", hostname);
        if ((argc < 2) || strcmp(argv[1], "notmuch")) {
     if (connected) {
        printf("Connected to %s.\n", hostname);
        if ((argc < 2) || strcmp(argv[1], "notmuch")) {
-           printf("Operating in %s.\n",
-                               modelist[getconnmode()].modedescriptions);
-           if (localchars) {
-               printf("Catching signals locally.\n");
+           int mode = getconnmode();
+
+           if (my_want_state_is_will(TELOPT_LINEMODE)) {
+               printf("Operating with LINEMODE option\n");
+               printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No");
+               printf("%s catching of signals\n",
+                                       (mode&MODE_TRAPSIG) ? "Local" : "No");
+               slcstate();
+#ifdef KLUDGELINEMODE
+           } else if (kludgelinemode && my_want_state_is_wont(TELOPT_SGA)) {
+               printf("Operating in obsolete linemode\n");
+#endif
+           } else {
+               printf("Operating in single character mode\n");
+               if (localchars)
+                   printf("Catching signals locally\n");
            }
            }
+           printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote");
+           if (my_want_state_is_will(TELOPT_LFLOW))
+               printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No");
        }
     } else {
        printf("No connection.\n");
        }
     } else {
        printf("No connection.\n");
@@ -1094,8 +1490,12 @@ tn(argc, argv)
     struct sockaddr_in sin;
     struct servent *sp = 0;
     static char        hnamebuf[32];
     struct sockaddr_in sin;
     struct servent *sp = 0;
     static char        hnamebuf[32];
-    unsigned long inet_addr();
+    unsigned long temp, inet_addr();
     extern char *inet_ntoa();
     extern char *inet_ntoa();
+#if    defined(SRCRT) && defined(IPPROTO_IP)
+    char *srp = 0, *strrchr();
+    unsigned long sourceroute(), srlen;
+#endif
 
 
 #if defined(MSDOS)
 
 
 #if defined(MSDOS)
@@ -1125,28 +1525,58 @@ tn(argc, argv)
        }
     }
 #endif /* defined(MSDOS) */
        }
     }
 #endif /* defined(MSDOS) */
-    sin.sin_addr.s_addr = inet_addr(argv[1]);
-    if (sin.sin_addr.s_addr != (unsigned long) -1) {
-       sin.sin_family = AF_INET;
-       (void) strcpy(hnamebuf, argv[1]);
-       hostname = hnamebuf;
+#if    defined(SRCRT) && defined(IPPROTO_IP)
+    if (argv[1][0] == '@' || argv[1][0] == '!') {
+       if ((hostname = strrchr(argv[1], ':')) == NULL)
+           hostname = strrchr(argv[1], '@');
+       hostname++;
+       srp = 0;
+       temp = sourceroute(argv[1], &srp, &srlen);
+       if (temp == 0) {
+           herror(srp);
+           return 0;
+       } else if (temp == -1) {
+           printf("Bad source route option: %s\n", argv[1]);
+           return 0;
+       } else {
+           sin.sin_addr.s_addr = temp;
+           sin.sin_family = AF_INET;
+       }
     } else {
     } else {
-       host = gethostbyname(argv[1]);
-       if (host) {
-           sin.sin_family = host->h_addrtype;
+#endif
+       temp = inet_addr(argv[1]);
+       if (temp != (unsigned long) -1) {
+           sin.sin_addr.s_addr = temp;
+           sin.sin_family = AF_INET;
+           (void) strcpy(hnamebuf, argv[1]);
+           hostname = hnamebuf;
+       } else {
+           host = gethostbyname(argv[1]);
+           if (host) {
+               sin.sin_family = host->h_addrtype;
 #if    defined(h_addr)         /* In 4.3, this is a #define */
 #if    defined(h_addr)         /* In 4.3, this is a #define */
-           memcpy((caddr_t)&sin.sin_addr,
+               memcpy((caddr_t)&sin.sin_addr,
                                host->h_addr_list[0], host->h_length);
 #else  /* defined(h_addr) */
                                host->h_addr_list[0], host->h_length);
 #else  /* defined(h_addr) */
-           memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
+               memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
 #endif /* defined(h_addr) */
 #endif /* defined(h_addr) */
-           hostname = host->h_name;
-       } else {
-           herror(argv[1]);
-           return 0;
+               hostname = host->h_name;
+           } else {
+               herror(argv[1]);
+               return 0;
+           }
        }
        }
+#if    defined(SRCRT) && defined(IPPROTO_IP)
     }
     }
+#endif
     if (argc == 3) {
     if (argc == 3) {
+       int tmp;
+
+       if (*argv[2] == '-') {
+           argv[2]++;
+           telnetport = 1;
+       } else
+           telnetport = 0;
        sin.sin_port = atoi(argv[2]);
        if (sin.sin_port == 0) {
            sp = getservbyname(argv[2], "tcp");
        sin.sin_port = atoi(argv[2]);
        if (sin.sin_port == 0) {
            sp = getservbyname(argv[2], "tcp");
@@ -1162,7 +1592,6 @@ tn(argc, argv)
 #endif /* !defined(htons) */
            sin.sin_port = htons(sin.sin_port);
        }
 #endif /* !defined(htons) */
            sin.sin_port = htons(sin.sin_port);
        }
-       telnetport = 0;
     } else {
        if (sp == 0) {
            sp = getservbyname("telnet", "tcp");
     } else {
        if (sp == 0) {
            sp = getservbyname("telnet", "tcp");
@@ -1181,6 +1610,10 @@ tn(argc, argv)
            perror("telnet: socket");
            return 0;
        }
            perror("telnet: socket");
            return 0;
        }
+#if    defined(SRCRT) && defined(IPPROTO_IP)
+       if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0)
+               perror("setsockopt (IP_OPTIONS)");
+#endif
        if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
                perror("setsockopt (SO_DEBUG)");
        }
        if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
                perror("setsockopt (SO_DEBUG)");
        }
@@ -1206,6 +1639,7 @@ tn(argc, argv)
        }
        connected++;
     } while (connected == 0);
        }
        connected++;
     } while (connected == 0);
+    cmdrc(argv[1], hostname);
     (void) call(status, "status", "notmuch", 0);
     if (setjmp(peerdied) == 0)
        telnet();
     (void) call(status, "status", "notmuch", 0);
     if (setjmp(peerdied) == 0)
        telnet();
@@ -1225,7 +1659,9 @@ static char
        helphelp[] =    "print help information",
        sendhelp[] =    "transmit special characters ('send ?' for more)",
        sethelp[] =     "set operating parameters ('set ?' for more)",
        helphelp[] =    "print help information",
        sendhelp[] =    "transmit special characters ('send ?' for more)",
        sethelp[] =     "set operating parameters ('set ?' for more)",
+       unsethelp[] =   "unset operating parameters ('unset ?' for more)",
        togglestring[] ="toggle operating parameters ('toggle ?' for more)",
        togglestring[] ="toggle operating parameters ('toggle ?' for more)",
+       slchelp[] =     "change state of special charaters ('slc ?' for more)",
        displayhelp[] = "display operating parameters",
 #if    defined(TN3270) && defined(unix)
        transcomhelp[] = "specify Unix command for transparent mode pipe",
        displayhelp[] = "display operating parameters",
 #if    defined(TN3270) && defined(unix)
        transcomhelp[] = "specify Unix command for transparent mode pipe",
@@ -1233,33 +1669,35 @@ static char
 #if    defined(unix)
        zhelp[] =       "suspend telnet",
 #endif /* defined(unix */
 #if    defined(unix)
        zhelp[] =       "suspend telnet",
 #endif /* defined(unix */
-#if    defined(TN3270)
        shellhelp[] =   "invoke a subshell",
        shellhelp[] =   "invoke a subshell",
-#endif /* defined(TN3270) */
-       modehelp[] = "try to enter line-by-line or character-at-a-time mode";
+       modestring[] = "try to enter line-by-line or character-at-a-time mode";
 
 extern int     help(), shell();
 
 static Command cmdtab[] = {
 
 extern int     help(), shell();
 
 static Command cmdtab[] = {
-       { "close",      closehelp,      bye,            1, 1 },
-       { "display",    displayhelp,    display,        1, 0 },
-       { "mode",       modehelp,       modecmd,        1, 1 },
-       { "open",       openhelp,       tn,             1, 0 },
-       { "quit",       quithelp,       quit,           1, 0 },
-       { "send",       sendhelp,       sendcmd,        1, 1 },
-       { "set",        sethelp,        setcmd,         1, 0 },
-       { "status",     statushelp,     status,         1, 0 },
-       { "toggle",     togglestring,   toggle,         1, 0 },
+       { "close",      closehelp,      bye,            1 },
+       { "display",    displayhelp,    display,        0 },
+       { "mode",       modestring,     modecmd,        0 },
+       { "open",       openhelp,       tn,             0 },
+       { "quit",       quithelp,       quit,           0 },
+       { "send",       sendhelp,       sendcmd,        0 },
+       { "set",        sethelp,        setcmd,         0 },
+       { "unset",      unsethelp,      unsetcmd,       0 },
+       { "status",     statushelp,     status,         0 },
+       { "toggle",     togglestring,   toggle,         0 },
+       { "slc",        slchelp,        slccmd,         0 },
 #if    defined(TN3270) && defined(unix)
 #if    defined(TN3270) && defined(unix)
-       { "transcom",   transcomhelp,   settranscom,    1, 0 },
+       { "transcom",   transcomhelp,   settranscom,    0 },
 #endif /* defined(TN3270) && defined(unix) */
 #if    defined(unix)
 #endif /* defined(TN3270) && defined(unix) */
 #if    defined(unix)
-       { "z",          zhelp,          suspend,        1, 0 },
+       { "z",          zhelp,          suspend,        0 },
 #endif /* defined(unix) */
 #if    defined(TN3270)
 #endif /* defined(unix) */
 #if    defined(TN3270)
-       { "!",          shellhelp,      shell,          1, 1 },
-#endif /* defined(TN3270) */
-       { "?",          helphelp,       help,           1, 0 },
+       { "!",          shellhelp,      shell,          1 },
+#else
+       { "!",          shellhelp,      shell,          0 },
+#endif
+       { "?",          helphelp,       help,           0 },
        0
 };
 
        0
 };
 
@@ -1267,9 +1705,9 @@ static char       crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
 static char    escapehelp[] =  "deprecated command -- use 'set escape' instead";
 
 static Command cmdtab2[] = {
 static char    escapehelp[] =  "deprecated command -- use 'set escape' instead";
 
 static Command cmdtab2[] = {
-       { "help",       helphelp,       help,           0, 0 },
-       { "escape",     escapehelp,     setescape,      1, 0 },
-       { "crmod",      crmodhelp,      togcrmod,       1, 0 },
+       { "help",       0,              help,           0 },
+       { "escape",     escapehelp,     setescape,      0 },
+       { "crmod",      crmodhelp,      togcrmod,       0 },
        0
 };
 
        0
 };
 
@@ -1322,8 +1760,10 @@ char *name;
 }
 
 void
 }
 
 void
-command(top)
+command(top, tbuf, cnt)
        int top;
        int top;
+       char *tbuf;
+       int cnt;
 {
     register Command *c;
 
 {
     register Command *c;
 
@@ -1338,10 +1778,23 @@ command(top)
     }
     for (;;) {
        printf("%s> ", prompt);
     }
     for (;;) {
        printf("%s> ", prompt);
-       if (gets(line) == NULL) {
-           if (feof(stdin) || ferror(stdin))
-               quit();
-           break;
+       if (tbuf) {
+           register char *cp;
+           cp = line;
+           while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
+               cnt--;
+           tbuf = 0;
+           if (cp == line || *--cp != '\n' || cp == line)
+               goto getline;
+           *cp = '\0';
+           printf("%s\n", line);
+       } else {
+       getline:
+           if (gets(line) == NULL) {
+               if (feof(stdin) || ferror(stdin))
+                   quit();
+               break;
+           }
        }
        if (line[0] == 0)
            break;
        }
        if (line[0] == 0)
            break;
@@ -1373,10 +1826,10 @@ command(top)
        }
 #if    defined(TN3270)
        if (shell_active == 0) {
        }
 #if    defined(TN3270)
        if (shell_active == 0) {
-           setconnmode();
+           setconnmode(0);
        }
 #else  /* defined(TN3270) */
        }
 #else  /* defined(TN3270) */
-       setconnmode();
+       setconnmode(0);
 #endif /* defined(TN3270) */
     }
 }
 #endif /* defined(TN3270) */
     }
 }
@@ -1394,7 +1847,7 @@ help(argc, argv)
        if (argc == 1) {
                printf("Commands may be abbreviated.  Commands are:\n\n");
                for (c = cmdtab; c->name; c++)
        if (argc == 1) {
                printf("Commands may be abbreviated.  Commands are:\n\n");
                for (c = cmdtab; c->name; c++)
-                       if (c->dohelp) {
+                       if (c->help) {
                                printf("%-*s\t%s\n", HELPINDENT, c->name,
                                                                    c->help);
                        }
                                printf("%-*s\t%s\n", HELPINDENT, c->name,
                                                                    c->help);
                        }
@@ -1413,3 +1866,248 @@ help(argc, argv)
        }
        return 0;
 }
        }
        return 0;
 }
+
+static char *rcname = 0;
+static char rcbuf[128];
+
+cmdrc(m1, m2)
+       char *m1, *m2;
+{
+    register Command *c;
+    FILE *rcfile;
+    int gotmachine = 0;
+    int l1 = strlen(m1);
+    int l2 = strlen(m2);
+    char m1save[64];
+
+    strcpy(m1save, m1);
+    m1 = m1save;
+
+    if (rcname == 0) {
+       rcname = getenv("HOME");
+       if (rcname)
+           strcpy(rcbuf, rcname);
+       else
+           rcbuf[0] = '\0';
+       strcat(rcbuf, "/.telnetrc");
+       rcname = rcbuf;
+    }
+
+    if ((rcfile = fopen(rcname, "r")) == 0) {
+       return;
+    }
+
+    for (;;) {
+       if (fgets(line, sizeof(line), rcfile) == NULL)
+           break;
+       if (line[0] == 0)
+           break;
+       if (line[0] == '#')
+           continue;
+       if (gotmachine == 0) {
+           if (isspace(line[0]))
+               continue;
+           if (strncasecmp(line, m1, l1) == 0)
+               strncpy(line, &line[l1], sizeof(line) - l1);
+           else if (strncasecmp(line, m2, l2) == 0)
+               strncpy(line, &line[l2], sizeof(line) - l2);
+           else
+               continue;
+           gotmachine = 1;
+       } else {
+           if (!isspace(line[0])) {
+               gotmachine = 0;
+               continue;
+           }
+       }
+       makeargv();
+       if (margv[0] == 0)
+           continue;
+       c = getcmd(margv[0]);
+       if (Ambiguous(c)) {
+           printf("?Ambiguous command: %s\n", margv[0]);
+           continue;
+       }
+       if (c == 0) {
+           printf("?Invalid command: %s\n", margv[0]);
+           continue;
+       }
+       /*
+        * This should never happen...
+        */
+       if (c->needconnect && !connected) {
+           printf("?Need to be connected first for %s.\n", margv[0]);
+           continue;
+       }
+       (*c->handler)(margc, margv);
+    }
+    fclose(rcfile);
+}
+
+#if    defined(SRCRT) && defined(IPPROTO_IP)
+
+/*
+ * Source route is handed in as
+ *     [!]@hop1@hop2...[@|:]dst
+ * If the leading ! is present, it is a
+ * strict source route, otherwise it is
+ * assmed to be a loose source route.
+ *
+ * We fill in the source route option as
+ *     hop1,hop2,hop3...dest
+ * and return a pointer to hop1, which will
+ * be the address to connect() to.
+ *
+ * Arguments:
+ *     arg:    pointer to route list to decipher
+ *
+ *     cpp:    If *cpp is not equal to NULL, this is a
+ *             pointer to a pointer to a character array
+ *             that should be filled in with the option.
+ *
+ *     lenp:   pointer to an integer that contains the
+ *             length of *cpp if *cpp != NULL.
+ *
+ * Return values:
+ *
+ *     Returns the address of the host to connect to.  If the
+ *     return value is -1, there was a syntax error in the
+ *     option, either unknown characters, or too many hosts.
+ *     If the return value is 0, one of the hostnames in the
+ *     path is unknown, and *cpp is set to point to the bad
+ *     hostname.
+ *
+ *     *cpp:   If *cpp was equal to NULL, it will be filled
+ *             in with a pointer to our static area that has
+ *             the option filled in.  This will be 32bit aligned.
+ * 
+ *     *lenp:  This will be filled in with how long the option
+ *             pointed to by *cpp is.
+ *     
+ */
+unsigned long
+sourceroute(arg, cpp, lenp)
+char   *arg;
+char   **cpp;
+int    *lenp;
+{
+       static char lsr[44];
+       char *cp, *cp2, *lsrp, *lsrep, *index();
+       register int tmp;
+       struct in_addr sin_addr;
+       register struct hostent *host = 0;
+       register char c;
+
+       /*
+        * Verify the arguments, and make sure we have
+        * at least 7 bytes for the option.
+        */
+       if (cpp == NULL || lenp == NULL)
+               return((unsigned long)-1);
+       if (*cpp != NULL && *lenp < 7)
+               return((unsigned long)-1);
+       /*
+        * Decide whether we have a buffer passed to us,
+        * or if we need to use our own static buffer.
+        */
+       if (*cpp) {
+               lsrp = *cpp;
+               lsrep = lsrp + *lenp;
+       } else {
+               *cpp = lsrp = lsr;
+               lsrep = lsrp + 44;
+       }
+
+       cp = arg;
+
+       /*
+        * Next, decide whether we have a loose source
+        * route or a strict source route, and fill in
+        * the begining of the option.
+        */
+       if (*cp == '!') {
+               cp++;
+               *lsrp++ = IPOPT_SSRR;
+       } else
+               *lsrp++ = IPOPT_LSRR;
+
+       if (*cp != '@')
+               return((unsigned long)-1);
+
+       lsrp++;         /* skip over length, we'll fill it in later */
+       *lsrp++ = 4;
+
+       cp++;
+
+       sin_addr.s_addr = 0;
+
+       for (c = 0;;) {
+               if (c == ':')
+                       cp2 = 0;
+               else for (cp2 = cp; c = *cp2; cp2++) {
+                       if (c == ',') {
+                               *cp2++ = '\0';
+                               if (*cp2 == '@')
+                                       cp2++;
+                       } else if (c == '@') {
+                               *cp2++ = '\0';
+                       } else if (c == ':') {
+                               *cp2++ = '\0';
+                       } else
+                               continue;
+                       break;
+               }
+               if (!c)
+                       cp2 = 0;
+
+               if ((tmp = inet_addr(cp)) != -1) {
+                       sin_addr.s_addr = tmp;
+               } else if (host = gethostbyname(cp)) {
+#if    defined(h_addr)
+                       memcpy((caddr_t)&sin_addr,
+                               host->h_addr_list[0], host->h_length);
+#else
+                       memcpy((caddr_t)&sin_addr, host->h_addr, host->h_length);
+#endif
+               } else {
+                       *cpp = cp;
+                       return(0);
+               }
+               memcpy(lsrp, (char *)&sin_addr, 4);
+               lsrp += 4;
+               if (cp2)
+                       cp = cp2;
+               else
+                       break;
+               /*
+                * Check to make sure there is space for next address
+                */
+               if (lsrp + 4 > lsrep)
+                       return((unsigned long)-1);
+       }
+       if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) {
+               *cpp = 0;
+               *lenp = 0;
+               return((unsigned long)-1);
+       }
+       *lsrp++ = IPOPT_NOP; /* 32 bit word align it */
+       *lenp = lsrp - *cpp;
+       return(sin_addr.s_addr);
+}
+#endif
+
+#if    defined(sun)
+strncasecmp(p1, p2, len)
+register char *p1, *p2;
+int len;
+{
+    while (len--) {
+       if (tolower(*p1) != tolower(*p2))
+          return(tolower(*p1) - tolower(*p2));
+       if (*p1 == '\0')
+           return(0);
+       p1++, p2++;
+    }
+    return(0);
+}
+#endif
index 4957c73..a95a67c 100644 (file)
@@ -14,7 +14,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- *     @(#)defines.h   1.7 (Berkeley) %G%
+ *     @(#)defines.h   1.8 (Berkeley) %G%
  */
 
 #define        settimer(x)     clocks.x = clocks.system++
  */
 
 #define        settimer(x)     clocks.x = clocks.system++
 #define        TTYROOM()       (ring_empty_count(&ttyoring))
 
 /*     Various modes */
 #define        TTYROOM()       (ring_empty_count(&ttyoring))
 
 /*     Various modes */
-#define        MODE_LINE(m)    (modelist[m].modetype & LINE)
-#define        MODE_LOCAL_CHARS(m)     (modelist[m].modetype &  LOCAL_CHARS)
-#define        MODE_LOCAL_ECHO(m)      (modelist[m].modetype & LOCAL_ECHO)
-#define        MODE_COMMAND_LINE(m)    (modelist[m].modetype & COMMAND_LINE)
-
-#define        LOCAL_CHARS     0x01            /* Characters processed locally */
-#define        LINE            0x02            /* Line-by-line mode of operation */
-#define        LOCAL_ECHO      0x04            /* Echoing locally */
-#define        COMMAND_LINE    0x08            /* Command line mode */
+#define        MODE_LOCAL_CHARS(m)     ((m)&(MODE_EDIT|MODE_TRAPSIG))
+#define        MODE_LOCAL_ECHO(m)      ((m)&MODE_ECHO)
+#define        MODE_COMMAND_LINE(m)    ((m)==-1)
index cc3a5b5..ab1d842 100644 (file)
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- *     @(#)externs.h   1.16 (Berkeley) %G%
+ *     @(#)externs.h   1.17 (Berkeley) %G%
  */
 
  */
 
+#ifdef CRAY
+#define        USE_TERMIO
+#endif
+
 #include <stdio.h>
 #include <setjmp.h>
 #include <stdio.h>
 #include <setjmp.h>
+#include <sys/ioctl.h>
+#ifdef USE_TERMIO
+#ifndef        VINTR
+#include <sys/termio.h>
+#endif
+#endif
 
 
-#define        SUBBUFSIZE      100
+#define        SUBBUFSIZE      256
 
 extern int errno;              /* outside this world */
 
 
 extern int errno;              /* outside this world */
 
@@ -49,6 +59,7 @@ extern int
     dontlecho,         /* do we suppress local echoing right now? */
     crmod,
     netdata,           /* Print out network data flow */
     dontlecho,         /* do we suppress local echoing right now? */
     crmod,
     netdata,           /* Print out network data flow */
+    prettydump,                /* Print "netdata" output in user readable format */
 #if    defined(unix)
 #if    defined(TN3270)
     cursesdata,                /* Print out curses data flow */
 #if    defined(unix)
 #if    defined(TN3270)
     cursesdata,                /* Print out curses data flow */
@@ -72,27 +83,71 @@ extern char
  * We keep track of each side of the option negotiation.
  */
 
  * We keep track of each side of the option negotiation.
  */
 
-#define        OPT_HE_SAID_DO          0x01
-#define        OPT_HE_SAID_WILL        0x02
-#define        OPT_I_SAID_DO           0x04
-#define        OPT_I_SAID_WILL         0x08
+#define        MY_STATE_WILL           0x01
+#define        MY_WANT_STATE_WILL      0x02
+#define        MY_STATE_DO             0x04
+#define        MY_WANT_STATE_DO        0x08
+
+/*
+ * Macros to check the current state of things
+ */
+
+#define        my_state_is_do(opt)             (options[opt]&MY_STATE_DO)
+#define        my_state_is_will(opt)           (options[opt]&MY_STATE_WILL)
+#define my_want_state_is_do(opt)       (options[opt]&MY_WANT_STATE_DO)
+#define my_want_state_is_will(opt)     (options[opt]&MY_WANT_STATE_WILL)
+
+#define        my_state_is_dont(opt)           (!my_state_is_do(opt))
+#define        my_state_is_wont(opt)           (!my_state_is_will(opt))
+#define my_want_state_is_dont(opt)     (!my_want_state_is_do(opt))
+#define my_want_state_is_wont(opt)     (!my_want_state_is_will(opt))
+
+#define        set_my_state_do(opt)            {options[opt] |= MY_STATE_DO;}
+#define        set_my_state_will(opt)          {options[opt] |= MY_STATE_WILL;}
+#define        set_my_want_state_do(opt)       {options[opt] |= MY_WANT_STATE_DO;}
+#define        set_my_want_state_will(opt)     {options[opt] |= MY_WANT_STATE_WILL;}
+
+#define        set_my_state_dont(opt)          {options[opt] &= ~MY_STATE_DO;}
+#define        set_my_state_wont(opt)          {options[opt] &= ~MY_STATE_WILL;}
+#define        set_my_want_state_dont(opt)     {options[opt] &= ~MY_WANT_STATE_DO;}
+#define        set_my_want_state_wont(opt)     {options[opt] &= ~MY_WANT_STATE_WILL;}
 
 /*
 
 /*
- * Macros to check out what has been said.
+ * Make everything symetrical
  */
 
  */
 
-#define        did_he_say_do(opt)      (options[opt]&OPT_HE_SAID_DO)
-#define        did_he_say_will(opt)    (options[opt]&OPT_HE_SAID_WILL)
-#define        did_I_say_do(opt)       (options[opt]&OPT_I_SAID_DO)
-#define        did_I_say_will(opt)     (options[opt]&OPT_I_SAID_WILL)
+#define        HIS_STATE_WILL                  MY_STATE_DO
+#define        HIS_WANT_STATE_WILL             MY_WANT_STATE_DO
+#define HIS_STATE_DO                   MY_STATE_WILL
+#define HIS_WANT_STATE_DO              MY_WANT_STATE_WILL
+
+#define        his_state_is_do                 my_state_is_will
+#define        his_state_is_will               my_state_is_do
+#define his_want_state_is_do           my_want_state_is_will
+#define his_want_state_is_will         my_want_state_is_do
+
+#define        his_state_is_dont               my_state_is_wont
+#define        his_state_is_wont               my_state_is_dont
+#define his_want_state_is_dont         my_want_state_is_wont
+#define his_want_state_is_wont         my_want_state_is_dont
+
+#define        set_his_state_do                set_my_state_will
+#define        set_his_state_will              set_my_state_do
+#define        set_his_want_state_do           set_my_want_state_will
+#define        set_his_want_state_will         set_my_want_state_do
+
+#define        set_his_state_dont              set_my_state_wont
+#define        set_his_state_wont              set_my_state_dont
+#define        set_his_want_state_dont         set_my_want_state_wont
+#define        set_his_want_state_wont         set_my_want_state_dont
 
 
-#define        should_he(opt) \
-           (did_he_say_will(opt) && did_I_say_do(opt))
-#define        should_I(opt) \
-           (did_I_say_will(opt) && did_he_say_do(opt))
 
 extern FILE
     *NetTrace;         /* Where debugging output goes */
 
 extern FILE
     *NetTrace;         /* Where debugging output goes */
+extern char
+    NetTraceFile[];    /* Name of file where debugging output goes */
+extern void
+    SetNetTrace();     /* Function to change where debugging goes */
 
 extern jmp_buf
     peerdied,
 
 extern jmp_buf
     peerdied,
@@ -127,14 +182,99 @@ extern int
     dosynch();
 #endif /* defined(NOT43) */
 
     dosynch();
 #endif /* defined(NOT43) */
 
+#if    !defined(MSDOS)
+# ifndef       USE_TERMIO
+
+extern struct  tchars ntc;
+extern struct  ltchars nltc;
+extern struct  sgttyb nttyb;
+
+#  define termEofChar          ntc.t_eofc
+#  define termEraseChar                nttyb.sg_erase
+#  define termFlushChar                nltc.t_flushc
+#  define termIntChar          ntc.t_intrc
+#  define termKillChar         nttyb.sg_kill
+#  define termLiteralNextChar  nltc.t_lnextc
+#  define termQuitChar         ntc.t_quitc
+#  define termSuspChar         nltc.t_suspc
+#  define termRprntChar                nltc.t_rprntc
+#  define termWerasChar                nltc.t_werasc
+#  define termStartChar                ntc.t_startc
+#  define termStopChar         ntc.t_stopc
+
+#  define termEofCharp         &ntc.t_eofc
+#  define termEraseCharp       &nttyb.sg_erase
+#  define termFlushCharp       &nltc.t_flushc
+#  define termIntCharp         &ntc.t_intrc
+#  define termKillCharp                &nttyb.sg_kill
+#  define termLiteralNextCharp &nltc.t_lnextc
+#  define termQuitCharp                &ntc.t_quitc
+#  define termSuspCharp                &nltc.t_suspc
+#  define termRprntCharp       &nltc.t_rprntc
+#  define termWerasCharp       &nltc.t_werasc
+#  define termStartCharp       &ntc.t_startc
+#  define termStopCharp                &ntc.t_stopc
+
+# else
+
+extern struct  termio new_tc;
+
+#  define termEofChar          new_tc.c_cc[VEOF]
+#  define termEraseChar                new_tc.c_cc[VERASE]
+#  define termIntChar          new_tc.c_cc[VINTR]
+#  define termKillChar         new_tc.c_cc[VKILL]
+#  define termQuitChar         new_tc.c_cc[VQUIT]
+
+extern char
+    termSuspChar,
+    termFlushChar,
+    termWerasChar,
+    termRprntChar,
+    termLiteralNextChar,
+    termStartChar,
+    termStopChar;
+
+# ifndef CRAY
+#  define termEofCharp         &new_tc.c_cc[VEOF]
+#  define termEraseCharp       &new_tc.c_cc[VERASE]
+#  define termIntCharp         &new_tc.c_cc[VINTR]
+#  define termKillCharp                &new_tc.c_cc[VKILL]
+#  define termQuitCharp                &new_tc.c_cc[VQUIT]
+# else
+       /* Work around a compiler bug */
+#  define termEofCharp         0
+#  define termEraseCharp       0
+#  define termIntCharp         0
+#  define termKillCharp                0
+#  define termQuitCharp                0
+# endif
+#  define termSuspCharp                &termSuspChar
+#  define termFlushCharp       &termFlushChar
+#  define termWerasCharp       &termWerasChar
+#  define termRprntCharp       &termRprntChar
+#  define termLiteralNextCharp &termLiteralNextChar
+#  define termStartCharp       &termStartChar
+#  define termStopCharp                &termStopChar
+# endif
+
+#else  /* MSDOS */
+
 extern char
     termEofChar,
     termEraseChar,
 extern char
     termEofChar,
     termEraseChar,
-    termFlushChar,
     termIntChar,
     termKillChar,
     termIntChar,
     termKillChar,
+    termQuitChar,
+    termSuspChar,
+    termFlushChar,
+    termWerasChar,
+    termRprntChar,
     termLiteralNextChar,
     termLiteralNextChar,
-    termQuitChar;
+    termStartChar,
+    termStopChar;
+
+#endif
+
 
 /* Ring buffer structures which are shared */
 
 
 /* Ring buffer structures which are shared */
 
index 0bee9d6..f2464fe 100644 (file)
@@ -22,7 +22,7 @@ char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)main.c     1.10 (Berkeley) %G%";
+static char sccsid[] = "@(#)main.c     1.11 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/types.h>
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -62,6 +62,9 @@ main(argc, argv)
        char *argv[];
 {
     tninit();          /* Clear out things */
        char *argv[];
 {
     tninit();          /* Clear out things */
+#ifdef CRAY
+    _setlist_init();   /* Work around compiler bug */
+#endif
 
     TerminalSaveState();
 
 
     TerminalSaveState();
 
@@ -71,12 +74,9 @@ main(argc, argv)
            debug = 1;
        } else if (!strcmp(argv[1], "-n")) {
            if ((argc > 1) && (argv[2][0] != '-')) {    /* get file name */
            debug = 1;
        } else if (!strcmp(argv[1], "-n")) {
            if ((argc > 1) && (argv[2][0] != '-')) {    /* get file name */
-               NetTrace = fopen(argv[2], "w");
+               SetNetTrace(argv[2]);
                argv++;
                argc--;
                argv++;
                argc--;
-               if (NetTrace == NULL) {
-                   NetTrace = stdout;
-               }
            }
        } else {
 #if    defined(TN3270) && defined(unix)
            }
        } else {
 #if    defined(TN3270) && defined(unix)
@@ -115,10 +115,10 @@ main(argc, argv)
     (void) setjmp(toplevel);
     for (;;) {
 #if    !defined(TN3270)
     (void) setjmp(toplevel);
     for (;;) {
 #if    !defined(TN3270)
-       command(1);
+       command(1, 0, 0);
 #else  /* !defined(TN3270) */
        if (!shell_active) {
 #else  /* !defined(TN3270) */
        if (!shell_active) {
-           command(1);
+           command(1, 0, 0);
        } else {
 #if    defined(TN3270)
            shell_continue();
        } else {
 #if    defined(TN3270)
            shell_continue();
index c3826a9..15b9397 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)ring.c     1.10 (Berkeley) %G%";
+static char sccsid[] = "@(#)ring.c     1.11 (Berkeley) %G%";
 #endif /* not lint */
 
 /*
 #endif /* not lint */
 
 /*
@@ -50,7 +50,7 @@ static char sccsid[] = "@(#)ring.c    1.10 (Berkeley) %G%";
 #define        MIN(a,b)        (((a)<(b))? (a):(b))
 #endif /* !defined(MIN) */
 
 #define        MIN(a,b)        (((a)<(b))? (a):(b))
 #endif /* !defined(MIN) */
 
-#define        ring_subtract(d,a,b)    ((((int)(a))-((int)(b)) >= 0)? \
+#define        ring_subtract(d,a,b)    (((a)-(b) >= 0)? \
                                        (a)-(b): (((a)-(b))+(d)->size))
 
 #define        ring_increment(d,a,c)   (((a)+(c) < (d)->top)? \
                                        (a)-(b): (((a)-(b))+(d)->size))
 
 #define        ring_increment(d,a,c)   (((a)+(c) < (d)->top)? \
index 86a9bc2..df33e64 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)sys_bsd.c  1.18 (Berkeley) %G%";
+static char sccsid[] = "@(#)sys_bsd.c  1.19 (Berkeley) %G%";
 #endif /* not lint */
 
 /*
 #endif /* not lint */
 
 /*
@@ -27,12 +27,12 @@ static char sccsid[] = "@(#)sys_bsd.c       1.18 (Berkeley) %G%";
 #if    defined(unix)
 
 #include <fcntl.h>
 #if    defined(unix)
 
 #include <fcntl.h>
-#include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <signal.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/socket.h>
 #include <signal.h>
 #include <errno.h>
+#include <arpa/telnet.h>
 
 #include "ring.h"
 
 
 #include "ring.h"
 
@@ -47,9 +47,20 @@ int
        tin,                    /* Input file descriptor */
        net;
 
        tin,                    /* Input file descriptor */
        net;
 
-static struct  tchars otc = { 0 }, ntc = { 0 };
-static struct  ltchars oltc = { 0 }, nltc = { 0 };
-static struct  sgttyb ottyb = { 0 }, nttyb = { 0 };
+#ifndef        USE_TERMIO
+struct tchars otc = { 0 }, ntc = { 0 };
+struct ltchars oltc = { 0 }, nltc = { 0 };
+struct sgttyb ottyb = { 0 }, nttyb = { 0 };
+
+#define        ISPEED  ottyb.sg_ispeed
+#define        OSPEED  ottyb.sg_ospeed
+#else  /* USE_TERMIO */
+struct termio old_tc = { 0 };
+extern struct termio new_tc;
+
+#define        ISPEED  (old_tc.c_cflag&CBAUD)
+#define        OSPEED  ISPEED
+#endif /* USE_TERMIO */
 
 static fd_set ibits, obits, xbits;
 
 
 static fd_set ibits, obits, xbits;
 
@@ -97,6 +108,9 @@ TerminalAutoFlush()
 #endif /* LNOFLSH */
 }
 
 #endif /* LNOFLSH */
 }
 
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+#endif
 /*
  * TerminalSpecialChars()
  *
 /*
  * TerminalSpecialChars()
  *
@@ -115,20 +129,34 @@ int       c;
 {
     void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
 
 {
     void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
 
-    if (c == ntc.t_intrc) {
+    if (c == termIntChar) {
        intp();
        return 0;
        intp();
        return 0;
-    } else if (c == ntc.t_quitc) {
-       sendbrk();
+    } else if (c == termQuitChar) {
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode)
+           sendbrk();
+       else
+#endif
+           sendabort();
        return 0;
        return 0;
-    } else if (c == nltc.t_flushc) {
+    } else if (c == termEofChar) {
+       if (my_want_state_is_will(TELOPT_LINEMODE)) {
+           sendeof();
+           return 0;
+       }
+       return 1;
+    } else if (c == termSuspChar) {
+       sendsusp();
+       return(0);
+    } else if (c == termFlushChar) {
        xmitAO();               /* Transmit Abort Output */
        return 0;
     } else if (!MODE_LOCAL_CHARS(globalmode)) {
        xmitAO();               /* Transmit Abort Output */
        return 0;
     } else if (!MODE_LOCAL_CHARS(globalmode)) {
-       if (c == nttyb.sg_kill) {
+       if (c == termKillChar) {
            xmitEL();
            return 0;
            xmitEL();
            return 0;
-       } else if (c == nttyb.sg_erase) {
+       } else if (c == termEraseChar) {
            xmitEC();           /* Transmit Erase Character */
            return 0;
        }
            xmitEC();           /* Transmit Erase Character */
            return 0;
        }
@@ -144,12 +172,17 @@ int       c;
 void
 TerminalFlushOutput()
 {
 void
 TerminalFlushOutput()
 {
+#ifndef        USE_TERMIO
     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
+#else
+    (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);
+#endif
 }
 
 void
 TerminalSaveState()
 {
 }
 
 void
 TerminalSaveState()
 {
+#ifndef        USE_TERMIO
     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);
@@ -158,12 +191,68 @@ TerminalSaveState()
     nltc = oltc;
     nttyb = ottyb;
 
     nltc = oltc;
     nttyb = ottyb;
 
-    termEofChar = ntc.t_eofc;
-    termEraseChar = nttyb.sg_erase;
-    termFlushChar = nltc.t_flushc;
-    termIntChar = ntc.t_intrc;
-    termKillChar = nttyb.sg_kill;
-    termQuitChar = ntc.t_quitc;
+#else  /* USE_TERMIO */
+    ioctl(0, TCGETA, &old_tc);
+
+    new_tc = old_tc;
+
+    termFlushChar = 'O'&0x37;
+    termWerasChar = 'W'&0x37;
+    termRprntChar = 'R'&0x37;
+    termLiteralNextChar = 'V'&0x37;
+    termStartChar = 'Q'&0x37;
+    termStopChar = 'S'&0x37;
+#endif /* USE_TERMIO */
+}
+
+char *
+tcval(func)
+register int func;
+{
+    switch(func) {
+    case SLC_IP:       return(&termIntChar);
+    case SLC_ABORT:    return(&termQuitChar);
+    case SLC_EOF:      return(&termEofChar);
+    case SLC_EC:       return(&termEraseChar);
+    case SLC_EL:       return(&termKillChar);
+    case SLC_XON:      return(&termStartChar);
+    case SLC_XOFF:     return(&termStopChar);
+#ifndef        CRAY
+    case SLC_AO:       return(&termFlushChar);
+    case SLC_SUSP:     return(&termSuspChar);
+    case SLC_EW:       return(&termWerasChar);
+    case SLC_RP:       return(&termRprntChar);
+    case SLC_LNEXT:    return(&termLiteralNextChar);
+#endif CRAY
+
+    case SLC_SYNCH:
+    case SLC_BRK:
+    case SLC_AYT:
+    case SLC_EOR:
+    case SLC_FORW1:
+    case SLC_FORW2:
+    default:
+       return((char *)0);
+    }
+}
+
+void
+TerminalDefaultChars()
+{
+#ifndef        USE_TERMIO
+    ntc = otc;
+    nltc = oltc;
+    nttyb.sg_kill = ottyb.sg_kill;
+    nttyb.sg_erase = ottyb.sg_erase;
+#else  /* USE_TERMIO */
+    memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
+    termFlushChar = 'O'&0x37;
+    termWerasChar = 'W'&0x37;
+    termRprntChar = 'R'&0x37;
+    termLiteralNextChar = 'V'&0x37;
+    termStartChar = 'Q'&0x37;
+    termStopChar = 'S'&0x37;
+#endif /* USE_TERMIO */
 }
 
 void
 }
 
 void
@@ -173,6 +262,24 @@ TerminalRestoreState()
 
 /*
  * TerminalNewMode - set up terminal to a specific mode.
 
 /*
  * TerminalNewMode - set up terminal to a specific mode.
+ *     MODE_ECHO: do local terminal echo
+ *     MODE_FLOW: do local flow control
+ *     MODE_TRAPSIG: do local mapping to TELNET IAC sequences
+ *     MODE_EDIT: do local line editing
+ *
+ *     Command mode:
+ *             MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
+ *             local echo
+ *             local editing
+ *             local xon/xoff
+ *             local signal mapping
+ *
+ *     Linemode:
+ *             local/no editing
+ *     Both Linemode and Single Character mode:
+ *             local/remote echo
+ *             local/no xon/xoff
+ *             local/no signal mapping
  */
 
 
  */
 
 
@@ -181,106 +288,153 @@ TerminalNewMode(f)
 register int f;
 {
     static int prevmode = 0;
 register int f;
 {
     static int prevmode = 0;
-    struct tchars *tc;
-    struct tchars tc3;
-    struct ltchars *ltc;
+#ifndef        USE_TERMIO
+    struct tchars tc;
+    struct ltchars ltc;
     struct sgttyb sb;
     struct sgttyb sb;
+#else  /* USE_TERMIO */
+    struct termio tmp_tc;
+#endif /* USE_TERMIO */
     int onoff;
     int old;
     int onoff;
     int 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;
+    globalmode = f&~MODE_FORCE;
     if (prevmode == f)
        return;
     if (prevmode == f)
        return;
+
+    /*
+     * Write any outstanding data before switching modes
+     * ttyflush() returns 0 only when there is no more data
+     * left to write out, it returns -1 if it couldn't do
+     * anything at all, otherwise it returns 1 + the number
+     * of characters left to write.
+     */
+    old = ttyflush(SYNCHing|flushout);
+    if (old < 0 || old > 1) {
+#ifndef        USE_TERMIO
+       ioctl(tin, TIOCGETP, (char *)&sb);
+#else  /* USE_TERMIO */
+       ioctl(tin, TCGETA, (char *)&tmp_tc);
+#endif /* USE_TERMIO */
+       do {
+           /*
+            * Wait for data to drain, then flush again.
+            */
+#ifndef        USE_TERMIO
+           ioctl(tin, TIOCSETP, (char *)&sb);
+#else  /* USE_TERMIO */
+           ioctl(tin, TCSETAW, (char *)&tmp_tc);
+#endif /* USE_TERMIO */
+           old = ttyflush(SYNCHing|flushout);
+       } while (old < 0 || old > 1);
+    }
+
     old = prevmode;
     old = prevmode;
-    prevmode = f;
+    prevmode = f&~MODE_FORCE;
+#ifndef        USE_TERMIO
     sb = nttyb;
     sb = nttyb;
+    tc = ntc;
+    ltc = nltc;
+#else
+    tmp_tc = new_tc;
+#endif
+
+    if (f&MODE_ECHO) {
+#ifndef        USE_TERMIO
+       sb.sg_flags |= ECHO;
+#else
+       tmp_tc.c_lflag |= ECHO;
+       tmp_tc.c_oflag |= ONLCR;
+       tmp_tc.c_iflag |= ICRNL;
+#endif
+    } else {
+#ifndef        USE_TERMIO
+       sb.sg_flags &= ~ECHO;
+#else
+       tmp_tc.c_lflag &= ~ECHO;
+       tmp_tc.c_oflag &= ~ONLCR;
+       tmp_tc.c_iflag &= ~ICRNL;
+#endif
+    }
 
 
-    switch (f) {
+    if ((f&MODE_FLOW) == 0) {
+#ifndef        USE_TERMIO
+       tc.t_startc = -1;
+       tc.t_stopc = -1;
+#else
+       tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON);
+    } else {
+       tmp_tc.c_iflag |= IXANY|IXOFF|IXON;
+#endif
+    }
 
 
-    case 0:
-       onoff = 0;
-       tc = &otc;
-       ltc = &oltc;
-       break;
-
-    case 1:            /* remote character processing, remote echo */
-    case 2:            /* remote character processing, local echo */
-    case 6:            /* 3270 mode - like 1, but with xon/xoff local */
-                   /* (might be nice to have "6" in telnet also...) */
-           sb.sg_flags |= CBREAK;
-           if ((f == 1) || (f == 6)) {
-               sb.sg_flags &= ~(ECHO|CRMOD);
-           } else {
-               sb.sg_flags |= ECHO|CRMOD;
-           }
-           sb.sg_erase = sb.sg_kill = -1;
-           if (localflow || (f == 6)) {
-               tc = &tc3;
-               tc3 = notc;
-                   /* get XON, XOFF characters */
-               tc3.t_startc = otc.t_startc;
-               tc3.t_stopc = otc.t_stopc;
-           } else {
-               /*
-                * If user hasn't specified one way or the other,
-                * then default to not trapping signals.
-                */
-               if (!donelclchars) {
-                   localchars = 0;
-               }
-               if (localchars) {
-                   notc2 = notc;
-                   notc2.t_intrc = ntc.t_intrc;
-                   notc2.t_quitc = ntc.t_quitc;
-                   tc = &notc2;
-               } else {
-                   tc = &notc;
-               }
-           }
-           ltc = &noltc;
-           onoff = 1;
-           break;
-    case 3:            /* local character processing, remote echo */
-    case 4:            /* local character processing, local echo */
-    case 5:            /* local character processing, no echo */
-           sb.sg_flags &= ~CBREAK;
+    if ((f&MODE_TRAPSIG) == 0) {
+#ifndef        USE_TERMIO
+       tc.t_intrc = -1;
+       tc.t_quitc = -1;
+       tc.t_eofc = -1;
+       ltc.t_suspc = -1;
+       ltc.t_dsuspc = -1;
+#else
+       tmp_tc.c_lflag &= ~ISIG;
+#endif
+       localchars = 0;
+    } else {
+#ifdef USE_TERMIO
+       tmp_tc.c_lflag |= ISIG;
+#endif
+       localchars = 1;
+    }
+
+    if (f&MODE_EDIT) {
+#ifndef        USE_TERMIO
+       sb.sg_flags &= ~CBREAK;
+       sb.sg_flags |= CRMOD;
+#else
+       tmp_tc.c_lflag |= ICANON;
+#endif
+    } else {
+#ifndef        USE_TERMIO
+       sb.sg_flags |= CBREAK;
+       if (f&MODE_ECHO)
            sb.sg_flags |= CRMOD;
            sb.sg_flags |= CRMOD;
-           if (f == 4)
-               sb.sg_flags |= ECHO;
-           else
-               sb.sg_flags &= ~ECHO;
-           notc2 = ntc;
-           tc = &notc2;
-           noltc2 = oltc;
-           ltc = &noltc2;
-           /*
-            * If user hasn't specified one way or the other,
-            * then default to trapping signals.
-            */
-           if (!donelclchars) {
-               localchars = 1;
-           }
-           if (localchars) {
-               notc2.t_brkc = nltc.t_flushc;
-               noltc2.t_flushc = -1;
-           } else {
-               notc2.t_intrc = notc2.t_quitc = -1;
-           }
-           noltc2.t_suspc = escape;
-           noltc2.t_dsuspc = -1;
-           onoff = 1;
-           break;
+       else
+           sb.sg_flags &= ~CRMOD;
+#else
+       tmp_tc.c_lflag &= ~ICANON;
+       tmp_tc.c_iflag &= ~ICRNL;
+       tmp_tc.c_cc[VMIN] = 1;
+       tmp_tc.c_cc[VTIME] = 0;
+#endif
+    }
 
 
-    default:
-           return;
+    if (f == -1) {
+       onoff = 0;
+    } else {
+       onoff = 1;
+    }
+
+#ifndef        USE_TERMIO
+    if (f != -1) {
+       if (f&MODE_EDIT) {
+           void doescape();
+
+           ltc.t_suspc = escape;
+           (void) signal(SIGTSTP, (int (*)())doescape);
+       } else if (old&MODE_EDIT) {
+           (void) signal(SIGTSTP, SIG_DFL);
+           sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
+       }
+       ioctl(tin, TIOCSLTC, (char *)&ltc);
+       ioctl(tin, TIOCSETC, (char *)&tc);
+       ioctl(tin, TIOCSETP, (char *)&sb);
+    } else {
+       (void) signal(SIGTSTP, SIG_DFL);
+       sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
+       ioctl(tin, TIOCSLTC, (char *)&oltc);
+       ioctl(tin, TIOCSETC, (char *)&otc);
+       ioctl(tin, TIOCSETP, (char *)&ottyb);
     }
     }
-    ioctl(tin, TIOCSLTC, (char *)ltc);
-    ioctl(tin, TIOCSETC, (char *)tc);
-    ioctl(tin, TIOCSETP, (char *)&sb);
 #if    (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
     ioctl(tin, FIONBIO, (char *)&onoff);
     ioctl(tout, FIONBIO, (char *)&onoff);
 #if    (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
     ioctl(tin, FIONBIO, (char *)&onoff);
     ioctl(tout, FIONBIO, (char *)&onoff);
@@ -290,15 +444,10 @@ register int f;
        ioctl(tin, FIOASYNC, (char *)&onoff);
     }
 #endif /* defined(TN3270) */
        ioctl(tin, FIOASYNC, (char *)&onoff);
     }
 #endif /* defined(TN3270) */
-
-    if (MODE_LINE(f)) {
-       void doescape();
-
-       (void) signal(SIGTSTP, (int (*)())doescape);
-    } else if (MODE_LINE(old)) {
-       (void) signal(SIGTSTP, SIG_DFL);
-       sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
-    }
+#else  /* USE_TERMIO */
+    if (ioctl(tin, TCSETAW, &tmp_tc) < 0)
+       ioctl(tin, TCSETA, &tmp_tc);
+#endif /* USE_TERMIO */
 }
 
 void
 }
 
 void
@@ -318,13 +467,13 @@ long *ospeed;
            600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 };
 #define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
 
            600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 };
 #define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
 
-    if ((ottyb.sg_ospeed < 0) || (ottyb.sg_ospeed > NUMSPEEDS) ||
-       (ottyb.sg_ispeed < 0) || (ottyb.sg_ispeed > NUMSPEEDS)) {
+    if ((OSPEED < 0) || (OSPEED > NUMSPEEDS) ||
+       (ISPEED < 0) || (ISPEED > NUMSPEEDS)) {
        ExitString("Invalid terminal speed.");
        /*NOTREACHED*/
     } else {
        ExitString("Invalid terminal speed.");
        /*NOTREACHED*/
     } else {
-       *ispeed = ttyspeeds[ottyb.sg_ispeed];
-       *ospeed = ttyspeeds[ottyb.sg_ospeed];
+       *ispeed = ttyspeeds[ISPEED];
+       *ospeed = ttyspeeds[OSPEED];
     }
 }
 
     }
 }
 
@@ -332,14 +481,16 @@ int
 TerminalWindowSize(rows, cols)
 long *rows, *cols;
 {
 TerminalWindowSize(rows, cols)
 long *rows, *cols;
 {
+#ifdef TIOCGWINSZ
     struct winsize ws;
 
     struct winsize ws;
 
-    if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) < 0) {
-       return 0;
+    if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
+       *rows = ws.ws_row;
+       *cols = ws.ws_col;
+       return 1;
     }
     }
-    *rows = ws.ws_row;
-    *cols = ws.ws_col;
-    return 1;
+#endif TIOCGWINSZ
+    return 0;
 }
 
 int
 }
 
 int
@@ -406,7 +557,12 @@ static void
 intr2()
 {
     if (localchars) {
 intr2()
 {
     if (localchars) {
-       sendbrk();
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode)
+           sendbrk();
+       else
+#endif
+           sendabort();
        return;
     }
 }
        return;
     }
 }
@@ -422,18 +578,26 @@ sendwin()
 static void
 doescape()
 {
 static void
 doescape()
 {
-    command(0);
+    command(0, 0, 0);
 }
 \f
 void
 sys_telnet_init()
 {
 }
 \f
 void
 sys_telnet_init()
 {
+#ifndef        CRAY
     (void) signal(SIGINT, (int (*)())intr);
     (void) signal(SIGQUIT, (int (*)())intr2);
     (void) signal(SIGPIPE, (int (*)())deadpeer);
     (void) signal(SIGINT, (int (*)())intr);
     (void) signal(SIGQUIT, (int (*)())intr2);
     (void) signal(SIGPIPE, (int (*)())deadpeer);
+#else
+    (void) signal(SIGINT, (void (*)())intr);
+    (void) signal(SIGQUIT, (void (*)())intr2);
+    (void) signal(SIGPIPE, (void (*)())deadpeer);
+#endif
+#ifdef SIGWINCH
     (void) signal(SIGWINCH, (int (*)())sendwin);
     (void) signal(SIGWINCH, (int (*)())sendwin);
+#endif
 
 
-    setconnmode();
+    setconnmode(0);
 
     NetNonblockingIO(net, 1);
 
 
     NetNonblockingIO(net, 1);
 
@@ -656,7 +820,7 @@ int poll;           /* If 0, then block until something to do */
     }
     if (FD_ISSET(tout, &obits)) {
        FD_CLR(tout, &obits);
     }
     if (FD_ISSET(tout, &obits)) {
        FD_CLR(tout, &obits);
-       returnValue |= ttyflush(SYNCHing|flushout);
+       returnValue |= (ttyflush(SYNCHing|flushout) > 0);
     }
 
     return returnValue;
     }
 
     return returnValue;
index e7e5398..cac5faa 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)telnet.c   5.40 (Berkeley) %G%";
+static char sccsid[] = "@(#)telnet.c   5.41 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/types.h>
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -58,15 +58,8 @@ static char  subbuffer[SUBBUFSIZE],
                        }
 
 char   options[256];           /* The combined options */
                        }
 
 char   options[256];           /* The combined options */
-#define        he_said_will(c)         options[c] |= OPT_HE_SAID_WILL
-#define        he_said_wont(c)         options[c] &= ~OPT_HE_SAID_WILL
-#define        he_said_do(c)           options[c] |= OPT_HE_SAID_DO
-#define        he_said_dont(c)         options[c] &= ~OPT_HE_SAID_DO
-
-#define        I_said_will(c)          options[c] |= OPT_I_SAID_WILL
-#define        I_said_wont(c)          options[c] &= ~OPT_I_SAID_WILL
-#define        I_said_do(c)            options[c] |= OPT_I_SAID_DO
-#define        I_said_dont(c)          options[c] &= ~OPT_I_SAID_DO
+char   do_dont_resp[256];
+char   will_wont_resp[256];
 
 int
        connected,
 
 int
        connected,
@@ -121,6 +114,10 @@ jmp_buf    peerdied;
 
 int    flushline;
 
 
 int    flushline;
 
+#ifdef KLUDGELINEMODE
+int    kludgelinemode = 1;
+#endif
+
 /*
  * The following are some clocks used to decide how to interpret
  * the relationship between various variables.
 /*
  * The following are some clocks used to decide how to interpret
  * the relationship between various variables.
@@ -128,6 +125,7 @@ int flushline;
 
 Clocks clocks;
 \f
 
 Clocks clocks;
 \f
+#ifdef notdef
 Modelist modelist[] = {
        { "telnet command mode", COMMAND_LINE },
        { "character-at-a-time mode", 0 },
 Modelist modelist[] = {
        { "telnet command mode", COMMAND_LINE },
        { "character-at-a-time mode", 0 },
@@ -137,6 +135,7 @@ Modelist modelist[] = {
        { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
        { "3270 mode", 0 },
 };
        { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
        { "3270 mode", 0 },
 };
+#endif
 
 \f
 /*
 
 \f
 /*
@@ -218,48 +217,67 @@ va_dcl
  * is in disagreement as to what the current state should be.
  */
 
  * is in disagreement as to what the current state should be.
  */
 
-void
-send_do(c)
+send_do(c, init)
+register int c, init;
 {
 {
-    if (!(did_he_say_will(c) && did_I_say_do(c))) {
-       NET2ADD(IAC, DO);
-       NETADD(c);
-       I_said_do(c);
-       printoption("SENT", "do", c);
+    if (init) {
+       if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
+                               my_want_state_is_do(c))
+           return;
+       set_my_want_state_do(c);
+       do_dont_resp[c]++;
     }
     }
+    NET2ADD(IAC, DO);
+    NETADD(c);
+    printoption("SENT", "do", c);
 }
 
 void
 }
 
 void
-send_dont(c)
+send_dont(c, init)
+register int c, init;
 {
 {
-    if (did_he_say_will(c) || did_I_say_do(c)) {
-       NET2ADD(IAC, DONT);
-       NETADD(c);
-       I_said_dont(c);
-       printoption("SENT", "dont", c);
+    if (init) {
+       if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
+                               my_want_state_is_dont(c))
+           return;
+       set_my_want_state_dont(c);
+       do_dont_resp[c]++;
     }
     }
+    NET2ADD(IAC, DONT);
+    NETADD(c);
+    printoption("SENT", "dont", c);
 }
 
 void
 }
 
 void
-send_will(c)
+send_will(c, init)
+register int c, init;
 {
 {
-    if (!(did_he_say_do(c) && did_I_say_will(c))) {
-       NET2ADD(IAC, WILL);
-       NETADD(c);
-       I_said_will(c);
-       printoption("SENT", "will", c);
+    if (init) {
+       if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
+                               my_want_state_is_will(c))
+           return;
+       set_my_want_state_will(c);
+       will_wont_resp[c]++;
     }
     }
+    NET2ADD(IAC, WILL);
+    NETADD(c);
+    printoption("SENT", "will", c);
 }
 
 void
 }
 
 void
-send_wont(c)
+send_wont(c, init)
+register int c, init;
 {
 {
-    if (did_he_say_do(c) || did_I_say_will(c)) {
-       NET2ADD(IAC, WONT);
-       NETADD(c);
-       I_said_wont(c);
-       printoption("SENT", "wont", c);
+    if (init) {
+       if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
+                               my_want_state_is_wont(c))
+           return;
+       set_my_want_state_wont(c);
+       will_wont_resp[c]++;
     }
     }
+    NET2ADD(IAC, WONT);
+    NETADD(c);
+    printoption("SENT", "wont", c);
 }
 
 
 }
 
 
@@ -268,51 +286,75 @@ willoption(option)
        int option;
 {
        char *fmt;
        int option;
 {
        char *fmt;
+       int new_state_ok = 0;
 
 
-       he_said_will(option);
+       if (do_dont_resp[option]) {
+           --do_dont_resp[option];
+           if (do_dont_resp[option] && my_state_is_do(option))
+               --do_dont_resp[option];
+       }
 
 
-       switch (option) {
+       if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
 
 
-       case TELOPT_ECHO:
-#      if defined(TN3270)
-           /*
-            * The following is a pain in the rear-end.
-            * Various IBM servers (some versions of Wiscnet,
-            * possibly Fibronics/Spartacus, and who knows who
-            * else) will NOT allow us to send "DO SGA" too early
-            * in the setup proceedings.  On the other hand,
-            * 4.2 servers (telnetd) won't set SGA correctly.
-            * So, we are stuck.  Empirically (but, based on
-            * a VERY small sample), the IBM servers don't send
-            * out anything about ECHO, so we postpone our sending
-            * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
-            * DO send).
-            */
-           {
-               if (askedSGA == 0) {
-                   askedSGA = 1;
-                   if (!did_I_say_do(TELOPT_SGA)) {
-                       send_do(TELOPT_SGA);
+           switch (option) {
+
+           case TELOPT_ECHO:
+#          if defined(TN3270)
+               /*
+                * The following is a pain in the rear-end.
+                * Various IBM servers (some versions of Wiscnet,
+                * possibly Fibronics/Spartacus, and who knows who
+                * else) will NOT allow us to send "DO SGA" too early
+                * in the setup proceedings.  On the other hand,
+                * 4.2 servers (telnetd) won't set SGA correctly.
+                * So, we are stuck.  Empirically (but, based on
+                * a VERY small sample), the IBM servers don't send
+                * out anything about ECHO, so we postpone our sending
+                * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
+                * DO send).
+                 */
+               {
+                   if (askedSGA == 0) {
+                       askedSGA = 1;
+                       if (my_want_state_is_dont(TELOPT_SGA))
+                           send_do(TELOPT_SGA, 1);
                    }
                }
                    }
                }
-           }
-               /* Fall through */
-       case TELOPT_EOR:
-       case TELOPT_BINARY:
-#endif /* defined(TN3270) */
-       case TELOPT_SGA:
+                   /* Fall through */
+           case TELOPT_EOR:
+           case TELOPT_BINARY:
+#endif     /* defined(TN3270) */
+           case TELOPT_SGA:
                settimer(modenegotiated);
                settimer(modenegotiated);
-               send_do(option);
-               setconnmode();          /* possibly set new tty mode */
+               new_state_ok = 1;
                break;
 
                break;
 
-       case TELOPT_TM:
+           case TELOPT_TM:
+               if (flushout)
+                   flushout = 0;
+               /*
+                * Special case for TM.  If we get back a WILL,
+                * pretend we got back a WONT.
+                */
+               set_my_want_state_dont(option);
+               set_my_state_dont(option);
                return;                 /* Never reply to TM will's/wont's */
 
                return;                 /* Never reply to TM will's/wont's */
 
-       default:
-               send_dont(option);
+           case TELOPT_LINEMODE:
+           default:
                break;
                break;
+           }
+
+           if (new_state_ok) {
+               set_my_want_state_do(option);
+               send_do(option, 0);
+               setconnmode(0);         /* possibly set new tty mode */
+           } else {
+               do_dont_resp[option]++;
+               send_dont(option, 0);
+           }
        }
        }
+       set_my_state_do(option);
 }
 
 void
 }
 
 void
@@ -321,23 +363,48 @@ wontoption(option)
 {
        char *fmt;
 
 {
        char *fmt;
 
-       he_said_wont(option);
+       if (do_dont_resp[option]) {
+           --do_dont_resp[option];
+           if (do_dont_resp[option] && my_state_is_dont(option))
+               --do_dont_resp[option];
+       }
+
+       if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
 
 
-       switch (option) {
+           switch (option) {
 
 
-       case TELOPT_ECHO:
-       case TELOPT_SGA:
+#ifdef KLUDGELINEMODE
+           case TELOPT_SGA:
+               if (!kludgelinemode)
+                   break;
+               /* FALL THROUGH */
+#endif
+           case TELOPT_ECHO:
                settimer(modenegotiated);
                settimer(modenegotiated);
-               send_dont(option);
-               setconnmode();                  /* Set new tty mode */
                break;
 
                break;
 
-       case TELOPT_TM:
+           case TELOPT_TM:
+               if (flushout)
+                   flushout = 0;
+               set_my_want_state_dont(option);
+               set_my_state_dont(option);
                return;         /* Never reply to TM will's/wont's */
 
                return;         /* Never reply to TM will's/wont's */
 
-       default:
-               send_dont(option);
+           default:
+               break;
+           }
+           set_my_want_state_dont(option);
+           send_dont(option, 0);
+           setconnmode(0);                     /* Set new tty mode */
+       } else if (option == TELOPT_TM) {
+           /*
+            * Special case for TM.
+            */
+           if (flushout)
+               flushout = 0;
+           set_my_want_state_dont(option);
        }
        }
+       set_my_state_dont(option);
 }
 
 static void
 }
 
 static void
@@ -345,29 +412,99 @@ dooption(option)
        int option;
 {
        char *fmt;
        int option;
 {
        char *fmt;
+       int new_state_ok = 0;
+
+       if (will_wont_resp[option]) {
+           --will_wont_resp[option];
+           if (will_wont_resp[option] && my_state_is_will(option))
+               --will_wont_resp[option];
+       }
 
 
-       he_said_do(option);
+       if (will_wont_resp[option] == 0) {
+         if (my_want_state_is_wont(option)) {
 
 
-       switch (option) {
+           switch (option) {
+
+           case TELOPT_TM:
+               /*
+                * Special case for TM.  We send a WILL, but pretend
+                * we sent WONT.
+                */
+               send_will(option, 0);
+               set_my_want_state_wont(TELOPT_TM);
+               set_my_state_wont(TELOPT_TM);
+               return;
 
 
-       case TELOPT_TM:
 #      if defined(TN3270)
 #      if defined(TN3270)
-       case TELOPT_EOR:                /* end of record */
-       case TELOPT_BINARY:             /* binary mode */
+           case TELOPT_EOR:            /* end of record */
+           case TELOPT_BINARY:         /* binary mode */
 #      endif   /* defined(TN3270) */
 #      endif   /* defined(TN3270) */
-       case TELOPT_NAWS:               /* window size */
-       case TELOPT_TSPEED:             /* terminal speed */
-       case TELOPT_LFLOW:              /* local flow control */
-       case TELOPT_TTYPE:              /* terminal type option */
-       case TELOPT_SGA:                /* no big deal */
-               send_will(option);
+           case TELOPT_NAWS:           /* window size */
+           case TELOPT_TSPEED:         /* terminal speed */
+           case TELOPT_LFLOW:          /* local flow control */
+           case TELOPT_TTYPE:          /* terminal type option */
+           case TELOPT_SGA:            /* no big deal */
+               new_state_ok = 1;
                break;
 
                break;
 
-       case TELOPT_ECHO:               /* We're never going to echo... */
-       default:
-               send_wont(option);
+           case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+               kludgelinemode = 0;
+#endif
+               set_my_want_state_will(TELOPT_LINEMODE);
+               send_will(option, 0);
+               set_my_state_will(TELOPT_LINEMODE);
+               slc_init();
+               return;
+
+           case TELOPT_ECHO:           /* We're never going to echo... */
+           default:
                break;
                break;
+           }
+
+           if (new_state_ok) {
+               set_my_want_state_will(option);
+               send_will(option, 0);
+           } else {
+               will_wont_resp[option]++;
+               send_wont(option, 0);
+           }
+         } else {
+           /*
+            * Handle options that need more things done after the
+            * other side has acknowledged the option.
+            */
+           switch (option) {
+           case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+               kludgelinemode = 0;
+#endif
+               set_my_state_will(option);
+               slc_init();
+               return;
+           }
+         }
        }
        }
+       set_my_state_will(option);
+}
+
+static void
+dontoption(option)
+       int option;
+{
+
+       if (will_wont_resp[option]) {
+           --will_wont_resp[option];
+           if (will_wont_resp[option] && my_state_is_wont(option))
+               --will_wont_resp[option];
+       }
+
+       if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
+           /* we always accept a DONT */
+           set_my_want_state_wont(option);
+           send_wont(option, 0);
+       }
+       set_my_state_wont(option);
 }
 
 /*
 }
 
 /*
@@ -381,19 +518,24 @@ dooption(option)
  *             Terminal type, send request.
  *             Terminal speed (send request).
  *             Local flow control (is request).
  *             Terminal type, send request.
  *             Terminal speed (send request).
  *             Local flow control (is request).
+ *             Linemode
  */
 
  */
 
+static char tty1[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_IS, 0 };
+static char tty2[] = { IAC, SE, 0 };
+
 static void
 suboption()
 {
 static void
 suboption()
 {
-    printsub("<", subbuffer, subend-subbuffer+1);
+    printsub('<', subbuffer, subend-subbuffer+2);
     switch (subbuffer[0]&0xff) {
     case TELOPT_TTYPE:
     switch (subbuffer[0]&0xff) {
     case TELOPT_TTYPE:
+       if (my_want_state_is_wont(TELOPT_TTYPE))
+           return;
        if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
            ;
        } else {
            char *name;
        if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
            ;
        } else {
            char *name;
-           char namebuf[41];
            extern char *getenv();
            int len;
 
            extern char *getenv();
            int len;
 
@@ -410,12 +552,13 @@ suboption()
            if ((len + 4+2) < NETROOM()) {
                char temp[50];
 
            if ((len + 4+2) < NETROOM()) {
                char temp[50];
 
-               strcpy(namebuf, name);
-               upcase(namebuf);
-               sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
-                                   TELQUAL_IS, namebuf, IAC, SE);
-               ring_supply_data(&netoring, temp, 4+strlen(namebuf)+2);
-               printsub(">", temp+2, 4+strlen(namebuf)+2-2-2);
+               strcpy(temp, tty1);
+               strcpy(&temp[4], name);
+               upcase(&temp[4]);
+               strcpy(&temp[4+len], tty2);
+               len += 6;
+               ring_supply_data(&netoring, temp, len);
+               printsub('>', temp+2, len-2);
            } else {
                ExitString("No room in buffer for terminal type.\n", 1);
                /*NOTREACHED*/
            } else {
                ExitString("No room in buffer for terminal type.\n", 1);
                /*NOTREACHED*/
@@ -423,36 +566,437 @@ suboption()
        }
        break;
     case TELOPT_TSPEED:
        }
        break;
     case TELOPT_TSPEED:
+       if (my_want_state_is_wont(TELOPT_TSPEED))
+           return;
        if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
        if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
-           long *ospeed,*ispeed;
-           char speedbuf[41];
-           char *getenv();
+           long ospeed,ispeed;
+           char temp[50];
            int len;
 
            TerminalSpeeds(&ispeed, &ospeed);
 
            int len;
 
            TerminalSpeeds(&ispeed, &ospeed);
 
-           sprintf(speedbuf, "%d,%d", ospeed, ispeed);
-           len = strlen(speedbuf);
+           sprintf(temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
+                   TELQUAL_IS, ospeed, ispeed, IAC, SE);
+           len = strlen(temp+4) + 4;   /* temp[3] is 0 ... */
 
 
-           if ((len + 4+2) < NETROOM()) {
-               printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TSPEED,
-                   TELQUAL_IS, speedbuf, IAC, SE);
+           if (len < NETROOM()) {
+               ring_supply_data(&netoring, temp, len);
+               printsub('>', temp+2, len - 2);
            }
        }
        break;
     case TELOPT_LFLOW:
            }
        }
        break;
     case TELOPT_LFLOW:
+       if (my_want_state_is_wont(TELOPT_LFLOW))
+           return;
        if ((subbuffer[1]&0xff) == 1) {
            localflow = 1;
        } else if ((subbuffer[1]&0xff) == 0) {
            localflow = 0;
        }
        setcommandmode();
        if ((subbuffer[1]&0xff) == 1) {
            localflow = 1;
        } else if ((subbuffer[1]&0xff) == 0) {
            localflow = 0;
        }
        setcommandmode();
-       setconnmode();
+       setconnmode(0);
+       break;
+
+    case TELOPT_LINEMODE:
+       if (my_want_state_is_wont(TELOPT_LINEMODE))
+           return;
+       switch (subbuffer[1]&0xff) {
+       case WILL:
+           lm_will(&subbuffer[2], subend - &subbuffer[2]);
+           break;
+       case WONT:
+           lm_wont(&subbuffer[2], subend - &subbuffer[2]);
+           break;
+       case DO:
+           lm_do(&subbuffer[2], subend - &subbuffer[2]);
+           break;
+       case DONT:
+           lm_dont(&subbuffer[2], subend - &subbuffer[2]);
+           break;
+       case LM_SLC:
+           slc(&subbuffer[2], subend - &subbuffer[2]);
+           break;
+       case LM_MODE:
+           lm_mode(&subbuffer[2], subend - &subbuffer[2], 0);
+           break;
+       default:
+               break;
+       }
        break;
     default:
        break;
     }
 }
        break;
     default:
        break;
     }
 }
+
+static char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
+
+lm_will(cmd, len)
+char *cmd;
+{
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:       /* We shouldn't ever get this... */
+    default:
+       str_lm[3] = DONT;
+       str_lm[4] = cmd[0];
+       if (NETROOM() > sizeof(str_lm)) {
+           ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+           printsub('>', &str_lm[2], sizeof(str_lm)-2);
+       }
+/*@*/  else printf("lm_will: not enough room in buffer\n");
+       break;
+    }
+}
+
+lm_wont(cmd, len)
+char *cmd;
+{
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:       /* We shouldn't ever get this... */
+    default:
+       /* We are always DONT, so don't respond */
+       return;
+    }
+}
+
+int linemode;
+
+lm_do(cmd, len)
+char *cmd;
+{
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:
+    default:
+       str_lm[3] = WONT;
+       str_lm[4] = cmd[0];
+       if (NETROOM() > sizeof(str_lm)) {
+           ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+           printsub('>', &str_lm[2], sizeof(str_lm)-2);
+       }
+/*@*/  else printf("lm_do: not enough room in buffer\n");
+       break;
+    }
+}
+
+lm_dont(cmd, len)
+char *cmd;
+{
+    switch(cmd[0]) {
+    case LM_FORWARDMASK:
+    default:
+       /* we are always WONT, so don't respond */
+       break;
+    }
+}
+
+static char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE };
+
+lm_mode(cmd, len, init)
+char *cmd;
+int len, init;
+{
+       if (len != 1)
+               return;
+       if ((linemode&(MODE_EDIT|MODE_TRAPSIG)) == *cmd)
+               return;
+       if (*cmd&MODE_ACK)
+               return;
+       linemode = (*cmd&(MODE_EDIT|MODE_TRAPSIG));
+       str_lm_mode[4] = linemode;
+       if (!init)
+           str_lm_mode[4] |= MODE_ACK;
+       if (NETROOM() > sizeof(str_lm_mode)) {
+           ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
+           printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
+       }
+/*@*/  else printf("lm_mode: not enough room in buffer\n");
+       setconnmode(0); /* set changed mode */
+}
+
+\f
+
+/*
+ * slc()
+ * Handle special character suboption of LINEMODE.
+ */
+
+struct spc {
+       char val;
+       char *valp;
+       char flags;     /* Current flags & level */
+       char mylevel;   /* Maximum level & flags */
+} spc_data[NSLC+1];
+
+#define SLC_IMPORT     0
+#define        SLC_EXPORT      1
+#define SLC_RVALUE     2
+static int slc_mode = SLC_EXPORT;
+
+slc_init()
+{
+       register struct spc *spcp;
+       extern char *tcval();
+
+       localchars = 1;
+       for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
+               spcp->val = 0;
+               spcp->valp = 0;
+               spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
+       }
+
+#define        initfunc(func, flags) { \
+                                       spcp = &spc_data[func]; \
+                                       if (spcp->valp = tcval(func)) { \
+                                           spcp->val = *spcp->valp; \
+                                           spcp->mylevel = SLC_VARIABLE|flags; \
+                                       } else { \
+                                           spcp->val = 0; \
+                                           spcp->mylevel = SLC_DEFAULT; \
+                                       } \
+                                   }
+
+       initfunc(SLC_SYNCH, 0);
+       /* No BRK */
+       initfunc(SLC_AO, 0);
+       initfunc(SLC_AYT, 0);
+       /* No EOR */
+       initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
+       initfunc(SLC_EOF, 0);
+#ifndef        CRAY
+       initfunc(SLC_SUSP, SLC_FLUSHIN);
+#endif
+       initfunc(SLC_EC, 0);
+       initfunc(SLC_EL, 0);
+#ifndef        CRAY
+       initfunc(SLC_EW, 0);
+       initfunc(SLC_RP, 0);
+       initfunc(SLC_LNEXT, 0);
+#endif
+       initfunc(SLC_XON, 0);
+       initfunc(SLC_XOFF, 0);
+#ifdef CRAY
+       spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
+       spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
+#endif
+       /* No FORW1 */
+       /* No FORW2 */
+
+       initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
+#undef initfunc
+
+       if (slc_mode == SLC_EXPORT)
+               slc_export();
+       else
+               slc_import(1);
+
+}
+
+slcstate()
+{
+    printf("Special characters are %s values\n",
+               slc_mode == SLC_IMPORT ? "remote default" :
+               slc_mode == SLC_EXPORT ? "local" :
+                                        "remote");
+}
+
+slc_mode_export()
+{
+    slc_mode = SLC_EXPORT;
+    if (my_state_is_will(TELOPT_LINEMODE))
+       slc_export();
+}
+
+slc_mode_import(def)
+{
+    slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
+    if (my_state_is_will(TELOPT_LINEMODE))
+       slc_import(def);
+}
+
+char slc_import_val[] = {
+       IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
+};
+char slc_import_def[] = {
+       IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
+};
+
+slc_import(def)
+int def;
+{
+    if (NETROOM() > sizeof(slc_import_val)) {
+       if (def) {
+           ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
+           printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
+       } else {
+           ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
+           printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
+       }
+    }
+/*@*/ else printf("slc_import: not enough room\n");
+}
+
+slc_export()
+{
+    register struct spc *spcp;
+
+    TerminalDefaultChars();
+
+    slc_start_reply();
+    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+       if (spcp->mylevel != SLC_NOSUPPORT) {
+           spcp->flags = spcp->mylevel;
+           if (spcp->valp)
+               spcp->val = *spcp->valp;
+           slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
+       }
+    }
+    slc_end_reply();
+    if (slc_update())
+       setconnmode(1); /* set the  new character values */
+}
+
+slc(cp, len)
+register char *cp;
+int len;
+{
+       register struct spc *spcp;
+       register int func,level;
+
+       slc_start_reply();
+
+       for (; len >= 3; len -=3, cp +=3) {
+
+               func = cp[SLC_FUNC];
+
+               if (func == 0) {
+                       /*
+                        * Client side: always ignore 0 function.
+                        */
+                       continue;
+               }
+               if (func > NSLC) {
+                       if (cp[SLC_FLAGS] & SLC_LEVELBITS != SLC_NOSUPPORT)
+                               slc_add_reply(func, SLC_NOSUPPORT, 0);
+                       continue;
+               }
+
+               spcp = &spc_data[func];
+
+               level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
+
+               if ((cp[SLC_VALUE] == spcp->val) &&
+                   ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
+                       continue;
+               }
+
+               if (level == (SLC_DEFAULT|SLC_ACK)) {
+                       /*
+                        * This is an error condition, the SLC_ACK
+                        * bit should never be set for the SLC_DEFAULT
+                        * level.  Our best guess to recover is to
+                        * ignore the SLC_ACK bit.
+                        */
+                       cp[SLC_FLAGS] &= ~SLC_ACK;
+               }
+
+               if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
+                       spcp->val = cp[SLC_VALUE];
+                       spcp->flags = cp[SLC_FLAGS];    /* include SLC_ACK */
+                       continue;
+               }
+
+               level &= ~SLC_ACK;
+
+               if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
+                       spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
+                       spcp->val = cp[SLC_VALUE];
+               }
+               if (level == SLC_DEFAULT) {
+                       if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
+                               spcp->flags = spcp->mylevel;
+                       else
+                               spcp->flags = SLC_NOSUPPORT;
+               }
+               slc_add_reply(func, spcp->flags, spcp->val);
+       }
+       slc_end_reply();
+       if (slc_update())
+               setconnmode(1); /* set the  new character values */
+}
+
+slc_check()
+{
+    register struct spc *spcp;
+
+    slc_start_reply();
+    for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+       if (spcp->valp && spcp->val != *spcp->valp) {
+           spcp->val = *spcp->valp;
+           slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
+       }
+    }
+    slc_end_reply();
+    setconnmode(1);
+}
+
+
+unsigned char slc_reply[128];
+unsigned char *slc_replyp;
+slc_start_reply()
+{
+       slc_replyp = slc_reply;
+       *slc_replyp++ = IAC;
+       *slc_replyp++ = SB;
+       *slc_replyp++ = TELOPT_LINEMODE;
+       *slc_replyp++ = LM_SLC;
+}
+
+slc_add_reply(func, flags, value)
+char func;
+char flags;
+char value;
+{
+       if ((*slc_replyp++ = func) == IAC)
+               *slc_replyp++ = IAC;
+       if ((*slc_replyp++ = flags) == IAC)
+               *slc_replyp++ = IAC;
+       if ((*slc_replyp++ = value) == IAC)
+               *slc_replyp++ = IAC;
+}
+
+slc_end_reply()
+{
+    register char *cp;
+    register int len;
+
+    *slc_replyp++ = IAC;
+    *slc_replyp++ = SE;
+    len = slc_replyp - slc_reply;
+    if (len <= 6)
+       return;
+    if (NETROOM() > len) {
+       ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
+       printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
+    }
+/*@*/else printf("slc_end_reply: not enough room\n");
+}
+
+slc_update()
+{
+       register struct spc *spcp;
+       int need_update = 0;
+
+       for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+               if (!(spcp->flags&SLC_ACK))
+                       continue;
+               spcp->flags &= ~SLC_ACK;
+               if (spcp->valp && (*spcp->valp != spcp->val)) {
+                       *spcp->valp = spcp->val;
+                       need_update = 1;
+               }
+       }
+       return(need_update);
+}
+
 \f
 
 int
 \f
 
 int
@@ -489,7 +1033,7 @@ telrcv()
            telrcv_state = TS_DATA;
            if (c == '\0') {
                break;  /* Ignore \0 after CR */
            telrcv_state = TS_DATA;
            if (c == '\0') {
                break;  /* Ignore \0 after CR */
-           } else if ((c == '\n') && (!should_he(TELOPT_ECHO)) && !crmod) {
+           } else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
                TTYADD(c);
                break;
            }
                TTYADD(c);
                break;
            }
@@ -520,14 +1064,14 @@ 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') && !should_he(TELOPT_BINARY)) {
+           if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
                if (scc > 0) {
                    c = *sbp&0xff;
                    if (c == 0) {
                        sbp++, scc--; count++;
                        /* a "true" CR */
                        TTYADD('\r');
                if (scc > 0) {
                    c = *sbp&0xff;
                    if (c == 0) {
                        sbp++, scc--; count++;
                        /* a "true" CR */
                        TTYADD('\r');
-                   } else if (!should_he(TELOPT_ECHO) &&
+                   } else if (my_want_state_is_dont(TELOPT_ECHO) &&
                                        (c == '\n')) {
                        sbp++, scc--; count++;
                        TTYADD('\n');
                                        (c == '\n')) {
                        sbp++, scc--; count++;
                        TTYADD('\n');
@@ -550,6 +1094,7 @@ telrcv()
            continue;
 
        case TS_IAC:
            continue;
 
        case TS_IAC:
+process_iac:
            switch (c) {
            
            case WILL:
            switch (c) {
            
            case WILL:
@@ -587,6 +1132,7 @@ telrcv()
            case SB:
                SB_CLEAR();
                telrcv_state = TS_SB;
            case SB:
                SB_CLEAR();
                telrcv_state = TS_SB;
+               printoption("RCVD", "IAC", SB);
                continue;
 
 #          if defined(TN3270)
                continue;
 
 #          if defined(TN3270)
@@ -623,26 +1169,14 @@ telrcv()
 
        case TS_WILL:
            printoption("RCVD", "will", c);
 
        case TS_WILL:
            printoption("RCVD", "will", c);
-           if (c == TELOPT_TM) {
-               if (flushout) {
-                   flushout = 0;
-               }
-           } else {
-               willoption(c);
-           }
+           willoption(c);
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
 
        case TS_WONT:
            printoption("RCVD", "wont", c);
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
 
        case TS_WONT:
            printoption("RCVD", "wont", c);
-           if (c == TELOPT_TM) {
-               if (flushout) {
-                   flushout = 0;
-               }
-           } else {
-               wontoption(c, 1);
-           }
+           wontoption(c);
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
@@ -656,22 +1190,16 @@ telrcv()
            } else if (c == TELOPT_LFLOW) {
                localflow = 1;
                setcommandmode();
            } else if (c == TELOPT_LFLOW) {
                localflow = 1;
                setcommandmode();
-               setconnmode();
+               setconnmode(0);
            }
            telrcv_state = TS_DATA;
            continue;
 
            }
            telrcv_state = TS_DATA;
            continue;
 
-       /*
-        * Now, I've never understood this.  Why do we
-        * need separate routines for will, wont, do,
-        * but not for dont?
-        */
        case TS_DONT:
            printoption("RCVD", "dont", c);
        case TS_DONT:
            printoption("RCVD", "dont", c);
-           he_said_dont(c);
-           send_wont(c);
+           dontoption(c);
            flushline = 1;
            flushline = 1;
-           setconnmode();      /* set new tty mode (maybe) */
+           setconnmode(0);     /* set new tty mode (maybe) */
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
            SetIn3270();
            telrcv_state = TS_DATA;
            continue;
@@ -687,12 +1215,33 @@ telrcv()
        case TS_SE:
            if (c != SE) {
                if (c != IAC) {
        case TS_SE:
            if (c != SE) {
                if (c != IAC) {
+                   /*
+                    * This is an error.  We only expect to get
+                    * "IAC IAC" or "IAC SE".  Several things may
+                    * have happend.  An IAC was not doubled, the
+                    * IAC SE was left off, or another option got
+                    * inserted into the suboption are all possibilities.
+                    * If we assume that the IAC was not doubled,
+                    * and really the IAC SE was left off, we could
+                    * get into an infinate loop here.  So, instead,
+                    * we terminate the suboption, and process the
+                    * partial suboption if we can.
+                    */
+                   SB_TERM();
                    SB_ACCUM(IAC);
                    SB_ACCUM(IAC);
+                   SB_ACCUM(c);
+                   printoption("In SUBOPTION processing, RCVD", "IAC", c);
+                   suboption();        /* handle sub-option */
+                   SetIn3270();
+                   telrcv_state = TS_IAC;
+                   goto process_iac;
                }
                SB_ACCUM(c);
                telrcv_state = TS_SB;
            } else {
                SB_TERM();
                }
                SB_ACCUM(c);
                telrcv_state = TS_SB;
            } else {
                SB_TERM();
+               SB_ACCUM(IAC);
+               SB_ACCUM(SE);
                suboption();    /* handle sub-option */
                SetIn3270();
                telrcv_state = TS_DATA;
                suboption();    /* handle sub-option */
                SetIn3270();
                telrcv_state = TS_DATA;
@@ -732,27 +1281,40 @@ telsnd()
        }
        c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
        if (sc == escape) {
        }
        c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
        if (sc == escape) {
-           command(0);
-           tcc = 0;
-           flushline = 1;
-           break;
-       } else if (MODE_LINE(globalmode) && (sc == echoc)) {
+           /*
+            * Double escape is a pass through of a single escape character.
+            */
+           if (tcc && strip(*tbp) == escape) {
+               tbp++;
+               tcc--;
+               count++;
+           } else {
+               command(0, tbp, tcc);
+               count += tcc;
+               tcc = 0;
+               flushline = 1;
+               break;
+           }
+       }
+#ifdef KLUDGELINEMODE
+       if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
            if (tcc > 0 && strip(*tbp) == echoc) {
                tcc--; tbp++; count++;
            } else {
                dontlecho = !dontlecho;
                settimer(echotoggle);
            if (tcc > 0 && strip(*tbp) == echoc) {
                tcc--; tbp++; count++;
            } else {
                dontlecho = !dontlecho;
                settimer(echotoggle);
-               setconnmode();
+               setconnmode(0);
                flushline = 1;
                break;
            }
        }
                flushline = 1;
                break;
            }
        }
-       if (localchars) {
+#endif
+       if (MODE_LOCAL_CHARS(globalmode)) {
            if (TerminalSpecialChars(sc) == 0) {
                break;
            }
        }
            if (TerminalSpecialChars(sc) == 0) {
                break;
            }
        }
-       if (!should_I(TELOPT_BINARY)) {
+       if (my_want_state_is_wont(TELOPT_BINARY)) {
            switch (c) {
            case '\n':
                    /*
            switch (c) {
            case '\n':
                    /*
@@ -817,7 +1379,13 @@ int       block;                  /* should we block in the select ? */
     /* Decide which rings should be processed */
 
     netout = ring_full_count(&netoring) &&
     /* Decide which rings should be processed */
 
     netout = ring_full_count(&netoring) &&
-           (!MODE_LINE(globalmode) || flushline || should_I(TELOPT_BINARY));
+           (flushline ||
+               (my_want_state_is_wont(TELOPT_LINEMODE)
+#ifdef KLUDGELINEMODE
+                       && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
+#endif
+               ) ||
+                       my_want_state_is_will(TELOPT_BINARY));
     ttyout = ring_full_count(&ttyoring);
 
 #if    defined(TN3270)
     ttyout = ring_full_count(&ttyoring);
 
 #if    defined(TN3270)
@@ -887,11 +1455,12 @@ telnet()
 
 #   if !defined(TN3270)
     if (telnetport) {
 
 #   if !defined(TN3270)
     if (telnetport) {
-       send_do(TELOPT_SGA);
-       send_will(TELOPT_TTYPE);
-       send_will(TELOPT_NAWS);
-       send_will(TELOPT_TSPEED);
-       send_will(TELOPT_LFLOW);
+       send_do(TELOPT_SGA, 1);
+       send_will(TELOPT_TTYPE, 1);
+       send_will(TELOPT_NAWS, 1);
+       send_will(TELOPT_TSPEED, 1);
+       send_will(TELOPT_LFLOW, 1);
+       send_will(TELOPT_LINEMODE, 1);
     }
 #   endif /* !defined(TN3270) */
 
     }
 #   endif /* !defined(TN3270) */
 
@@ -1132,6 +1701,39 @@ sendbrk()
        dosynch();
     }
 }
        dosynch();
     }
 }
+
+void
+sendabort()
+{
+    NET2ADD(IAC, ABORT);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
+}
+
+void
+sendsusp()
+{
+    NET2ADD(IAC, SUSP);
+    flushline = 1;
+    if (autoflush) {
+       doflush();
+    }
+    if (autosynch) {
+       dosynch();
+    }
+}
+
+void
+sendeof()
+{
+   NET2ADD(IAC, xEOF);
+}
+
 /*
  * Send a window size update to the remote system.
  */
 /*
  * Send a window size update to the remote system.
  */
@@ -1140,38 +1742,42 @@ void
 sendnaws()
 {
     long rows, cols;
 sendnaws()
 {
     long rows, cols;
+    unsigned char tmp[16];
+    register unsigned char *cp;
 
 
-#define        NETADDCHAR(x) \
-    { \
-       if (((x) & 0xff) == 0xff) { \
-           NET2ADD(IAC, IAC) \
-       } else { \
-           NETADD(x) \
-       } \
-    }
-#define        NETADDSHORT(x)  { NETADDCHAR(x >> 8); NETADDCHAR(x & 0xff) }
+    if (my_state_is_wont(TELOPT_NAWS))
+       return;
+
+#define        PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
+                           if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
 
     if (TerminalWindowSize(&rows, &cols) == 0) {       /* Failed */
        return;
     }
 
 
     if (TerminalWindowSize(&rows, &cols) == 0) {       /* Failed */
        return;
     }
 
-    if (NETROOM() >= 3 + 8 + 2) {
-       NET2ADD(IAC, SB);
-       NETADD(TELOPT_NAWS);
-       NETADDSHORT(cols);
-       NETADDSHORT(rows);
-       NET2ADD(IAC, SE);
+    cp = tmp;
+
+    *cp++ = IAC;
+    *cp++ = SB;
+    *cp++ = TELOPT_NAWS;
+    PUTSHORT(cp, cols);
+    PUTSHORT(cp, rows);
+    *cp++ = IAC;
+    *cp++ = SE;
+    if (NETROOM() >= cp - tmp) {
+       ring_supply_data(&netoring, tmp, cp-tmp);
+       printsub('>', tmp+2, cp - tmp - 2);
     }
 }
 
 tel_enter_binary()
 {
     }
 }
 
 tel_enter_binary()
 {
-    send_do(TELOPT_BINARY);
-    send_will(TELOPT_BINARY);
+    send_do(TELOPT_BINARY, 1);
+    send_will(TELOPT_BINARY, 1);
 }
 
 tel_leave_binary()
 {
 }
 
 tel_leave_binary()
 {
-    send_dont(TELOPT_BINARY);
-    send_wont(TELOPT_BINARY);
+    send_dont(TELOPT_BINARY, 1);
+    send_wont(TELOPT_BINARY, 1);
 }
 }
index 09e41c7..1f6c997 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)terminal.c 1.15 (Berkeley) %G%";
+static char sccsid[] = "@(#)terminal.c 1.16 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <arpa/telnet.h>
 #endif /* not lint */
 
 #include <arpa/telnet.h>
@@ -32,16 +32,16 @@ char        ttyobuf[2*BUFSIZ], ttyibuf[BUFSIZ];
 
 int termdata;                  /* Debugging flag */
 
 
 int termdata;                  /* Debugging flag */
 
+#ifdef USE_TERMIO
 char
 char
-    termEofChar,
-    termEraseChar,
     termFlushChar,
     termFlushChar,
-    termIntChar,
-    termKillChar,
-#if    defined(MSDOS)
     termLiteralNextChar,
     termLiteralNextChar,
-#endif /* defined(MSDOS) */
-    termQuitChar;
+    termSuspChar,
+    termWerasChar,
+    termRprntChar,
+    termStartChar,
+    termStopChar;
+#endif
 
 /*
  * initialize the terminal data structures.
 
 /*
  * initialize the terminal data structures.
@@ -62,8 +62,11 @@ init_terminal()
 /*
  *             Send as much data as possible to the terminal.
  *
 /*
  *             Send as much data as possible to the terminal.
  *
- *             The return value indicates whether we did any
- *     useful work.
+ *             Return value:
+ *                     -1: No useful work done, data waiting to go out.
+ *                      0: No data was waiting, so nothing was done.
+ *                      1: All waiting data was written out.
+ *                      n: All data - n was written out.
  */
 
 
  */
 
 
@@ -99,7 +102,14 @@ int drop;
        }
        ring_consumed(&ttyoring, n);
     }
        }
        ring_consumed(&ttyoring, n);
     }
-    return n > 0;
+    if (n < 0)
+       return -1;
+    if (n == n0) {
+       if (n0)
+           return -1;
+       return 0;
+    }
+    return n0 - n + 1;
 }
 
 \f
 }
 
 \f
@@ -112,34 +122,46 @@ int drop;
 int
 getconnmode()
 {
 int
 getconnmode()
 {
-    static char newmode[16] =
-                       { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
-    int modeindex = 0;
-
-    if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
-       modeindex += 1;
-    }
-    if (should_he(TELOPT_ECHO)) {
-       modeindex += 2;
-    }
-    if (should_he(TELOPT_SGA)) {
-       modeindex += 4;
-    }
-    if (In3270) {
-       modeindex += 8;
+    extern int linemode;
+    int mode = 0;
+#ifdef KLUDGELINEMODE
+    extern int kludgelinemode;
+#endif
+
+    if (In3270)
+       return(MODE_FLOW);
+
+    if (my_want_state_is_dont(TELOPT_ECHO))
+       mode |= MODE_ECHO;
+
+    if (localflow)
+       mode |= MODE_FLOW;
+
+#ifdef KLUDGELINEMODE
+    if (kludgelinemode) {
+       if (my_want_state_is_dont(TELOPT_SGA)) {
+           mode |= (MODE_TRAPSIG|MODE_EDIT);
+           if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
+               mode &= ~MODE_ECHO;
+           }
+       }
+       return(mode);
     }
     }
-    return newmode[modeindex];
+#endif
+    if (my_want_state_is_will(TELOPT_LINEMODE))
+       mode |= linemode;
+    return(mode);
 }
 
 void
 }
 
 void
-setconnmode()
+setconnmode(force)
 {
 {
-    TerminalNewMode(getconnmode());
+    TerminalNewMode(getconnmode()|(force?MODE_FORCE:0));
 }
 
 
 void
 setcommandmode()
 {
 }
 
 
 void
 setcommandmode()
 {
-    TerminalNewMode(0);
+    TerminalNewMode(-1);
 }
 }
index d16b554..aa99cc6 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)tn3270.c   1.18 (Berkeley) %G%";
+static char sccsid[] = "@(#)tn3270.c   1.19 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/types.h>
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -296,21 +296,21 @@ char c;
 void
 SetIn3270()
 {
 void
 SetIn3270()
 {
-    if (Sent3270TerminalType && should_I(TELOPT_BINARY)
-                           && should_he(TELOPT_BINARY) && !donebinarytoggle) {
+    if (Sent3270TerminalType && my_want_state_is_will(TELOPT_BINARY)
+               && my_want_state_is_do(TELOPT_BINARY) && !donebinarytoggle) {
        if (!In3270) {
            In3270 = 1;
            Init3270();         /* Initialize 3270 functions */
            /* initialize terminal key mapping */
            InitTerminal();     /* Start terminal going */
        if (!In3270) {
            In3270 = 1;
            Init3270();         /* Initialize 3270 functions */
            /* initialize terminal key mapping */
            InitTerminal();     /* Start terminal going */
-           setconnmode();
+           setconnmode(0);
        }
     } else {
        if (In3270) {
            StopScreen(1);
            In3270 = 0;
            Stop3270();         /* Tell 3270 we aren't here anymore */
        }
     } else {
        if (In3270) {
            StopScreen(1);
            In3270 = 0;
            Stop3270();         /* Tell 3270 we aren't here anymore */
-           setconnmode();
+           setconnmode(0);
        }
     }
 }
        }
     }
 }
@@ -359,7 +359,7 @@ tn3270_ttype()
                                                                1);
            /*NOTREACHED*/
        }
                                                                1);
            /*NOTREACHED*/
        }
-       printsub(">", sb_terminal+2, sizeof sb_terminal-2);
+       printsub('>', sb_terminal+2, sizeof sb_terminal-2);
        ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal);
        return 1;
     } else {
        ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal);
        return 1;
     } else {
index 1a5346a..a86dce9 100644 (file)
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)utilities.c        1.13 (Berkeley) %G%";
+static char sccsid[] = "@(#)utilities.c        1.14 (Berkeley) %G%";
 #endif /* not lint */
 
 #define        TELOPTS
 #endif /* not lint */
 
 #define        TELOPTS
+#define        TELCMDS
 #include <arpa/telnet.h>
 #include <sys/types.h>
 
 #include <arpa/telnet.h>
 #include <sys/types.h>
 
@@ -36,6 +37,7 @@ static char sccsid[] = "@(#)utilities.c       1.13 (Berkeley) %G%";
 #include "externs.h"
 
 FILE   *NetTrace = 0;          /* Not in bss, since needs to stay */
 #include "externs.h"
 
 FILE   *NetTrace = 0;          /* Not in bss, since needs to stay */
+int    prettydump;
 
 /*
  * upcase()
 
 /*
  * upcase()
@@ -88,6 +90,25 @@ int
  * The following are routines used to print out debugging information.
  */
 
  * The following are routines used to print out debugging information.
  */
 
+char NetTraceFile[256] = "(standard output)";
+
+void
+SetNetTrace(file)
+register char *file;
+{
+    if (NetTrace && NetTrace != stdout)
+       fclose(NetTrace);
+    if (file  && (strcmp(file, "-") != 0)) {
+       NetTrace = fopen(file, "w");
+       if (NetTrace) {
+           strcpy(NetTraceFile, file);
+           return;
+       }
+       fprintf(stderr, "Cannot open %s.\n", file);
+    }
+    NetTrace = stdout;
+    strcpy(NetTraceFile, "(standard output)");
+}
 
 void
 Dump(direction, buffer, length)
 
 void
 Dump(direction, buffer, length)
@@ -99,6 +120,7 @@ int  length;
 #   define min(x,y)    ((x<y)? x:y)
     char *pThis;
     int offset;
 #   define min(x,y)    ((x<y)? x:y)
     char *pThis;
     int offset;
+    extern pettydump;
 
     offset = 0;
 
 
     offset = 0;
 
@@ -106,10 +128,20 @@ int       length;
        /* print one line */
        fprintf(NetTrace, "%c 0x%x\t", direction, offset);
        pThis = buffer;
        /* 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++;
+       if (prettydump) {
+           buffer = buffer + min(length, BYTES_PER_LINE);
+           while (pThis < buffer) {
+               fprintf(NetTrace, "%c%.2x",
+                   (((*pThis)&0xff) == 0xff) ? '*' : ' ',
+                   (*pThis)&0xff);
+               pThis++;
+           }
+       } else {
+           buffer = buffer + min(length, BYTES_PER_LINE/2);
+           while (pThis < buffer) {
+               fprintf(NetTrace, "%.2x", (*pThis)&0xff);
+               pThis++;
+           }
        }
        if (NetTrace == stdout) {
            fprintf(NetTrace, "\r\n");
        }
        if (NetTrace == stdout) {
            fprintf(NetTrace, "\r\n");
@@ -136,54 +168,319 @@ printoption(direction, fmt, option)
        if (!showoptions)
                return;
        fprintf(NetTrace, "%s ", direction);
        if (!showoptions)
                return;
        fprintf(NetTrace, "%s ", direction);
-       if (option < (sizeof telopts/sizeof telopts[0]))
-               fprintf(NetTrace, "%s %s", fmt, telopts[option]);
+       if (TELOPT_OK(option))
+               fprintf(NetTrace, "%s %s", fmt, TELOPT(option));
+       else if (TELCMD_OK(option))
+               fprintf(NetTrace, "%s %s", fmt, TELCMD(option));
        else
                fprintf(NetTrace, "%s %d", fmt, option);
        else
                fprintf(NetTrace, "%s %d", fmt, option);
-       if (NetTrace == stdout) {
+       if (NetTrace == stdout)
            fprintf(NetTrace, "\r\n");
            fprintf(NetTrace, "\r\n");
-       } else {
+       else
            fprintf(NetTrace, "\n");
            fprintf(NetTrace, "\n");
-       }
        return;
 }
 
        return;
 }
 
+optionstatus()
+{
+    register int i;
+    extern char will_wont_resp[], do_dont_resp[];
+
+    for (i = 0; i < 256; i++) {
+       if (do_dont_resp[i]) {
+           if (TELOPT_OK(i))
+               printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]);
+           else if (TELCMD_OK(i))
+               printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]);
+           else
+               printf("resp DO_DONT %d: %d\n", i,
+                               do_dont_resp[i]);
+           if (my_want_state_is_do(i)) {
+               if (TELOPT_OK(i))
+                   printf("want DO   %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want DO   %s\n", TELCMD(i));
+               else
+                   printf("want DO   %d\n", i);
+           } else {
+               if (TELOPT_OK(i))
+                   printf("want DONT %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want DONT %s\n", TELCMD(i));
+               else
+                   printf("want DONT %d\n", i);
+           }
+       } else {
+           if (my_state_is_do(i)) {
+               if (TELOPT_OK(i))
+                   printf("     DO   %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("     DO   %s\n", TELCMD(i));
+               else
+                   printf("     DO   %d\n", i);
+           }
+       }
+       if (will_wont_resp[i]) {
+           if (TELOPT_OK(i))
+               printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]);
+           else if (TELCMD_OK(i))
+               printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]);
+           else
+               printf("resp WILL_WONT %d: %d\n",
+                               i, will_wont_resp[i]);
+           if (my_want_state_is_will(i)) {
+               if (TELOPT_OK(i))
+                   printf("want WILL %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want WILL %s\n", TELCMD(i));
+               else
+                   printf("want WILL %d\n", i);
+           } else {
+               if (TELOPT_OK(i))
+                   printf("want WONT %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("want WONT %s\n", TELCMD(i));
+               else
+                   printf("want WONT %d\n", i);
+           }
+       } else {
+           if (my_state_is_will(i)) {
+               if (TELOPT_OK(i))
+                   printf("     WILL %s\n", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   printf("     WILL %s\n", TELCMD(i));
+               else
+                   printf("     WILL %d\n", i);
+           }
+       }
+    }
+
+}
+
+char *slcnames[] = { SLC_NAMES };
+
 void
 printsub(direction, pointer, length)
 void
 printsub(direction, pointer, length)
-char   *direction,             /* "<" or ">" */
-       *pointer;               /* where suboption data sits */
+char   direction;              /* '<' or '>' */
+unsigned char  *pointer;       /* where suboption data sits */
 int    length;                 /* length of suboption data */
 {
 int    length;                 /* length of suboption data */
 {
+    register int i;
+
     if (showoptions) {
        fprintf(NetTrace, "%s suboption ",
     if (showoptions) {
        fprintf(NetTrace, "%s suboption ",
-                               (direction[0] == '<')? "Received":"Sent");
+                               (direction == '<')? "Received":"Sent");
+       if (length >= 3) {
+           register int j;
+
+           i = pointer[length-2];
+           j = pointer[length-1];
+
+           if (i != IAC || j != SE) {
+               fprintf(NetTrace, "(terminated by ");
+               if (TELOPT_OK(i))
+                   fprintf(NetTrace, "%s ", TELOPT(i));
+               else if (TELCMD_OK(i))
+                   fprintf(NetTrace, "%s ", TELCMD(i));
+               else
+                   fprintf(NetTrace, "%d ", i);
+               if (TELOPT_OK(j))
+                   fprintf(NetTrace, "%s", TELOPT(j));
+               else if (TELCMD_OK(j))
+                   fprintf(NetTrace, "%s", TELCMD(j));
+               else
+                   fprintf(NetTrace, "%d", j);
+               fprintf(NetTrace, ", not IAC SE!) ");
+           }
+       }
+       length -= 2;
+       if (length < 1) {
+           fprintf(NetTrace, "(Empty suboption???)");
+           return;
+       }
        switch (pointer[0]) {
        case TELOPT_TTYPE:
        switch (pointer[0]) {
        case TELOPT_TTYPE:
-           fprintf(NetTrace, "Terminal type ");
+           fprintf(NetTrace, "TERMINAL-TYPE ");
            switch (pointer[1]) {
            case TELQUAL_IS:
            switch (pointer[1]) {
            case TELQUAL_IS:
-               {
-                   char tmpbuf[SUBBUFSIZE];
-                   int minlen = min(length, sizeof tmpbuf);
-
-                   memcpy(tmpbuf, pointer+2, minlen);
-                   tmpbuf[minlen-1] = 0;
-                   fprintf(NetTrace, "is %s.\n", tmpbuf);
-               }
+               fprintf(NetTrace, "IS \"%.*s\"", length-2, pointer+2);
                break;
            case TELQUAL_SEND:
                break;
            case TELQUAL_SEND:
-               fprintf(NetTrace, "- request to send.\n");
+               fprintf(NetTrace, "SEND");
                break;
            default:
                fprintf(NetTrace,
                break;
            default:
                fprintf(NetTrace,
-                               "- unknown qualifier %d (0x%x).\n",
+                               "- unknown qualifier %d (0x%x).",
                                pointer[1], pointer[1]);
            }
            break;
                                pointer[1], pointer[1]);
            }
            break;
+       case TELOPT_TSPEED:
+           fprintf(NetTrace, "TERMINAL-SPEED");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption???)");
+               break;
+           }
+           switch (pointer[1]) {
+           case 0:
+               fprintf(NetTrace, " IS ");
+               fprintf(NetTrace, "%.*s", length-2, pointer+2);
+               break;
+           default:
+               if (pointer[1] == 1)
+                   fprintf(NetTrace, " SEND");
+               else
+                   fprintf(NetTrace, " %d (unknown)");
+               for (i = 2; i < length; i++)
+                   fprintf(NetTrace, " ?%d?", pointer[i]);
+               break;
+           }
+           break;
+
+       case TELOPT_LFLOW:
+           fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption???)");
+               break;
+           }
+           switch (pointer[1]) {
+           case 0:
+               fprintf(NetTrace, " OFF"); break;
+           case 1:
+               fprintf(NetTrace, " ON"); break;
+           default:
+               fprintf(NetTrace, " %d (unknown)");
+           }
+           for (i = 2; i < length; i++)
+               fprintf(NetTrace, " ?%d?", pointer[i]);
+           break;
+
+       case TELOPT_NAWS:
+           fprintf(NetTrace, "NAWS");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption???)");
+               break;
+           }
+           if (length == 2) {
+               fprintf(NetTrace, " ?%d?", pointer[1]);
+               break;
+           }
+           fprintf(NetTrace, " %d %d (%d)",
+               pointer[1], pointer[2],
+               (((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]));
+           if (length == 4) {
+               fprintf(NetTrace, " ?%d?", pointer[3]);
+               break;
+           }
+           fprintf(NetTrace, " %d %d (%d)",
+               pointer[3], pointer[4],
+               (((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]));
+           for (i = 5; i < length; i++)
+               fprintf(NetTrace, " ?%d?", pointer[i]);
+           break;
+
+       case TELOPT_LINEMODE:
+           fprintf(NetTrace, "LINEMODE ");
+           if (length < 2) {
+               fprintf(NetTrace, " (empty suboption???)");
+               break;
+           }
+           switch (pointer[1]) {
+           case WILL:
+               fprintf(NetTrace, "WILL ");
+               goto common;
+           case WONT:
+               fprintf(NetTrace, "WONT ");
+               goto common;
+           case DO:
+               fprintf(NetTrace, "DO ");
+               goto common;
+           case DONT:
+               fprintf(NetTrace, "DONT ");
+           common:
+               if (length < 3) {
+                   fprintf(NetTrace, "(no option???)");
+                   break;
+               }
+               switch (pointer[2]) {
+               case LM_FORWARDMASK:
+                   fprintf(NetTrace, "Forward Mask");
+                   for (i = 3; i < length; i++)
+                       fprintf(NetTrace, " %x", pointer[i]);
+                   break;
+               default:
+                   fprintf(NetTrace, "%d (unknown)", pointer[2]);
+                   for (i = 3; i < length; i++)
+                       fprintf(NetTrace, " %d", pointer[i]);
+                   break;
+               }
+               break;
+               
+           case LM_SLC:
+               fprintf(NetTrace, "SLC");
+               for (i = 2; i < length - 2; i += 3) {
+                   if (pointer[i+SLC_FUNC] <= NSLC)
+                       fprintf(NetTrace, " %s", slcnames[pointer[i+SLC_FUNC]]);
+                   else
+                       fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]);
+                   switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
+                   case SLC_NOSUPPORT:
+                       fprintf(NetTrace, " NOSUPPORT"); break;
+                   case SLC_CANTCHANGE:
+                       fprintf(NetTrace, " CANTCHANGE"); break;
+                   case SLC_VARIABLE:
+                       fprintf(NetTrace, " VARIABLE"); break;
+                   case SLC_DEFAULT:
+                       fprintf(NetTrace, " DEFAULT"); break;
+                   }
+                   fprintf(NetTrace, "%s%s%s",
+                       pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
+                       pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
+                       pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
+                   if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
+                                               SLC_FLUSHOUT| SLC_LEVELBITS))
+                       fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]);
+                   fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]);
+               }
+               for (; i < length; i++)
+                   fprintf(NetTrace, " ?%d?", pointer[i]);
+               break;
+
+           case LM_MODE:
+               fprintf(NetTrace, "MODE ");
+               if (length < 3) {
+                   fprintf(NetTrace, "(no mode???)");
+                   break;
+               }
+               {
+                   char tbuf[32];
+                   sprintf(tbuf, "%s%s%s",
+                       pointer[2]&MODE_EDIT ? "|EDIT" : "",
+                       pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
+                       pointer[2]&MODE_ACK ? "|ACK" : "");
+                   fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0");
+               }
+               if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK))
+                   fprintf(NetTrace, " (0x%x)", pointer[2]);
+               for (i = 3; i < length; i++)
+                   fprintf(NetTrace, " ?0x%x?", pointer[i]);
+               break;
+           default:
+               fprintf(NetTrace, "%d (unknown)", pointer[1]);
+               for (i = 2; i < length; i++)
+                   fprintf(NetTrace, " %d", pointer[i]);
+           }
+           break;
+
        default:
        default:
-           fprintf(NetTrace, "Unknown option %d (0x%x)\n",
-                                       pointer[0], pointer[0]);
+           fprintf(NetTrace, "Unknown option ");
+           for (i = 0; i < length; i++)
+               fprintf(NetTrace, " %d", pointer[i]);
+           break;
        }
        }
+       if (NetTrace == stdout)
+           fprintf(NetTrace, "\r\n");
+       else
+           fprintf(NetTrace, "\n");
     }
 }
 
     }
 }
 
@@ -222,7 +519,7 @@ EmptyTerminal()
 void
 SetForExit()
 {
 void
 SetForExit()
 {
-    setconnmode();
+    setconnmode(0);
 #if    defined(TN3270)
     if (In3270) {
        Finish3270();
 #if    defined(TN3270)
     if (In3270) {
        Finish3270();
@@ -241,7 +538,7 @@ SetForExit()
        StopScreen(1);
     }
 #endif /* defined(TN3270) */
        StopScreen(1);
     }
 #endif /* defined(TN3270) */
-    setconnmode();
+    setconnmode(0);
     EmptyTerminal();                   /* Flush the path to the tty */
     setcommandmode();
 }
     EmptyTerminal();                   /* Flush the path to the tty */
     setcommandmode();
 }