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