* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)telnet.c 5.16 (Berkeley) %G%";
* Many of the FUNCTIONAL changes in this newest version of telnet
* were suggested by Dave Borman of Cray Research, Inc.
* 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)
char ttyobuf
[2*BUFSIZ
], *tfrontp
= ttyobuf
, *tbackp
= ttyobuf
;
#define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
#define TTYLOC() (tfrontp)
#define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
#define TTYMIN() (netobuf)
#define TTYBYTES() (tfrontp-tbackp)
#define TTYROOM() (TTYMAX()-TTYLOC()+1)
char netobuf
[2*BUFSIZ
], *nfrontp
= netobuf
, *nbackp
= netobuf
;
#define NETADD(c) { *nfrontp++ = c; }
#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
#define NETLOC() (nfrontp)
#define NETMAX() (netobuf+sizeof netobuf-1)
#define NETBYTES() (nfrontp-nbackp)
#define NETROOM() (NETMAX()-NETLOC()+1)
char *neturg
= 0; /* one past last byte of urgent data */
char 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)) { \
char doopt
[] = { IAC
, DO
, '%', 'c', 0 };
char dont
[] = { IAC
, DONT
, '%', 'c', 0 };
char will
[] = { IAC
, WILL
, '%', 'c', 0 };
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? */
int SYNCHing
= 0; /* we are in TELNET SYNCH mode */
int flushout
= 0; /* flush output */
int autoflush
= 0; /* flush output when interrupting? */
int autosynch
= 0; /* send interrupt characters with SYNCH? */
int localchars
= 0; /* we recognize interrupt/quit */
int donelclchars
= 0; /* the user has set "localchars" */
int dontlecho
= 0; /* do we suppress local echoing right now? */
struct ltchars oltc
, nltc
;
struct sgttyb ottyb
, nttyb
;
* 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++
* Various utility routines.
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
; c
= (*next
)(c
)) {
for (q
= name
; *q
== *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.
* 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.
if ((n
= nfrontp
- nbackp
) > 0) {
n
= write(fd
, nbackp
, n
); /* 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(fd
, nbackp
, n
-1, 0); /* send URGENT all by itself */
n
= send(fd
, 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
));
bcopy(thisitem
, good
, length
);
thisitem
= nextitem(thisitem
);
nfrontp
= good
; /* next byte to be sent */
* Send as much data as possible to the terminal.
if ((n
= tfrontp
- tbackp
) > 0) {
if (!(SYNCHing
||flushout
)) {
n
= write(tout
, tbackp
, n
);
ioctl(fileno(stdout
), TIOCFLUSH
, (char *) 0);
/* we leave 'n' alone! */
tbackp
= tfrontp
= ttyobuf
;
* Various signal handling routines.
* 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
)
printf("%s ", direction
+1);
if (option
< (sizeof telopts
/sizeof telopts
[0]))
printf("%s %s", fmt
, telopts
[option
]);
printf("%s %d", fmt
, option
);
printf(" (%s)\r\n", what
? "reply" : "don't reply");
* Mode - set up terminal to a specific mode.
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 */
sb
.sg_flags
&= ~(ECHO
|CRMOD
);
sb
.sg_flags
|= ECHO
|CRMOD
;
sb
.sg_erase
= sb
.sg_kill
= -1;
* 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(fileno(stdin
), TIOCSLTC
, (char *)ltc
);
ioctl(fileno(stdin
), TIOCSETC
, (char *)tc
);
ioctl(fileno(stdin
), TIOCSETP
, (char *)&sb
);
ioctl(fileno(stdin
), FIONBIO
, (char *)&onoff
);
ioctl(fileno(stdout
), FIONBIO
, (char *)&onoff
);
signal(SIGTSTP
, doescape
);
signal(SIGTSTP
, SIG_DFL
);
sigsetmask(sigblock(0) & ~(1<<(SIGTSTP
-1)));
* These routines decides on what the mode should be (based on the values
* of various global variables).
char *modedescriptions
[] = {
"telnet command mode", /* 0 */
"character-at-a-time mode", /* 1 */
"character-at-a-time mode (local echo)", /* 2 */
"line-by-line mode (remote echo)", /* 3 */
"line-by-line mode", /* 4 */
"line-by-line mode (local echoing suppressed)", /* 5 */
static char newmode
[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
if (hisopts
[TELOPT_ECHO
]) {
if (hisopts
[TELOPT_SGA
]) {
if (dontlecho
&& (clocks
.echotoggle
> clocks
.modenegotiated
)) {
return newmode
[modeindex
];
char sibuf
[BUFSIZ
], *sbp
;
char tibuf
[BUFSIZ
], *tbp
;
* Select from tty and network...
fd_set ibits
, obits
, xbits
;
ioctl(net
, FIONBIO
, (char *)&on
);
#if defined(SO_OOBINLINE)
setsockopt(net
, SOL_SOCKET
, SO_OOBINLINE
, &on
, sizeof on
);
#endif /* defined(SO_OOBINLINE) */
if (!hisopts
[TELOPT_SGA
]) {
willoption(TELOPT_SGA
, 0);
if (!myopts
[TELOPT_TTYPE
]) {
dooption(TELOPT_TTYPE
, 0);
if (scc
< 0 && tcc
< 0) {
if (((globalmode
< 4) || flushline
) && NETBYTES()) {
if ((c
= select(16, &ibits
, &obits
, &xbits
,
(struct timeval
*)0)) < 1) {
* we can get EINTR if we are in line mode,
* and the user does an escape (TSTP), or
* some other signal generator.
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
;
#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
, sibuf
, canread
, MSG_OOB
);
if ((c
== -1) && (errno
== EINVAL
)) {
c
= read(net
, sibuf
, canread
);
if (clocks
.didnetreceive
< clocks
.gotDM
) {
SYNCHing
= stilloob(net
);
c
= read(net
, sibuf
, canread
);
c
= read(net
, sibuf
, canread
);
#else /* !defined(SO_OOBINLINE) */
c
= read(net
, sbp
, canread
);
#endif /* !defined(SO_OOBINLINE) */
if (c
< 0 && errno
== EWOULDBLOCK
) {
* Something to read from the tty...
if (FD_ISSET(tin
, &ibits
)) {
tbp
= tibuf
; /* nothing left, reset */
c
= read(tin
, tbp
, tibuf
+sizeof tibuf
- tbp
);
if (c
< 0 && errno
== EWOULDBLOCK
) {
/* EOF detection for line mode!!!! */
if (c
== 0 && globalmode
>= 3) {
c
= *tbp
++ & 0xff, sc
= strip(c
), tcc
--;
} else if ((globalmode
>= 4) && (sc
== echoc
)) {
if (tcc
> 0 && strip(*tbp
) == echoc
) {
} else if (sc
== ntc
.t_quitc
) {
} else if (sc
== nltc
.t_flushc
) {
} else if (globalmode
> 2) {
} else if (sc
== nttyb
.sg_kill
) {
} else if (sc
== nttyb
.sg_erase
) {
* If we are in CRMOD mode (\r ==> \n)
* on our local machine, then probably
* a newline (unix) is CRLF (TELNET).
if (((globalmode
< 4) || flushline
) &&
FD_ISSET(net
, &obits
) && (NETBYTES() > 0)) {
if (FD_ISSET(tout
, &obits
) && (TTYBYTES() > 0)) {
* Telnet receiver states for fsm
#define TS_SB 7 /* sub-option collection */
#define TS_SE 8 /* looking for sub-option end */
static int state
= TS_DATA
;
while ((scc
> 0) && (TTYROOM() > 2)) {
c
= *sbp
++ & 0xff, scc
--;
break; /* Ignore \0 after CR */
if (hisopts
[TELOPT_ECHO
] && !crmod
) {
* 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
);
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
);
suboption(); /* handle sub-option */
willoption(option
, reply
)
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
);
printoption("<SENT", fmt
, option
);
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
);
printoption("<SENT", fmt
, option
);
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
);
* Look at the sub-option buffer, and try to be helpful to the other
* Currently we recognize:
* Terminal type, send request.
switch (subbuffer
[0]&0xff) {
if ((subbuffer
[1]&0xff) != TELQUAL_SEND
) {
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
);
nfrontp
+= 4+strlen(namebuf
)+2;
* 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) */
netclear(); /* clear the path to the network */
neturg
= NETLOC()-1; /* Some systems are off by one XXX */
/* do printoption AFTER flush, otherwise the output gets tossed... */
printoption("<SENT", doopt
, TELOPT_TM
);
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" },
struct sendlist Sendlist2
[] = { /* some synonyms */
{ "help", SENDQUESTION
, 0 },
struct sendlist
*c
= (struct sendlist
*) name
;
if (sl
= (struct sendlist
*)
genget(name
, (char **) Sendlist
, getnextsend
)) {
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 hadsynch
; /* are we going to process a "synch"? */
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
]))) {
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
, (char *)&debug
, sizeof(debug
))
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");
char *name
; /* name of toggle */
char *help
; /* help message */
int (*handler
)(); /* routine to do actual setting */
int dohelp
; /* should we display help information */
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 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
;
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 */
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
},
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);
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");
/* reget parameters in case they were changed */
ioctl(0, TIOCGETP
, (char *)&ottyb
);
ioctl(0, TIOCGETC
, (char *)&otc
);
ioctl(0, TIOCGLTC
, (char *)&oltc
);
printf("Connection closed.\n");
for (op
= hisopts
; op
< &hisopts
[256]; op
++)
(void) call(bye
, "bye", 0);
* Print status about the connection.
printf("Connected to %s.\n", hostname
);
printf("Operating in %s.\n", modedescriptions
[getconnmode()]);
printf("Catching signals locally.\n");
printf("No connection.\n");
printf("Escape character is '%s'.\n", control(escape
));
register struct hostent
*host
= 0;
printf("?Already connected to %s\n", hostname
);
(void) strcpy(line
, "Connect ");
gets(&line
[strlen(line
)]);
printf("usage: %s host-name [port]\n", argv
[0]);
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
;
bcopy(host
->h_addr_list
[0], (caddr_t
)&sin
.sin_addr
,
bcopy(host
->h_addr
, (caddr_t
)&sin
.sin_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
);
net
= socket(AF_INET
, SOCK_STREAM
, 0);
perror("telnet: socket");
setsockopt(net
, SOL_SOCKET
, SO_DEBUG
,
(char *)&debug
, sizeof(debug
)) < 0)
if (debug
&& setsockopt(net
, SOL_SOCKET
, SO_DEBUG
, 0, 0) < 0)
perror("setsockopt (SO_DEBUG)");
if (connect(net
, (struct sockaddr
*)&sin
, sizeof (sin
)) < 0) {
if (host
&& host
->h_addr_list
[1]) {
"telnet: connect to address %s: ",
inet_ntoa(sin
.sin_addr
));
bcopy(host
->h_addr_list
[0],
(caddr_t
)&sin
.sin_addr
, host
->h_length
);
fprintf(stderr
, "Trying %s...\n",
inet_ntoa(sin
.sin_addr
));
perror("telnet: connect");
signal(SIGQUIT
, SIG_DFL
);
} while (connected
== 0);
call(status
, "status", "notmuch", 0);
if (setjmp(peerdied
) == 0)
fprintf(stderr
, "Connection closed by foreign host.\n");
#define HELPINDENT (sizeof ("connect"))
char openhelp
[] = "connect to a site";
char closehelp
[] = "close current connection";
char quithelp
[] = "exit telnet";
char zhelp
[] = "suspend telnet";
char statushelp
[] = "print status information";
char helphelp
[] = "print help information";
char sendhelp
[] = "transmit special characters ('send ?' for more)";
char sethelp
[] = "set operating parameters ('set ?' for more)";
char togglestring
[] ="toggle operating parameters ('toggle ?' for more)";
char displayhelp
[] = "display operating parameters";
"try to enter line-by-line or character-at-a-time mode";
{ "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 },
{ "z", zhelp
, suspend
, 1, 0 },
{ "?", helphelp
, help
, 1, 0 },
char crmodhelp
[] = "deprecated command -- use 'toggle crmod' instead";
char escapehelp
[] = "deprecated command -- use 'set escape' instead";
{ "help", helphelp
, help
, 0, 0 },
{ "escape", escapehelp
, setescape
, 1, 0 },
{ "crmod", crmodhelp
, togcrmod
, 1, 0 },
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
);
* Call routine with argc, argv set from args (terminated by 0).
for (argc
= 0, argp
= &args
; *argp
++ != 0; argc
++)
return (*routine
)(argc
, &args
);
register char **argp
= margv
;
while (*cp
!= '\0' && !isspace(*cp
))
struct cmd
*c
= (struct cmd
*) name
;
if (cm
= (struct cmd
*) genget(name
, (char **) cmdtab
, getnextcmd
)) {
return (struct cmd
*) genget(name
, (char **) cmdtab2
, getnextcmd
);
signal(SIGQUIT
, SIG_DFL
);
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
)) {
* main. Parse arguments, invoke the protocol or command parser.
sp
= getservbyname("telnet", "tcp");
fprintf(stderr
, "telnet: tcp/telnet: unknown service\n");
ioctl(0, TIOCGETP
, (char *)&ottyb
);
ioctl(0, TIOCGETC
, (char *)&otc
);
ioctl(0, TIOCGLTC
, (char *)&oltc
);
ioctl(0, TIOCLGET
, (char *)&autoflush
);
autoflush
= !(autoflush
&LNOFLSH
); /* if LNOFLSH, no autoflush */
setbuf(stdin
, (char *)0);
setbuf(stdout
, (char *)0);
if (argc
> 1 && !strcmp(argv
[1], "-d")) {
if (argc
> 1 && !strcmp(argv
[1], "-n")) {
if (argc
> 1) { /* get file name */
NetTrace
= fopen(argv
[1], "w");
if (setjmp(toplevel
) != 0)