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