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