* Copyright (c) 1984-1987 by the Regents of the
* University of California and by Gregory Glenn Minshall.
* Permission to use, copy, modify, and distribute these
* 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.
static char copyright
[] =
"@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
static char sccsid
[] = "@(#)telnet.c 1.17 (Berkeley) %G%";
* 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.
* NOT43 - Allows the program to compile and run on
* 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
* 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
* LNOFLSH - Detects the presence of the LNOFLSH bit
* unix - Compiles in unix specific stuff.
* MSDOS - Compiles in MSDOS specific stuff.
#define ExitString(f,s,r) { fprintf(f, s); exit(r); }
void setcommandmode(), command(); /* forward declarations */
#endif /* !defined(TN3270) */
/* By the way, we need to include curses.h before telnet.h since,
* among other things, telnet.h #defines 'DO', which is a variable
#endif /* defined(unix) */
#else /* !defined(NOT43) */
extern unsigned long inet_addr();
extern char *inet_ntoa();
#endif /* !defined(NOT43) */
#else /* defined(unix) */
#endif /* defined(unix) */
#include "ascii/termin.ext"
#include "ctlr/options.ext"
#include "ctlr/outbound.ext"
#include "general/globals.h"
#endif /* defined(TN3270) */
#include "general/general.h"
* 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)
#define strip(x) ((x)&0x7f)
#define min(x,y) ((x<y)? x:y)
static char Ibuf
[8*BUFSIZ
], *Ifrontp
, *Ibackp
;
#endif /* defined(TN3270) */
static char ttyobuf
[2*BUFSIZ
], *tfrontp
, *tbackp
;
#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
, *nbackp
;
#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
; /* one past last byte of urgent data */
static char subbuffer
[100],
*subpointer
, *subend
; /* buffer for sub-options */
#define SB_CLEAR() subpointer = subbuffer;
#define SB_TERM() subend = subpointer;
#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
static char sb_terminal
[] = { IAC
, SB
,
TELOPT_TTYPE
, TELQUAL_IS
,
'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
static char hisopts
[256];
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 };
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
;
static char tibuf
[BUFSIZ
], *tbp
;
static fd_set ibits
, obits
, xbits
;
In3270
, /* Are we in 3270 mode? */
ISend
, /* trying to send network data in */
noasynch
= 0, /* User specified "-noasynch" on command line */
askedSGA
= 0, /* We have talked about suppress go ahead */
static FILE *NetTrace
= 0; /* Not in bss, since needs to stay */
#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
SYNCHing
, /* we are in TELNET SYNCH mode */
flushout
, /* flush output */
autoflush
= 0, /* flush output when interrupting? */
autosynch
, /* send interrupt characters with SYNCH? */
localchars
, /* we recognize interrupt/quit */
donelclchars
, /* the user has set "localchars" */
donebinarytoggle
, /* the user has put us in binary */
dontlecho
, /* do we suppress local echoing right now? */
/* The following are some tn3270 specific flags */
Sent3270TerminalType
; /* Have we said we are a 3270? */
/* Some real, live, globals. */
tout
, /* Output file descriptor */
tin
; /* Input file descriptor */
#else /* defined(TN3270) */
static int tin
, tout
; /* file descriptors */
#endif /* defined(TN3270) */
* Telnet receiver states for fsm
#define TS_SB 7 /* sub-option collection */
#define TS_SE 8 /* looking for sub-option end */
static int telrcv_state
= TS_DATA
;
static jmp_buf toplevel
= { 0 };
static struct sockaddr_in sin
;
static struct servent
*sp
= 0;
static char hnamebuf
[32];
* The following are some clocks used to decide how to interpret
* the relationship between various variables.
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 */
#define settimer(x) clocks.x = clocks.system++
#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 */
{ "telnet command mode", COMMAND_LINE
},
{ "character-at-a-time mode", 0 },
{ "character-at-a-time mode (local echo)", LOCAL_ECHO
|LOCAL_CHARS
},
{ "line-by-line mode (remote echo)", LINE
| LOCAL_CHARS
},
{ "line-by-line mode", LINE
| LOCAL_ECHO
| LOCAL_CHARS
},
{ "line-by-line mode (local echoing suppressed)", LINE
| LOCAL_CHARS
},
* The following routines try to encapsulate what is system dependent
* (at least between 4.x and dos) which is used in telnet.c.
HaveInput
; /* There is input available to scan */
char *transcom
= 0; /* transparent mode command (default: none) */
#endif /* defined(TN3270) */
static struct tchars otc
= { 0 }, ntc
= { 0 };
static struct ltchars oltc
= { 0 }, nltc
= { 0 };
static struct sgttyb ottyb
= { 0 }, nttyb
= { 0 };
#define TerminalWrite(fd,buf,n) write(fd,buf,n)
#define TerminalRead(fd,buf,n) read(fd,buf,n)
TerminalAutoFlush() /* unix */
ioctl(0, TIOCLGET
, (char *)&autoflush
);
return !(autoflush
&LNOFLSH
); /* if LNOFLSH, no autoflush */
* Look at an input character to see if it is a special character
* 0 Don't add this character.
* 1 Do add this character
TerminalSpecialChars(c
) /* unix */
void doflush(), intp(), sendbrk();
} else if (c
== ntc
.t_quitc
) {
} else if (c
== nltc
.t_flushc
) {
} else if (!MODE_LOCAL_CHARS(globalmode
)) {
if (c
== nttyb
.sg_kill
) {
} else if (c
== nttyb
.sg_erase
) {
* Flush output to the terminal
TerminalFlushOutput() /* unix */
(void) ioctl(fileno(stdout
), TIOCFLUSH
, (char *) 0);
TerminalSaveState() /* unix */
ioctl(0, TIOCGETP
, (char *)&ottyb
);
ioctl(0, TIOCGETC
, (char *)&otc
);
ioctl(0, TIOCGLTC
, (char *)&oltc
);
TerminalRestoreState() /* unix */
* TerminalNewMode - set up terminal to a specific mode.
TerminalNewMode(f
) /* unix */
static struct tchars notc
= { -1, -1, -1, -1, -1, -1 };
static struct ltchars noltc
= { -1, -1, -1, -1, -1, -1 };
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...) */
if ((f
== 1) || (f
== 6)) {
sb
.sg_flags
&= ~(ECHO
|CRMOD
);
sb
.sg_flags
|= ECHO
|CRMOD
;
sb
.sg_erase
= sb
.sg_kill
= -1;
/* get XON, XOFF characters */
tc3
.t_startc
= otc
.t_startc
;
tc3
.t_stopc
= otc
.t_stopc
;
* If user hasn't specified one way or the other,
* then default to not trapping signals.
notc2
.t_intrc
= ntc
.t_intrc
;
notc2
.t_quitc
= ntc
.t_quitc
;
case 3: /* local character processing, remote echo */
case 4: /* local character processing, local echo */
case 5: /* local character processing, no echo */
* If user hasn't specified one way or the other,
* then default to trapping signals.
notc2
.t_brkc
= nltc
.t_flushc
;
notc2
.t_intrc
= notc2
.t_quitc
= -1;
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)) */
ioctl(tin
, FIOASYNC
, (char *)&onoff
);
#endif /* defined(TN3270) */
signal(SIGTSTP
, doescape
);
} else if (MODE_LINE(old
)) {
signal(SIGTSTP
, SIG_DFL
);
sigsetmask(sigblock(0) & ~(1<<(SIGTSTP
-1)));
NetNonblockingIO(fd
, onoff
) /* unix */
ioctl(net
, FIONBIO
, (char *)&onoff
);
NetSigIO(fd
, onoff
) /* unix */
ioctl(net
, FIOASYNC
, (char *)&onoff
); /* hear about input */
NetSetPgrp(fd
) /* unix */
#endif /* defined(NOT43) */
ioctl(net
, SIOCSPGRP
, (char *)&myPid
); /* set my pid */
#endif /* defined(unix) */
#if !defined(SO_OOBINLINE)
#endif /* !defined(SO_OOBINLINE) */
* MSDOS doesn't have anyway of deciding whether a full-edited line
* is ready to be read in, so we need to do character-by-character
* reads, and then do the editing in the program (in the case where
* we are supporting line-by-line mode).
* The following routines, which are internal to the MSDOS-specific
* code, accomplish this miracle.
#define Hex(c) HEX[(c)&0xff]
static survivorSetup
= 0; /* Do we have ^C hooks in? */
lineend
= 0, /* There is a line terminator */
static char linein
[200], /* Where input line is assembled */
*nextin
= linein
, /* Next input character */
*nextout
= linein
; /* Next character to be consumed */
if ((++nextout) >= nextin) { \
nextout = nextin = linein; \
#define characteratatime() (!MODE_LINE(globalmode)) /* one by one */
* Erase the last character on the line.
return; /* Nothing to do */
if (!(isspace(*nextin
) || isprint(*nextin
))) {
* Decide if it's time to send the current line up to the user
if (characteratatime()) {
} else if (nextin
>= (linein
+sizeof linein
)) {
} else if (c
== termFlushChar
) {
} else if ((c
== '\n') || (c
== '\r')) {
/* Otherwise, leave it alone (reset by 'consumechar') */
* OK, what we do here is:
* o If we are echoing, then
* o Look for character erase, line kill characters
* o Echo the character (using '^' if a control character)
* o Put the character in the input buffer
* o Set 'lineend' as necessary
int c
; /* Character to process */
static char literalnextcharacter
= 0;
if (nextin
>= (linein
+sizeof linein
)) {
putchar('\7'); /* Ring bell */
if (MODE_LOCAL_CHARS(globalmode
)) {
/* Look for some special character */
if (!literalnextcharacter
) {
if (c
== termEraseChar
) {
} else if (c
== termKillChar
) {
while (nextin
!= linein
) {
} else if (c
== termLiteralNextChar
) {
literalnextcharacter
= 1;
if (MODE_LOCAL_ECHO(globalmode
)) {
if ((literalnextcharacter
== 0) && ((c
== '\r') || (c
== '\n'))) {
} else if (!isprint(c
) && !isspace(c
)) {
literalnextcharacter
= 0;
#if 1 /* For BIOS variety of calls */
input
= getch(); /* MSC - get console character */
DoNextChar(0x01); /* ^A */
if ((input
= dirconio()) == -1) {
if ((input
&0xff00) == 0x0300) { /* Null */
DoNextChar((input
>>8)&0x7f);
DoNextChar((input
>>8)&0xff);
if (!MODE_COMMAND_LINE(globalmode
)) {
signal(SIGINT
, CtrlCInterrupt
);
* The MSDOS routines, called from elsewhere.
TerminalAutoFlush() /* MSDOS */
* Flush output to the terminal
TerminalFlushOutput() /* MSDOS */
TerminalNewMode(f
) /* MSDOS */
static old_1b_offset
= 0, old_1b_segment
= 0;
signal(SIGINT
, CtrlCInterrupt
);
if (MODE_COMMAND_LINE(f
)) {
if (old_1b_segment
|old_1b_offset
) {
inregs
.x
.dx
= old_1b_offset
;
segregs
.ds
= old_1b_segment
;
intdosx(&inregs
, &inregs
, &segregs
);
old_1b_segment
= old_1b_offset
= 0;
if ((old_1b_segment
|old_1b_offset
) == 0) {
void (far
*foo_subr
)() = iret_subr
;
intdosx(&inregs
, &inregs
, &segregs
);
old_1b_segment
= segregs
.es
;
old_1b_offset
= inregs
.x
.bx
;
inregs
.x
.dx
= FP_OFF(foo_subr
);
segregs
.ds
= FP_SEG(foo_subr
);
intdosx(&inregs
, &inregs
, &segregs
);
TerminalRead(fd
, buffer
, count
)
while (inputExists() && (done
< count
)) {
TerminalSaveState() /* MSDOS */
TerminalSpecialChars(c
) /* MSDOS */
TerminalRestoreState() /* MSDOS */
TerminalWrite(fd
, buffer
, count
) /* MSDOS */
return fwrite(buffer
, sizeof (char), count
, stdout
);
NetNonblockingIO(fd
, onoff
) /* MSDOS */
if (SetSockOpt(net
, SOL_SOCKET
, SO_NONBLOCKING
, onoff
)) {
perror("setsockop (SO_NONBLOCKING) ");
ExitString(stderr
, "exiting\n", 1);
NetSetPgrp(fd
) /* MSDOS */
#endif /* defined(MSDOS) */
Sent3270TerminalType
= 0;
#endif /* defined(TN3270) */
tfrontp
= tbackp
= ttyobuf
;
nfrontp
= nbackp
= netobuf
;
/* Don't change telnetport */
connected
= net
= scc
= tcc
= In3270
= ISend
= donebinarytoggle
= 0;
#endif /* defined(unix) */
/* Don't change NetTrace */
sp
= getservbyname("telnet", "tcp");
ExitString(stderr
, "telnet: tcp/telnet: unknown service\n",1);
init_ctlr(); /* Initialize some things */
#endif /* defined(TN3270) */
* Various utility routines.
register char **argp
= margv
;
if (*cp
== '!') { /* Special case shell escape */
*argp
++ = "!"; /* No room in string to get this */
while (*cp
!= '\0' && !isspace(*cp
))
static char *ambiguous
; /* special return value */
#define Ambiguous(t) ((t)&ambiguous)
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 **c
, **found
;
register int nmatches
, longest
;
for (c
= table
; (p
= *c
) != 0; c
= (*next
)(c
)) {
(*q
== *p
) || (isupper(*q
) && tolower(*q
) == *p
); p
++, q
++)
if (*q
== 0) /* exact match? */
if (!*q
) { /* the name was a prefix */
if (q
- name
> longest
) {
} else if (q
- name
== longest
)
return Ambiguous(char **);
* Make a character string into a number.
* Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
* Construct a control character sequence
* for a special character.
* Upcase (in place) the argument.
while ((c
= *argument
) != 0) {
* Compensate for differences in 4.2 and 4.3 systems.
SetSockOpt(fd
, level
, option
, yesno
)
return setsockopt(fd
, level
, option
,
(char *)&yesno
, sizeof yesno
);
if (yesno
== 0) { /* Can't do that in 4.2! */
fprintf(stderr
, "Error: attempt to turn off an option 0x%x.\n",
return setsockopt(fd
, level
, option
, 0, 0);
* The following are routines used to print out debugging information.
Dump(direction
, buffer
, length
)
# define BYTES_PER_LINE 32
# define min(x,y) ((x<y)? x:y)
fprintf(NetTrace
, "%c 0x%x\t", direction
, offset
);
buffer
= buffer
+min(length
, BYTES_PER_LINE
);
fprintf(NetTrace
, "%.2x", (*pThis
)&0xff);
length
-= BYTES_PER_LINE
;
offset
+= BYTES_PER_LINE
;
/* find next unique line */
printoption(direction
, fmt
, option
, what
)
fprintf(NetTrace
, "%s ", direction
+1);
if (option
< (sizeof telopts
/sizeof telopts
[0]))
fprintf(NetTrace
, "%s %s", fmt
, telopts
[option
]);
fprintf(NetTrace
, "%s %d", fmt
, option
);
fprintf(NetTrace
, "\r\n");
fprintf(NetTrace
, " (%s)\r\n", what
? "reply" : "don't reply");
printsub(direction
, pointer
, length
)
char *direction
, /* "<" or ">" */
*pointer
; /* where suboption data sits */
int length
; /* length of suboption data */
fprintf(NetTrace
, "%s suboption ",
(direction
[0] == '<')? "Received":"Sent");
fprintf(NetTrace
, "Terminal type ");
char tmpbuf
[sizeof subbuffer
];
int minlen
= min(length
, sizeof tmpbuf
);
memcpy(tmpbuf
, pointer
+2, minlen
);
fprintf(NetTrace
, "is %s.\n", tmpbuf
);
fprintf(NetTrace
, "- request to send.\n");
"- unknown qualifier %d (0x%x).\n", pointer
[1]);
fprintf(NetTrace
, "Unknown option %d (0x%x)\n",
* Check to see if any out-of-band data exists on a socket (for
* Telnet "synch" processing).
int s
; /* socket number */
static struct timeval timeout
= { 0 };
value
= select(s
+1, (fd_set
*)0, (fd_set
*)0, &excepts
, &timeout
);
} while ((value
== -1) && (errno
== EINTR
));
if (FD_ISSET(s
, &excepts
)) {
* Send as much data as possible to the network,
* handling requests for urgent data.
* The return value indicates whether we did any
if ((n
= nfrontp
- nbackp
) > 0) {
n
= send(net
, nbackp
, n
, 0); /* normal write */
* 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).
n
= send(net
, nbackp
, n
-1, 0); /* send URGENT all by itself */
n
= send(net
, nbackp
, n
, MSG_OOB
); /* URGENT data */
if (errno
!= ENOBUFS
&& errno
!= EWOULDBLOCK
) {
nbackp
= nfrontp
= netobuf
;
* 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")
if ((*current
&0xff) != IAC
) {
switch (*(current
+1)&0xff) {
case SB
: /* loop forever looking for the SE */
register char *look
= current
+2;
if ((*look
++&0xff) == IAC
) {
if ((*look
++&0xff) == SE
) {
* 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
register char *thisitem
, *next
;
#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
while ((next
= nextitem(thisitem
)) <= nbackp
) {
/* Now, thisitem is first before/at boundary. */
good
= netobuf
; /* where the good bytes go */
while (nfrontp
> thisitem
) {
} while (wewant(next
) && (nfrontp
> next
));
memcpy(good
, thisitem
, length
);
thisitem
= nextitem(thisitem
);
nfrontp
= good
; /* next byte to be sent */
* These routines add various telnet commands to the data stream.
#else /* defined(NOT43) */
#endif /* defined(NOT43) */
netclear(); /* clear the path to the network */
neturg
= NETLOC()-1; /* Some systems are off by one XXX */
#endif /* defined(NOT43) */
/* do printoption AFTER flush, otherwise the output gets tossed... */
printoption("<SENT", doopt
, TELOPT_TM
, 0);
* Send as much data as possible to the terminal.
* The return value indicates whether we did any
if ((n
= tfrontp
- tbackp
) > 0) {
if (!(SYNCHing
||flushout
)) {
n
= TerminalWrite(tout
, tbackp
, n
);
/* we leave 'n' alone! */
tbackp
= tfrontp
= ttyobuf
;
#endif /* defined(unix) */
#endif /* defined(TN3270) */
* Various signal handling routines.
#endif /* defined(unix) */
* These routines decides on what the mode should be (based on the values
* of various global variables).
static char newmode
[16] =
{ 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
if (dontlecho
&& (clocks
.echotoggle
> clocks
.modenegotiated
)) {
if (hisopts
[TELOPT_ECHO
]) {
if (hisopts
[TELOPT_SGA
]) {
return newmode
[modeindex
];
TerminalNewMode(getconnmode());
willoption(option
, reply
)
* 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
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
#endif /* defined(TN3270) */
settimer(modenegotiated
);
setconnmode(); /* possibly set new tty mode */
return; /* Never reply to TM will's/wont's */
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (dont
) - 2;
printoption(">SENT", fmt
, option
, reply
);
printoption("<SENT", fmt
, option
, reply
);
wontoption(option
, reply
)
settimer(modenegotiated
);
setconnmode(); /* Set new tty mode */
return; /* Never reply to TM will's/wont's */
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
printoption(">SENT", fmt
, option
, reply
);
printoption("<SENT", fmt
, option
, reply
);
# endif /* defined(TN3270) */
case TELOPT_TTYPE
: /* terminal type option */
case TELOPT_SGA
: /* no big deal */
case TELOPT_ECHO
: /* We're never going to echo... */
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
printoption(">SENT", fmt
, option
, 0);
* Look at the sub-option buffer, and try to be helpful to the other
* Currently we recognize:
* Terminal type, send request.
printsub("<", subbuffer
, subend
-subbuffer
+1);
switch (subbuffer
[0]&0xff) {
if ((subbuffer
[1]&0xff) != TELQUAL_SEND
) {
* Try to send a 3270 type terminal name. Decide which one based
* on the format of our screen, and (in the future) color
if (initscr() != ERR
) { /* Initialize curses to get line size */
#else /* defined(unix) */
#endif /* defined(unix) */
if ((MaxNumberLines
>= 24) && (MaxNumberColumns
>= 80)) {
Sent3270TerminalType
= 1;
if ((MaxNumberLines
>= 27) && (MaxNumberColumns
>= 132)) {
sb_terminal
[SBTERMMODEL
] = '5';
} else if (MaxNumberLines
>= 43) {
sb_terminal
[SBTERMMODEL
] = '4';
} else if (MaxNumberLines
>= 32) {
sb_terminal
[SBTERMMODEL
] = '3';
sb_terminal
[SBTERMMODEL
] = '2';
NumberLines
= 24; /* before we start out... */
ScreenSize
= NumberLines
*NumberColumns
;
if ((MaxNumberLines
*MaxNumberColumns
) > MAXSCREENSIZE
) {
"Programming error: MAXSCREENSIZE too small.\n", 1);
memcpy(nfrontp
, sb_terminal
, sizeof sb_terminal
);
printsub(">", nfrontp
+2, sizeof sb_terminal
-2);
nfrontp
+= sizeof sb_terminal
;
#endif /* defined(TN3270) */
if ((name
== 0) || ((len
= strlen(name
)) > 40)) {
if ((len
+ 4+2) < NETROOM()) {
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;
ExitString(stderr
, "No room in buffer for terminal type.\n",
if (Sent3270TerminalType
&& myopts
[TELOPT_BINARY
]
&& hisopts
[TELOPT_BINARY
] && !donebinarytoggle
) {
Init3270(); /* Initialize 3270 functions */
/* initialize terminal key mapping */
InitTerminal(); /* Start terminal going */
Stop3270(); /* Tell 3270 we aren't here anymore */
#endif /* defined(TN3270) */
static int telrcv_state
= TS_DATA
;
# endif /* defined(TN3270) */
while ((scc
> 0) && (TTYROOM() > 2)) {
c
= *sbp
++ & 0xff, scc
--;
break; /* Ignore \0 after CR */
if (hisopts
[TELOPT_ECHO
] && !crmod
) {
c
= *Sbp
++ & 0377, Scc
--;
# 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).
} else if (!hisopts
[TELOPT_ECHO
] &&
* We may have missed an urgent notification,
* so make sure we flush whatever is in the
SYNCHing
= stilloob(net
);
Ibackp
+= DataFromNetwork(Ibackp
, Ifrontp
-Ibackp
, 1);
ISend
= 0; /* should have been! */
# endif /* defined(TN3270) */
# else /* !defined(TN3270) */
# endif /* !defined(TN3270) */
printoption(">RCVD", will
, c
, !hisopts
[c
]);
} else if (!hisopts
[c
]) {
printoption(">RCVD", wont
, c
, hisopts
[c
]);
printoption(">RCVD", doopt
, c
, !myopts
[c
]);
printoption(">RCVD", dont
, c
, myopts
[c
]);
sprintf(nfrontp
, wont
, c
);
nfrontp
+= sizeof (wont
) - 2;
setconnmode(); /* set new tty mode (maybe) */
printoption(">SENT", wont
, c
, 0);
suboption(); /* handle sub-option */
* 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 */
* 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.
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 */
if ((netobuf
+sizeof netobuf
- nfrontp
) < 6) {
while ((netobuf
+sizeof netobuf
- nfrontp
) < 6) {
(void) select(net
+1, (fd_set
*) 0, &o
, (fd_set
*) 0,
netflush(); /* try to move along as quickly as ... */
return(origCount
- count
);
/* DataToTerminal - queue up some data to go to terminal. */
DataToTerminal(buffer
, count
)
register char *buffer
; /* where the data is */
register int count
; /* how much to send */
#endif /* defined(unix) */
if (tfrontp
>= ttyobuf
+sizeof ttyobuf
) {
while (tfrontp
>= ttyobuf
+sizeof ttyobuf
) {
(void) select(tout
+1, (fd_set
*) 0, &o
, (fd_set
*) 0,
#endif /* defined(unix) */
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).
#endif /* defined(unix) */
(void) select(tout
+1, (int *) 0, &o
, (int *) 0,
(struct timeval
*) 0); /* wait for TTLOWAT */
#endif /* defined(unix) */
while (tfrontp
!= tbackp
) {
(void) select(tout
+1, (int *) 0, &o
, (int *) 0,
(struct timeval
*) 0); /* wait for TTLOWAT */
#endif /* defined(unix) */
* Push3270 - Try to send data along the 3270 output (to screen) direction.
if (Ifrontp
+scc
> Ibuf
+sizeof Ibuf
) {
memcpy(Ibuf
, Ibackp
, Ifrontp
-Ibackp
);
Ifrontp
-= (Ibackp
-Ibuf
);
if (Ifrontp
+scc
< Ibuf
+sizeof Ibuf
) {
* Finish3270 - get the last dregs of 3270 data out to the terminal
while (Push3270() || !DoTerminalOutput()) {
#endif /* defined(unix) */
/* StringToTerminal - output a null terminated string to the terminal */
(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.
if (tfrontp
>= ttyobuf
+sizeof ttyobuf
) {
(void) DataToTerminal(&c
, 1);
*tfrontp
++ = c
; /* optimize if possible. */
#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
ExitString(file
, string
, returnCode
)
fwrite(string
, 1, strlen(string
), file
);
ExitPerror(string
, returnCode
)
#endif /* defined(TN3270) */
* If we do something useful, return 1; else return 0.
int block
; /* should we block in the select ? */
/* 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).
static struct timeval TimeValue
= { 0 };
if (scc
< 0 && tcc
< 0) {
if ((!MODE_LINE(globalmode
) || flushline
) && NETBYTES()) {
if ((tcc
== 0) && NETROOM() && (shell_active
== 0)) {
#endif /* !defined(MSDOS) */
# else /* !defined(TN3270) */
if (!ISend
&& TTYROOM()) {
# endif /* !defined(TN3270) */
# if defined(TN3270) && defined(unix)
signal(SIGIO
, inputAvailable
);
#endif /* defined(TN3270) && defined(unix) */
if ((c
= select(16, &ibits
, &obits
, &xbits
,
block
? (struct timeval
*)0 : &TimeValue
)) < 0) {
* we can get EINTR if we are in line mode,
* and the user does an escape (TSTP), or
* some other signal generator.
* we can get EBADF if we were in transparent
* mode, and the transcom process died.
* zero the bits (even though kernel does it)
* to make sure we are selecting on the right
# endif /* defined(TN3270) */
/* I don't like this, does it ever happen? */
printf("sleep(5) from telnet, after select\r\n");
#endif /* defined(unix) */
if (FD_ISSET(net
, &xbits
)) {
ttyflush(); /* flush already enqueued data */
* Something to read from the network...
if (FD_ISSET(net
, &ibits
)) {
canread
= sibuf
+ sizeof sibuf
- (sbp
+scc
);
#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
* In addition, in 4.2 (and...), a special protocol
* is needed to pick up the TCP Urgent data in
* 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
* 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").
ioctl(net
, SIOCATMARK
, (char *)&atmark
);
c
= recv(net
, sbp
+scc
, canread
, MSG_OOB
);
if ((c
== -1) && (errno
== EINVAL
)) {
c
= recv(net
, sbp
+scc
, canread
, 0);
if (clocks
.didnetreceive
< clocks
.gotDM
) {
SYNCHing
= stilloob(net
);
c
= recv(net
, sbp
+scc
, canread
, 0);
c
= recv(net
, sbp
+scc
, canread
, 0);
#else /* !defined(SO_OOBINLINE) */
c
= recv(net
, sbp
+scc
, canread
, 0);
#endif /* !defined(SO_OOBINLINE) */
if (c
< 0 && errno
== EWOULDBLOCK
) {
* Something to read from the tty...
if ((tcc
== 0) && NETROOM() && (shell_active
== 0) && TerminalCanRead())
#else /* defined(MSDOS) */
if (FD_ISSET(tin
, &ibits
))
#endif /* defined(MSDOS) */
tbp
= tibuf
; /* nothing left, reset */
c
= TerminalRead(tin
, tbp
, tibuf
+sizeof tibuf
- tbp
);
if (c
< 0 && errno
== EWOULDBLOCK
) {
/* EOF detection for line mode!!!! */
if (c
== 0 && MODE_LOCAL_CHARS(globalmode
)) {
#endif /* defined(unix) */
returnValue
= 1; /* did something useful */
c
= DataFromTerminal(tbp
, tcc
);
# endif /* defined(TN3270) */
c
= *tbp
++ & 0xff, sc
= strip(c
), tcc
--;
} else if (MODE_LINE(globalmode
) && (sc
== echoc
)) {
if (tcc
> 0 && strip(*tbp
) == echoc
) {
if (TerminalSpecialChars(sc
) == 0) {
* 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
)) {
# endif /* defined(TN3270) */
if ((!MODE_LINE(globalmode
) || flushline
) &&
FD_ISSET(net
, &obits
) && (NETBYTES() > 0)) {
returnValue
= netflush();
# else /* !defined(TN3270) */
returnValue
= Push3270();
# endif /* !defined(TN3270) */
#else /* defined(MSDOS) */
if (FD_ISSET(tout
, &obits
) && (TTYBYTES() > 0))
#endif /* defined(MSDOS) */
returnValue
= ttyflush();
* Select from tty and network...
#define SCHED_BLOCK 0 /* Don't block in MSDOS */
#else /* defined(MSDOS) */
#endif /* defined(MSDOS) */
#if defined(TN3270) && defined(unix)
#endif /* defined(TN3270) */
NetNonblockingIO(net
, 1);
if (noasynch
== 0) { /* DBX can't handle! */
#endif /* defined(TN3270) */
#if defined(SO_OOBINLINE) && !defined(MSDOS)
SetSockOpt(net
, SOL_SOCKET
, SO_OOBINLINE
, 1);
#endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
if (!myopts
[TELOPT_TTYPE
]) {
dooption(TELOPT_TTYPE
, 0);
# endif /* !defined(TN3270) */
if (Scheduler(SCHED_BLOCK
) == -1) {
# else /* !defined(TN3270) */
while (!In3270
&& !shell_active
) {
if (Scheduler(SCHED_BLOCK
) == -1) {
while ((schedValue
= Scheduler(0)) != 0) {
/* If there is data waiting to go out to terminal, don't
* schedule any more data for the terminal.
if (shell_continue() == 0) {
schedValue
= DoTerminalOutput();
if (schedValue
&& (shell_active
== 0)) {
if (Scheduler(SCHED_BLOCK
) == -1) {
# endif /* !defined(TN3270) */
* The following are data structures and routines for
char *name
; /* How user refers to it (case independent) */
int what
; /* Character to be sent (<0 ==> special) */
char *help
; /* Help information (0 ==> no help) */
int (*routine
)(); /* Routine to perform (for special ops) */
#else /* defined(NOT43) */
void (*routine
)(); /* Routine to perform (for special ops) */
#endif /* defined(NOT43) */
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" },
static struct sendlist Sendlist2
[] = { /* some synonyms */
{ "help", SENDQUESTION
, 0 },
struct sendlist
*c
= (struct sendlist
*) name
;
if ((sl
= (struct sendlist
*)
genget(name
, (char **) Sendlist
, getnextsend
)) != 0) {
return (struct sendlist
*)
genget(name
, (char **) Sendlist2
, getnextsend
);
int what
; /* what we are sending this time */
int count
; /* how many bytes we are going to need to send */
int question
= 0; /* was at least one argument a question */
struct sendlist
*s
; /* pointer to current command */
printf("need at least one argument for 'send' command\n");
printf("'send ?' for help\n");
* 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).
for (i
= 1; i
< argc
; i
++) {
printf("Unknown send argument '%s'\n'send ?' for help.\n",
} else if (s
== Ambiguous(struct sendlist
*)) {
printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
/* Now, do we have enough room? */
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");
/* 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");
switch (what
= s
->what
) {
for (s
= Sendlist
; s
->name
; s
++) {
* The following are the routines and data structures referred
* to by the arguments to the "toggle" command.
(SetSockOpt(net
, SOL_SOCKET
, SO_DEBUG
, debug
)) < 0) {
perror("setsockopt (SO_DEBUG)");
if (net
> 0 && SetSockOpt(net
, SOL_SOCKET
, SO_DEBUG
, 0, 0) < 0)
perror("setsockopt (SO_DEBUG)");
printf("Cannot turn off socket debugging\n");
if (myopts
[TELOPT_BINARY
] == 0) { /* Go into binary mode */
printoption("<SENT", doopt
, TELOPT_BINARY
, 0);
printoption("<SENT", doopt
, TELOPT_BINARY
, 0);
hisopts
[TELOPT_BINARY
] = myopts
[TELOPT_BINARY
] = 1;
printf("Negotiating binary mode with remote host.\n");
} else { /* Turn off binary mode */
printoption("<SENT", dont
, TELOPT_BINARY
, 0);
printoption("<SENT", dont
, TELOPT_BINARY
, 0);
hisopts
[TELOPT_BINARY
] = myopts
[TELOPT_BINARY
] = 0;
printf("Negotiating network ascii mode with remote host.\n");
char *name
; /* name of toggle */
char *help
; /* help message */
int (*handler
)(); /* routine to do actual setting */
int dohelp
; /* should we display help information */
static struct togglelist Togglelist
[] = {
"toggle flushing of output when sending interrupt characters",
"flush output when sending interrupt characters" },
"toggle automatic sending of interrupt characters in urgent mode",
"send interrupt characters in urgent mode" },
"toggle sending and receiving of binary data",
"send and receive network data in binary mode" },
"toggle mapping of received carriage returns",
"map carriage return on output" },
"toggle local recognition of certain control characters",
"recognize certain control characters" },
{ " ", "", 0, 1 }, /* empty line */
"(debugging) toggle debugging",
"turn on socket level debugging" },
"(debugging) toggle printing of hexadecimal network data",
"print hexadecimal representation of network traffic" },
"(debugging) toggle viewing of options processing",
"show option processing" },
{ " ", "", 0, 1 }, /* empty line */
"display help information",
"display help information",
for (c
= Togglelist
; c
->name
; c
++) {
printf("%s\t%s\n", c
->name
, c
->help
);
struct togglelist
*c
= (struct togglelist
*) name
;
static struct togglelist
*
return (struct togglelist
*)
genget(name
, (char **) Togglelist
, getnexttoggle
);
"Need an argument to 'toggle' command. 'toggle ?' for help.\n");
if (c
== Ambiguous(struct togglelist
*)) {
fprintf(stderr
, "'%s': ambiguous argument ('toggle ?' for help).\n",
fprintf(stderr
, "'%s': unknown argument ('toggle ?' for help).\n",
*c
->variable
= !*c
->variable
; /* invert it */
printf("%s %s.\n", *c
->variable
? "Will" : "Won't",
retval
&= (*c
->handler
)(c
);
* The following perform the "set" command.
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
},
#endif /* defined(unix) */
{ "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
},
#endif /* defined(MSDOS) */
struct setlist
*c
= (struct setlist
*)name
;
return (struct setlist
*) genget(name
, (char **) Setlist
, getnextset
);
/* XXX back we go... sigh */
((!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");
printf("Format is 'set Name Value'\n'set ?' for help.\n");
fprintf(stderr
, "'%s': unknown argument ('set ?' for help).\n",
} else if (ct
== Ambiguous(struct setlist
*)) {
fprintf(stderr
, "'%s': ambiguous argument ('set ?' for help).\n",
if (strcmp("off", argv
[2])) {
value
= special(argv
[2]);
printf("%s character is '%s'.\n", ct
->name
, control(*(ct
->charp
)));
* The following are the data structures and routines for the
if (hisopts
[TELOPT_SGA
]) {
wontoption(TELOPT_SGA
, 0);
if (hisopts
[TELOPT_ECHO
]) {
wontoption(TELOPT_ECHO
, 0);
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
if (!hisopts
[TELOPT_ECHO
]) {
willoption(TELOPT_ECHO
, 0);
static struct cmd Modelist
[] = {
{ "character", "character-at-a-time mode", docharmode
, 1, 1 },
{ "line", "line-by-line mode", dolinemode
, 1, 1 },
struct cmd
*c
= (struct cmd
*) name
;
return (struct cmd
*) genget(name
, (char **) Modelist
, getnextmode
);
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
);
mt
= getmodecmd(argv
[1]);
fprintf(stderr
, "Unknown mode '%s' ('mode ?' for help).\n", argv
[1]);
} else if (mt
== Ambiguous(struct cmd
*)) {
fprintf(stderr
, "Ambiguous mode '%s' ('mode ?' for help).\n", argv
[1]);
* The following data structures and routines implement the
#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
printf(" %s.\n", tl->actionexplanation); \
#define doset(sl) if (sl->name && *sl->name != ' ') { \
printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
for (tl
= Togglelist
; tl
->name
; tl
++) {
for (sl
= Setlist
; sl
->name
; sl
++) {
for (i
= 1; i
< argc
; i
++) {
if ((sl
== Ambiguous(struct setlist
*)) ||
(tl
== Ambiguous(struct togglelist
*))) {
printf("?Ambiguous argument '%s'.\n", argv
[i
]);
printf("?Unknown argument '%s'.\n", argv
[i
]);
* The following are the data structures, and many of the routines,
* relating to command processing.
* Set the escape character.
"Deprecated usage - please use 'set escape%s%s' in the future.\n",
(argc
> 2)? " ":"", (argc
> 2)? argv
[1]: "");
printf("new escape character: ");
printf("Escape character is '%s'.\n", control(escape
));
printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
printf("%s map carriage return on output.\n", crmod
? "Will" : "Won't");
#endif /* defined(unix) */
/* reget parameters in case they were changed */
int argc
; /* Number of arguments */
char *argv
[]; /* arguments */
printf("Connection closed.\n");
SetIn3270(); /* Get out of 3270 mode */
#endif /* defined(TN3270) */
if ((argc
!= 2) || (strcmp(argv
[1], "fromquit") != 0)) {
return 1; /* Keep lint, etc., happy */
(void) call(bye
, "bye", "fromquit", 0);
return 1; /* just to keep lint happy */
* Print status about the connection.
printf("Connected to %s.\n", hostname
);
printf("Operating in %s.\n",
modelist
[getconnmode()].modedescriptions
);
printf("Catching signals locally.\n");
printf("No connection.\n");
printf("Escape character is '%s'.\n", control(escape
));
# else /* !defined(TN3270) */
if ((!In3270
) && ((argc
< 2) || strcmp(argv
[1], "notmuch"))) {
printf("Escape character is '%s'.\n", control(escape
));
if (In3270
&& transcom
) {
printf("Transparent mode command is '%s'.\n", transcom
);
# endif /* defined(unix) */
# endif /* defined(TN3270) */
#if defined(TN3270) && defined(unix)
char *strcpy(), *strcat();
if (argc
== 1 && transcom
) {
for (i
= 1; i
< argc
; ++i
) {
len
+= 1 + strlen(argv
[1]);
(void) strcpy(transcom
, argv
[1]);
for (i
= 2; i
< argc
; ++i
) {
(void) strcat(transcom
, " ");
(void) strcat(transcom
, argv
[i
]);
#endif /* defined(TN3270) && defined(unix) */
register struct hostent
*host
= 0;
#endif /* defined(MSDOS) */
printf("?Already connected to %s\n", hostname
);
(void) strcpy(line
, "Connect ");
gets(&line
[strlen(line
)]);
if ((argc
< 2) || (argc
> 3)) {
printf("usage: %s host-name [port]\n", argv
[0]);
for (cp
= argv
[1]; *cp
; 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]);
host
= gethostbyname(argv
[1]);
sin
.sin_family
= host
->h_addrtype
;
#if defined(h_addr) /* In 4.3, this is a #define */
memcpy((caddr_t
)&sin
.sin_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
);
#endif /* defined(h_addr) */
printf("%s: unknown host\n", argv
[1]);
sin
.sin_port
= sp
->s_port
;
sin
.sin_port
= atoi(argv
[2]);
sp
= getservbyname(argv
[2], "tcp");
sin
.sin_port
= sp
->s_port
;
printf("%s: bad port number\n", argv
[2]);
sin
.sin_port
= atoi(argv
[2]);
sin
.sin_port
= htons(sin
.sin_port
);
signal(SIGPIPE
, deadpeer
);
#endif /* defined(unix) */
net
= socket(AF_INET
, SOCK_STREAM
, 0);
perror("telnet: socket");
if (debug
&& SetSockOpt(net
, SOL_SOCKET
, SO_DEBUG
, 1) < 0) {
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]) {
fprintf(stderr
, "telnet: connect to address %s: ",
inet_ntoa(sin
.sin_addr
));
memcpy((caddr_t
)&sin
.sin_addr
,
host
->h_addr_list
[0], host
->h_length
);
fprintf(stderr
, "Trying %s...\n",
inet_ntoa(sin
.sin_addr
));
#endif /* defined(h_addr) */
perror("telnet: Unable to connect to remote host");
signal(SIGQUIT
, SIG_DFL
);
#endif /* defined(unix) */
} while (connected
== 0);
call(status
, "status", "notmuch", 0);
if (setjmp(peerdied
) == 0)
ExitString(stderr
, "Connection closed by foreign host.\n",1);
#define HELPINDENT (sizeof ("connect"))
openhelp
[] = "connect to a site",
closehelp
[] = "close current connection",
quithelp
[] = "exit 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) */
zhelp
[] = "suspend telnet",
#endif /* defined(unix */
shellhelp
[] = "invoke a subshell",
#endif /* defined(TN3270) */
modehelp
[] = "try to enter line-by-line or character-at-a-time mode";
extern int help(), shell();
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 },
#endif /* defined(unix) */
{ "!", shellhelp
, shell
, 1, 1 },
#endif /* defined(TN3270) */
{ "?", helphelp
, help
, 1, 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 },
* Call routine with argc, argv set from args (terminated by 0).
for (argc
= 0, argp
= &args
; *argp
++ != 0; argc
++)
return (*routine
)(argc
, &args
);
struct cmd
*c
= (struct cmd
*) name
;
if ((cm
= (struct cmd
*) genget(name
, (char **) cmdtab
, getnextcmd
)) != 0) {
return (struct cmd
*) genget(name
, (char **) cmdtab2
, getnextcmd
);
signal(SIGQUIT
, SIG_DFL
);
#endif /* defined(unix) */
if (gets(line
) == NULL
) {
if (feof(stdin
) || ferror(stdin
))
if (c
== Ambiguous(struct cmd
*)) {
printf("?Ambiguous command\n");
printf("?Invalid command\n");
if (c
->needconnect
&& !connected
) {
printf("?Need to be connected first.\n");
if ((*c
->handler
)(margc
, margv
)) {
printf("Commands may be abbreviated. Commands are:\n\n");
for (c
= cmdtab
; c
->name
; c
++)
printf("%-*s\t%s\n", HELPINDENT
, c
->name
,
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
);
* main. Parse arguments, invoke the protocol or command parser.
tninit(); /* Clear out things */
autoflush
= TerminalAutoFlush();
while ((argc
> 1) && (argv
[1][0] == '-')) {
if (!strcmp(argv
[1], "-d")) {
} else if (!strcmp(argv
[1], "-n")) {
if ((argc
> 1) && (argv
[2][0] != '-')) { /* get file name */
NetTrace
= fopen(argv
[2], "w");
#if defined(TN3270) && defined(unix)
if (!strcmp(argv
[1], "-t")) {
if ((argc
> 1) && (argv
[2][0] != '-')) { /* get file name */
(void) strcpy(transcom
, argv
[1]);
} else if (!strcmp(argv
[1], "-noasynch")) {
#endif /* defined(TN3270) && defined(unix) */
if (argv
[1][1] != '\0') {
fprintf(stderr
, "Unknown option *%s*.\n", argv
[1]);
if (setjmp(toplevel
) != 0)
#else /* !defined(TN3270) */
#endif /* defined(TN3270) */
#endif /* !defined(TN3270) */