Document distributed with 4.3bsd
[unix-history] / usr / src / usr.bin / telnet / telnet.c
CommitLineData
22e155fc
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1983 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
f18adf63 13#ifndef lint
5b80ec57 14static char sccsid[] = "@(#)telnet.c 5.15 (Berkeley) %G%";
22e155fc 15#endif not lint
f18adf63 16
a19db822
BJ
17/*
18 * User telnet program.
5b80ec57
GM
19 *
20 * Many of the FUNCTIONAL changes in this newest version of telnet
21 * were suggested by Dave Borman of Cray Research, Inc.
a19db822 22 */
5b80ec57 23
de3b21e8
SL
24#include <sys/types.h>
25#include <sys/socket.h>
fb8e28da 26#include <sys/ioctl.h>
040b6435 27#include <sys/time.h>
de3b21e8
SL
28
29#include <netinet/in.h>
30
9c1dab9e
SL
31#define TELOPTS
32#include <arpa/telnet.h>
f2bf20fe 33#include <arpa/inet.h>
9c1dab9e 34
a19db822
BJ
35#include <stdio.h>
36#include <ctype.h>
37#include <errno.h>
38#include <signal.h>
a19db822 39#include <setjmp.h>
9f005877 40#include <netdb.h>
f2bf20fe 41#include <strings.h>
de3b21e8 42
040b6435
GM
43
44
0ea293c6 45#ifndef FD_SETSIZE
040b6435
GM
46/*
47 * The following is defined just in case someone should want to run
48 * this telnet on a 4.2 system.
49 *
040b6435 50 */
040b6435 51
0ea293c6
GM
52#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
53#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
54#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
55#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
040b6435
GM
56
57#endif
58\f
792ffc71 59#define strip(x) ((x)&0x7f)
a19db822 60
792ffc71
GM
61char ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
62#define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
63#define TTYLOC() (tfrontp)
64#define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
65#define TTYMIN() (netobuf)
66#define TTYBYTES() (tfrontp-tbackp)
67#define TTYROOM() (TTYMAX()-TTYLOC()+1)
3e16c811 68
792ffc71 69char netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
3e16c811
GM
70#define NETADD(c) { *nfrontp++ = c; }
71#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
72#define NETLOC() (nfrontp)
792ffc71
GM
73#define NETMAX() (netobuf+sizeof netobuf-1)
74#define NETBYTES() (nfrontp-nbackp)
75#define NETROOM() (NETMAX()-NETLOC()+1)
3e16c811 76char *neturg = 0; /* one past last byte of urgent data */
a19db822 77
0ea293c6
GM
78char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */
79#define SB_CLEAR() subpointer = subbuffer;
80#define SB_TERM() subend = subpointer;
81#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
82 *subpointer++ = (c); \
83 }
84
a19db822
BJ
85char hisopts[256];
86char myopts[256];
87
88char doopt[] = { IAC, DO, '%', 'c', 0 };
89char dont[] = { IAC, DONT, '%', 'c', 0 };
90char will[] = { IAC, WILL, '%', 'c', 0 };
91char wont[] = { IAC, WONT, '%', 'c', 0 };
92
3e16c811
GM
93struct cmd {
94 char *name; /* command name */
95 char *help; /* help string */
96 int (*handler)(); /* routine which executes command */
97 int dohelp; /* Should we give general help information? */
98 int needconnect; /* Do we need to be connected to execute? */
99};
100
a19db822
BJ
101int connected;
102int net;
3e16c811 103int tout;
fb8e28da 104int showoptions = 0;
4971ba8c 105int debug = 0;
fb8e28da 106int crmod = 0;
3e16c811 107int netdata = 0;
238f35b1 108static FILE *NetTrace;
01ac0b83 109int telnetport = 1;
3e16c811 110
3e16c811 111
a19db822 112char *prompt;
fb8e28da 113char escape = CTRL(]);
40e8c2a3 114char echoc = CTRL(E);
f2bf20fe
GM
115
116int SYNCHing = 0; /* we are in TELNET SYNCH mode */
117int flushout = 0; /* flush output */
792ffc71 118int autoflush = 0; /* flush output when interrupting? */
f2bf20fe 119int autosynch = 0; /* send interrupt characters with SYNCH? */
3087f2df
GM
120int localchars = 0; /* we recognize interrupt/quit */
121int donelclchars = 0; /* the user has set "localchars" */
f2bf20fe 122int dontlecho = 0; /* do we suppress local echoing right now? */
a19db822
BJ
123
124char line[200];
125int margc;
126char *margv[20];
127
128jmp_buf toplevel;
129jmp_buf peerdied;
130
131extern int errno;
132
a19db822 133
fb8e28da 134struct sockaddr_in sin;
a19db822 135
a19db822 136struct cmd *getcmd();
9f005877 137struct servent *sp;
a19db822 138
40e8c2a3 139struct tchars otc, ntc;
792ffc71 140struct ltchars oltc, nltc;
40e8c2a3
GM
141struct sgttyb ottyb, nttyb;
142int globalmode = 0;
143int flushline = 1;
144
145char *hostname;
146char hnamebuf[32];
147
148/*
149 * The following are some clocks used to decide how to interpret
040b6435 150 * the relationship between various variables.
40e8c2a3
GM
151 */
152
153struct {
154 int
155 system, /* what the current time is */
156 echotoggle, /* last time user entered echo character */
040b6435
GM
157 modenegotiated, /* last time operating mode negotiated */
158 didnetreceive, /* last time we read data from network */
159 gotDM; /* when did we last see a data mark */
f2bf20fe 160} clocks;
40e8c2a3 161
f2bf20fe 162#define settimer(x) clocks.x = clocks.system++
40e8c2a3
GM
163\f
164/*
165 * Various utility routines.
166 */
3e16c811 167
f2bf20fe
GM
168char *ambiguous; /* special return value */
169#define Ambiguous(t) ((t)&ambiguous)
170
171
3e16c811
GM
172char **
173genget(name, table, next)
174char *name; /* name to match */
175char **table; /* name entry in table */
176char **(*next)(); /* routine to return next entry in table */
a19db822 177{
3e16c811
GM
178 register char *p, *q;
179 register char **c, **found;
180 register int nmatches, longest;
a19db822 181
3e16c811
GM
182 longest = 0;
183 nmatches = 0;
184 found = 0;
185 for (c = table; p = *c; c = (*next)(c)) {
186 for (q = name; *q == *p++; q++)
187 if (*q == 0) /* exact match? */
188 return (c);
189 if (!*q) { /* the name was a prefix */
190 if (q - name > longest) {
191 longest = q - name;
192 nmatches = 1;
193 found = c;
194 } else if (q - name == longest)
195 nmatches++;
de372207
SL
196 }
197 }
3e16c811 198 if (nmatches > 1)
f2bf20fe 199 return Ambiguous(char **);
3e16c811 200 return (found);
a19db822
BJ
201}
202
40e8c2a3
GM
203/*
204 * Make a character string into a number.
205 *
f2bf20fe 206 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
40e8c2a3
GM
207 */
208
209special(s)
210register char *s;
211{
212 register char c;
213 char b;
214
215 switch (*s) {
216 case '^':
217 b = *++s;
218 if (b == '?') {
792ffc71 219 c = b | 0x40; /* DEL */
40e8c2a3
GM
220 } else {
221 c = b & 0x1f;
222 }
223 break;
224 default:
225 c = *s;
226 break;
227 }
228 return c;
229}
f2bf20fe
GM
230
231/*
232 * Construct a control character sequence
233 * for a special character.
234 */
235char *
236control(c)
237 register int c;
238{
239 static char buf[3];
240
792ffc71 241 if (c == 0x7f)
f2bf20fe
GM
242 return ("^?");
243 if (c == '\377') {
244 return "off";
245 }
246 if (c >= 0x20) {
247 buf[0] = c;
248 buf[1] = 0;
249 } else {
250 buf[0] = '^';
251 buf[1] = '@'+c;
252 buf[2] = 0;
253 }
254 return (buf);
255}
0ea293c6
GM
256
257
258/*
259 * upcase()
260 *
261 * Upcase (in place) the argument.
262 */
263
264void
265upcase(argument)
266register char *argument;
267{
268 register int c;
269
270 while (c = *argument) {
271 if (islower(c)) {
272 *argument = toupper(c);
273 }
274 argument++;
275 }
276}
f2bf20fe
GM
277\f
278/*
279 * Check to see if any out-of-band data exists on a socket (for
280 * Telnet "synch" processing).
281 */
282
283int
284stilloob(s)
285int s; /* socket number */
286{
287 static struct timeval timeout = { 0 };
288 fd_set excepts;
289 int value;
290
291 do {
292 FD_ZERO(&excepts);
293 FD_SET(s, &excepts);
294 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
295 } while ((value == -1) && (errno = EINTR));
296
297 if (value < 0) {
298 perror("select");
299 quit();
300 }
301 if (FD_ISSET(s, &excepts)) {
302 return 1;
303 } else {
304 return 0;
305 }
306}
307
308
309/*
310 * netflush
311 * Send as much data as possible to the network,
312 * handling requests for urgent data.
313 */
314
315
316netflush(fd)
317{
318 int n;
319
320 if ((n = nfrontp - nbackp) > 0) {
321 if (!neturg) {
322 n = write(fd, nbackp, n); /* normal write */
323 } else {
324 n = neturg - nbackp;
325 /*
326 * In 4.2 (and 4.3) systems, there is some question about
327 * what byte in a sendOOB operation is the "OOB" data.
328 * To make ourselves compatible, we only send ONE byte
329 * out of band, the one WE THINK should be OOB (though
330 * we really have more the TCP philosophy of urgent data
331 * rather than the Unix philosophy of OOB data).
332 */
333 if (n > 1) {
334 n = send(fd, nbackp, n-1, 0); /* send URGENT all by itself */
335 } else {
336 n = send(fd, nbackp, n, MSG_OOB); /* URGENT data */
337 }
338 }
339 }
340 if (n < 0) {
341 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
342 setcommandmode();
343 perror(hostname);
344 close(fd);
345 neturg = 0;
346 longjmp(peerdied, -1);
347 /*NOTREACHED*/
348 }
349 n = 0;
350 }
351 if (netdata && n) {
352 Dump('>', nbackp, n);
353 }
354 nbackp += n;
355 if (nbackp >= neturg) {
356 neturg = 0;
357 }
358 if (nbackp == nfrontp) {
359 nbackp = nfrontp = netobuf;
360 }
361}
3087f2df
GM
362\f
363/*
364 * nextitem()
365 *
366 * Return the address of the next "item" in the TELNET data
367 * stream. This will be the address of the next character if
368 * the current address is a user data character, or it will
369 * be the address of the character following the TELNET command
370 * if the current address is a TELNET IAC ("I Am a Command")
371 * character.
372 */
373
374char *
375nextitem(current)
376char *current;
377{
378 if ((*current&0xff) != IAC) {
379 return current+1;
380 }
381 switch (*(current+1)&0xff) {
382 case DO:
383 case DONT:
384 case WILL:
385 case WONT:
386 return current+3;
387 case SB: /* loop forever looking for the SE */
388 {
389 register char *look = current+2;
390
391 for (;;) {
392 if ((*look++&0xff) == IAC) {
393 if ((*look++&0xff) == SE) {
394 return look;
395 }
396 }
397 }
398 }
399 default:
400 return current+2;
401 }
402}
403/*
404 * netclear()
405 *
406 * We are about to do a TELNET SYNCH operation. Clear
407 * the path to the network.
408 *
409 * Things are a bit tricky since we may have sent the first
410 * byte or so of a previous TELNET command into the network.
411 * So, we have to scan the network buffer from the beginning
412 * until we are up to where we want to be.
413 *
414 * A side effect of what we do, just to keep things
415 * simple, is to clear the urgent data pointer. The principal
416 * caller should be setting the urgent data pointer AFTER calling
417 * us in any case.
418 */
419
420netclear()
421{
422 register char *thisitem, *next;
423 char *good;
424#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
425 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
f2bf20fe 426
3087f2df
GM
427 thisitem = netobuf;
428
429 while ((next = nextitem(thisitem)) <= nbackp) {
430 thisitem = next;
431 }
432
433 /* Now, thisitem is first before/at boundary. */
434
435 good = netobuf; /* where the good bytes go */
436
437 while (nfrontp > thisitem) {
438 if (wewant(thisitem)) {
439 int length;
440
441 next = thisitem;
442 do {
443 next = nextitem(next);
444 } while (wewant(next) && (nfrontp > next));
445 length = next-thisitem;
446 bcopy(thisitem, good, length);
447 good += length;
448 thisitem = next;
449 } else {
450 thisitem = nextitem(thisitem);
451 }
452 }
453
454 nbackp = netobuf;
455 nfrontp = good; /* next byte to be sent */
456 neturg = 0;
457}
458\f
f2bf20fe
GM
459/*
460 * Send as much data as possible to the terminal.
461 */
462
463
464ttyflush()
465{
466 int n;
467
468 if ((n = tfrontp - tbackp) > 0) {
792ffc71 469 if (!(SYNCHing||flushout)) {
f2bf20fe
GM
470 n = write(tout, tbackp, n);
471 } else {
472 ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
792ffc71 473 /* we leave 'n' alone! */
f2bf20fe
GM
474 }
475 }
476 if (n < 0) {
477 return;
478 }
479 tbackp += n;
480 if (tbackp == tfrontp) {
481 tbackp = tfrontp = ttyobuf;
482 }
483}
40e8c2a3
GM
484\f
485/*
486 * Various signal handling routines.
487 */
488
489deadpeer()
490{
491 setcommandmode();
492 longjmp(peerdied, -1);
493}
494
495intr()
496{
3087f2df 497 if (localchars) {
40e8c2a3
GM
498 intp();
499 return;
500 }
501 setcommandmode();
502 longjmp(toplevel, -1);
503}
504
505intr2()
506{
3087f2df 507 if (localchars) {
40e8c2a3
GM
508 sendbrk();
509 return;
510 }
511}
512
513doescape()
514{
515 command(0);
516}
517\f
f2bf20fe
GM
518/*
519 * The following are routines used to print out debugging information.
520 */
521
522
523static
524Dump(direction, buffer, length)
525char direction;
526char *buffer;
527int length;
528{
529# define BYTES_PER_LINE 32
530# define min(x,y) ((x<y)? x:y)
531 char *pThis;
532 int offset;
533
534 offset = 0;
535
536 while (length) {
537 /* print one line */
538 fprintf(NetTrace, "%c 0x%x\t", direction, offset);
539 pThis = buffer;
540 buffer = buffer+min(length, BYTES_PER_LINE);
541 while (pThis < buffer) {
542 fprintf(NetTrace, "%.2x", (*pThis)&0xff);
543 pThis++;
544 }
545 fprintf(NetTrace, "\n");
546 length -= BYTES_PER_LINE;
547 offset += BYTES_PER_LINE;
548 if (length < 0) {
549 return;
550 }
551 /* find next unique line */
552 }
553}
554
555
556/*VARARGS*/
557printoption(direction, fmt, option, what)
558 char *direction, *fmt;
559 int option, what;
560{
561 if (!showoptions)
562 return;
0ea293c6 563 printf("%s ", direction+1);
f2bf20fe
GM
564 if (fmt == doopt)
565 fmt = "do";
566 else if (fmt == dont)
567 fmt = "dont";
568 else if (fmt == will)
569 fmt = "will";
570 else if (fmt == wont)
571 fmt = "wont";
572 else
573 fmt = "???";
0ea293c6 574 if (option < (sizeof telopts/sizeof telopts[0]))
f2bf20fe
GM
575 printf("%s %s", fmt, telopts[option]);
576 else
577 printf("%s %d", fmt, option);
578 if (*direction == '<') {
579 printf("\r\n");
580 return;
581 }
582 printf(" (%s)\r\n", what ? "reply" : "don't reply");
583}
584\f
40e8c2a3
GM
585/*
586 * Mode - set up terminal to a specific mode.
587 */
a19db822 588
a19db822 589
3e16c811
GM
590mode(f)
591 register int f;
a19db822 592{
3e16c811
GM
593 static int prevmode = 0;
594 struct tchars *tc;
595 struct ltchars *ltc;
596 struct sgttyb sb;
597 int onoff, old;
792ffc71
GM
598 struct tchars notc2;
599 struct ltchars noltc2;
600 static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
601 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
a19db822 602
40e8c2a3 603 globalmode = f;
3e16c811 604 if (prevmode == f)
f2bf20fe 605 return;
3e16c811
GM
606 old = prevmode;
607 prevmode = f;
40e8c2a3 608 sb = nttyb;
3e16c811 609 switch (f) {
a19db822 610
3e16c811
GM
611 case 0:
612 onoff = 0;
613 tc = &otc;
614 ltc = &oltc;
615 break;
a19db822 616
40e8c2a3
GM
617 case 1: /* remote character processing, remote echo */
618 case 2: /* remote character processing, local echo */
3e16c811
GM
619 sb.sg_flags |= CBREAK;
620 if (f == 1)
621 sb.sg_flags &= ~(ECHO|CRMOD);
622 else
623 sb.sg_flags |= ECHO|CRMOD;
624 sb.sg_erase = sb.sg_kill = -1;
625 tc = &notc;
40e8c2a3
GM
626 /*
627 * If user hasn't specified one way or the other,
628 * then default to not trapping signals.
629 */
3087f2df
GM
630 if (!donelclchars) {
631 localchars = 0;
792ffc71 632 }
3087f2df 633 if (localchars) {
40e8c2a3
GM
634 notc2 = notc;
635 notc2.t_intrc = ntc.t_intrc;
636 notc2.t_quitc = ntc.t_quitc;
40e8c2a3
GM
637 tc = &notc2;
638 } else
639 tc = &notc;
3e16c811
GM
640 ltc = &noltc;
641 onoff = 1;
40e8c2a3
GM
642 break;
643 case 3: /* local character processing, remote echo */
644 case 4: /* local character processing, local echo */
645 case 5: /* local character processing, no echo */
646 sb.sg_flags &= ~CBREAK;
647 sb.sg_flags |= CRMOD;
648 if (f == 4)
649 sb.sg_flags |= ECHO;
650 else
651 sb.sg_flags &= ~ECHO;
792ffc71
GM
652 notc2 = ntc;
653 tc = &notc2;
654 noltc2 = oltc;
655 ltc = &noltc2;
40e8c2a3
GM
656 /*
657 * If user hasn't specified one way or the other,
658 * then default to trapping signals.
659 */
3087f2df
GM
660 if (!donelclchars) {
661 localchars = 1;
792ffc71 662 }
3087f2df 663 if (localchars) {
792ffc71
GM
664 notc2.t_brkc = nltc.t_flushc;
665 noltc2.t_flushc = -1;
666 } else {
40e8c2a3 667 notc2.t_intrc = notc2.t_quitc = -1;
40e8c2a3 668 }
40e8c2a3
GM
669 noltc2.t_suspc = escape;
670 noltc2.t_dsuspc = -1;
40e8c2a3 671 onoff = 1;
3e16c811
GM
672 break;
673
674 default:
675 return;
a19db822 676 }
3e16c811
GM
677 ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
678 ioctl(fileno(stdin), TIOCSETC, (char *)tc);
679 ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
f2bf20fe
GM
680 ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
681 ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
40e8c2a3
GM
682 if (f >= 3)
683 signal(SIGTSTP, doescape);
684 else if (old >= 3) {
685 signal(SIGTSTP, SIG_DFL);
686 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
687 }
a19db822 688}
f2bf20fe 689\f
40e8c2a3
GM
690/*
691 * These routines decides on what the mode should be (based on the values
692 * of various global variables).
693 */
694
040b6435
GM
695char *modedescriptions[] = {
696 "telnet command mode", /* 0 */
697 "character-at-a-time mode", /* 1 */
698 "character-at-a-time mode (local echo)", /* 2 */
699 "line-by-line mode (remote echo)", /* 3 */
700 "line-by-line mode", /* 4 */
701 "line-by-line mode (local echoing suppressed)", /* 5 */
702};
703
704getconnmode()
40e8c2a3
GM
705{
706 static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
f2bf20fe 707 int modeindex = 0;
40e8c2a3
GM
708
709 if (hisopts[TELOPT_ECHO]) {
f2bf20fe 710 modeindex += 2;
40e8c2a3
GM
711 }
712 if (hisopts[TELOPT_SGA]) {
f2bf20fe 713 modeindex += 4;
40e8c2a3 714 }
f2bf20fe
GM
715 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
716 modeindex += 1;
40e8c2a3 717 }
f2bf20fe 718 return newmode[modeindex];
040b6435
GM
719}
720
721setconnmode()
722{
723 mode(getconnmode());
40e8c2a3
GM
724}
725
726
727setcommandmode()
728{
729 mode(0);
730}
731\f
3e16c811
GM
732char sibuf[BUFSIZ], *sbp;
733char tibuf[BUFSIZ], *tbp;
734int scc, tcc;
a19db822 735
792ffc71 736
a19db822 737/*
3e16c811 738 * Select from tty and network...
a19db822 739 */
3e16c811 740telnet()
a19db822
BJ
741{
742 register int c;
3e16c811 743 int tin = fileno(stdin);
a19db822 744 int on = 1;
3087f2df 745 fd_set ibits, obits, xbits;
a19db822 746
3e16c811 747 tout = fileno(stdout);
40e8c2a3 748 setconnmode();
792ffc71
GM
749 scc = 0;
750 tcc = 0;
3087f2df
GM
751 FD_ZERO(&ibits);
752 FD_ZERO(&obits);
753 FD_ZERO(&xbits);
754
f2bf20fe 755 ioctl(net, FIONBIO, (char *)&on);
0ea293c6
GM
756#if defined(SO_OOBINLINE)
757 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
758#endif /* defined(SO_OOBINLINE) */
759 if (telnetport) {
760 if (!hisopts[TELOPT_SGA]) {
761 willoption(TELOPT_SGA, 0);
762 }
763 if (!myopts[TELOPT_TTYPE]) {
764 dooption(TELOPT_TTYPE, 0);
765 }
3e16c811 766 }
a19db822 767 for (;;) {
40e8c2a3 768 if (scc < 0 && tcc < 0) {
a19db822 769 break;
40e8c2a3
GM
770 }
771
792ffc71 772 if (((globalmode < 4) || flushline) && NETBYTES()) {
40e8c2a3 773 FD_SET(net, &obits);
3e16c811 774 } else {
40e8c2a3 775 FD_SET(tin, &ibits);
3e16c811 776 }
792ffc71 777 if (TTYBYTES()) {
40e8c2a3
GM
778 FD_SET(tout, &obits);
779 } else {
780 FD_SET(net, &ibits);
781 }
f2bf20fe 782 if (!SYNCHing) {
40e8c2a3
GM
783 FD_SET(net, &xbits);
784 }
f2bf20fe
GM
785 if ((c = select(16, &ibits, &obits, &xbits,
786 (struct timeval *)0)) < 1) {
40e8c2a3
GM
787 if (c == -1) {
788 /*
789 * we can get EINTR if we are in line mode,
790 * and the user does an escape (TSTP), or
791 * some other signal generator.
792 */
793 if (errno == EINTR) {
794 continue;
795 }
796 }
a19db822
BJ
797 sleep(5);
798 continue;
799 }
800
3e16c811
GM
801 /*
802 * Any urgent data?
803 */
40e8c2a3 804 if (FD_ISSET(net, &xbits)) {
3087f2df 805 FD_CLR(net, &xbits);
f2bf20fe 806 SYNCHing = 1;
3e16c811
GM
807 ttyflush(); /* flush already enqueued data */
808 }
809
a19db822
BJ
810 /*
811 * Something to read from the network...
812 */
40e8c2a3 813 if (FD_ISSET(net, &ibits)) {
792ffc71
GM
814 int canread;
815
3087f2df 816 FD_CLR(net, &ibits);
792ffc71
GM
817 if (scc == 0) {
818 sbp = sibuf;
819 }
820 canread = sibuf + sizeof sibuf - sbp;
0ea293c6 821#if !defined(SO_OOBINLINE)
040b6435
GM
822 /*
823 * In 4.2 (and some early 4.3) systems, the
824 * OOB indication and data handling in the kernel
825 * is such that if two separate TCP Urgent requests
826 * come in, one byte of TCP data will be overlaid.
827 * This is fatal for Telnet, but we try to live
828 * with it.
829 *
830 * In addition, in 4.2 (and...), a special protocol
831 * is needed to pick up the TCP Urgent data in
832 * the correct sequence.
833 *
834 * What we do is: if we think we are in urgent
835 * mode, we look to see if we are "at the mark".
836 * If we are, we do an OOB receive. If we run
837 * this twice, we will do the OOB receive twice,
838 * but the second will fail, since the second
839 * time we were "at the mark", but there wasn't
840 * any data there (the kernel doesn't reset
841 * "at the mark" until we do a normal read).
842 * Once we've read the OOB data, we go ahead
843 * and do normal reads.
844 *
845 * There is also another problem, which is that
846 * since the OOB byte we read doesn't put us
847 * out of OOB state, and since that byte is most
848 * likely the TELNET DM (data mark), we would
f2bf20fe 849 * stay in the TELNET SYNCH (SYNCHing) state.
040b6435
GM
850 * So, clocks to the rescue. If we've "just"
851 * received a DM, then we test for the
852 * presence of OOB data when the receive OOB
853 * fails (and AFTER we did the normal mode read
854 * to clear "at the mark").
855 */
f2bf20fe 856 if (SYNCHing) {
040b6435
GM
857 int atmark;
858
f2bf20fe 859 ioctl(net, SIOCATMARK, (char *)&atmark);
040b6435 860 if (atmark) {
792ffc71
GM
861 c = recv(net, sibuf, canread, MSG_OOB);
862 if ((c == -1) && (errno == EINVAL)) {
863 c = read(net, sibuf, canread);
f2bf20fe
GM
864 if (clocks.didnetreceive < clocks.gotDM) {
865 SYNCHing = stilloob(net);
238f35b1 866 }
040b6435
GM
867 }
868 } else {
792ffc71 869 c = read(net, sibuf, canread);
040b6435
GM
870 }
871 } else {
792ffc71 872 c = read(net, sibuf, canread);
040b6435
GM
873 }
874 settimer(didnetreceive);
0ea293c6 875#else /* !defined(SO_OOBINLINE) */
792ffc71 876 c = read(net, sbp, canread);
0ea293c6 877#endif /* !defined(SO_OOBINLINE) */
792ffc71
GM
878 if (c < 0 && errno == EWOULDBLOCK) {
879 c = 0;
880 } else if (c <= 0) {
881 break;
040b6435 882 }
792ffc71
GM
883 if (netdata) {
884 Dump('<', sbp, c);
885 }
886 scc += c;
a19db822
BJ
887 }
888
889 /*
890 * Something to read from the tty...
891 */
40e8c2a3 892 if (FD_ISSET(tin, &ibits)) {
3087f2df 893 FD_CLR(tin, &ibits);
792ffc71
GM
894 if (tcc == 0) {
895 tbp = tibuf; /* nothing left, reset */
896 }
897 c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
898 if (c < 0 && errno == EWOULDBLOCK) {
899 c = 0;
0ea293c6
GM
900 } else {
901 /* EOF detection for line mode!!!! */
902 if (c == 0 && globalmode >= 3) {
903 /* must be an EOF... */
904 *tbp = ntc.t_eofc;
905 c = 1;
906 }
907 if (c <= 0) {
908 tcc = c;
909 break;
910 }
a19db822 911 }
792ffc71 912 tcc += c;
a19db822
BJ
913 }
914
915 while (tcc > 0) {
f2bf20fe 916 register int sc;
a19db822 917
792ffc71 918 if (NETROOM() < 2) {
40e8c2a3 919 flushline = 1;
a19db822 920 break;
40e8c2a3 921 }
f2bf20fe
GM
922 c = *tbp++ & 0xff, sc = strip(c), tcc--;
923 if (sc == escape) {
a19db822
BJ
924 command(0);
925 tcc = 0;
40e8c2a3 926 flushline = 1;
a19db822 927 break;
3087f2df 928 } else if ((globalmode >= 4) && (sc == echoc)) {
40e8c2a3
GM
929 if (tcc > 0 && strip(*tbp) == echoc) {
930 tbp++;
931 tcc--;
932 } else {
933 dontlecho = !dontlecho;
934 settimer(echotoggle);
935 setconnmode();
936 tcc = 0;
937 flushline = 1;
938 break;
939 }
940 }
3087f2df 941 if (localchars) {
f2bf20fe 942 if (sc == ntc.t_intrc) {
40e8c2a3
GM
943 intp();
944 break;
f2bf20fe 945 } else if (sc == ntc.t_quitc) {
40e8c2a3
GM
946 sendbrk();
947 break;
792ffc71
GM
948 } else if (sc == nltc.t_flushc) {
949 NET2ADD(IAC, AO);
950 if (autoflush) {
951 doflush();
952 }
953 break;
40e8c2a3
GM
954 } else if (globalmode > 2) {
955 ;
f2bf20fe 956 } else if (sc == nttyb.sg_kill) {
40e8c2a3
GM
957 NET2ADD(IAC, EL);
958 break;
f2bf20fe 959 } else if (sc == nttyb.sg_erase) {
40e8c2a3
GM
960 NET2ADD(IAC, EC);
961 break;
962 }
a19db822 963 }
40bf00b7
RC
964 switch (c) {
965 case '\n':
238f35b1
GM
966 /*
967 * If echoing is happening locally,
968 * then a newline (unix) is CRLF (TELNET).
969 */
3e16c811
GM
970 if (!hisopts[TELOPT_ECHO]) {
971 NETADD('\r');
972 }
973 NETADD('\n');
40e8c2a3 974 flushline = 1;
40bf00b7
RC
975 break;
976 case '\r':
3e16c811 977 NET2ADD('\r', '\0');
40e8c2a3 978 flushline = 1;
40bf00b7
RC
979 break;
980 case IAC:
3e16c811 981 NET2ADD(IAC, IAC);
238f35b1 982 break;
40bf00b7 983 default:
3e16c811 984 NETADD(c);
40bf00b7
RC
985 break;
986 }
a19db822 987 }
40e8c2a3 988 if (((globalmode < 4) || flushline) &&
792ffc71 989 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
3087f2df 990 FD_CLR(net, &obits);
3e16c811 991 netflush(net);
40e8c2a3 992 }
a19db822
BJ
993 if (scc > 0)
994 telrcv();
3087f2df
GM
995 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
996 FD_CLR(tout, &obits);
3e16c811 997 ttyflush();
3087f2df 998 }
a19db822 999 }
40e8c2a3 1000 setcommandmode();
a19db822 1001}
40e8c2a3 1002\f
a19db822
BJ
1003/*
1004 * Telnet receiver states for fsm
1005 */
1006#define TS_DATA 0
1007#define TS_IAC 1
1008#define TS_WILL 2
1009#define TS_WONT 3
1010#define TS_DO 4
1011#define TS_DONT 5
238f35b1 1012#define TS_CR 6
0ea293c6
GM
1013#define TS_SB 7 /* sub-option collection */
1014#define TS_SE 8 /* looking for sub-option end */
a19db822
BJ
1015
1016telrcv()
1017{
1018 register int c;
1019 static int state = TS_DATA;
1020
792ffc71 1021 while ((scc > 0) && (TTYROOM() > 2)) {
f2bf20fe 1022 c = *sbp++ & 0xff, scc--;
a19db822
BJ
1023 switch (state) {
1024
238f35b1
GM
1025 case TS_CR:
1026 state = TS_DATA;
792ffc71
GM
1027 if (c == '\0') {
1028 break; /* Ignore \0 after CR */
1029 } else if (c == '\n') {
1030 if (hisopts[TELOPT_ECHO] && !crmod) {
1031 TTYADD(c);
1032 }
1033 break;
238f35b1 1034 }
792ffc71 1035 /* Else, fall through */
238f35b1 1036
a19db822 1037 case TS_DATA:
fb8e28da 1038 if (c == IAC) {
a19db822 1039 state = TS_IAC;
fb8e28da
SL
1040 continue;
1041 }
792ffc71
GM
1042 /*
1043 * The 'crmod' hack (see following) is needed
1044 * since we can't * set CRMOD on output only.
1045 * Machines like MULTICS like to send \r without
1046 * \n; since we must turn off CRMOD to get proper
1047 * input, the mapping is done here (sigh).
1048 */
238f35b1
GM
1049 if (c == '\r') {
1050 if (scc > 0) {
f2bf20fe 1051 c = *sbp&0xff;
238f35b1
GM
1052 if (c == 0) {
1053 sbp++, scc--;
792ffc71 1054 /* a "true" CR */
3e16c811 1055 TTYADD('\r');
238f35b1
GM
1056 } else if (!hisopts[TELOPT_ECHO] &&
1057 (c == '\n')) {
1058 sbp++, scc--;
3e16c811 1059 TTYADD('\n');
238f35b1 1060 } else {
3e16c811 1061 TTYADD('\r');
792ffc71
GM
1062 if (crmod) {
1063 TTYADD('\n');
1064 }
238f35b1
GM
1065 }
1066 } else {
1067 state = TS_CR;
3e16c811 1068 TTYADD('\r');
792ffc71
GM
1069 if (crmod) {
1070 TTYADD('\n');
1071 }
238f35b1
GM
1072 }
1073 } else {
3e16c811 1074 TTYADD(c);
238f35b1 1075 }
a19db822
BJ
1076 continue;
1077
1078 case TS_IAC:
1079 switch (c) {
1080
1081 case WILL:
1082 state = TS_WILL;
1083 continue;
1084
1085 case WONT:
1086 state = TS_WONT;
1087 continue;
1088
1089 case DO:
1090 state = TS_DO;
1091 continue;
1092
1093 case DONT:
1094 state = TS_DONT;
1095 continue;
1096
1097 case DM:
3e16c811
GM
1098 /*
1099 * We may have missed an urgent notification,
1100 * so make sure we flush whatever is in the
1101 * buffer currently.
1102 */
f2bf20fe 1103 SYNCHing = 1;
3e16c811 1104 ttyflush();
f2bf20fe 1105 SYNCHing = stilloob(net);
040b6435 1106 settimer(gotDM);
a19db822
BJ
1107 break;
1108
1109 case NOP:
1110 case GA:
1111 break;
1112
0ea293c6
GM
1113 case SB:
1114 SB_CLEAR();
1115 state = TS_SB;
1116 continue;
1117
a19db822
BJ
1118 default:
1119 break;
1120 }
1121 state = TS_DATA;
1122 continue;
1123
1124 case TS_WILL:
0ea293c6 1125 printoption(">RCVD", will, c, !hisopts[c]);
40e8c2a3
GM
1126 if (c == TELOPT_TM) {
1127 if (flushout) {
f2bf20fe 1128 flushout = 0;
40e8c2a3
GM
1129 }
1130 } else if (!hisopts[c]) {
0ea293c6 1131 willoption(c, 1);
40e8c2a3 1132 }
a19db822
BJ
1133 state = TS_DATA;
1134 continue;
1135
1136 case TS_WONT:
0ea293c6 1137 printoption(">RCVD", wont, c, hisopts[c]);
40e8c2a3
GM
1138 if (c == TELOPT_TM) {
1139 if (flushout) {
f2bf20fe 1140 flushout = 0;
40e8c2a3
GM
1141 }
1142 } else if (hisopts[c]) {
0ea293c6 1143 wontoption(c, 1);
40e8c2a3 1144 }
a19db822
BJ
1145 state = TS_DATA;
1146 continue;
1147
1148 case TS_DO:
0ea293c6 1149 printoption(">RCVD", doopt, c, !myopts[c]);
a19db822
BJ
1150 if (!myopts[c])
1151 dooption(c);
1152 state = TS_DATA;
1153 continue;
1154
1155 case TS_DONT:
0ea293c6 1156 printoption(">RCVD", dont, c, myopts[c]);
a19db822
BJ
1157 if (myopts[c]) {
1158 myopts[c] = 0;
1159 sprintf(nfrontp, wont, c);
a50d5753 1160 nfrontp += sizeof (wont) - 2;
40e8c2a3
GM
1161 flushline = 1;
1162 setconnmode(); /* set new tty mode (maybe) */
0ea293c6 1163 printoption(">SENT", wont, c);
a19db822
BJ
1164 }
1165 state = TS_DATA;
1166 continue;
0ea293c6
GM
1167 case TS_SB:
1168 if (c == IAC) {
1169 state = TS_SE;
1170 } else {
1171 SB_ACCUM(c);
1172 }
1173 continue;
1174
1175 case TS_SE:
1176 if (c != SE) {
1177 if (c != IAC) {
1178 SB_ACCUM(IAC);
1179 }
1180 SB_ACCUM(c);
1181 state = TS_SB;
1182 } else {
1183 SB_TERM();
1184 suboption(); /* handle sub-option */
1185 state = TS_DATA;
1186 }
a19db822
BJ
1187 }
1188 }
1189}
40e8c2a3 1190\f
0ea293c6
GM
1191willoption(option, reply)
1192 int option, reply;
a19db822
BJ
1193{
1194 char *fmt;
1195
1196 switch (option) {
1197
1198 case TELOPT_ECHO:
a19db822 1199 case TELOPT_SGA:
40e8c2a3 1200 settimer(modenegotiated);
a19db822
BJ
1201 hisopts[option] = 1;
1202 fmt = doopt;
40e8c2a3 1203 setconnmode(); /* possibly set new tty mode */
a19db822
BJ
1204 break;
1205
1206 case TELOPT_TM:
40e8c2a3 1207 return; /* Never reply to TM will's/wont's */
a19db822
BJ
1208
1209 default:
1210 fmt = dont;
1211 break;
1212 }
de372207 1213 sprintf(nfrontp, fmt, option);
a50d5753 1214 nfrontp += sizeof (dont) - 2;
0ea293c6
GM
1215 if (reply)
1216 printoption(">SENT", fmt, option);
1217 else
1218 printoption("<SENT", fmt, option);
a19db822
BJ
1219}
1220
0ea293c6
GM
1221wontoption(option, reply)
1222 int option, reply;
a19db822
BJ
1223{
1224 char *fmt;
1225
1226 switch (option) {
1227
1228 case TELOPT_ECHO:
a19db822 1229 case TELOPT_SGA:
40e8c2a3 1230 settimer(modenegotiated);
a19db822
BJ
1231 hisopts[option] = 0;
1232 fmt = dont;
40e8c2a3 1233 setconnmode(); /* Set new tty mode */
a19db822
BJ
1234 break;
1235
40e8c2a3
GM
1236 case TELOPT_TM:
1237 return; /* Never reply to TM will's/wont's */
1238
a19db822
BJ
1239 default:
1240 fmt = dont;
1241 }
1242 sprintf(nfrontp, fmt, option);
a50d5753 1243 nfrontp += sizeof (doopt) - 2;
0ea293c6
GM
1244 if (reply)
1245 printoption(">SENT", fmt, option);
1246 else
1247 printoption("<SENT", fmt, option);
a19db822
BJ
1248}
1249
1250dooption(option)
1251 int option;
1252{
1253 char *fmt;
1254
1255 switch (option) {
1256
1257 case TELOPT_TM:
680e328e 1258 fmt = will;
680e328e 1259 break;
a19db822 1260
0ea293c6 1261 case TELOPT_TTYPE: /* terminal type option */
40e8c2a3 1262 case TELOPT_SGA: /* no big deal */
a19db822 1263 fmt = will;
40e8c2a3 1264 myopts[option] = 1;
a19db822
BJ
1265 break;
1266
40e8c2a3 1267 case TELOPT_ECHO: /* We're never going to echo... */
a19db822
BJ
1268 default:
1269 fmt = wont;
1270 break;
1271 }
1272 sprintf(nfrontp, fmt, option);
a50d5753 1273 nfrontp += sizeof (doopt) - 2;
0ea293c6
GM
1274 printoption(">SENT", fmt, option);
1275}
1276
1277/*
1278 * suboption()
1279 *
1280 * Look at the sub-option buffer, and try to be helpful to the other
1281 * side.
1282 *
1283 * Currently we recognize:
1284 *
1285 * Terminal type, send request.
1286 */
1287
1288suboption()
1289{
1290 switch (subbuffer[0]&0xff) {
1291 case TELOPT_TTYPE:
1292 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1293 ;
1294 } else {
1295 char *name;
1296 char namebuf[41];
1297 char *getenv();
1298 int len;
1299
1300 name = getenv("TERM");
1301 if ((name == 0) || ((len = strlen(name)) > 40)) {
1302 name = "UNKNOWN";
1303 }
1304 if ((len + 4+2) < NETROOM()) {
1305 strcpy(namebuf, name);
1306 upcase(namebuf);
1307 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1308 TELQUAL_IS, namebuf, IAC, SE);
1309 nfrontp += 4+strlen(namebuf)+2;
1310 }
1311 }
1312
1313 default:
1314 break;
1315 }
a19db822 1316}
40e8c2a3 1317\f
3e16c811
GM
1318/*
1319 * The following are data structures and routines for
1320 * the "send" command.
1321 *
1322 */
1323
1324struct sendlist {
1325 char *name; /* How user refers to it (case independent) */
1326 int what; /* Character to be sent (<0 ==> special) */
1327 char *help; /* Help information (0 ==> no help) */
1328 int (*routine)(); /* Routine to perform (for special ops) */
1329};
1330
f2bf20fe 1331/*ARGSUSED*/
3e16c811
GM
1332dosynch(s)
1333struct sendlist *s;
1334{
3087f2df 1335 netclear(); /* clear the path to the network */
3e16c811 1336 NET2ADD(IAC, DM);
f2bf20fe 1337 neturg = NETLOC()-1; /* Some systems are off by one XXX */
3e16c811
GM
1338}
1339
792ffc71
GM
1340doflush()
1341{
792ffc71
GM
1342 NET2ADD(IAC, DO);
1343 NETADD(TELOPT_TM);
792ffc71
GM
1344 flushline = 1;
1345 flushout = 1;
1346 ttyflush();
0ea293c6
GM
1347 /* do printoption AFTER flush, otherwise the output gets tossed... */
1348 printoption("<SENT", doopt, TELOPT_TM);
792ffc71
GM
1349}
1350
3e16c811
GM
1351intp()
1352{
40e8c2a3 1353 NET2ADD(IAC, IP);
792ffc71
GM
1354 if (autoflush) {
1355 doflush();
1356 }
1357 if (autosynch) {
1358 dosynch();
1359 }
40e8c2a3
GM
1360}
1361
1362sendbrk()
1363{
f2bf20fe 1364 NET2ADD(IAC, BREAK);
792ffc71
GM
1365 if (autoflush) {
1366 doflush();
1367 }
1368 if (autosynch) {
1369 dosynch();
1370 }
3e16c811
GM
1371}
1372
1373
1374#define SENDQUESTION -1
3e16c811
GM
1375#define SENDESCAPE -3
1376
1377struct sendlist Sendlist[] = {
3e16c811 1378 { "ao", AO, "Send Telnet Abort output" },
3e16c811 1379 { "ayt", AYT, "Send Telnet 'Are You There'" },
3087f2df 1380 { "brk", BREAK, "Send Telnet Break" },
3e16c811
GM
1381 { "ec", EC, "Send Telnet Erase Character" },
1382 { "el", EL, "Send Telnet Erase Line" },
3087f2df 1383 { "escape", SENDESCAPE, "Send current escape character" },
3e16c811 1384 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
3087f2df 1385 { "ip", IP, "Send Telnet Interrupt Process" },
3e16c811 1386 { "nop", NOP, "Send Telnet 'No operation'" },
3087f2df 1387 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
3e16c811 1388 { "?", SENDQUESTION, "Display send options" },
3e16c811
GM
1389 { 0 }
1390};
1391
3087f2df
GM
1392struct sendlist Sendlist2[] = { /* some synonyms */
1393 { "break", BREAK, 0 },
1394
1395 { "intp", IP, 0 },
1396 { "interrupt", IP, 0 },
1397 { "intr", IP, 0 },
1398
1399 { "help", SENDQUESTION, 0 },
1400
1401 { 0 }
1402};
1403
3e16c811
GM
1404char **
1405getnextsend(name)
1406char *name;
1407{
1408 struct sendlist *c = (struct sendlist *) name;
1409
1410 return (char **) (c+1);
1411}
1412
1413struct sendlist *
1414getsend(name)
1415char *name;
1416{
3087f2df
GM
1417 struct sendlist *sl;
1418
1419 if (sl = (struct sendlist *)
1420 genget(name, (char **) Sendlist, getnextsend)) {
1421 return sl;
1422 } else {
1423 return (struct sendlist *)
1424 genget(name, (char **) Sendlist2, getnextsend);
1425 }
3e16c811
GM
1426}
1427
1428sendcmd(argc, argv)
1429int argc;
1430char **argv;
1431{
1432 int what; /* what we are sending this time */
1433 int count; /* how many bytes we are going to need to send */
1434 int hadsynch; /* are we going to process a "synch"? */
1435 int i;
3087f2df 1436 int question = 0; /* was at least one argument a question */
3e16c811
GM
1437 struct sendlist *s; /* pointer to current command */
1438
1439 if (argc < 2) {
1440 printf("need at least one argument for 'send' command\n");
1441 printf("'send ?' for help\n");
3087f2df 1442 return 0;
3e16c811
GM
1443 }
1444 /*
1445 * First, validate all the send arguments.
1446 * In addition, we see how much space we are going to need, and
1447 * whether or not we will be doing a "SYNCH" operation (which
1448 * flushes the network queue).
1449 */
1450 count = 0;
1451 hadsynch = 0;
1452 for (i = 1; i < argc; i++) {
1453 s = getsend(argv[i]);
1454 if (s == 0) {
1455 printf("Unknown send argument '%s'\n'send ?' for help.\n",
1456 argv[i]);
3087f2df 1457 return 0;
f2bf20fe 1458 } else if (s == Ambiguous(struct sendlist *)) {
3e16c811
GM
1459 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
1460 argv[i]);
3087f2df 1461 return 0;
3e16c811
GM
1462 }
1463 switch (s->what) {
1464 case SENDQUESTION:
3e16c811
GM
1465 break;
1466 case SENDESCAPE:
1467 count += 1;
1468 break;
1469 case SYNCH:
1470 hadsynch = 1;
1471 count += 2;
1472 break;
1473 default:
1474 count += 2;
1475 break;
1476 }
1477 }
1478 /* Now, do we have enough room? */
792ffc71 1479 if (NETROOM() < count) {
3e16c811
GM
1480 printf("There is not enough room in the buffer TO the network\n");
1481 printf("to process your request. Nothing will be done.\n");
1482 printf("('send synch' will throw away most data in the network\n");
1483 printf("buffer, if this might help.)\n");
3087f2df 1484 return 0;
3e16c811
GM
1485 }
1486 /* OK, they are all OK, now go through again and actually send */
1487 for (i = 1; i < argc; i++) {
1488 if (!(s = getsend(argv[i]))) {
1489 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
1490 quit();
1491 /*NOTREACHED*/
1492 }
1493 if (s->routine) {
1494 (*s->routine)(s);
1495 } else {
1496 switch (what = s->what) {
3087f2df
GM
1497 case SYNCH:
1498 dosynch();
1499 break;
3e16c811 1500 case SENDQUESTION:
3e16c811 1501 for (s = Sendlist; s->name; s++) {
3087f2df 1502 if (s->help) {
3e16c811
GM
1503 printf(s->name);
1504 if (s->help) {
1505 printf("\t%s", s->help);
1506 }
1507 printf("\n");
1508 }
1509 }
3087f2df 1510 question = 1;
3e16c811
GM
1511 break;
1512 case SENDESCAPE:
1513 NETADD(escape);
1514 break;
1515 default:
1516 NET2ADD(IAC, what);
1517 break;
1518 }
1519 }
1520 }
3087f2df 1521 return !question;
3e16c811
GM
1522}
1523\f
1524/*
1525 * The following are the routines and data structures referred
1526 * to by the arguments to the "toggle" command.
1527 */
1528
3087f2df 1529lclchars()
40e8c2a3 1530{
3087f2df
GM
1531 donelclchars = 1;
1532 return 1;
40e8c2a3
GM
1533}
1534
040b6435 1535togdebug()
3e16c811 1536{
0ea293c6 1537#ifndef NOT43
40e8c2a3 1538 if (net > 0 &&
f2bf20fe
GM
1539 setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
1540 < 0) {
40e8c2a3 1541 perror("setsockopt (SO_DEBUG)");
f2bf20fe 1542 }
0ea293c6
GM
1543#else NOT43
1544 if (debug) {
1545 if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
1546 perror("setsockopt (SO_DEBUG)");
1547 } else
1548 printf("Cannot turn off socket debugging\n");
1549#endif NOT43
3087f2df 1550 return 1;
3e16c811
GM
1551}
1552
3e16c811 1553
3e16c811
GM
1554
1555int togglehelp();
1556
040b6435
GM
1557struct togglelist {
1558 char *name; /* name of toggle */
1559 char *help; /* help message */
f2bf20fe 1560 int (*handler)(); /* routine to do actual setting */
040b6435
GM
1561 int dohelp; /* should we display help information */
1562 int *variable;
1563 char *actionexplanation;
1564};
1565
1566struct togglelist Togglelist[] = {
3087f2df
GM
1567 { "autoflush",
1568 "toggle flushing of output when sending interrupt characters",
f2bf20fe 1569 0,
040b6435 1570 1,
3087f2df
GM
1571 &autoflush,
1572 "flush output when sending interrupt characters" },
f2bf20fe
GM
1573 { "autosynch",
1574 "toggle automatic sending of interrupt characters in urgent mode",
1575 0,
1576 1,
1577 &autosynch,
1578 "send interrupt characters in urgent mode" },
040b6435 1579 { "crmod",
3087f2df 1580 "toggle mapping of received carriage returns",
f2bf20fe 1581 0,
040b6435
GM
1582 1,
1583 &crmod,
1584 "map carriage return on output" },
3087f2df
GM
1585 { "localchars",
1586 "toggle local recognition of certain control characters",
1587 lclchars,
1588 1,
1589 &localchars,
1590 "recognize certain control characters" },
40e8c2a3 1591 { " ", "", 0, 1 }, /* empty line */
040b6435
GM
1592 { "debug",
1593 "(debugging) toggle debugging",
1594 togdebug,
1595 1,
1596 &debug,
1597 "turn on socket level debugging" },
040b6435
GM
1598 { "netdata",
1599 "(debugging) toggle printing of hexadecimal network data",
f2bf20fe 1600 0,
040b6435
GM
1601 1,
1602 &netdata,
1603 "print hexadecimal representation of network traffic" },
3087f2df
GM
1604 { "options",
1605 "(debugging) toggle viewing of options processing",
1606 0,
1607 1,
1608 &showoptions,
1609 "show option processing" },
1610 { " ", "", 0, 1 }, /* empty line */
040b6435
GM
1611 { "?",
1612 "display help information",
1613 togglehelp,
1614 1 },
1615 { "help",
1616 "display help information",
1617 togglehelp,
1618 0 },
3e16c811
GM
1619 { 0 }
1620};
1621
1622togglehelp()
1623{
040b6435 1624 struct togglelist *c;
3e16c811 1625
040b6435 1626 for (c = Togglelist; c->name; c++) {
3e16c811
GM
1627 if (c->dohelp) {
1628 printf("%s\t%s\n", c->name, c->help);
1629 }
1630 }
3087f2df 1631 return 0;
3e16c811
GM
1632}
1633
1634char **
1635getnexttoggle(name)
1636char *name;
1637{
040b6435 1638 struct togglelist *c = (struct togglelist *) name;
3e16c811
GM
1639
1640 return (char **) (c+1);
1641}
1642
040b6435 1643struct togglelist *
3e16c811
GM
1644gettoggle(name)
1645char *name;
1646{
040b6435
GM
1647 return (struct togglelist *)
1648 genget(name, (char **) Togglelist, getnexttoggle);
3e16c811
GM
1649}
1650
1651toggle(argc, argv)
1652int argc;
1653char *argv[];
1654{
3087f2df 1655 int retval = 1;
3e16c811 1656 char *name;
040b6435 1657 struct togglelist *c;
3e16c811
GM
1658
1659 if (argc < 2) {
1660 fprintf(stderr,
1661 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
3087f2df 1662 return 0;
3e16c811
GM
1663 }
1664 argc--;
1665 argv++;
1666 while (argc--) {
1667 name = *argv++;
1668 c = gettoggle(name);
f2bf20fe 1669 if (c == Ambiguous(struct togglelist *)) {
3e16c811
GM
1670 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
1671 name);
3087f2df 1672 return 0;
3e16c811
GM
1673 } else if (c == 0) {
1674 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
1675 name);
3087f2df 1676 return 0;
3e16c811 1677 } else {
f2bf20fe
GM
1678 if (c->variable) {
1679 *c->variable = !*c->variable; /* invert it */
1680 printf("%s %s.\n", *c->variable? "Will" : "Won't",
1681 c->actionexplanation);
1682 }
1683 if (c->handler) {
3087f2df 1684 retval &= (*c->handler)(c);
f2bf20fe 1685 }
3e16c811
GM
1686 }
1687 }
3087f2df 1688 return retval;
3e16c811
GM
1689}
1690\f
40e8c2a3
GM
1691/*
1692 * The following perform the "set" command.
1693 */
1694
040b6435
GM
1695struct setlist {
1696 char *name; /* name */
40e8c2a3
GM
1697 char *help; /* help information */
1698 char *charp; /* where it is located at */
1699};
1700
040b6435 1701struct setlist Setlist[] = {
40e8c2a3
GM
1702 { "echo", "character to toggle local echoing on/off", &echoc },
1703 { "escape", "character to escape back to telnet command mode", &escape },
3087f2df
GM
1704 { " ", "" },
1705 { " ", "The following need 'localchars' to be toggled true", 0 },
40e8c2a3 1706 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
3087f2df
GM
1707 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
1708 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
40e8c2a3 1709 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
3087f2df 1710 { "quit", "character to cause a Break", &ntc.t_quitc },
0ea293c6 1711 { "eof", "character to cause an EOF ", &ntc.t_eofc },
40e8c2a3
GM
1712 { 0 }
1713};
1714
1715char **
040b6435 1716getnextset(name)
40e8c2a3
GM
1717char *name;
1718{
040b6435 1719 struct setlist *c = (struct setlist *)name;
40e8c2a3
GM
1720
1721 return (char **) (c+1);
1722}
1723
040b6435
GM
1724struct setlist *
1725getset(name)
40e8c2a3
GM
1726char *name;
1727{
040b6435 1728 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
40e8c2a3
GM
1729}
1730
1731setcmd(argc, argv)
1732int argc;
1733char *argv[];
1734{
1735 int value;
040b6435 1736 struct setlist *ct;
40e8c2a3
GM
1737
1738 /* XXX back we go... sigh */
1739 if (argc != 3) {
3087f2df
GM
1740 if ((argc == 2) &&
1741 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
1742 for (ct = Setlist; ct->name; ct++) {
1743 printf("%s\t%s\n", ct->name, ct->help);
1744 }
1745 printf("?\tdisplay help information\n");
1746 } else {
1747 printf("Format is 'set Name Value'\n'set ?' for help.\n");
40e8c2a3 1748 }
3087f2df 1749 return 0;
40e8c2a3
GM
1750 }
1751
040b6435 1752 ct = getset(argv[1]);
40e8c2a3
GM
1753 if (ct == 0) {
1754 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
1755 argv[1]);
3087f2df 1756 return 0;
f2bf20fe 1757 } else if (ct == Ambiguous(struct setlist *)) {
40e8c2a3
GM
1758 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
1759 argv[1]);
3087f2df 1760 return 0;
40e8c2a3
GM
1761 } else {
1762 if (strcmp("off", argv[2])) {
1763 value = special(argv[2]);
1764 } else {
1765 value = -1;
1766 }
1767 *(ct->charp) = value;
040b6435 1768 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
40e8c2a3 1769 }
3087f2df 1770 return 1;
40e8c2a3
GM
1771}
1772\f
1773/*
1774 * The following are the data structures and routines for the
1775 * 'mode' command.
1776 */
1777
1778dolinemode()
1779{
1780 if (hisopts[TELOPT_SGA]) {
0ea293c6 1781 wontoption(TELOPT_SGA, 0);
40e8c2a3
GM
1782 }
1783 if (hisopts[TELOPT_ECHO]) {
0ea293c6 1784 wontoption(TELOPT_ECHO, 0);
40e8c2a3
GM
1785 }
1786}
1787
1788docharmode()
1789{
1790 if (!hisopts[TELOPT_SGA]) {
0ea293c6 1791 willoption(TELOPT_SGA, 0);
40e8c2a3
GM
1792 }
1793 if (!hisopts[TELOPT_ECHO]) {
0ea293c6 1794 willoption(TELOPT_ECHO, 0);
40e8c2a3
GM
1795 }
1796}
1797
1798struct cmd Modelist[] = {
40e8c2a3 1799 { "character", "character-at-a-time mode", docharmode, 1, 1 },
3087f2df 1800 { "line", "line-by-line mode", dolinemode, 1, 1 },
40e8c2a3
GM
1801 { 0 },
1802};
1803
1804char **
1805getnextmode(name)
1806char *name;
1807{
1808 struct cmd *c = (struct cmd *) name;
1809
1810 return (char **) (c+1);
1811}
1812
1813struct cmd *
1814getmodecmd(name)
1815char *name;
1816{
1817 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
1818}
1819
1820modecmd(argc, argv)
1821int argc;
1822char *argv[];
1823{
1824 struct cmd *mt;
1825
1826 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
1827 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
1828 for (mt = Modelist; mt->name; mt++) {
1829 printf("%s\t%s\n", mt->name, mt->help);
1830 }
3087f2df 1831 return 0;
40e8c2a3
GM
1832 }
1833 mt = getmodecmd(argv[1]);
1834 if (mt == 0) {
1835 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
3087f2df 1836 return 0;
f2bf20fe 1837 } else if (mt == Ambiguous(struct cmd *)) {
40e8c2a3 1838 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
3087f2df 1839 return 0;
40e8c2a3
GM
1840 } else {
1841 (*mt->handler)();
1842 }
3087f2df 1843 return 1;
40e8c2a3
GM
1844}
1845\f
040b6435
GM
1846/*
1847 * The following data structures and routines implement the
1848 * "display" command.
1849 */
1850
1851display(argc, argv)
1852int argc;
1853char *argv[];
1854{
1855#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
1856 if (*tl->variable) { \
1857 printf("will"); \
1858 } else { \
1859 printf("won't"); \
1860 } \
1861 printf(" %s.\n", tl->actionexplanation); \
1862 }
1863
3087f2df
GM
1864#define doset(sl) if (sl->name && *sl->name != ' ') { \
1865 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
1866 }
040b6435
GM
1867
1868 struct togglelist *tl;
1869 struct setlist *sl;
1870
1871 if (argc == 1) {
1872 for (tl = Togglelist; tl->name; tl++) {
1873 dotog(tl);
1874 }
3087f2df 1875 printf("\n");
040b6435
GM
1876 for (sl = Setlist; sl->name; sl++) {
1877 doset(sl);
1878 }
1879 } else {
1880 int i;
1881
1882 for (i = 1; i < argc; i++) {
1883 sl = getset(argv[i]);
1884 tl = gettoggle(argv[i]);
f2bf20fe
GM
1885 if ((sl == Ambiguous(struct setlist *)) ||
1886 (tl == Ambiguous(struct togglelist *))) {
040b6435 1887 printf("?Ambiguous argument '%s'.\n", argv[i]);
3087f2df 1888 return 0;
040b6435
GM
1889 } else if (!sl && !tl) {
1890 printf("?Unknown argument '%s'.\n", argv[i]);
3087f2df 1891 return 0;
040b6435 1892 } else {
f2bf20fe
GM
1893 if (tl) {
1894 dotog(tl);
1895 }
1896 if (sl) {
1897 doset(sl);
1898 }
040b6435
GM
1899 }
1900 }
1901 }
3087f2df 1902 return 1;
040b6435
GM
1903#undef doset(sl)
1904#undef dotog(tl)
1905}
1906\f
3e16c811
GM
1907/*
1908 * The following are the data structures, and many of the routines,
1909 * relating to command processing.
1910 */
1911
1912/*
1913 * Set the escape character.
1914 */
1915setescape(argc, argv)
1916 int argc;
1917 char *argv[];
1918{
1919 register char *arg;
1920 char buf[50];
1921
f2bf20fe
GM
1922 printf(
1923 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
1924 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
3e16c811
GM
1925 if (argc > 2)
1926 arg = argv[1];
1927 else {
1928 printf("new escape character: ");
1929 gets(buf);
1930 arg = buf;
1931 }
1932 if (arg[0] != '\0')
1933 escape = arg[0];
1934 printf("Escape character is '%s'.\n", control(escape));
1935 fflush(stdout);
3087f2df
GM
1936 return 1;
1937}
1938
1939/*VARARGS*/
1940togcrmod()
1941{
1942 crmod = !crmod;
1943 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
1944 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1945 fflush(stdout);
1946 return 1;
3e16c811
GM
1947}
1948
3e16c811
GM
1949/*VARARGS*/
1950suspend()
1951{
40e8c2a3 1952 setcommandmode();
3e16c811
GM
1953 kill(0, SIGTSTP);
1954 /* reget parameters in case they were changed */
1955 ioctl(0, TIOCGETP, (char *)&ottyb);
1956 ioctl(0, TIOCGETC, (char *)&otc);
1957 ioctl(0, TIOCGLTC, (char *)&oltc);
3087f2df 1958 return 1;
3e16c811
GM
1959}
1960
1961/*VARARGS*/
1962bye()
1963{
1964 register char *op;
1965
3e16c811
GM
1966 if (connected) {
1967 shutdown(net, 2);
1968 printf("Connection closed.\n");
1969 close(net);
1970 connected = 0;
1971 /* reset his options */
1972 for (op = hisopts; op < &hisopts[256]; op++)
1973 *op = 0;
1974 }
3087f2df 1975 return 1;
3e16c811
GM
1976}
1977
1978/*VARARGS*/
1979quit()
1980{
3087f2df 1981 (void) call(bye, "bye", 0);
3e16c811 1982 exit(0);
3087f2df 1983 /*NOTREACHED*/
3e16c811
GM
1984}
1985
1986/*
1987 * Print status about the connection.
1988 */
f2bf20fe 1989/*ARGSUSED*/
040b6435
GM
1990status(argc, argv)
1991int argc;
1992char *argv[];
3e16c811 1993{
040b6435
GM
1994 if (connected) {
1995 printf("Connected to %s.\n", hostname);
1996 if (argc < 2) {
1997 printf("Operating in %s.\n", modedescriptions[getconnmode()]);
3087f2df 1998 if (localchars) {
040b6435
GM
1999 printf("Catching signals locally.\n");
2000 }
40e8c2a3 2001 }
040b6435
GM
2002 } else {
2003 printf("No connection.\n");
2004 }
2005 printf("Escape character is '%s'.\n", control(escape));
2006 fflush(stdout);
3087f2df 2007 return 1;
3e16c811
GM
2008}
2009
2010tn(argc, argv)
2011 int argc;
2012 char *argv[];
2013{
3e16c811
GM
2014 register struct hostent *host = 0;
2015
2016 if (connected) {
2017 printf("?Already connected to %s\n", hostname);
3087f2df 2018 return 0;
3e16c811
GM
2019 }
2020 if (argc < 2) {
f2bf20fe 2021 (void) strcpy(line, "Connect ");
3e16c811
GM
2022 printf("(to) ");
2023 gets(&line[strlen(line)]);
2024 makeargv();
2025 argc = margc;
2026 argv = margv;
2027 }
2028 if (argc > 3) {
2029 printf("usage: %s host-name [port]\n", argv[0]);
3087f2df 2030 return 0;
3e16c811
GM
2031 }
2032 sin.sin_addr.s_addr = inet_addr(argv[1]);
2033 if (sin.sin_addr.s_addr != -1) {
2034 sin.sin_family = AF_INET;
f2bf20fe 2035 (void) strcpy(hnamebuf, argv[1]);
3e16c811
GM
2036 hostname = hnamebuf;
2037 } else {
2038 host = gethostbyname(argv[1]);
2039 if (host) {
2040 sin.sin_family = host->h_addrtype;
0ea293c6 2041#ifndef NOT43
3e16c811
GM
2042 bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
2043 host->h_length);
0ea293c6
GM
2044#else NOT43
2045 bcopy(host->h_addr, (caddr_t)&sin.sin_addr,
2046 host->h_length);
2047#endif NOT43
3e16c811
GM
2048 hostname = host->h_name;
2049 } else {
2050 printf("%s: unknown host\n", argv[1]);
3087f2df 2051 return 0;
3e16c811
GM
2052 }
2053 }
2054 sin.sin_port = sp->s_port;
2055 if (argc == 3) {
2056 sin.sin_port = atoi(argv[2]);
f2bf20fe 2057 if (sin.sin_port == 0) {
3e16c811
GM
2058 sp = getservbyname(argv[2], "tcp");
2059 if (sp)
2060 sin.sin_port = sp->s_port;
2061 else {
2062 printf("%s: bad port number\n", argv[2]);
3087f2df 2063 return 0;
3e16c811
GM
2064 }
2065 } else {
2066 sin.sin_port = atoi(argv[2]);
2067 sin.sin_port = htons(sin.sin_port);
2068 }
2069 telnetport = 0;
40e8c2a3
GM
2070 } else {
2071 telnetport = 1;
3e16c811
GM
2072 }
2073 signal(SIGINT, intr);
40e8c2a3 2074 signal(SIGQUIT, intr2);
3e16c811
GM
2075 signal(SIGPIPE, deadpeer);
2076 printf("Trying...\n");
2077 do {
2078 net = socket(AF_INET, SOCK_STREAM, 0);
2079 if (net < 0) {
2080 perror("telnet: socket");
3087f2df 2081 return 0;
3e16c811 2082 }
0ea293c6 2083#ifndef NOT43
f2bf20fe
GM
2084 if (debug &&
2085 setsockopt(net, SOL_SOCKET, SO_DEBUG,
0ea293c6
GM
2086 (char *)&debug, sizeof(debug)) < 0)
2087#else NOT43
2088 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
2089#endif NOT43
3e16c811 2090 perror("setsockopt (SO_DEBUG)");
0ea293c6 2091
f2bf20fe 2092 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
0ea293c6 2093#ifndef NOT43
3e16c811
GM
2094 if (host && host->h_addr_list[1]) {
2095 int oerrno = errno;
2096
2097 fprintf(stderr,
2098 "telnet: connect to address %s: ",
2099 inet_ntoa(sin.sin_addr));
2100 errno = oerrno;
f2bf20fe 2101 perror((char *)0);
3e16c811
GM
2102 host->h_addr_list++;
2103 bcopy(host->h_addr_list[0],
2104 (caddr_t)&sin.sin_addr, host->h_length);
2105 fprintf(stderr, "Trying %s...\n",
2106 inet_ntoa(sin.sin_addr));
2107 (void) close(net);
2108 continue;
2109 }
0ea293c6 2110#endif NOT43
3e16c811
GM
2111 perror("telnet: connect");
2112 signal(SIGINT, SIG_DFL);
3087f2df
GM
2113 signal(SIGQUIT, SIG_DFL);
2114 return 0;
3e16c811
GM
2115 }
2116 connected++;
2117 } while (connected == 0);
040b6435 2118 call(status, "status", "notmuch", 0);
3e16c811
GM
2119 if (setjmp(peerdied) == 0)
2120 telnet();
2121 fprintf(stderr, "Connection closed by foreign host.\n");
2122 exit(1);
3087f2df 2123 /*NOTREACHED*/
3e16c811
GM
2124}
2125
2126
2127#define HELPINDENT (sizeof ("connect"))
2128
2129char openhelp[] = "connect to a site";
2130char closehelp[] = "close current connection";
2131char quithelp[] = "exit telnet";
2132char zhelp[] = "suspend telnet";
3e16c811
GM
2133char statushelp[] = "print status information";
2134char helphelp[] = "print help information";
40e8c2a3 2135char sendhelp[] = "transmit special characters ('send ?' for more)";
040b6435
GM
2136char sethelp[] = "set operating parameters ('set ?' for more)";
2137char togglestring[] ="toggle operating parameters ('toggle ?' for more)";
2138char displayhelp[] = "display operating parameters";
2139char modehelp[] =
2140 "try to enter line-by-line or character-at-a-time mode";
3e16c811
GM
2141
2142int help();
2143
2144struct cmd cmdtab[] = {
40e8c2a3 2145 { "close", closehelp, bye, 1, 1 },
3087f2df
GM
2146 { "display", displayhelp, display, 1, 0 },
2147 { "mode", modehelp, modecmd, 1, 1 },
2148 { "open", openhelp, tn, 1, 0 },
40e8c2a3 2149 { "quit", quithelp, quit, 1, 0 },
40e8c2a3 2150 { "send", sendhelp, sendcmd, 1, 1 },
040b6435 2151 { "set", sethelp, setcmd, 1, 0 },
3087f2df 2152 { "status", statushelp, status, 1, 0 },
40e8c2a3 2153 { "toggle", togglestring, toggle, 1, 0 },
3087f2df 2154 { "z", zhelp, suspend, 1, 0 },
40e8c2a3 2155 { "?", helphelp, help, 1, 0 },
3e16c811
GM
2156 0
2157};
2158
3087f2df
GM
2159char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
2160char escapehelp[] = "deprecated command -- use 'set escape' instead";
2161
2162struct cmd cmdtab2[] = {
2163 { "help", helphelp, help, 0, 0 },
2164 { "escape", escapehelp, setescape, 1, 0 },
2165 { "crmod", crmodhelp, togcrmod, 1, 0 },
2166 0
2167};
3e16c811
GM
2168
2169/*
2170 * Help command.
2171 */
2172help(argc, argv)
2173 int argc;
2174 char *argv[];
2175{
2176 register struct cmd *c;
2177
2178 if (argc == 1) {
2179 printf("Commands may be abbreviated. Commands are:\n\n");
2180 for (c = cmdtab; c->name; c++)
2181 if (c->dohelp) {
2182 printf("%-*s\t%s\n", HELPINDENT, c->name,
2183 c->help);
2184 }
3087f2df 2185 return 0;
3e16c811
GM
2186 }
2187 while (--argc > 0) {
2188 register char *arg;
2189 arg = *++argv;
2190 c = getcmd(arg);
f2bf20fe 2191 if (c == Ambiguous(struct cmd *))
3e16c811
GM
2192 printf("?Ambiguous help command %s\n", arg);
2193 else if (c == (struct cmd *)0)
2194 printf("?Invalid help command %s\n", arg);
2195 else
2196 printf("%s\n", c->help);
2197 }
3087f2df 2198 return 0;
3e16c811
GM
2199}
2200/*
2201 * Call routine with argc, argv set from args (terminated by 0).
2202 * VARARGS2
2203 */
2204call(routine, args)
2205 int (*routine)();
f2bf20fe 2206 char *args;
3e16c811 2207{
f2bf20fe 2208 register char **argp;
3e16c811
GM
2209 register int argc;
2210
2211 for (argc = 0, argp = &args; *argp++ != 0; argc++)
2212 ;
3087f2df 2213 return (*routine)(argc, &args);
3e16c811
GM
2214}
2215
2216makeargv()
2217{
2218 register char *cp;
2219 register char **argp = margv;
2220
2221 margc = 0;
2222 for (cp = line; *cp;) {
2223 while (isspace(*cp))
2224 cp++;
2225 if (*cp == '\0')
2226 break;
2227 *argp++ = cp;
2228 margc += 1;
2229 while (*cp != '\0' && !isspace(*cp))
2230 cp++;
2231 if (*cp == '\0')
2232 break;
2233 *cp++ = '\0';
2234 }
2235 *argp++ = 0;
2236}
2237
2238char **
2239getnextcmd(name)
2240char *name;
2241{
2242 struct cmd *c = (struct cmd *) name;
2243
2244 return (char **) (c+1);
2245}
2246
2247struct cmd *
2248getcmd(name)
2249char *name;
2250{
3087f2df
GM
2251 struct cmd *cm;
2252
2253 if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) {
2254 return cm;
2255 } else {
2256 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
2257 }
3e16c811
GM
2258}
2259
2260command(top)
2261 int top;
2262{
2263 register struct cmd *c;
3e16c811 2264
40e8c2a3 2265 setcommandmode();
3087f2df 2266 if (!top) {
3e16c811 2267 putchar('\n');
3087f2df 2268 } else {
3e16c811 2269 signal(SIGINT, SIG_DFL);
3087f2df
GM
2270 signal(SIGQUIT, SIG_DFL);
2271 }
3e16c811
GM
2272 for (;;) {
2273 printf("%s> ", prompt);
2274 if (gets(line) == 0) {
2275 if (feof(stdin))
2276 quit();
2277 break;
2278 }
2279 if (line[0] == 0)
2280 break;
2281 makeargv();
2282 c = getcmd(margv[0]);
f2bf20fe 2283 if (c == Ambiguous(struct cmd *)) {
3e16c811
GM
2284 printf("?Ambiguous command\n");
2285 continue;
2286 }
2287 if (c == 0) {
2288 printf("?Invalid command\n");
2289 continue;
2290 }
2291 if (c->needconnect && !connected) {
2292 printf("?Need to be connected first.\n");
2293 continue;
2294 }
3087f2df 2295 if ((*c->handler)(margc, margv)) {
3e16c811 2296 break;
3087f2df 2297 }
3e16c811
GM
2298 }
2299 if (!top) {
40e8c2a3 2300 if (!connected) {
3e16c811 2301 longjmp(toplevel, 1);
40e8c2a3
GM
2302 /*NOTREACHED*/
2303 }
2304 setconnmode();
3e16c811
GM
2305 }
2306}
f2bf20fe
GM
2307\f
2308/*
2309 * main. Parse arguments, invoke the protocol or command parser.
2310 */
2311
2312
2313main(argc, argv)
2314 int argc;
2315 char *argv[];
2316{
2317 sp = getservbyname("telnet", "tcp");
2318 if (sp == 0) {
2319 fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
2320 exit(1);
2321 }
2322 NetTrace = stdout;
2323 ioctl(0, TIOCGETP, (char *)&ottyb);
2324 ioctl(0, TIOCGETC, (char *)&otc);
2325 ioctl(0, TIOCGLTC, (char *)&oltc);
792ffc71
GM
2326#if defined(LNOFLSH)
2327 ioctl(0, TIOCLGET, (char *)&autoflush);
3087f2df
GM
2328 autoflush = !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
2329#else /* LNOFLSH */
2330 autoflush = 1;
792ffc71 2331#endif /* LNOFLSH */
f2bf20fe 2332 ntc = otc;
792ffc71 2333 nltc = oltc;
f2bf20fe
GM
2334 nttyb = ottyb;
2335 setbuf(stdin, (char *)0);
2336 setbuf(stdout, (char *)0);
2337 prompt = argv[0];
2338 if (argc > 1 && !strcmp(argv[1], "-d")) {
2339 debug = 1;
2340 argv++;
2341 argc--;
2342 }
2343 if (argc > 1 && !strcmp(argv[1], "-n")) {
2344 argv++;
2345 argc--;
2346 if (argc > 1) { /* get file name */
2347 NetTrace = fopen(argv[1], "w");
2348 argv++;
2349 argc--;
2350 if (NetTrace == NULL) {
2351 NetTrace = stdout;
2352 }
2353 }
2354 }
2355 if (argc != 1) {
2356 if (setjmp(toplevel) != 0)
2357 exit(0);
2358 tn(argc, argv);
2359 }
2360 setjmp(toplevel);
2361 for (;;)
2362 command(1);
2363}