Fix bug in DISC logic.
[unix-history] / usr / src / usr.bin / tn3270 / telnet.c
CommitLineData
3e91ea2a
GM
1/*
2 * Copyright (c) 1984, 1985, 1986 by the Regents of the
3 * University of California and by Gregory Glenn Minshall.
4 *
5 * Permission to use, copy, modify, and distribute these
6 * programs and their documentation for any purpose and
7 * without fee is hereby granted, provided that this
8 * copyright and permission appear on all copies and
9 * supporting documentation, the name of the Regents of
10 * the University of California not be used in advertising
11 * or publicity pertaining to distribution of the programs
12 * without specific prior permission, and notice be given in
13 * supporting documentation that copying and distribution is
14 * by permission of the Regents of the University of California
15 * and by Gregory Glenn Minshall. Neither the Regents of the
16 * University of California nor Gregory Glenn Minshall make
17 * representations about the suitability of this software
18 * for any purpose. It is provided "as is" without
19 * express or implied warranty.
20 */
21
22#ifndef lint
23static char copyright[] =
24"@(#) Copyright (c) 1984, 1985, 1986 Regents of the University of California.\n\
25 All rights reserved.\n";
26#endif not lint
27
28#ifndef lint
29static char sccsid[] = "@(#)telnet.c 3.1 10/29/86";
30#endif not lint
31
32/*
33 * User telnet program, modified for use by tn3270.c.
34 *
35 * Many of the FUNCTIONAL changes in this newest version of TELNET
36 * were suggested by Dave Borman of Cray Research, Inc.
37 *
38 * Other changes in the tn3270 side come from Alan Crosswell (Columbia),
39 * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley).
40 *
41 * This code is common between telnet(1c) and tn3270(1c). There are the
42 * following defines used to generate the various versions:
43 *
44 * TN3270 - This is to be linked with tn3270.
45 *
46 * DEBUG - Allow for some extra debugging operations.
47 *
48 * NOT43 - Allows the program to compile and run on
49 * a 4.2BSD system.
50 *
51 * PUTCHAR - Within tn3270, on a NOT43 system,
52 * allows the use of the 4.3 curses
53 * (greater speed updating the screen).
54 * You need the 4.3 curses for this to work.
55 *
56 * FD_SETSIZE - On whichever system, if this isn't defined,
57 * we patch over the FD_SET, etc., macros with
58 * some homebrewed ones.
59 *
60 * SO_OOBINLINE - This is a socket option which we would like
61 * to set to allow TCP urgent data to come
62 * to us "inline". This is NECESSARY for
63 * CORRECT operation, and desireable for
64 * simpler operation.
65 *
66 * LNOFLSH - Detects the presence of the LNOFLSH bit
67 * in the tty structure.
68 *
69 * unix - Compiles in unix specific stuff.
70 *
71 * msdos - Compiles in msdos specific stuff.
72 *
73 */
74
75#if !defined(TN3270)
76#define ExitString(f,s,r) { fprintf(f, s); exit(r); }
77#define Exit(x) exit(x)
78#define SetIn3270()
79
80void setcommandmode(), command(); /* forward declarations */
81#endif /* !defined(TN3270) */
82
83#include <sys/types.h>
84#include <sys/socket.h>
85#include <sys/ioctl.h>
86#include <sys/time.h>
87
88#include <netinet/in.h>
89
90#include <curses.h>
91
92#define TELOPTS
93#include <arpa/telnet.h>
94
95#if !defined(NOT43)
96#include <arpa/inet.h>
97#else /* !defined(NOT43) */
98extern unsigned long inet_addr();
99extern char *inet_ntoa();
100#endif /* !defined(NOT43) */
101
102#include <stdio.h>
103#include <ctype.h>
104#include <errno.h>
105#include <signal.h>
106#include <setjmp.h>
107#include <netdb.h>
108#include <strings.h>
109
110#if defined(TN3270)
111#include "ctlr/screen.h"
112#include "system/globals.h"
113#include "telnet.ext"
114#include "ctlr/options.ext"
115#include "ctlr/outbound.ext"
116#include "keyboard/termin.ext"
117#endif /* defined(TN3270) */
118
119
120
121#ifndef FD_SETSIZE
122/*
123 * The following is defined just in case someone should want to run
124 * this telnet on a 4.2 system.
125 *
126 */
127
128#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
129#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
130#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
131#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
132
133#endif
134\f
135#define strip(x) ((x)&0x7f)
136#define min(x,y) ((x<y)? x:y)
137
138#if defined(TN3270)
139static char Ibuf[8*BUFSIZ], *Ifrontp = Ibuf, *Ibackp = Ibuf;
140#endif /* defined(TN3270) */
141
142static char ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
143#define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
144#define TTYLOC() (tfrontp)
145#define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
146#define TTYMIN() (netobuf)
147#define TTYBYTES() (tfrontp-tbackp)
148#define TTYROOM() (TTYMAX()-TTYLOC()+1)
149
150static char netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
151#define NETADD(c) { *nfrontp++ = c; }
152#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
153#define NETLOC() (nfrontp)
154#define NETMAX() (netobuf+sizeof netobuf-1)
155#define NETBYTES() (nfrontp-nbackp)
156#define NETROOM() (NETMAX()-NETLOC()+1)
157static char *neturg = 0; /* one past last byte of urgent data */
158
159static char subbuffer[100] = 0,
160 *subpointer, *subend = 0; /* buffer for sub-options */
161#define SB_CLEAR() subpointer = subbuffer;
162#define SB_TERM() subend = subpointer;
163#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
164 *subpointer++ = (c); \
165 }
166
167static char sb_terminal[] = { IAC, SB,
168 TELOPT_TTYPE, TELQUAL_IS,
169 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
170 IAC, SE };
171#define SBTERMMODEL 13
172
173
174static char hisopts[256] = 0;
175static char myopts[256] = 0;
176
177static char doopt[] = { IAC, DO, '%', 'c', 0 };
178static char dont[] = { IAC, DONT, '%', 'c', 0 };
179static char will[] = { IAC, WILL, '%', 'c', 0 };
180static char wont[] = { IAC, WONT, '%', 'c', 0 };
181
182struct cmd {
183 char *name; /* command name */
184 char *help; /* help string */
185 int (*handler)(); /* routine which executes command */
186 int dohelp; /* Should we give general help information? */
187 int needconnect; /* Do we need to be connected to execute? */
188};
189
190static char sibuf[BUFSIZ], *sbp = 0;
191static char tibuf[BUFSIZ], *tbp;
192static fd_set ibits, obits, xbits;
193
194
195static int
196 connected = 0,
197 net = 0,
198 scc = 0,
199 tcc = 0,
200 showoptions = 0,
201 In3270 = 0, /* Are we in 3270 mode? */
202 ISend = 0, /* trying to send network data in */
203 debug = 0,
204 crmod = 0,
205 netdata = 0,
206 telnetport = 1;
207
208static FILE *NetTrace = 0;
209
210#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
211
212static char
213 *prompt = 0,
214 escape = CONTROL(']'),
215 echoc = CONTROL('E');
216
217static int
218 SYNCHing = 0, /* we are in TELNET SYNCH mode */
219 flushout = 0, /* flush output */
220 autoflush = 0, /* flush output when interrupting? */
221 autosynch = 0, /* send interrupt characters with SYNCH? */
222 localchars = 0, /* we recognize interrupt/quit */
223 donelclchars = 0, /* the user has set "localchars" */
224 dontlecho = 0; /* do we suppress local echoing right now? */
225
226/* The following are some tn3270 specific flags */
227#if defined(TN3270)
228
229static int
230 Sent3270TerminalType = 0; /* Have we said we are a 3270? */
231
232/* Some real, live, globals. */
233int
234#if defined(unix)
235 HaveInput = 0, /* There is input available to scan */
236#endif /* defined(unix) */
237 tout = 0, /* Output file descriptor */
238 tin = 0; /* Input file descriptor */
239#if defined(unix)
240char *transcom = 0; /* transparent mode command (default: none) */
241#endif /* defined(unix) */
242
243#else /* defined(TN3270) */
244static int tin = 0, tout = 0; /* file descriptors */
245#endif /* defined(TN3270) */
246
247static char line[200];
248#if defined(TN3270) && defined(unix)
249static char tline[200];
250#endif /* defined(TN3270) && defined(unix) */
251static int margc = 0;
252static char *margv[20];
253
254static jmp_buf toplevel;
255static jmp_buf peerdied;
256
257extern int errno;
258
259
260static struct sockaddr_in sin;
261
262static struct servent *sp = 0;
263
264static struct tchars otc = { 0 }, ntc = { 0 };
265static struct ltchars oltc = { 0 }, nltc = { 0 };
266static struct sgttyb ottyb = { 0 }, nttyb = { 0 };
267static int flushline = 1;
268
269static char *hostname = 0;
270static char hnamebuf[32];
271
272/*
273 * The following are some clocks used to decide how to interpret
274 * the relationship between various variables.
275 */
276
277static struct {
278 int
279 system, /* what the current time is */
280 echotoggle, /* last time user entered echo character */
281 modenegotiated, /* last time operating mode negotiated */
282 didnetreceive, /* last time we read data from network */
283 gotDM; /* when did we last see a data mark */
284} clocks = { 0 };
285
286#define settimer(x) clocks.x = clocks.system++
287\f
288/*
289 * Various utility routines.
290 */
291
292static void
293makeargv()
294{
295 register char *cp;
296 register char **argp = margv;
297
298 margc = 0;
299 for (cp = line; *cp;) {
300 while (isspace(*cp))
301 cp++;
302 if (*cp == '\0')
303 break;
304 *argp++ = cp;
305 margc += 1;
306 while (*cp != '\0' && !isspace(*cp))
307 cp++;
308 if (*cp == '\0')
309 break;
310 *cp++ = '\0';
311 }
312 *argp++ = 0;
313}
314
315static char *ambiguous; /* special return value */
316#define Ambiguous(t) ((t)&ambiguous)
317
318
319static char **
320genget(name, table, next)
321char *name; /* name to match */
322char **table; /* name entry in table */
323char **(*next)(); /* routine to return next entry in table */
324{
325 register char *p, *q;
326 register char **c, **found;
327 register int nmatches, longest;
328
329 longest = 0;
330 nmatches = 0;
331 found = 0;
332 for (c = table; (p = *c) != 0; c = (*next)(c)) {
333 for (q = name;
334 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
335 if (*q == 0) /* exact match? */
336 return (c);
337 if (!*q) { /* the name was a prefix */
338 if (q - name > longest) {
339 longest = q - name;
340 nmatches = 1;
341 found = c;
342 } else if (q - name == longest)
343 nmatches++;
344 }
345 }
346 if (nmatches > 1)
347 return Ambiguous(char **);
348 return (found);
349}
350
351/*
352 * Make a character string into a number.
353 *
354 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
355 */
356
357static
358special(s)
359register char *s;
360{
361 register char c;
362 char b;
363
364 switch (*s) {
365 case '^':
366 b = *++s;
367 if (b == '?') {
368 c = b | 0x40; /* DEL */
369 } else {
370 c = b & 0x1f;
371 }
372 break;
373 default:
374 c = *s;
375 break;
376 }
377 return c;
378}
379
380/*
381 * Construct a control character sequence
382 * for a special character.
383 */
384static char *
385control(c)
386 register int c;
387{
388 static char buf[3];
389
390 if (c == 0x7f)
391 return ("^?");
392 if (c == '\377') {
393 return "off";
394 }
395 if (c >= 0x20) {
396 buf[0] = c;
397 buf[1] = 0;
398 } else {
399 buf[0] = '^';
400 buf[1] = '@'+c;
401 buf[2] = 0;
402 }
403 return (buf);
404}
405
406
407/*
408 * upcase()
409 *
410 * Upcase (in place) the argument.
411 */
412
413static void
414upcase(argument)
415register char *argument;
416{
417 register int c;
418
419 while ((c = *argument) != 0) {
420 if (islower(c)) {
421 *argument = toupper(c);
422 }
423 argument++;
424 }
425}
426\f
427/*
428 * The following are routines used to print out debugging information.
429 */
430
431
432static void
433Dump(direction, buffer, length)
434char direction;
435char *buffer;
436int length;
437{
438# define BYTES_PER_LINE 32
439# define min(x,y) ((x<y)? x:y)
440 char *pThis;
441 int offset;
442
443 offset = 0;
444
445 while (length) {
446 /* print one line */
447 fprintf(NetTrace, "%c 0x%x\t", direction, offset);
448 pThis = buffer;
449 buffer = buffer+min(length, BYTES_PER_LINE);
450 while (pThis < buffer) {
451 fprintf(NetTrace, "%.2x", (*pThis)&0xff);
452 pThis++;
453 }
454 fprintf(NetTrace, "\n");
455 length -= BYTES_PER_LINE;
456 offset += BYTES_PER_LINE;
457 if (length < 0) {
458 return;
459 }
460 /* find next unique line */
461 }
462}
463
464
465/*VARARGS*/
466static void
467printoption(direction, fmt, option, what)
468 char *direction, *fmt;
469 int option, what;
470{
471 if (!showoptions)
472 return;
473 fprintf(NetTrace, "%s ", direction+1);
474 if (fmt == doopt)
475 fmt = "do";
476 else if (fmt == dont)
477 fmt = "dont";
478 else if (fmt == will)
479 fmt = "will";
480 else if (fmt == wont)
481 fmt = "wont";
482 else
483 fmt = "???";
484 if (option < (sizeof telopts/sizeof telopts[0]))
485 fprintf(NetTrace, "%s %s", fmt, telopts[option]);
486 else
487 fprintf(NetTrace, "%s %d", fmt, option);
488 if (*direction == '<') {
489 fprintf(NetTrace, "\r\n");
490 return;
491 }
492 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
493}
494
495static void
496printsub(direction, pointer, length)
497char *direction, /* "<" or ">" */
498 *pointer; /* where suboption data sits */
499int length; /* length of suboption data */
500{
501 if (showoptions) {
502 fprintf(NetTrace, "%s suboption ",
503 (direction[0] == '<')? "Received":"Sent");
504 switch (pointer[0]) {
505 case TELOPT_TTYPE:
506 fprintf(NetTrace, "Terminal type ");
507 switch (pointer[1]) {
508 case TELQUAL_IS:
509 {
510 char tmpbuf[sizeof subbuffer];
511 int minlen = min(length, sizeof tmpbuf);
512
513 bcopy(pointer+2, tmpbuf, minlen);
514 tmpbuf[minlen-1] = 0;
515 fprintf(NetTrace, "is %s.\n", tmpbuf);
516 }
517 break;
518 case TELQUAL_SEND:
519 fprintf(NetTrace, "- request to send.\n");
520 break;
521 default:
522 fprintf(NetTrace,
523 "- unknown qualifier %d (0x%x).\n", pointer[1]);
524 }
525 break;
526 default:
527 fprintf(NetTrace, "Unknown option %d (0x%x)\n",
528 pointer[0], pointer[0]);
529 }
530 }
531}
532\f
533/*
534 * Check to see if any out-of-band data exists on a socket (for
535 * Telnet "synch" processing).
536 */
537
538static int
539stilloob(s)
540int s; /* socket number */
541{
542 static struct timeval timeout = { 0 };
543 fd_set excepts;
544 int value;
545
546 do {
547 FD_ZERO(&excepts);
548 FD_SET(s, &excepts);
549 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
550 } while ((value == -1) && (errno == EINTR));
551
552 if (value < 0) {
553 perror("select");
554 quit();
555 }
556 if (FD_ISSET(s, &excepts)) {
557 return 1;
558 } else {
559 return 0;
560 }
561}
562
563
564/*
565 * netflush
566 * Send as much data as possible to the network,
567 * handling requests for urgent data.
568 *
569 * The return value indicates whether we did any
570 * useful work.
571 */
572
573
574int
575netflush()
576{
577 int n;
578
579 if ((n = nfrontp - nbackp) > 0) {
580 if (!neturg) {
581 n = write(net, nbackp, n); /* normal write */
582 } else {
583 n = neturg - nbackp;
584 /*
585 * In 4.2 (and 4.3) systems, there is some question about
586 * what byte in a sendOOB operation is the "OOB" data.
587 * To make ourselves compatible, we only send ONE byte
588 * out of band, the one WE THINK should be OOB (though
589 * we really have more the TCP philosophy of urgent data
590 * rather than the Unix philosophy of OOB data).
591 */
592 if (n > 1) {
593 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
594 } else {
595 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
596 }
597 }
598 }
599 if (n < 0) {
600 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
601 setcommandmode();
602 perror(hostname);
603 close(net);
604 neturg = 0;
605 longjmp(peerdied, -1);
606 /*NOTREACHED*/
607 }
608 n = 0;
609 }
610 if (netdata && n) {
611 Dump('>', nbackp, n);
612 }
613 nbackp += n;
614 if (nbackp >= neturg) {
615 neturg = 0;
616 }
617 if (nbackp == nfrontp) {
618 nbackp = nfrontp = netobuf;
619 }
620 return n > 0;
621}
622\f
623/*
624 * nextitem()
625 *
626 * Return the address of the next "item" in the TELNET data
627 * stream. This will be the address of the next character if
628 * the current address is a user data character, or it will
629 * be the address of the character following the TELNET command
630 * if the current address is a TELNET IAC ("I Am a Command")
631 * character.
632 */
633
634static char *
635nextitem(current)
636char *current;
637{
638 if ((*current&0xff) != IAC) {
639 return current+1;
640 }
641 switch (*(current+1)&0xff) {
642 case DO:
643 case DONT:
644 case WILL:
645 case WONT:
646 return current+3;
647 case SB: /* loop forever looking for the SE */
648 {
649 register char *look = current+2;
650
651 for (;;) {
652 if ((*look++&0xff) == IAC) {
653 if ((*look++&0xff) == SE) {
654 return look;
655 }
656 }
657 }
658 }
659 default:
660 return current+2;
661 }
662}
663/*
664 * netclear()
665 *
666 * We are about to do a TELNET SYNCH operation. Clear
667 * the path to the network.
668 *
669 * Things are a bit tricky since we may have sent the first
670 * byte or so of a previous TELNET command into the network.
671 * So, we have to scan the network buffer from the beginning
672 * until we are up to where we want to be.
673 *
674 * A side effect of what we do, just to keep things
675 * simple, is to clear the urgent data pointer. The principal
676 * caller should be setting the urgent data pointer AFTER calling
677 * us in any case.
678 */
679
680static void
681netclear()
682{
683 register char *thisitem, *next;
684 char *good;
685#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
686 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
687
688 thisitem = netobuf;
689
690 while ((next = nextitem(thisitem)) <= nbackp) {
691 thisitem = next;
692 }
693
694 /* Now, thisitem is first before/at boundary. */
695
696 good = netobuf; /* where the good bytes go */
697
698 while (nfrontp > thisitem) {
699 if (wewant(thisitem)) {
700 int length;
701
702 next = thisitem;
703 do {
704 next = nextitem(next);
705 } while (wewant(next) && (nfrontp > next));
706 length = next-thisitem;
707 bcopy(thisitem, good, length);
708 good += length;
709 thisitem = next;
710 } else {
711 thisitem = nextitem(thisitem);
712 }
713 }
714
715 nbackp = netobuf;
716 nfrontp = good; /* next byte to be sent */
717 neturg = 0;
718}
719\f
720/*
721 * These routines add various telnet commands to the data stream.
722 */
723
724#if defined(NOT43)
725static int
726#else /* defined(NOT43) */
727static void
728#endif /* defined(NOT43) */
729dosynch()
730{
731 netclear(); /* clear the path to the network */
732 NET2ADD(IAC, DM);
733 neturg = NETLOC()-1; /* Some systems are off by one XXX */
734
735#if defined(NOT43)
736 return 0;
737#endif /* defined(NOT43) */
738}
739
740static void
741doflush()
742{
743 NET2ADD(IAC, DO);
744 NETADD(TELOPT_TM);
745 flushline = 1;
746 flushout = 1;
747 ttyflush();
748 /* do printoption AFTER flush, otherwise the output gets tossed... */
749 printoption("<SENT", doopt, TELOPT_TM, 0);
750}
751
752static void
753intp()
754{
755 NET2ADD(IAC, IP);
756 if (autoflush) {
757 doflush();
758 }
759 if (autosynch) {
760 dosynch();
761 }
762}
763
764static void
765sendbrk()
766{
767 NET2ADD(IAC, BREAK);
768 if (autoflush) {
769 doflush();
770 }
771 if (autosynch) {
772 dosynch();
773 }
774}
775\f
776/*
777 * Send as much data as possible to the terminal.
778 *
779 * The return value indicates whether we did any
780 * useful work.
781 */
782
783
784static int
785ttyflush()
786{
787 int n;
788
789 if ((n = tfrontp - tbackp) > 0) {
790 if (!(SYNCHing||flushout)) {
791 n = write(tout, tbackp, n);
792 } else {
793 ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
794 /* we leave 'n' alone! */
795 }
796 }
797 if (n >= 0) {
798 tbackp += n;
799 if (tbackp == tfrontp) {
800 tbackp = tfrontp = ttyobuf;
801 }
802 }
803 return n > 0;
804}
805
806#if defined(TN3270)
807
808#if defined(unix)
809static void
810inputAvailable()
811{
812 HaveInput = 1;
813}
814#endif /* defined(unix) */
815
816void
817outputPurge()
818{
819 int tmp = flushout;
820
821 flushout = 1;
822
823 ttyflush();
824
825 flushout = tmp;
826}
827
828#endif /* defined(TN3270) */
829\f
830#if defined(unix)
831/*
832 * Various signal handling routines.
833 */
834
835static void
836deadpeer()
837{
838 setcommandmode();
839 longjmp(peerdied, -1);
840}
841
842static void
843intr()
844{
845 if (localchars) {
846 intp();
847 return;
848 }
849 setcommandmode();
850 longjmp(toplevel, -1);
851}
852
853static void
854intr2()
855{
856 if (localchars) {
857 sendbrk();
858 return;
859 }
860}
861
862static void
863doescape()
864{
865 command(0);
866}
867#endif /* defined(unix) */
868\f
869static int globalmode = 0;
870/* Various modes */
871#define MODE_LINE(m) (modelist[m].modetype & LINE)
872#define MODE_LOCAL_CHARS(m) (modelist[m].modetype & LOCAL_CHARS)
873
874#define LOCAL_CHARS 0x01 /* Characters processed locally */
875#define LINE 0x02 /* Line-by-line mode of operation */
876
877static struct {
878 char *modedescriptions;
879 char modetype;
880} modelist[] = {
881 { "telnet command mode", 0 },
882 { "character-at-a-time mode", 0 },
883 { "character-at-a-time mode (local echo)", LOCAL_CHARS },
884 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
885 { "line-by-line mode", LINE | LOCAL_CHARS },
886 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
887 { "3270 mode", 0 },
888};
889/*
890 * Mode - set up terminal to a specific mode.
891 */
892
893
894static void
895mode(f)
896 register int f;
897{
898 static int prevmode = 0;
899 struct tchars *tc;
900 struct tchars tc3;
901 struct ltchars *ltc;
902 struct sgttyb sb;
903 int onoff;
904#if defined(unix)
905 int old;
906#endif /* defined(unix) */
907 struct tchars notc2;
908 struct ltchars noltc2;
909 static struct tchars notc = { -1, -1, -1, -1, -1, -1 };
910 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
911
912 globalmode = f;
913 if (prevmode == f)
914 return;
915#if defined(unix)
916 old = prevmode;
917#endif /* defined(unix) */
918 prevmode = f;
919 sb = nttyb;
920
921 switch (f) {
922
923 case 0:
924 onoff = 0;
925 tc = &otc;
926 ltc = &oltc;
927 break;
928
929 case 1: /* remote character processing, remote echo */
930 case 2: /* remote character processing, local echo */
931 case 6: /* 3270 mode - like 1, but with xon/xoff local */
932 /* (might be nice to have "6" in telnet also...) */
933 sb.sg_flags |= CBREAK;
934 if ((f == 1) || (f == 6)) {
935 sb.sg_flags &= ~(ECHO|CRMOD);
936 } else {
937 sb.sg_flags |= ECHO|CRMOD;
938 }
939 sb.sg_erase = sb.sg_kill = -1;
940 if (f == 6) {
941 tc = &tc3;
942 tc3 = notc;
943 /* get XON, XOFF characters */
944 tc3.t_startc = otc.t_startc;
945 tc3.t_stopc = otc.t_stopc;
946 } else {
947 /*
948 * If user hasn't specified one way or the other,
949 * then default to not trapping signals.
950 */
951 if (!donelclchars) {
952 localchars = 0;
953 }
954 if (localchars) {
955 notc2 = notc;
956 notc2.t_intrc = ntc.t_intrc;
957 notc2.t_quitc = ntc.t_quitc;
958 tc = &notc2;
959 } else {
960 tc = &notc;
961 }
962 }
963 ltc = &noltc;
964 onoff = 1;
965 break;
966 case 3: /* local character processing, remote echo */
967 case 4: /* local character processing, local echo */
968 case 5: /* local character processing, no echo */
969 sb.sg_flags &= ~CBREAK;
970 sb.sg_flags |= CRMOD;
971 if (f == 4)
972 sb.sg_flags |= ECHO;
973 else
974 sb.sg_flags &= ~ECHO;
975 notc2 = ntc;
976 tc = &notc2;
977 noltc2 = oltc;
978 ltc = &noltc2;
979 /*
980 * If user hasn't specified one way or the other,
981 * then default to trapping signals.
982 */
983 if (!donelclchars) {
984 localchars = 1;
985 }
986 if (localchars) {
987 notc2.t_brkc = nltc.t_flushc;
988 noltc2.t_flushc = -1;
989 } else {
990 notc2.t_intrc = notc2.t_quitc = -1;
991 }
992 noltc2.t_suspc = escape;
993 noltc2.t_dsuspc = -1;
994 onoff = 1;
995 break;
996
997 default:
998 return;
999 }
1000 ioctl(tin, TIOCSLTC, (char *)ltc);
1001 ioctl(tin, TIOCSETC, (char *)tc);
1002 ioctl(tin, TIOCSETP, (char *)&sb);
1003#if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
1004 ioctl(tin, FIONBIO, (char *)&onoff);
1005 ioctl(tout, FIONBIO, (char *)&onoff);
1006#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
1007#if defined(TN3270) && !defined(DEBUG)
1008 ioctl(tin, FIOASYNC, (char *)&onoff);
1009#endif /* defined(TN3270) && !defined(DEBUG) */
1010
1011#if defined(unix)
1012 if (MODE_LINE(f)) {
1013 signal(SIGTSTP, doescape);
1014 } else if (MODE_LINE(old)) {
1015 signal(SIGTSTP, SIG_DFL);
1016 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
1017 }
1018#endif /* defined(unix) */
1019}
1020\f
1021/*
1022 * These routines decides on what the mode should be (based on the values
1023 * of various global variables).
1024 */
1025
1026
1027static
1028getconnmode()
1029{
1030 static char newmode[16] =
1031 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1032 int modeindex = 0;
1033
1034 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1035 modeindex += 1;
1036 }
1037 if (hisopts[TELOPT_ECHO]) {
1038 modeindex += 2;
1039 }
1040 if (hisopts[TELOPT_SGA]) {
1041 modeindex += 4;
1042 }
1043 if (In3270) {
1044 modeindex += 8;
1045 }
1046 return newmode[modeindex];
1047}
1048
1049void
1050setconnmode()
1051{
1052 mode(getconnmode());
1053}
1054
1055
1056void
1057setcommandmode()
1058{
1059 mode(0);
1060}
1061\f
1062static void
1063willoption(option, reply)
1064 int option, reply;
1065{
1066 char *fmt;
1067
1068 switch (option) {
1069
1070# if defined(TN3270)
1071 case TELOPT_EOR:
1072 case TELOPT_BINARY:
1073#endif /* defined(TN3270) */
1074 case TELOPT_ECHO:
1075 case TELOPT_SGA:
1076 settimer(modenegotiated);
1077 hisopts[option] = 1;
1078 fmt = doopt;
1079 setconnmode(); /* possibly set new tty mode */
1080 break;
1081
1082 case TELOPT_TM:
1083 return; /* Never reply to TM will's/wont's */
1084
1085 default:
1086 fmt = dont;
1087 break;
1088 }
1089 sprintf(nfrontp, fmt, option);
1090 nfrontp += sizeof (dont) - 2;
1091 if (reply)
1092 printoption(">SENT", fmt, option, reply);
1093 else
1094 printoption("<SENT", fmt, option, reply);
1095}
1096
1097static void
1098wontoption(option, reply)
1099 int option, reply;
1100{
1101 char *fmt;
1102
1103 switch (option) {
1104
1105 case TELOPT_ECHO:
1106 case TELOPT_SGA:
1107 settimer(modenegotiated);
1108 hisopts[option] = 0;
1109 fmt = dont;
1110 setconnmode(); /* Set new tty mode */
1111 break;
1112
1113 case TELOPT_TM:
1114 return; /* Never reply to TM will's/wont's */
1115
1116 default:
1117 fmt = dont;
1118 }
1119 sprintf(nfrontp, fmt, option);
1120 nfrontp += sizeof (doopt) - 2;
1121 if (reply)
1122 printoption(">SENT", fmt, option, reply);
1123 else
1124 printoption("<SENT", fmt, option, reply);
1125}
1126
1127static void
1128dooption(option)
1129 int option;
1130{
1131 char *fmt;
1132
1133 switch (option) {
1134
1135 case TELOPT_TM:
1136 fmt = will;
1137 break;
1138
1139# if defined(TN3270)
1140 case TELOPT_EOR:
1141 case TELOPT_BINARY:
1142# endif /* defined(TN3270) */
1143 case TELOPT_TTYPE: /* terminal type option */
1144 case TELOPT_SGA: /* no big deal */
1145 fmt = will;
1146 myopts[option] = 1;
1147 break;
1148
1149 case TELOPT_ECHO: /* We're never going to echo... */
1150 default:
1151 fmt = wont;
1152 break;
1153 }
1154 sprintf(nfrontp, fmt, option);
1155 nfrontp += sizeof (doopt) - 2;
1156 printoption(">SENT", fmt, option, 0);
1157}
1158
1159/*
1160 * suboption()
1161 *
1162 * Look at the sub-option buffer, and try to be helpful to the other
1163 * side.
1164 *
1165 * Currently we recognize:
1166 *
1167 * Terminal type, send request.
1168 */
1169
1170static void
1171suboption()
1172{
1173 printsub("<", subbuffer, subend-subbuffer+1);
1174 switch (subbuffer[0]&0xff) {
1175 case TELOPT_TTYPE:
1176 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1177 ;
1178 } else {
1179 char *name;
1180 char namebuf[41];
1181 extern char *getenv();
1182 int len;
1183
1184#if defined(TN3270)
1185 /*
1186 * Try to send a 3270 type terminal name. Decide which one base
1187 * on the format of our screen, and (in the future) color
1188 * capaiblities.
1189 */
1190 if ((initscr() != ERR) && /* Initialize curses to get line size */
1191 (LINES >= 24) && (COLS >= 80)) {
1192 Sent3270TerminalType = 1;
1193 if ((LINES >= 27) && (COLS >= 132)) {
1194 MaxNumberLines = 27;
1195 MaxNumberColumns = 132;
1196 sb_terminal[SBTERMMODEL] = '5';
1197 } else if (LINES >= 43) {
1198 MaxNumberLines = 43;
1199 MaxNumberColumns = 80;
1200 sb_terminal[SBTERMMODEL] = '4';
1201 } else if (LINES >= 32) {
1202 MaxNumberLines = 32;
1203 MaxNumberColumns = 80;
1204 sb_terminal[SBTERMMODEL] = '3';
1205 } else {
1206 MaxNumberLines = 24;
1207 MaxNumberColumns = 80;
1208 sb_terminal[SBTERMMODEL] = '2';
1209 }
1210 NumberLines = 24; /* before we start out... */
1211 NumberColumns = 80;
1212 ScreenSize = NumberLines*NumberColumns;
1213 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1214 ExitString(stderr,
1215 "Programming error: MAXSCREENSIZE too small.\n", 1);
1216 /*NOTREACHED*/
1217 }
1218 bcopy(sb_terminal, nfrontp, sizeof sb_terminal);
1219 printsub(">", nfrontp+2, sizeof sb_terminal-2);
1220 nfrontp += sizeof sb_terminal;
1221 return;
1222 }
1223#endif /* defined(TN3270) */
1224
1225 name = getenv("TERM");
1226 if ((name == 0) || ((len = strlen(name)) > 40)) {
1227 name = "UNKNOWN";
1228 }
1229 if ((len + 4+2) < NETROOM()) {
1230 strcpy(namebuf, name);
1231 upcase(namebuf);
1232 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1233 TELQUAL_IS, namebuf, IAC, SE);
1234 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1235 nfrontp += 4+strlen(namebuf)+2;
1236 } else {
1237 ExitString(stderr, "No room in buffer for terminal type.\n",
1238 1);
1239 /*NOTREACHED*/
1240 }
1241 }
1242
1243 default:
1244 break;
1245 }
1246}
1247
1248#if defined(TN3270)
1249static void
1250SetIn3270()
1251{
1252 if (Sent3270TerminalType && myopts[TELOPT_BINARY]
1253 && hisopts[TELOPT_BINARY]) {
1254 if (!In3270) {
1255 In3270 = 1;
1256 OptInit(); /* initialize mappings */
1257 /* initialize terminal key mapping */
1258 (void) DataFromTerminal(ttyobuf, 0);
1259 StartScreen(); /* Start terminal going */
1260 setconnmode();
1261 }
1262 } else {
1263 if (In3270) {
1264 StopScreen(1);
1265 In3270 = 0;
1266 setconnmode();
1267 }
1268 }
1269}
1270#endif /* defined(TN3270) */
1271\f
1272/*
1273 * Telnet receiver states for fsm
1274 */
1275#define TS_DATA 0
1276#define TS_IAC 1
1277#define TS_WILL 2
1278#define TS_WONT 3
1279#define TS_DO 4
1280#define TS_DONT 5
1281#define TS_CR 6
1282#define TS_SB 7 /* sub-option collection */
1283#define TS_SE 8 /* looking for sub-option end */
1284
1285static void
1286telrcv()
1287{
1288 register int c;
1289 static int state = TS_DATA;
1290# if defined(TN3270)
1291 register int Scc;
1292 register char *Sbp;
1293# endif /* defined(TN3270) */
1294
1295 while ((scc > 0) && (TTYROOM() > 2)) {
1296 c = *sbp++ & 0xff, scc--;
1297 switch (state) {
1298
1299 case TS_CR:
1300 state = TS_DATA;
1301 if (c == '\0') {
1302 break; /* Ignore \0 after CR */
1303 } else if (c == '\n') {
1304 if (hisopts[TELOPT_ECHO] && !crmod) {
1305 TTYADD(c);
1306 }
1307 break;
1308 }
1309 /* Else, fall through */
1310
1311 case TS_DATA:
1312 if (c == IAC) {
1313 state = TS_IAC;
1314 continue;
1315 }
1316# if defined(TN3270)
1317 if (In3270) {
1318 *Ifrontp++ = c;
1319 Sbp = sbp;
1320 Scc = scc;
1321 while (Scc > 0) {
1322 c = *Sbp++ & 0377, Scc--;
1323 if (c == IAC) {
1324 state = TS_IAC;
1325 break;
1326 }
1327 *Ifrontp++ = c;
1328 }
1329 sbp = Sbp;
1330 scc = Scc;
1331 } else
1332# endif /* defined(TN3270) */
1333 /*
1334 * The 'crmod' hack (see following) is needed
1335 * since we can't * set CRMOD on output only.
1336 * Machines like MULTICS like to send \r without
1337 * \n; since we must turn off CRMOD to get proper
1338 * input, the mapping is done here (sigh).
1339 */
1340 if (c == '\r') {
1341 if (scc > 0) {
1342 c = *sbp&0xff;
1343 if (c == 0) {
1344 sbp++, scc--;
1345 /* a "true" CR */
1346 TTYADD('\r');
1347 } else if (!hisopts[TELOPT_ECHO] &&
1348 (c == '\n')) {
1349 sbp++, scc--;
1350 TTYADD('\n');
1351 } else {
1352 TTYADD('\r');
1353 if (crmod) {
1354 TTYADD('\n');
1355 }
1356 }
1357 } else {
1358 state = TS_CR;
1359 TTYADD('\r');
1360 if (crmod) {
1361 TTYADD('\n');
1362 }
1363 }
1364 } else {
1365 TTYADD(c);
1366 }
1367 continue;
1368
1369 case TS_IAC:
1370 switch (c) {
1371
1372 case WILL:
1373 state = TS_WILL;
1374 continue;
1375
1376 case WONT:
1377 state = TS_WONT;
1378 continue;
1379
1380 case DO:
1381 state = TS_DO;
1382 continue;
1383
1384 case DONT:
1385 state = TS_DONT;
1386 continue;
1387
1388 case DM:
1389 /*
1390 * We may have missed an urgent notification,
1391 * so make sure we flush whatever is in the
1392 * buffer currently.
1393 */
1394 SYNCHing = 1;
1395 ttyflush();
1396 SYNCHing = stilloob(net);
1397 settimer(gotDM);
1398 break;
1399
1400 case NOP:
1401 case GA:
1402 break;
1403
1404 case SB:
1405 SB_CLEAR();
1406 state = TS_SB;
1407 continue;
1408
1409# if defined(TN3270)
1410 case EOR:
1411 if (In3270) {
1412 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
1413 if (Ibackp == Ifrontp) {
1414 Ibackp = Ifrontp = Ibuf;
1415 ISend = 0; /* should have been! */
1416 } else {
1417 ISend = 1;
1418 }
1419 }
1420 break;
1421# endif /* defined(TN3270) */
1422
1423 case IAC:
1424# if !defined(TN3270)
1425 TTYADD(IAC);
1426# else /* !defined(TN3270) */
1427 if (In3270) {
1428 *Ifrontp++ = IAC;
1429 } else {
1430 TTYADD(IAC);
1431 }
1432# endif /* !defined(TN3270) */
1433 break;
1434
1435 default:
1436 break;
1437 }
1438 state = TS_DATA;
1439 continue;
1440
1441 case TS_WILL:
1442 printoption(">RCVD", will, c, !hisopts[c]);
1443 if (c == TELOPT_TM) {
1444 if (flushout) {
1445 flushout = 0;
1446 }
1447 } else if (!hisopts[c]) {
1448 willoption(c, 1);
1449 }
1450 SetIn3270();
1451 state = TS_DATA;
1452 continue;
1453
1454 case TS_WONT:
1455 printoption(">RCVD", wont, c, hisopts[c]);
1456 if (c == TELOPT_TM) {
1457 if (flushout) {
1458 flushout = 0;
1459 }
1460 } else if (hisopts[c]) {
1461 wontoption(c, 1);
1462 }
1463 SetIn3270();
1464 state = TS_DATA;
1465 continue;
1466
1467 case TS_DO:
1468 printoption(">RCVD", doopt, c, !myopts[c]);
1469 if (!myopts[c])
1470 dooption(c);
1471 SetIn3270();
1472 state = TS_DATA;
1473 continue;
1474
1475 case TS_DONT:
1476 printoption(">RCVD", dont, c, myopts[c]);
1477 if (myopts[c]) {
1478 myopts[c] = 0;
1479 sprintf(nfrontp, wont, c);
1480 nfrontp += sizeof (wont) - 2;
1481 flushline = 1;
1482 setconnmode(); /* set new tty mode (maybe) */
1483 printoption(">SENT", wont, c, 0);
1484 }
1485 SetIn3270();
1486 state = TS_DATA;
1487 continue;
1488
1489 case TS_SB:
1490 if (c == IAC) {
1491 state = TS_SE;
1492 } else {
1493 SB_ACCUM(c);
1494 }
1495 continue;
1496
1497 case TS_SE:
1498 if (c != SE) {
1499 if (c != IAC) {
1500 SB_ACCUM(IAC);
1501 }
1502 SB_ACCUM(c);
1503 state = TS_SB;
1504 } else {
1505 SB_TERM();
1506 suboption(); /* handle sub-option */
1507 SetIn3270();
1508 state = TS_DATA;
1509 }
1510 }
1511 }
1512}
1513\f
1514#if defined(TN3270)
1515
1516/*
1517 * The following routines are places where the various tn3270
1518 * routines make calls into telnet.c.
1519 */
1520
1521/* TtyChars() - returns the number of characters in the TTY buffer */
1522TtyChars()
1523{
1524 return(tfrontp-tbackp);
1525}
1526
1527/*
1528 * DataToNetwork - queue up some data to go to network. If "done" is set,
1529 * then when last byte is queued, we add on an IAC EOR sequence (so,
1530 * don't call us with "done" until you want that done...)
1531 *
1532 * We actually do send all the data to the network buffer, since our
1533 * only client needs for us to do that.
1534 */
1535
1536int
1537DataToNetwork(buffer, count, done)
1538register char *buffer; /* where the data is */
1539register int count; /* how much to send */
1540int done; /* is this the last of a logical block */
1541{
1542 register int c;
1543 int origCount;
1544 fd_set o;
1545
1546 origCount = count;
1547 FD_ZERO(&o);
1548
1549 while (count) {
1550 if ((netobuf+sizeof netobuf - nfrontp) < 6) {
1551 netflush();
1552 while ((netobuf+sizeof netobuf - nfrontp) < 6) {
1553 FD_SET(net, &o);
1554 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
1555 (struct timeval *) 0);
1556 netflush();
1557 }
1558 }
1559 c = *buffer++;
1560 count--;
1561 if (c == IAC) {
1562 *nfrontp++ = IAC;
1563 *nfrontp++ = IAC;
1564 } else {
1565 *nfrontp++ = c;
1566 }
1567 }
1568
1569 if (done && !count) {
1570 *nfrontp++ = IAC;
1571 *nfrontp++ = EOR;
1572 netflush(); /* try to move along as quickly as ... */
1573 }
1574 return(origCount - count);
1575}
1576
1577/* DataToTerminal - queue up some data to go to terminal. */
1578
1579int
1580DataToTerminal(buffer, count)
1581register char *buffer; /* where the data is */
1582register int count; /* how much to send */
1583{
1584 int origCount;
1585 fd_set o;
1586
1587 origCount = count;
1588 FD_ZERO(&o);
1589
1590 while (count) {
1591 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
1592 ttyflush();
1593 while (tfrontp >= ttyobuf+sizeof ttyobuf) {
1594 FD_SET(tout, &o);
1595 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
1596 (struct timeval *) 0);
1597 ttyflush();
1598 }
1599 }
1600 *tfrontp++ = *buffer++;
1601 count--;
1602 }
1603 return(origCount - count);
1604}
1605
1606/* EmptyTerminal - called to make sure that the terminal buffer is empty.
1607 * Note that we consider the buffer to run all the
1608 * way to the kernel (thus the select).
1609 */
1610
1611void
1612EmptyTerminal()
1613{
1614 fd_set o;
1615
1616 FD_ZERO(&o);
1617
1618 if (tfrontp == tbackp) {
1619 FD_SET(tout, &o);
1620 (void) select(tout+1, (int *) 0, &o, (int *) 0,
1621 (struct timeval *) 0); /* wait for TTLOWAT */
1622 } else {
1623 while (tfrontp != tbackp) {
1624 ttyflush();
1625 FD_SET(tout, &o);
1626 (void) select(tout+1, (int *) 0, &o, (int *) 0,
1627 (struct timeval *) 0); /* wait for TTLOWAT */
1628 }
1629 }
1630}
1631
1632/*
1633 * Push3270 - Try to send data along the 3270 output (to screen) direction.
1634 */
1635
1636static int
1637Push3270()
1638{
1639 int save = scc;
1640
1641 if (scc) {
1642 if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
1643 if (Ibackp != Ibuf) {
1644 bcopy(Ibackp, Ibuf, Ifrontp-Ibackp);
1645 Ifrontp -= (Ibackp-Ibuf);
1646 Ibackp = Ibuf;
1647 }
1648 }
1649 if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
1650 telrcv();
1651 }
1652 }
1653 return save != scc;
1654}
1655
1656
1657/*
1658 * Finish3270 - get the last dregs of 3270 data out to the terminal
1659 * before quitting.
1660 */
1661
1662static void
1663Finish3270()
1664{
1665 while (Push3270() || !DoTerminalOutput()) {
1666 ;
1667 }
1668}
1669
1670
1671
1672/* StringToTerminal - output a null terminated string to the terminal */
1673
1674void
1675StringToTerminal(s)
1676char *s;
1677{
1678 int count;
1679
1680 count = strlen(s);
1681 if (count) {
1682 (void) DataToTerminal(s, count); /* we know it always goes... */
1683 }
1684}
1685
1686
1687#if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
1688/* _putchar - output a single character to the terminal. This name is so that
1689 * curses(3x) can call us to send out data.
1690 */
1691
1692void
1693_putchar(c)
1694char c;
1695{
1696 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
1697 (void) DataToTerminal(&c, 1);
1698 } else {
1699 *tfrontp++ = c; /* optimize if possible. */
1700 }
1701}
1702#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
1703\f
1704static void
1705SetForExit()
1706{
1707 setconnmode();
1708 if (In3270) {
1709 Finish3270();
1710 }
1711 setcommandmode();
1712 fflush(stdout);
1713 fflush(stderr);
1714 if (In3270) {
1715 StopScreen(1);
1716 }
1717 setconnmode();
1718 setcommandmode();
1719}
1720
1721static void
1722Exit(returnCode)
1723int returnCode;
1724{
1725 SetForExit();
1726 exit(returnCode);
1727}
1728
1729void
1730ExitString(file, string, returnCode)
1731FILE *file;
1732char *string;
1733int returnCode;
1734{
1735 SetForExit();
1736 fwrite(string, 1, strlen(string), file);
1737 exit(returnCode);
1738}
1739
1740void
1741ExitPerror(string, returnCode)
1742char *string;
1743int returnCode;
1744{
1745 SetForExit();
1746 perror(string);
1747 exit(returnCode);
1748}
1749
1750#endif /* defined(TN3270) */
1751\f
1752static
1753Scheduler(block)
1754int block; /* should we block in the select ? */
1755{
1756 register int c;
1757 /* One wants to be a bit careful about setting returnValue
1758 * to one, since a one implies we did some useful work,
1759 * and therefore probably won't be called to block next
1760 * time (TN3270 mode only).
1761 */
1762 int returnValue = 0;
1763 static struct timeval TimeValue = { 0 };
1764
1765 if (scc < 0 && tcc < 0) {
1766 return -1;
1767 }
1768
1769 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
1770 FD_SET(net, &obits);
1771 }
1772 if (TTYBYTES()) {
1773 FD_SET(tout, &obits);
1774 }
1775 if ((tcc == 0) && NETROOM()) {
1776 FD_SET(tin, &ibits);
1777 }
1778# if !defined(TN3270)
1779 if (TTYROOM()) {
1780 FD_SET(net, &ibits);
1781 }
1782# else /* !defined(TN3270) */
1783 if (!ISend && TTYROOM()) {
1784 FD_SET(net, &ibits);
1785 }
1786# endif /* !defined(TN3270) */
1787 if (!SYNCHing) {
1788 FD_SET(net, &xbits);
1789 }
1790# if defined(TN3270) && defined(unix)
1791 if (HaveInput) {
1792 HaveInput = 0;
1793 signal(SIGIO, inputAvailable);
1794 }
1795#endif /* defined(TN3270) && defined(unix) */
1796 if ((c = select(16, &ibits, &obits, &xbits,
1797 block? (struct timeval *)0 : &TimeValue)) < 1) {
1798 if (c == -1) {
1799 /*
1800 * we can get EINTR if we are in line mode,
1801 * and the user does an escape (TSTP), or
1802 * some other signal generator.
1803 */
1804 if (errno == EINTR) {
1805 return 0;
1806 }
1807# if defined(TN3270)
1808 /*
1809 * we can get EBADF if we were in transparent
1810 * mode, and the transcom process died.
1811 */
1812 if (errno == EBADF) {
1813 /*
1814 * zero the bits (even though kernel does it)
1815 * to make sure we are selecting on the right
1816 * ones.
1817 */
1818 FD_ZERO(&ibits);
1819 FD_ZERO(&obits);
1820 FD_ZERO(&xbits);
1821 return 0;
1822 }
1823# endif /* defined(TN3270) */
1824 /* I don't like this, does it ever happen? */
1825 printf("sleep(5) from telnet, after select\r\n");
1826#if defined(unix)
1827 sleep(5);
1828#endif /* defined(unix) */
1829 }
1830 return 0;
1831 }
1832
1833 /*
1834 * Any urgent data?
1835 */
1836 if (FD_ISSET(net, &xbits)) {
1837 FD_CLR(net, &xbits);
1838 SYNCHing = 1;
1839 ttyflush(); /* flush already enqueued data */
1840 }
1841
1842 /*
1843 * Something to read from the network...
1844 */
1845 if (FD_ISSET(net, &ibits)) {
1846 int canread;
1847
1848 FD_CLR(net, &ibits);
1849 if (scc == 0) {
1850 sbp = sibuf;
1851 }
1852 canread = sibuf + sizeof sibuf - sbp;
1853#if !defined(SO_OOBINLINE)
1854 /*
1855 * In 4.2 (and some early 4.3) systems, the
1856 * OOB indication and data handling in the kernel
1857 * is such that if two separate TCP Urgent requests
1858 * come in, one byte of TCP data will be overlaid.
1859 * This is fatal for Telnet, but we try to live
1860 * with it.
1861 *
1862 * In addition, in 4.2 (and...), a special protocol
1863 * is needed to pick up the TCP Urgent data in
1864 * the correct sequence.
1865 *
1866 * What we do is: if we think we are in urgent
1867 * mode, we look to see if we are "at the mark".
1868 * If we are, we do an OOB receive. If we run
1869 * this twice, we will do the OOB receive twice,
1870 * but the second will fail, since the second
1871 * time we were "at the mark", but there wasn't
1872 * any data there (the kernel doesn't reset
1873 * "at the mark" until we do a normal read).
1874 * Once we've read the OOB data, we go ahead
1875 * and do normal reads.
1876 *
1877 * There is also another problem, which is that
1878 * since the OOB byte we read doesn't put us
1879 * out of OOB state, and since that byte is most
1880 * likely the TELNET DM (data mark), we would
1881 * stay in the TELNET SYNCH (SYNCHing) state.
1882 * So, clocks to the rescue. If we've "just"
1883 * received a DM, then we test for the
1884 * presence of OOB data when the receive OOB
1885 * fails (and AFTER we did the normal mode read
1886 * to clear "at the mark").
1887 */
1888 if (SYNCHing) {
1889 int atmark;
1890
1891 ioctl(net, SIOCATMARK, (char *)&atmark);
1892 if (atmark) {
1893 c = recv(net, sibuf, canread, MSG_OOB);
1894 if ((c == -1) && (errno == EINVAL)) {
1895 c = read(net, sibuf, canread);
1896 if (clocks.didnetreceive < clocks.gotDM) {
1897 SYNCHing = stilloob(net);
1898 }
1899 }
1900 } else {
1901 c = read(net, sibuf, canread);
1902 }
1903 } else {
1904 c = read(net, sibuf, canread);
1905 }
1906 settimer(didnetreceive);
1907#else /* !defined(SO_OOBINLINE) */
1908 c = read(net, sbp, canread);
1909#endif /* !defined(SO_OOBINLINE) */
1910 if (c < 0 && errno == EWOULDBLOCK) {
1911 c = 0;
1912 } else if (c <= 0) {
1913 return -1;
1914 }
1915 if (netdata) {
1916 Dump('<', sbp, c);
1917 }
1918 scc += c;
1919 returnValue = 1;
1920 }
1921
1922 /*
1923 * Something to read from the tty...
1924 */
1925 if (FD_ISSET(tin, &ibits)) {
1926 FD_CLR(tin, &ibits);
1927 if (tcc == 0) {
1928 tbp = tibuf; /* nothing left, reset */
1929 }
1930 c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
1931 if (c < 0 && errno == EWOULDBLOCK) {
1932 c = 0;
1933 } else {
1934 /* EOF detection for line mode!!!! */
1935 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
1936 /* must be an EOF... */
1937 *tbp = ntc.t_eofc;
1938 c = 1;
1939 }
1940 if (c <= 0) {
1941 tcc = c;
1942 return -1;
1943 }
1944 }
1945 tcc += c;
1946 returnValue = 1; /* did something useful */
1947 }
1948
1949# if defined(TN3270)
1950 if (tcc > 0) {
1951 if (In3270) {
1952 c = DataFromTerminal(tbp, tcc);
1953 if (c) {
1954 returnValue = 1;
1955 }
1956 tcc -= c;
1957 tbp += c;
1958 } else {
1959# endif defined(TN3270)
1960 returnValue = 1;
1961 while (tcc > 0) {
1962 register int sc;
1963
1964 if (NETROOM() < 2) {
1965 flushline = 1;
1966 break;
1967 }
1968 c = *tbp++ & 0xff, sc = strip(c), tcc--;
1969 if (sc == escape) {
1970 command(0);
1971 tcc = 0;
1972 flushline = 1;
1973 break;
1974 } else if (MODE_LINE(globalmode) && (sc == echoc)) {
1975 if (tcc > 0 && strip(*tbp) == echoc) {
1976 tbp++;
1977 tcc--;
1978 } else {
1979 dontlecho = !dontlecho;
1980 settimer(echotoggle);
1981 setconnmode();
1982 tcc = 0;
1983 flushline = 1;
1984 break;
1985 }
1986 }
1987 if (localchars) {
1988 if (sc == ntc.t_intrc) {
1989 intp();
1990 break;
1991 } else if (sc == ntc.t_quitc) {
1992 sendbrk();
1993 break;
1994 } else if (sc == nltc.t_flushc) {
1995 NET2ADD(IAC, AO);
1996 if (autoflush) {
1997 doflush();
1998 }
1999 break;
2000 } else if (MODE_LOCAL_CHARS(globalmode)) {
2001 ;
2002 } else if (sc == nttyb.sg_kill) {
2003 NET2ADD(IAC, EL);
2004 break;
2005 } else if (sc == nttyb.sg_erase) {
2006 NET2ADD(IAC, EC);
2007 break;
2008 }
2009 }
2010 switch (c) {
2011 case '\n':
2012 /*
2013 * If we are in CRMOD mode (\r ==> \n)
2014 * on our local machine, then probably
2015 * a newline (unix) is CRLF (TELNET).
2016 */
2017 if (MODE_LOCAL_CHARS(globalmode)) {
2018 NETADD('\r');
2019 }
2020 NETADD('\n');
2021 flushline = 1;
2022 break;
2023 case '\r':
2024 NET2ADD('\r', '\0');
2025 flushline = 1;
2026 break;
2027 case IAC:
2028 NET2ADD(IAC, IAC);
2029 break;
2030 default:
2031 NETADD(c);
2032 break;
2033 }
2034 }
2035# if defined(TN3270)
2036 }
2037 }
2038# endif /* defined(TN3270) */
2039
2040 if ((!MODE_LINE(globalmode) || flushline) &&
2041 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2042 FD_CLR(net, &obits);
2043 returnValue = netflush();
2044 }
2045 if (scc > 0) {
2046# if !defined(TN3270)
2047 telrcv();
2048 returnValue = 1;
2049# else /* !defined(TN3270) */
2050 returnValue = Push3270();
2051# endif /* !defined(TN3270) */
2052 }
2053 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
2054 FD_CLR(tout, &obits);
2055 returnValue = ttyflush();
2056 }
2057 return returnValue;
2058}
2059\f
2060/*
2061 * Select from tty and network...
2062 */
2063static void
2064telnet()
2065{
2066 int on = 1;
2067#if defined(TN3270) && defined(unix)
2068 int myPid;
2069#endif /* defined(TN3270) */
2070
2071 tout = fileno(stdout);
2072 tin = fileno(stdin);
2073 setconnmode();
2074 scc = 0;
2075 tcc = 0;
2076 FD_ZERO(&ibits);
2077 FD_ZERO(&obits);
2078 FD_ZERO(&xbits);
2079
2080 ioctl(net, FIONBIO, (char *)&on);
2081
2082#if defined(TN3270)
2083#if !defined(DEBUG) /* DBX can't handle! */
2084 ioctl(net, FIOASYNC, (char *)&on); /* hear about input */
2085#endif /* !defined(DEBUG) */
2086
2087#if defined(unix)
2088 myPid = getpid();
2089#if defined(NOT43)
2090 myPid = -myPid;
2091#endif /* defined(NOT43) */
2092 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */
2093#endif /* defined(unix) */
2094
2095#endif /* defined(TN3270) */
2096
2097#if defined(SO_OOBINLINE)
2098 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
2099#endif /* defined(SO_OOBINLINE) */
2100
2101 if (telnetport) {
2102 if (!hisopts[TELOPT_SGA]) {
2103 willoption(TELOPT_SGA, 0);
2104 }
2105# if !defined(TN3270)
2106 if (!myopts[TELOPT_TTYPE]) {
2107 dooption(TELOPT_TTYPE, 0);
2108 }
2109# endif !defined(TN3270)
2110 }
2111
2112# if !defined(TN3270)
2113 for (;;) {
2114 if (Scheduler(1) == -1) {
2115 setcommandmode();
2116 return;
2117 }
2118 }
2119# else /* !defined(TN3270) */
2120 for (;;) {
2121 int schedValue;
2122
2123 while (!In3270) {
2124 if (Scheduler(1) == -1) {
2125 setcommandmode();
2126 return;
2127 }
2128 }
2129
2130 while ((schedValue = Scheduler(0)) != 0) {
2131 if (schedValue == -1) {
2132 setcommandmode();
2133 return;
2134 }
2135 }
2136 /* If there is data waiting to go out to terminal, don't
2137 * schedule any more data for the terminal.
2138 */
2139 if (tfrontp-tbackp) {
2140 schedValue = 1;
2141 } else {
2142 schedValue = DoTerminalOutput();
2143 }
2144 if (schedValue) {
2145 if (Scheduler(1) == -1) {
2146 setcommandmode();
2147 return;
2148 }
2149 }
2150 }
2151# endif /* !defined(TN3270) */
2152}
2153\f
2154/*
2155 * The following are data structures and routines for
2156 * the "send" command.
2157 *
2158 */
2159
2160struct sendlist {
2161 char *name; /* How user refers to it (case independent) */
2162 int what; /* Character to be sent (<0 ==> special) */
2163 char *help; /* Help information (0 ==> no help) */
2164#if defined(NOT43)
2165 int (*routine)(); /* Routine to perform (for special ops) */
2166#else /* defined(NOT43) */
2167 void (*routine)(); /* Routine to perform (for special ops) */
2168#endif /* defined(NOT43) */
2169};
2170\f
2171#define SENDQUESTION -1
2172#define SENDESCAPE -3
2173
2174static struct sendlist Sendlist[] = {
2175 { "ao", AO, "Send Telnet Abort output" },
2176 { "ayt", AYT, "Send Telnet 'Are You There'" },
2177 { "brk", BREAK, "Send Telnet Break" },
2178 { "ec", EC, "Send Telnet Erase Character" },
2179 { "el", EL, "Send Telnet Erase Line" },
2180 { "escape", SENDESCAPE, "Send current escape character" },
2181 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2182 { "ip", IP, "Send Telnet Interrupt Process" },
2183 { "nop", NOP, "Send Telnet 'No operation'" },
2184 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2185 { "?", SENDQUESTION, "Display send options" },
2186 { 0 }
2187};
2188
2189static struct sendlist Sendlist2[] = { /* some synonyms */
2190 { "break", BREAK, 0 },
2191
2192 { "intp", IP, 0 },
2193 { "interrupt", IP, 0 },
2194 { "intr", IP, 0 },
2195
2196 { "help", SENDQUESTION, 0 },
2197
2198 { 0 }
2199};
2200
2201static char **
2202getnextsend(name)
2203char *name;
2204{
2205 struct sendlist *c = (struct sendlist *) name;
2206
2207 return (char **) (c+1);
2208}
2209
2210static struct sendlist *
2211getsend(name)
2212char *name;
2213{
2214 struct sendlist *sl;
2215
2216 if ((sl = (struct sendlist *)
2217 genget(name, (char **) Sendlist, getnextsend)) != 0) {
2218 return sl;
2219 } else {
2220 return (struct sendlist *)
2221 genget(name, (char **) Sendlist2, getnextsend);
2222 }
2223}
2224
2225static
2226sendcmd(argc, argv)
2227int argc;
2228char **argv;
2229{
2230 int what; /* what we are sending this time */
2231 int count; /* how many bytes we are going to need to send */
2232 int i;
2233 int question = 0; /* was at least one argument a question */
2234 struct sendlist *s; /* pointer to current command */
2235
2236 if (argc < 2) {
2237 printf("need at least one argument for 'send' command\n");
2238 printf("'send ?' for help\n");
2239 return 0;
2240 }
2241 /*
2242 * First, validate all the send arguments.
2243 * In addition, we see how much space we are going to need, and
2244 * whether or not we will be doing a "SYNCH" operation (which
2245 * flushes the network queue).
2246 */
2247 count = 0;
2248 for (i = 1; i < argc; i++) {
2249 s = getsend(argv[i]);
2250 if (s == 0) {
2251 printf("Unknown send argument '%s'\n'send ?' for help.\n",
2252 argv[i]);
2253 return 0;
2254 } else if (s == Ambiguous(struct sendlist *)) {
2255 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2256 argv[i]);
2257 return 0;
2258 }
2259 switch (s->what) {
2260 case SENDQUESTION:
2261 break;
2262 case SENDESCAPE:
2263 count += 1;
2264 break;
2265 case SYNCH:
2266 count += 2;
2267 break;
2268 default:
2269 count += 2;
2270 break;
2271 }
2272 }
2273 /* Now, do we have enough room? */
2274 if (NETROOM() < count) {
2275 printf("There is not enough room in the buffer TO the network\n");
2276 printf("to process your request. Nothing will be done.\n");
2277 printf("('send synch' will throw away most data in the network\n");
2278 printf("buffer, if this might help.)\n");
2279 return 0;
2280 }
2281 /* OK, they are all OK, now go through again and actually send */
2282 for (i = 1; i < argc; i++) {
2283 if ((s = getsend(argv[i])) == 0) {
2284 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
2285 quit();
2286 /*NOTREACHED*/
2287 }
2288 if (s->routine) {
2289 (*s->routine)(s);
2290 } else {
2291 switch (what = s->what) {
2292 case SYNCH:
2293 dosynch();
2294 break;
2295 case SENDQUESTION:
2296 for (s = Sendlist; s->name; s++) {
2297 if (s->help) {
2298 printf(s->name);
2299 if (s->help) {
2300 printf("\t%s", s->help);
2301 }
2302 printf("\n");
2303 }
2304 }
2305 question = 1;
2306 break;
2307 case SENDESCAPE:
2308 NETADD(escape);
2309 break;
2310 default:
2311 NET2ADD(IAC, what);
2312 break;
2313 }
2314 }
2315 }
2316 return !question;
2317}
2318\f
2319/*
2320 * The following are the routines and data structures referred
2321 * to by the arguments to the "toggle" command.
2322 */
2323
2324static
2325lclchars()
2326{
2327 donelclchars = 1;
2328 return 1;
2329}
2330
2331static
2332togdebug()
2333{
2334#ifndef NOT43
2335 if (net > 0 &&
2336 setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
2337 < 0) {
2338 perror("setsockopt (SO_DEBUG)");
2339 }
2340#else NOT43
2341 if (debug) {
2342 if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
2343 perror("setsockopt (SO_DEBUG)");
2344 } else
2345 printf("Cannot turn off socket debugging\n");
2346#endif NOT43
2347 return 1;
2348}
2349
2350
2351
2352extern int togglehelp();
2353
2354struct togglelist {
2355 char *name; /* name of toggle */
2356 char *help; /* help message */
2357 int (*handler)(); /* routine to do actual setting */
2358 int dohelp; /* should we display help information */
2359 int *variable;
2360 char *actionexplanation;
2361};
2362
2363static struct togglelist Togglelist[] = {
2364 { "autoflush",
2365 "toggle flushing of output when sending interrupt characters",
2366 0,
2367 1,
2368 &autoflush,
2369 "flush output when sending interrupt characters" },
2370 { "autosynch",
2371 "toggle automatic sending of interrupt characters in urgent mode",
2372 0,
2373 1,
2374 &autosynch,
2375 "send interrupt characters in urgent mode" },
2376 { "crmod",
2377 "toggle mapping of received carriage returns",
2378 0,
2379 1,
2380 &crmod,
2381 "map carriage return on output" },
2382 { "localchars",
2383 "toggle local recognition of certain control characters",
2384 lclchars,
2385 1,
2386 &localchars,
2387 "recognize certain control characters" },
2388 { " ", "", 0, 1 }, /* empty line */
2389 { "debug",
2390 "(debugging) toggle debugging",
2391 togdebug,
2392 1,
2393 &debug,
2394 "turn on socket level debugging" },
2395 { "netdata",
2396 "(debugging) toggle printing of hexadecimal network data",
2397 0,
2398 1,
2399 &netdata,
2400 "print hexadecimal representation of network traffic" },
2401 { "options",
2402 "(debugging) toggle viewing of options processing",
2403 0,
2404 1,
2405 &showoptions,
2406 "show option processing" },
2407 { " ", "", 0, 1 }, /* empty line */
2408 { "?",
2409 "display help information",
2410 togglehelp,
2411 1 },
2412 { "help",
2413 "display help information",
2414 togglehelp,
2415 0 },
2416 { 0 }
2417};
2418
2419static
2420togglehelp()
2421{
2422 struct togglelist *c;
2423
2424 for (c = Togglelist; c->name; c++) {
2425 if (c->dohelp) {
2426 printf("%s\t%s\n", c->name, c->help);
2427 }
2428 }
2429 return 0;
2430}
2431
2432static char **
2433getnexttoggle(name)
2434char *name;
2435{
2436 struct togglelist *c = (struct togglelist *) name;
2437
2438 return (char **) (c+1);
2439}
2440
2441static struct togglelist *
2442gettoggle(name)
2443char *name;
2444{
2445 return (struct togglelist *)
2446 genget(name, (char **) Togglelist, getnexttoggle);
2447}
2448
2449static
2450toggle(argc, argv)
2451int argc;
2452char *argv[];
2453{
2454 int retval = 1;
2455 char *name;
2456 struct togglelist *c;
2457
2458 if (argc < 2) {
2459 fprintf(stderr,
2460 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
2461 return 0;
2462 }
2463 argc--;
2464 argv++;
2465 while (argc--) {
2466 name = *argv++;
2467 c = gettoggle(name);
2468 if (c == Ambiguous(struct togglelist *)) {
2469 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
2470 name);
2471 return 0;
2472 } else if (c == 0) {
2473 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
2474 name);
2475 return 0;
2476 } else {
2477 if (c->variable) {
2478 *c->variable = !*c->variable; /* invert it */
2479 printf("%s %s.\n", *c->variable? "Will" : "Won't",
2480 c->actionexplanation);
2481 }
2482 if (c->handler) {
2483 retval &= (*c->handler)(c);
2484 }
2485 }
2486 }
2487 return retval;
2488}
2489\f
2490/*
2491 * The following perform the "set" command.
2492 */
2493
2494struct setlist {
2495 char *name; /* name */
2496 char *help; /* help information */
2497 char *charp; /* where it is located at */
2498};
2499
2500static struct setlist Setlist[] = {
2501 { "echo", "character to toggle local echoing on/off", &echoc },
2502 { "escape", "character to escape back to telnet command mode", &escape },
2503 { " ", "" },
2504 { " ", "The following need 'localchars' to be toggled true", 0 },
2505 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
2506 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
2507 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
2508 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
2509 { "quit", "character to cause a Break", &ntc.t_quitc },
2510 { "eof", "character to cause an EOF ", &ntc.t_eofc },
2511 { 0 }
2512};
2513
2514static char **
2515getnextset(name)
2516char *name;
2517{
2518 struct setlist *c = (struct setlist *)name;
2519
2520 return (char **) (c+1);
2521}
2522
2523static struct setlist *
2524getset(name)
2525char *name;
2526{
2527 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
2528}
2529
2530static
2531setcmd(argc, argv)
2532int argc;
2533char *argv[];
2534{
2535 int value;
2536 struct setlist *ct;
2537
2538 /* XXX back we go... sigh */
2539 if (argc != 3) {
2540 if ((argc == 2) &&
2541 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
2542 for (ct = Setlist; ct->name; ct++) {
2543 printf("%s\t%s\n", ct->name, ct->help);
2544 }
2545 printf("?\tdisplay help information\n");
2546 } else {
2547 printf("Format is 'set Name Value'\n'set ?' for help.\n");
2548 }
2549 return 0;
2550 }
2551
2552 ct = getset(argv[1]);
2553 if (ct == 0) {
2554 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
2555 argv[1]);
2556 return 0;
2557 } else if (ct == Ambiguous(struct setlist *)) {
2558 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
2559 argv[1]);
2560 return 0;
2561 } else {
2562 if (strcmp("off", argv[2])) {
2563 value = special(argv[2]);
2564 } else {
2565 value = -1;
2566 }
2567 *(ct->charp) = value;
2568 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
2569 }
2570 return 1;
2571}
2572\f
2573/*
2574 * The following are the data structures and routines for the
2575 * 'mode' command.
2576 */
2577
2578static
2579dolinemode()
2580{
2581 if (hisopts[TELOPT_SGA]) {
2582 wontoption(TELOPT_SGA, 0);
2583 }
2584 if (hisopts[TELOPT_ECHO]) {
2585 wontoption(TELOPT_ECHO, 0);
2586 }
2587 return 1;
2588}
2589
2590static
2591docharmode()
2592{
2593 if (!hisopts[TELOPT_SGA]) {
2594 willoption(TELOPT_SGA, 0);
2595 }
2596 if (!hisopts[TELOPT_ECHO]) {
2597 willoption(TELOPT_ECHO, 0);
2598 }
2599 return 1;
2600}
2601
2602static struct cmd Modelist[] = {
2603 { "character", "character-at-a-time mode", docharmode, 1, 1 },
2604 { "line", "line-by-line mode", dolinemode, 1, 1 },
2605 { 0 },
2606};
2607
2608static char **
2609getnextmode(name)
2610char *name;
2611{
2612 struct cmd *c = (struct cmd *) name;
2613
2614 return (char **) (c+1);
2615}
2616
2617static struct cmd *
2618getmodecmd(name)
2619char *name;
2620{
2621 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
2622}
2623
2624static
2625modecmd(argc, argv)
2626int argc;
2627char *argv[];
2628{
2629 struct cmd *mt;
2630
2631 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
2632 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
2633 for (mt = Modelist; mt->name; mt++) {
2634 printf("%s\t%s\n", mt->name, mt->help);
2635 }
2636 return 0;
2637 }
2638 mt = getmodecmd(argv[1]);
2639 if (mt == 0) {
2640 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
2641 return 0;
2642 } else if (mt == Ambiguous(struct cmd *)) {
2643 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
2644 return 0;
2645 } else {
2646 (*mt->handler)();
2647 }
2648 return 1;
2649}
2650\f
2651/*
2652 * The following data structures and routines implement the
2653 * "display" command.
2654 */
2655
2656static
2657display(argc, argv)
2658int argc;
2659char *argv[];
2660{
2661#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
2662 if (*tl->variable) { \
2663 printf("will"); \
2664 } else { \
2665 printf("won't"); \
2666 } \
2667 printf(" %s.\n", tl->actionexplanation); \
2668 }
2669
2670#define doset(sl) if (sl->name && *sl->name != ' ') { \
2671 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
2672 }
2673
2674 struct togglelist *tl;
2675 struct setlist *sl;
2676
2677 if (argc == 1) {
2678 for (tl = Togglelist; tl->name; tl++) {
2679 dotog(tl);
2680 }
2681 printf("\n");
2682 for (sl = Setlist; sl->name; sl++) {
2683 doset(sl);
2684 }
2685 } else {
2686 int i;
2687
2688 for (i = 1; i < argc; i++) {
2689 sl = getset(argv[i]);
2690 tl = gettoggle(argv[i]);
2691 if ((sl == Ambiguous(struct setlist *)) ||
2692 (tl == Ambiguous(struct togglelist *))) {
2693 printf("?Ambiguous argument '%s'.\n", argv[i]);
2694 return 0;
2695 } else if (!sl && !tl) {
2696 printf("?Unknown argument '%s'.\n", argv[i]);
2697 return 0;
2698 } else {
2699 if (tl) {
2700 dotog(tl);
2701 }
2702 if (sl) {
2703 doset(sl);
2704 }
2705 }
2706 }
2707 }
2708 return 1;
2709#undef doset
2710#undef dotog
2711}
2712\f
2713/*
2714 * The following are the data structures, and many of the routines,
2715 * relating to command processing.
2716 */
2717
2718/*
2719 * Set the escape character.
2720 */
2721static
2722setescape(argc, argv)
2723 int argc;
2724 char *argv[];
2725{
2726 register char *arg;
2727 char buf[50];
2728
2729 printf(
2730 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
2731 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
2732 if (argc > 2)
2733 arg = argv[1];
2734 else {
2735 printf("new escape character: ");
2736 gets(buf);
2737 arg = buf;
2738 }
2739 if (arg[0] != '\0')
2740 escape = arg[0];
2741 if (!In3270) {
2742 printf("Escape character is '%s'.\n", control(escape));
2743 }
2744 fflush(stdout);
2745 return 1;
2746}
2747
2748/*VARARGS*/
2749static
2750togcrmod()
2751{
2752 crmod = !crmod;
2753 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
2754 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
2755 fflush(stdout);
2756 return 1;
2757}
2758
2759/*VARARGS*/
2760suspend()
2761{
2762 setcommandmode();
2763#if defined(unix)
2764 kill(0, SIGTSTP);
2765#endif /* defined(unix) */
2766 /* reget parameters in case they were changed */
2767 ioctl(0, TIOCGETP, (char *)&ottyb);
2768 ioctl(0, TIOCGETC, (char *)&otc);
2769 ioctl(0, TIOCGLTC, (char *)&oltc);
2770 return 1;
2771}
2772
2773/*VARARGS*/
2774static
2775bye()
2776{
2777 if (connected) {
2778 shutdown(net, 2);
2779 printf("Connection closed.\n");
2780 close(net);
2781 connected = 0;
2782 /* reset options */
2783 bzero((char *)hisopts, sizeof hisopts);
2784 bzero((char *)myopts, sizeof myopts);
2785 SYNCHing = flushout = 0;
2786 flushline = 1;
2787#if defined(TN3270)
2788 /*
2789 * The problem is that we were called from command() which
2790 * was called from DataFrom3270() which was called from
2791 * DataFromTerminal() which was called from...
2792 *
2793 * So, just quit.
2794 */
2795 if (In3270) {
2796 Exit(0);
2797 }
2798#endif /* defined(TN3270) */
2799 }
2800 return 1;
2801}
2802
2803/*VARARGS*/
2804quit()
2805{
2806 (void) call(bye, "bye", 0);
2807 Exit(0);
2808 /*NOTREACHED*/
2809 return 1; /* just to keep lint happy */
2810}
2811
2812/*
2813 * Print status about the connection.
2814 */
2815static
2816status(argc, argv)
2817int argc;
2818char *argv[];
2819{
2820 if (connected) {
2821 printf("Connected to %s.\n", hostname);
2822 if (argc < 2) {
2823 printf("Operating in %s.\n",
2824 modelist[getconnmode()].modedescriptions);
2825 if (localchars) {
2826 printf("Catching signals locally.\n");
2827 }
2828 }
2829 } else {
2830 printf("No connection.\n");
2831 }
2832# if !defined(TN3270)
2833 printf("Escape character is '%s'.\n", control(escape));
2834 fflush(stdout);
2835# else /* !defined(TN3270) */
2836 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
2837 printf("Escape character is '%s'.\n", control(escape));
2838 }
2839# if defined(unix)
2840 if (In3270 && transcom) {
2841 printf("Transparent mode command is '%s'.\n", transcom);
2842 }
2843# endif /* defined(unix) */
2844 fflush(stdout);
2845 if (In3270) {
2846 return 0;
2847 }
2848# endif /* defined(TN3270) */
2849 return 1;
2850}
2851
2852#if defined(TN3270) && defined(unix)
2853static
2854settranscom(argc, argv)
2855 int argc;
2856 char *argv[];
2857{
2858 int i, len = 0;
2859 char *strcpy(), *strcat();
2860
2861 if (argc == 1 && transcom) {
2862 transcom = 0;
2863 }
2864 if (argc == 1) {
2865 return;
2866 }
2867 for (i = 1; i < argc; ++i) {
2868 len += 1 + strlen(argv[1]);
2869 }
2870 transcom = tline;
2871 (void) strcpy(transcom, argv[1]);
2872 for (i = 2; i < argc; ++i) {
2873 (void) strcat(transcom, " ");
2874 (void) strcat(transcom, argv[i]);
2875 }
2876}
2877#endif /* defined(TN3270) && defined(unix) */
2878
2879
2880static
2881tn(argc, argv)
2882 int argc;
2883 char *argv[];
2884{
2885 register struct hostent *host = 0;
2886#if defined(msdos)
2887 char *cp;
2888#endif /* defined(msdos) */
2889
2890 if (connected) {
2891 printf("?Already connected to %s\n", hostname);
2892 return 0;
2893 }
2894 if (argc < 2) {
2895 (void) strcpy(line, "Connect ");
2896 printf("(to) ");
2897 gets(&line[strlen(line)]);
2898 makeargv();
2899 argc = margc;
2900 argv = margv;
2901 }
2902 if (argc > 3) {
2903 printf("usage: %s host-name [port]\n", argv[0]);
2904 return 0;
2905 }
2906#if defined(msdos)
2907 for (cp = argv[1]; *cp; cp++) {
2908 if (isupper(*cp)) {
2909 *cp = tolower(*cp);
2910 }
2911 }
2912#endif /* defined(msdos) */
2913 sin.sin_addr.s_addr = inet_addr(argv[1]);
2914 if (sin.sin_addr.s_addr != -1) {
2915 sin.sin_family = AF_INET;
2916 (void) strcpy(hnamebuf, argv[1]);
2917 hostname = hnamebuf;
2918 } else {
2919 host = gethostbyname(argv[1]);
2920 if (host) {
2921 sin.sin_family = host->h_addrtype;
2922#if defined(h_addr) /* In 4.3, this is a #define */
2923 bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr, host->h_length);
2924#else /* defined(h_addr) */
2925 bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
2926#endif /* defined(h_addr) */
2927 hostname = host->h_name;
2928 } else {
2929 printf("%s: unknown host\n", argv[1]);
2930 return 0;
2931 }
2932 }
2933 sin.sin_port = sp->s_port;
2934 if (argc == 3) {
2935 sin.sin_port = atoi(argv[2]);
2936 if (sin.sin_port == 0) {
2937 sp = getservbyname(argv[2], "tcp");
2938 if (sp)
2939 sin.sin_port = sp->s_port;
2940 else {
2941 printf("%s: bad port number\n", argv[2]);
2942 return 0;
2943 }
2944 } else {
2945 sin.sin_port = atoi(argv[2]);
2946 sin.sin_port = htons(sin.sin_port);
2947 }
2948 telnetport = 0;
2949 } else {
2950 telnetport = 1;
2951 }
2952#if defined(unix)
2953 signal(SIGINT, intr);
2954 signal(SIGQUIT, intr2);
2955 signal(SIGPIPE, deadpeer);
2956#endif /* defined(unix) */
2957 printf("Trying...\n");
2958 do {
2959 net = socket(AF_INET, SOCK_STREAM, 0);
2960 if (net < 0) {
2961 perror("telnet: socket");
2962 return 0;
2963 }
2964#ifndef NOT43
2965 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG,
2966 (char *)&debug, sizeof(debug)) < 0)
2967#else NOT43
2968 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
2969#endif NOT43
2970 perror("setsockopt (SO_DEBUG)");
2971
2972 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
2973#if defined(h_addr) /* In 4.3, this is a #define */
2974 if (host && host->h_addr_list[1]) {
2975 int oerrno = errno;
2976
2977 fprintf(stderr, "telnet: connect to address %s: ",
2978 inet_ntoa(sin.sin_addr));
2979 errno = oerrno;
2980 perror((char *)0);
2981 host->h_addr_list++;
2982 bcopy(host->h_addr_list[0],
2983 (caddr_t)&sin.sin_addr, host->h_length);
2984 fprintf(stderr, "Trying %s...\n",
2985 inet_ntoa(sin.sin_addr));
2986 (void) close(net);
2987 continue;
2988 }
2989#endif /* defined(h_addr) */
2990 perror("telnet: Unable to connect to remote host");
2991#if defined(unix)
2992 signal(SIGINT, SIG_DFL);
2993 signal(SIGQUIT, SIG_DFL);
2994#endif /* defined(unix) */
2995 return 0;
2996 }
2997 connected++;
2998 } while (connected == 0);
2999 call(status, "status", "notmuch", 0);
3000 if (setjmp(peerdied) == 0)
3001 telnet();
3002 ExitString(stderr, "Connection closed by foreign host.\n",1);
3003 /*NOTREACHED*/
3004}
3005
3006
3007#define HELPINDENT (sizeof ("connect"))
3008
3009static char
3010 openhelp[] = "connect to a site",
3011 closehelp[] = "close current connection",
3012 quithelp[] = "exit telnet",
3013 zhelp[] = "suspend telnet",
3014 statushelp[] = "print status information",
3015 helphelp[] = "print help information",
3016 sendhelp[] = "transmit special characters ('send ?' for more)",
3017 sethelp[] = "set operating parameters ('set ?' for more)",
3018 togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3019 displayhelp[] = "display operating parameters",
3020#if defined(TN3270) && defined(unix)
3021 transcomhelp[] = "specify Unix command for transparent mode pipe",
3022#endif /* defined(TN3270) && defined(unix) */
3023 modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3024
3025extern int help();
3026
3027static struct cmd cmdtab[] = {
3028 { "close", closehelp, bye, 1, 1 },
3029 { "display", displayhelp, display, 1, 0 },
3030 { "mode", modehelp, modecmd, 1, 1 },
3031 { "open", openhelp, tn, 1, 0 },
3032 { "quit", quithelp, quit, 1, 0 },
3033 { "send", sendhelp, sendcmd, 1, 1 },
3034 { "set", sethelp, setcmd, 1, 0 },
3035 { "status", statushelp, status, 1, 0 },
3036 { "toggle", togglestring, toggle, 1, 0 },
3037#if defined(TN3270) && defined(unix)
3038 { "transcom", transcomhelp, settranscom, 1, 0 },
3039#endif /* defined(TN3270) && defined(unix) */
3040 { "z", zhelp, suspend, 1, 0 },
3041 { "?", helphelp, help, 1, 0 },
3042 0
3043};
3044
3045static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
3046static char escapehelp[] = "deprecated command -- use 'set escape' instead";
3047
3048static struct cmd cmdtab2[] = {
3049 { "help", helphelp, help, 0, 0 },
3050 { "escape", escapehelp, setescape, 1, 0 },
3051 { "crmod", crmodhelp, togcrmod, 1, 0 },
3052 0
3053};
3054
3055/*
3056 * Call routine with argc, argv set from args (terminated by 0).
3057 * VARARGS2
3058 */
3059static
3060call(routine, args)
3061 int (*routine)();
3062 char *args;
3063{
3064 register char **argp;
3065 register int argc;
3066
3067 for (argc = 0, argp = &args; *argp++ != 0; argc++)
3068 ;
3069 return (*routine)(argc, &args);
3070}
3071
3072static char **
3073getnextcmd(name)
3074char *name;
3075{
3076 struct cmd *c = (struct cmd *) name;
3077
3078 return (char **) (c+1);
3079}
3080
3081static struct cmd *
3082getcmd(name)
3083char *name;
3084{
3085 struct cmd *cm;
3086
3087 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3088 return cm;
3089 } else {
3090 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3091 }
3092}
3093
3094void
3095command(top)
3096 int top;
3097{
3098 register struct cmd *c;
3099
3100 setcommandmode();
3101 if (!top) {
3102 putchar('\n');
3103 } else {
3104#if defined(unix)
3105 signal(SIGINT, SIG_DFL);
3106 signal(SIGQUIT, SIG_DFL);
3107#endif /* defined(unix) */
3108 }
3109 for (;;) {
3110 printf("%s> ", prompt);
3111 if (gets(line) == NULL) {
3112 if (feof(stdin) || ferror(stdin))
3113 quit();
3114 break;
3115 }
3116 if (line[0] == 0)
3117 break;
3118 makeargv();
3119 c = getcmd(margv[0]);
3120 if (c == Ambiguous(struct cmd *)) {
3121 printf("?Ambiguous command\n");
3122 continue;
3123 }
3124 if (c == 0) {
3125 printf("?Invalid command\n");
3126 continue;
3127 }
3128 if (c->needconnect && !connected) {
3129 printf("?Need to be connected first.\n");
3130 continue;
3131 }
3132 if ((*c->handler)(margc, margv)) {
3133 break;
3134 }
3135 }
3136 if (!top) {
3137 if (!connected) {
3138 longjmp(toplevel, 1);
3139 /*NOTREACHED*/
3140 }
3141 setconnmode();
3142 }
3143}
3144\f
3145/*
3146 * Help command.
3147 */
3148static
3149help(argc, argv)
3150 int argc;
3151 char *argv[];
3152{
3153 register struct cmd *c;
3154
3155 if (argc == 1) {
3156 printf("Commands may be abbreviated. Commands are:\n\n");
3157 for (c = cmdtab; c->name; c++)
3158 if (c->dohelp) {
3159 printf("%-*s\t%s\n", HELPINDENT, c->name,
3160 c->help);
3161 }
3162 return 0;
3163 }
3164 while (--argc > 0) {
3165 register char *arg;
3166 arg = *++argv;
3167 c = getcmd(arg);
3168 if (c == Ambiguous(struct cmd *))
3169 printf("?Ambiguous help command %s\n", arg);
3170 else if (c == (struct cmd *)0)
3171 printf("?Invalid help command %s\n", arg);
3172 else
3173 printf("%s\n", c->help);
3174 }
3175 return 0;
3176}
3177\f
3178/*
3179 * main. Parse arguments, invoke the protocol or command parser.
3180 */
3181
3182
3183void
3184main(argc, argv)
3185 int argc;
3186 char *argv[];
3187{
3188 sp = getservbyname("telnet", "tcp");
3189 if (sp == 0) {
3190 ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1);
3191 /*NOTREACHED*/
3192 }
3193 NetTrace = stdout;
3194 ioctl(0, TIOCGETP, (char *)&ottyb);
3195 ioctl(0, TIOCGETC, (char *)&otc);
3196 ioctl(0, TIOCGLTC, (char *)&oltc);
3197#if defined(LNOFLSH)
3198 ioctl(0, TIOCLGET, (char *)&autoflush);
3199 autoflush = !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
3200#else /* LNOFLSH */
3201 autoflush = 1;
3202#endif /* LNOFLSH */
3203 ntc = otc;
3204 nltc = oltc;
3205 nttyb = ottyb;
3206 setbuf(stdin, (char *)0);
3207 setbuf(stdout, (char *)0);
3208 prompt = argv[0];
3209 if (argc > 1 && !strcmp(argv[1], "-d")) {
3210 debug = 1;
3211 argv++;
3212 argc--;
3213 }
3214 if (argc > 1 && !strcmp(argv[1], "-n")) {
3215 argv++;
3216 argc--;
3217 if (argc > 1) { /* get file name */
3218 NetTrace = fopen(argv[1], "w");
3219 argv++;
3220 argc--;
3221 if (NetTrace == NULL) {
3222 NetTrace = stdout;
3223 }
3224 }
3225 }
3226#if defined(TN3270) && defined(unix)
3227 if (argc > 1 && !strcmp(argv[1], "-t")) {
3228 argv++;
3229 argc--;
3230 if (argc > 1) { /* get command name */
3231 transcom = tline;
3232 (void) strcpy(transcom, argv[1]);
3233 argv++;
3234 argc--;
3235 }
3236 }
3237#endif /* defined(TN3270) && defined(unix) */
3238 if (argc != 1) {
3239 if (setjmp(toplevel) != 0)
3240 Exit(0);
3241 tn(argc, argv);
3242 }
3243 setjmp(toplevel);
3244 for (;;)
3245 command(1);
3246}