Fix bug when network input buffer has data and we read more
[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;
de7a7ce6 1013#if defined(unix)
3e91ea2a
GM
1014 /*
1015 * If user hasn't specified one way or the other,
1016 * then default to trapping signals.
1017 */
1018 if (!donelclchars) {
1019 localchars = 1;
1020 }
1021 if (localchars) {
1022 notc2.t_brkc = nltc.t_flushc;
1023 noltc2.t_flushc = -1;
1024 } else {
1025 notc2.t_intrc = notc2.t_quitc = -1;
1026 }
de7a7ce6
GM
1027#else /* defined(unix) */
1028 notc2.t_intrc = -1;
1029#endif /* defined(unix) */
3e91ea2a
GM
1030 noltc2.t_suspc = escape;
1031 noltc2.t_dsuspc = -1;
1032 onoff = 1;
1033 break;
1034
1035 default:
1036 return;
1037 }
1038 ioctl(tin, TIOCSLTC, (char *)ltc);
1039 ioctl(tin, TIOCSETC, (char *)tc);
1040 ioctl(tin, TIOCSETP, (char *)&sb);
1041#if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
1042 ioctl(tin, FIONBIO, (char *)&onoff);
1043 ioctl(tout, FIONBIO, (char *)&onoff);
1044#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
1045#if defined(TN3270) && !defined(DEBUG)
1046 ioctl(tin, FIOASYNC, (char *)&onoff);
1047#endif /* defined(TN3270) && !defined(DEBUG) */
1048
1049#if defined(unix)
1050 if (MODE_LINE(f)) {
1051 signal(SIGTSTP, doescape);
1052 } else if (MODE_LINE(old)) {
1053 signal(SIGTSTP, SIG_DFL);
1054 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
1055 }
1056#endif /* defined(unix) */
1057}
1058\f
1059/*
1060 * These routines decides on what the mode should be (based on the values
1061 * of various global variables).
1062 */
1063
1064
1065static
1066getconnmode()
1067{
1068 static char newmode[16] =
1069 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1070 int modeindex = 0;
1071
1072 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1073 modeindex += 1;
1074 }
1075 if (hisopts[TELOPT_ECHO]) {
1076 modeindex += 2;
1077 }
1078 if (hisopts[TELOPT_SGA]) {
1079 modeindex += 4;
1080 }
1081 if (In3270) {
1082 modeindex += 8;
1083 }
1084 return newmode[modeindex];
1085}
1086
1087void
1088setconnmode()
1089{
1090 mode(getconnmode());
1091}
1092
1093
1094void
1095setcommandmode()
1096{
1097 mode(0);
1098}
1099\f
1100static void
1101willoption(option, reply)
1102 int option, reply;
1103{
1104 char *fmt;
1105
1106 switch (option) {
1107
de7a7ce6 1108 case TELOPT_ECHO:
3e91ea2a 1109# if defined(TN3270)
de7a7ce6
GM
1110 /*
1111 * The following is a pain in the rear-end.
1112 * Various IBM servers (some versions of Wiscnet,
1113 * possibly Fibronics/Spartacus, and who knows who
1114 * else) will NOT allow us to send "DO SGA" too early
1115 * in the setup proceedings. On the other hand,
1116 * 4.2 servers (telnetd) won't set SGA correctly.
1117 * So, we are stuck. Empirically (but, based on
1118 * a VERY small sample), the IBM servers don't send
1119 * out anything about ECHO, so we postpone our sending
1120 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
1121 * DO send).
1122 */
1123 {
1124 static int askedSGA = 0;
1125
1126 if (askedSGA == 0) {
1127 askedSGA = 1;
1128 if (!hisopts[TELOPT_SGA]) {
1129 willoption(TELOPT_SGA, 0);
1130 }
1131 }
1132 }
1133 /* Fall through */
3e91ea2a
GM
1134 case TELOPT_EOR:
1135 case TELOPT_BINARY:
1136#endif /* defined(TN3270) */
3e91ea2a
GM
1137 case TELOPT_SGA:
1138 settimer(modenegotiated);
1139 hisopts[option] = 1;
1140 fmt = doopt;
1141 setconnmode(); /* possibly set new tty mode */
1142 break;
1143
1144 case TELOPT_TM:
1145 return; /* Never reply to TM will's/wont's */
1146
1147 default:
1148 fmt = dont;
1149 break;
1150 }
1151 sprintf(nfrontp, fmt, option);
1152 nfrontp += sizeof (dont) - 2;
1153 if (reply)
1154 printoption(">SENT", fmt, option, reply);
1155 else
1156 printoption("<SENT", fmt, option, reply);
1157}
1158
1159static void
1160wontoption(option, reply)
1161 int option, reply;
1162{
1163 char *fmt;
1164
1165 switch (option) {
1166
1167 case TELOPT_ECHO:
1168 case TELOPT_SGA:
1169 settimer(modenegotiated);
1170 hisopts[option] = 0;
1171 fmt = dont;
1172 setconnmode(); /* Set new tty mode */
1173 break;
1174
1175 case TELOPT_TM:
1176 return; /* Never reply to TM will's/wont's */
1177
1178 default:
1179 fmt = dont;
1180 }
1181 sprintf(nfrontp, fmt, option);
1182 nfrontp += sizeof (doopt) - 2;
1183 if (reply)
1184 printoption(">SENT", fmt, option, reply);
1185 else
1186 printoption("<SENT", fmt, option, reply);
1187}
1188
1189static void
1190dooption(option)
1191 int option;
1192{
1193 char *fmt;
1194
1195 switch (option) {
1196
1197 case TELOPT_TM:
1198 fmt = will;
1199 break;
1200
1201# if defined(TN3270)
1202 case TELOPT_EOR:
1203 case TELOPT_BINARY:
1204# endif /* defined(TN3270) */
1205 case TELOPT_TTYPE: /* terminal type option */
1206 case TELOPT_SGA: /* no big deal */
1207 fmt = will;
1208 myopts[option] = 1;
1209 break;
1210
1211 case TELOPT_ECHO: /* We're never going to echo... */
1212 default:
1213 fmt = wont;
1214 break;
1215 }
1216 sprintf(nfrontp, fmt, option);
1217 nfrontp += sizeof (doopt) - 2;
1218 printoption(">SENT", fmt, option, 0);
1219}
1220
1221/*
1222 * suboption()
1223 *
1224 * Look at the sub-option buffer, and try to be helpful to the other
1225 * side.
1226 *
1227 * Currently we recognize:
1228 *
1229 * Terminal type, send request.
1230 */
1231
1232static void
1233suboption()
1234{
1235 printsub("<", subbuffer, subend-subbuffer+1);
1236 switch (subbuffer[0]&0xff) {
1237 case TELOPT_TTYPE:
1238 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1239 ;
1240 } else {
1241 char *name;
1242 char namebuf[41];
1243 extern char *getenv();
1244 int len;
1245
1246#if defined(TN3270)
1247 /*
0d3c45d4 1248 * Try to send a 3270 type terminal name. Decide which one based
3e91ea2a
GM
1249 * on the format of our screen, and (in the future) color
1250 * capaiblities.
1251 */
1252 if ((initscr() != ERR) && /* Initialize curses to get line size */
1253 (LINES >= 24) && (COLS >= 80)) {
1254 Sent3270TerminalType = 1;
1255 if ((LINES >= 27) && (COLS >= 132)) {
1256 MaxNumberLines = 27;
1257 MaxNumberColumns = 132;
1258 sb_terminal[SBTERMMODEL] = '5';
1259 } else if (LINES >= 43) {
1260 MaxNumberLines = 43;
1261 MaxNumberColumns = 80;
1262 sb_terminal[SBTERMMODEL] = '4';
1263 } else if (LINES >= 32) {
1264 MaxNumberLines = 32;
1265 MaxNumberColumns = 80;
1266 sb_terminal[SBTERMMODEL] = '3';
1267 } else {
1268 MaxNumberLines = 24;
1269 MaxNumberColumns = 80;
1270 sb_terminal[SBTERMMODEL] = '2';
1271 }
1272 NumberLines = 24; /* before we start out... */
1273 NumberColumns = 80;
1274 ScreenSize = NumberLines*NumberColumns;
1275 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1276 ExitString(stderr,
1277 "Programming error: MAXSCREENSIZE too small.\n", 1);
1278 /*NOTREACHED*/
1279 }
1280 bcopy(sb_terminal, nfrontp, sizeof sb_terminal);
1281 printsub(">", nfrontp+2, sizeof sb_terminal-2);
1282 nfrontp += sizeof sb_terminal;
1283 return;
1284 }
1285#endif /* defined(TN3270) */
1286
1287 name = getenv("TERM");
1288 if ((name == 0) || ((len = strlen(name)) > 40)) {
1289 name = "UNKNOWN";
1290 }
1291 if ((len + 4+2) < NETROOM()) {
1292 strcpy(namebuf, name);
1293 upcase(namebuf);
1294 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1295 TELQUAL_IS, namebuf, IAC, SE);
1296 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1297 nfrontp += 4+strlen(namebuf)+2;
1298 } else {
1299 ExitString(stderr, "No room in buffer for terminal type.\n",
1300 1);
1301 /*NOTREACHED*/
1302 }
1303 }
1304
1305 default:
1306 break;
1307 }
1308}
1309
1310#if defined(TN3270)
1311static void
1312SetIn3270()
1313{
1314 if (Sent3270TerminalType && myopts[TELOPT_BINARY]
1315 && hisopts[TELOPT_BINARY]) {
1316 if (!In3270) {
1317 In3270 = 1;
0d3c45d4 1318 Init3270(); /* Initialize 3270 functions */
3e91ea2a 1319 /* initialize terminal key mapping */
0d3c45d4 1320 InitTerminal(); /* Start terminal going */
3e91ea2a
GM
1321 setconnmode();
1322 }
1323 } else {
1324 if (In3270) {
1325 StopScreen(1);
1326 In3270 = 0;
1327 setconnmode();
1328 }
1329 }
1330}
1331#endif /* defined(TN3270) */
1332\f
1333/*
1334 * Telnet receiver states for fsm
1335 */
1336#define TS_DATA 0
1337#define TS_IAC 1
1338#define TS_WILL 2
1339#define TS_WONT 3
1340#define TS_DO 4
1341#define TS_DONT 5
1342#define TS_CR 6
1343#define TS_SB 7 /* sub-option collection */
1344#define TS_SE 8 /* looking for sub-option end */
1345
1346static void
1347telrcv()
1348{
1349 register int c;
1350 static int state = TS_DATA;
1351# if defined(TN3270)
1352 register int Scc;
1353 register char *Sbp;
1354# endif /* defined(TN3270) */
1355
1356 while ((scc > 0) && (TTYROOM() > 2)) {
1357 c = *sbp++ & 0xff, scc--;
1358 switch (state) {
1359
1360 case TS_CR:
1361 state = TS_DATA;
1362 if (c == '\0') {
1363 break; /* Ignore \0 after CR */
1364 } else if (c == '\n') {
1365 if (hisopts[TELOPT_ECHO] && !crmod) {
1366 TTYADD(c);
1367 }
1368 break;
1369 }
1370 /* Else, fall through */
1371
1372 case TS_DATA:
1373 if (c == IAC) {
1374 state = TS_IAC;
1375 continue;
1376 }
1377# if defined(TN3270)
1378 if (In3270) {
1379 *Ifrontp++ = c;
1380 Sbp = sbp;
1381 Scc = scc;
1382 while (Scc > 0) {
1383 c = *Sbp++ & 0377, Scc--;
1384 if (c == IAC) {
1385 state = TS_IAC;
1386 break;
1387 }
1388 *Ifrontp++ = c;
1389 }
1390 sbp = Sbp;
1391 scc = Scc;
1392 } else
1393# endif /* defined(TN3270) */
1394 /*
1395 * The 'crmod' hack (see following) is needed
1396 * since we can't * set CRMOD on output only.
1397 * Machines like MULTICS like to send \r without
1398 * \n; since we must turn off CRMOD to get proper
1399 * input, the mapping is done here (sigh).
1400 */
1401 if (c == '\r') {
1402 if (scc > 0) {
1403 c = *sbp&0xff;
1404 if (c == 0) {
1405 sbp++, scc--;
1406 /* a "true" CR */
1407 TTYADD('\r');
1408 } else if (!hisopts[TELOPT_ECHO] &&
1409 (c == '\n')) {
1410 sbp++, scc--;
1411 TTYADD('\n');
1412 } else {
1413 TTYADD('\r');
1414 if (crmod) {
1415 TTYADD('\n');
1416 }
1417 }
1418 } else {
1419 state = TS_CR;
1420 TTYADD('\r');
1421 if (crmod) {
1422 TTYADD('\n');
1423 }
1424 }
1425 } else {
1426 TTYADD(c);
1427 }
1428 continue;
1429
1430 case TS_IAC:
1431 switch (c) {
1432
1433 case WILL:
1434 state = TS_WILL;
1435 continue;
1436
1437 case WONT:
1438 state = TS_WONT;
1439 continue;
1440
1441 case DO:
1442 state = TS_DO;
1443 continue;
1444
1445 case DONT:
1446 state = TS_DONT;
1447 continue;
1448
1449 case DM:
1450 /*
1451 * We may have missed an urgent notification,
1452 * so make sure we flush whatever is in the
1453 * buffer currently.
1454 */
1455 SYNCHing = 1;
1456 ttyflush();
1457 SYNCHing = stilloob(net);
1458 settimer(gotDM);
1459 break;
1460
1461 case NOP:
1462 case GA:
1463 break;
1464
1465 case SB:
1466 SB_CLEAR();
1467 state = TS_SB;
1468 continue;
1469
1470# if defined(TN3270)
1471 case EOR:
1472 if (In3270) {
1473 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
1474 if (Ibackp == Ifrontp) {
1475 Ibackp = Ifrontp = Ibuf;
1476 ISend = 0; /* should have been! */
1477 } else {
1478 ISend = 1;
1479 }
1480 }
1481 break;
1482# endif /* defined(TN3270) */
1483
1484 case IAC:
1485# if !defined(TN3270)
1486 TTYADD(IAC);
1487# else /* !defined(TN3270) */
1488 if (In3270) {
1489 *Ifrontp++ = IAC;
1490 } else {
1491 TTYADD(IAC);
1492 }
1493# endif /* !defined(TN3270) */
1494 break;
1495
1496 default:
1497 break;
1498 }
1499 state = TS_DATA;
1500 continue;
1501
1502 case TS_WILL:
1503 printoption(">RCVD", will, c, !hisopts[c]);
1504 if (c == TELOPT_TM) {
1505 if (flushout) {
1506 flushout = 0;
1507 }
1508 } else if (!hisopts[c]) {
1509 willoption(c, 1);
1510 }
1511 SetIn3270();
1512 state = TS_DATA;
1513 continue;
1514
1515 case TS_WONT:
1516 printoption(">RCVD", wont, c, hisopts[c]);
1517 if (c == TELOPT_TM) {
1518 if (flushout) {
1519 flushout = 0;
1520 }
1521 } else if (hisopts[c]) {
1522 wontoption(c, 1);
1523 }
1524 SetIn3270();
1525 state = TS_DATA;
1526 continue;
1527
1528 case TS_DO:
1529 printoption(">RCVD", doopt, c, !myopts[c]);
1530 if (!myopts[c])
1531 dooption(c);
1532 SetIn3270();
1533 state = TS_DATA;
1534 continue;
1535
1536 case TS_DONT:
1537 printoption(">RCVD", dont, c, myopts[c]);
1538 if (myopts[c]) {
1539 myopts[c] = 0;
1540 sprintf(nfrontp, wont, c);
1541 nfrontp += sizeof (wont) - 2;
1542 flushline = 1;
1543 setconnmode(); /* set new tty mode (maybe) */
1544 printoption(">SENT", wont, c, 0);
1545 }
1546 SetIn3270();
1547 state = TS_DATA;
1548 continue;
1549
1550 case TS_SB:
1551 if (c == IAC) {
1552 state = TS_SE;
1553 } else {
1554 SB_ACCUM(c);
1555 }
1556 continue;
1557
1558 case TS_SE:
1559 if (c != SE) {
1560 if (c != IAC) {
1561 SB_ACCUM(IAC);
1562 }
1563 SB_ACCUM(c);
1564 state = TS_SB;
1565 } else {
1566 SB_TERM();
1567 suboption(); /* handle sub-option */
1568 SetIn3270();
1569 state = TS_DATA;
1570 }
1571 }
1572 }
1573}
1574\f
1575#if defined(TN3270)
1576
1577/*
1578 * The following routines are places where the various tn3270
1579 * routines make calls into telnet.c.
1580 */
1581
1582/* TtyChars() - returns the number of characters in the TTY buffer */
1583TtyChars()
1584{
1585 return(tfrontp-tbackp);
1586}
1587
1588/*
1589 * DataToNetwork - queue up some data to go to network. If "done" is set,
1590 * then when last byte is queued, we add on an IAC EOR sequence (so,
1591 * don't call us with "done" until you want that done...)
1592 *
1593 * We actually do send all the data to the network buffer, since our
1594 * only client needs for us to do that.
1595 */
1596
1597int
1598DataToNetwork(buffer, count, done)
1599register char *buffer; /* where the data is */
1600register int count; /* how much to send */
1601int done; /* is this the last of a logical block */
1602{
1603 register int c;
1604 int origCount;
1605 fd_set o;
1606
1607 origCount = count;
1608 FD_ZERO(&o);
1609
1610 while (count) {
1611 if ((netobuf+sizeof netobuf - nfrontp) < 6) {
1612 netflush();
1613 while ((netobuf+sizeof netobuf - nfrontp) < 6) {
1614 FD_SET(net, &o);
1615 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
1616 (struct timeval *) 0);
1617 netflush();
1618 }
1619 }
1620 c = *buffer++;
1621 count--;
1622 if (c == IAC) {
1623 *nfrontp++ = IAC;
1624 *nfrontp++ = IAC;
1625 } else {
1626 *nfrontp++ = c;
1627 }
1628 }
1629
1630 if (done && !count) {
1631 *nfrontp++ = IAC;
1632 *nfrontp++ = EOR;
1633 netflush(); /* try to move along as quickly as ... */
1634 }
1635 return(origCount - count);
1636}
1637
1638/* DataToTerminal - queue up some data to go to terminal. */
1639
1640int
1641DataToTerminal(buffer, count)
1642register char *buffer; /* where the data is */
1643register int count; /* how much to send */
1644{
1645 int origCount;
1646 fd_set o;
1647
1648 origCount = count;
1649 FD_ZERO(&o);
1650
1651 while (count) {
1652 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
1653 ttyflush();
1654 while (tfrontp >= ttyobuf+sizeof ttyobuf) {
1655 FD_SET(tout, &o);
1656 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
1657 (struct timeval *) 0);
1658 ttyflush();
1659 }
1660 }
1661 *tfrontp++ = *buffer++;
1662 count--;
1663 }
1664 return(origCount - count);
1665}
1666
1667/* EmptyTerminal - called to make sure that the terminal buffer is empty.
1668 * Note that we consider the buffer to run all the
1669 * way to the kernel (thus the select).
1670 */
1671
1672void
1673EmptyTerminal()
1674{
1675 fd_set o;
1676
1677 FD_ZERO(&o);
1678
1679 if (tfrontp == tbackp) {
1680 FD_SET(tout, &o);
1681 (void) select(tout+1, (int *) 0, &o, (int *) 0,
1682 (struct timeval *) 0); /* wait for TTLOWAT */
1683 } else {
1684 while (tfrontp != tbackp) {
1685 ttyflush();
1686 FD_SET(tout, &o);
1687 (void) select(tout+1, (int *) 0, &o, (int *) 0,
1688 (struct timeval *) 0); /* wait for TTLOWAT */
1689 }
1690 }
1691}
1692
1693/*
1694 * Push3270 - Try to send data along the 3270 output (to screen) direction.
1695 */
1696
1697static int
1698Push3270()
1699{
1700 int save = scc;
1701
1702 if (scc) {
1703 if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
1704 if (Ibackp != Ibuf) {
1705 bcopy(Ibackp, Ibuf, Ifrontp-Ibackp);
1706 Ifrontp -= (Ibackp-Ibuf);
1707 Ibackp = Ibuf;
1708 }
1709 }
1710 if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
1711 telrcv();
1712 }
1713 }
1714 return save != scc;
1715}
1716
1717
1718/*
1719 * Finish3270 - get the last dregs of 3270 data out to the terminal
1720 * before quitting.
1721 */
1722
1723static void
1724Finish3270()
1725{
1726 while (Push3270() || !DoTerminalOutput()) {
1727 ;
1728 }
1729}
1730
1731
1732
1733/* StringToTerminal - output a null terminated string to the terminal */
1734
1735void
1736StringToTerminal(s)
1737char *s;
1738{
1739 int count;
1740
1741 count = strlen(s);
1742 if (count) {
1743 (void) DataToTerminal(s, count); /* we know it always goes... */
1744 }
1745}
1746
1747
1748#if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
1749/* _putchar - output a single character to the terminal. This name is so that
1750 * curses(3x) can call us to send out data.
1751 */
1752
1753void
1754_putchar(c)
1755char c;
1756{
1757 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
1758 (void) DataToTerminal(&c, 1);
1759 } else {
1760 *tfrontp++ = c; /* optimize if possible. */
1761 }
1762}
1763#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
1764\f
1765static void
1766SetForExit()
1767{
1768 setconnmode();
1769 if (In3270) {
1770 Finish3270();
1771 }
1772 setcommandmode();
1773 fflush(stdout);
1774 fflush(stderr);
1775 if (In3270) {
1776 StopScreen(1);
1777 }
1778 setconnmode();
1779 setcommandmode();
1780}
1781
1782static void
1783Exit(returnCode)
1784int returnCode;
1785{
1786 SetForExit();
1787 exit(returnCode);
1788}
1789
1790void
1791ExitString(file, string, returnCode)
1792FILE *file;
1793char *string;
1794int returnCode;
1795{
1796 SetForExit();
1797 fwrite(string, 1, strlen(string), file);
1798 exit(returnCode);
1799}
1800
1801void
1802ExitPerror(string, returnCode)
1803char *string;
1804int returnCode;
1805{
1806 SetForExit();
1807 perror(string);
1808 exit(returnCode);
1809}
1810
1811#endif /* defined(TN3270) */
1812\f
1813static
1814Scheduler(block)
1815int block; /* should we block in the select ? */
1816{
1817 register int c;
1818 /* One wants to be a bit careful about setting returnValue
1819 * to one, since a one implies we did some useful work,
1820 * and therefore probably won't be called to block next
1821 * time (TN3270 mode only).
1822 */
1823 int returnValue = 0;
1824 static struct timeval TimeValue = { 0 };
1825
1826 if (scc < 0 && tcc < 0) {
1827 return -1;
1828 }
1829
1830 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
1831 FD_SET(net, &obits);
1832 }
1833 if (TTYBYTES()) {
1834 FD_SET(tout, &obits);
1835 }
1836 if ((tcc == 0) && NETROOM()) {
1837 FD_SET(tin, &ibits);
1838 }
1839# if !defined(TN3270)
1840 if (TTYROOM()) {
1841 FD_SET(net, &ibits);
1842 }
1843# else /* !defined(TN3270) */
1844 if (!ISend && TTYROOM()) {
1845 FD_SET(net, &ibits);
1846 }
1847# endif /* !defined(TN3270) */
1848 if (!SYNCHing) {
1849 FD_SET(net, &xbits);
1850 }
1851# if defined(TN3270) && defined(unix)
1852 if (HaveInput) {
1853 HaveInput = 0;
1854 signal(SIGIO, inputAvailable);
1855 }
1856#endif /* defined(TN3270) && defined(unix) */
1857 if ((c = select(16, &ibits, &obits, &xbits,
1858 block? (struct timeval *)0 : &TimeValue)) < 1) {
1859 if (c == -1) {
1860 /*
1861 * we can get EINTR if we are in line mode,
1862 * and the user does an escape (TSTP), or
1863 * some other signal generator.
1864 */
1865 if (errno == EINTR) {
1866 return 0;
1867 }
1868# if defined(TN3270)
1869 /*
1870 * we can get EBADF if we were in transparent
1871 * mode, and the transcom process died.
1872 */
1873 if (errno == EBADF) {
1874 /*
1875 * zero the bits (even though kernel does it)
1876 * to make sure we are selecting on the right
1877 * ones.
1878 */
1879 FD_ZERO(&ibits);
1880 FD_ZERO(&obits);
1881 FD_ZERO(&xbits);
1882 return 0;
1883 }
1884# endif /* defined(TN3270) */
1885 /* I don't like this, does it ever happen? */
1886 printf("sleep(5) from telnet, after select\r\n");
1887#if defined(unix)
1888 sleep(5);
1889#endif /* defined(unix) */
1890 }
1891 return 0;
1892 }
1893
1894 /*
1895 * Any urgent data?
1896 */
1897 if (FD_ISSET(net, &xbits)) {
1898 FD_CLR(net, &xbits);
1899 SYNCHing = 1;
1900 ttyflush(); /* flush already enqueued data */
1901 }
1902
1903 /*
1904 * Something to read from the network...
1905 */
1906 if (FD_ISSET(net, &ibits)) {
1907 int canread;
1908
1909 FD_CLR(net, &ibits);
1910 if (scc == 0) {
1911 sbp = sibuf;
1912 }
1913 canread = sibuf + sizeof sibuf - sbp;
1914#if !defined(SO_OOBINLINE)
1915 /*
1916 * In 4.2 (and some early 4.3) systems, the
1917 * OOB indication and data handling in the kernel
1918 * is such that if two separate TCP Urgent requests
1919 * come in, one byte of TCP data will be overlaid.
1920 * This is fatal for Telnet, but we try to live
1921 * with it.
1922 *
1923 * In addition, in 4.2 (and...), a special protocol
1924 * is needed to pick up the TCP Urgent data in
1925 * the correct sequence.
1926 *
1927 * What we do is: if we think we are in urgent
1928 * mode, we look to see if we are "at the mark".
1929 * If we are, we do an OOB receive. If we run
1930 * this twice, we will do the OOB receive twice,
1931 * but the second will fail, since the second
1932 * time we were "at the mark", but there wasn't
1933 * any data there (the kernel doesn't reset
1934 * "at the mark" until we do a normal read).
1935 * Once we've read the OOB data, we go ahead
1936 * and do normal reads.
1937 *
1938 * There is also another problem, which is that
1939 * since the OOB byte we read doesn't put us
1940 * out of OOB state, and since that byte is most
1941 * likely the TELNET DM (data mark), we would
1942 * stay in the TELNET SYNCH (SYNCHing) state.
1943 * So, clocks to the rescue. If we've "just"
1944 * received a DM, then we test for the
1945 * presence of OOB data when the receive OOB
1946 * fails (and AFTER we did the normal mode read
1947 * to clear "at the mark").
1948 */
1949 if (SYNCHing) {
1950 int atmark;
1951
1952 ioctl(net, SIOCATMARK, (char *)&atmark);
1953 if (atmark) {
1954 c = recv(net, sibuf, canread, MSG_OOB);
1955 if ((c == -1) && (errno == EINVAL)) {
1956 c = read(net, sibuf, canread);
1957 if (clocks.didnetreceive < clocks.gotDM) {
1958 SYNCHing = stilloob(net);
1959 }
1960 }
1961 } else {
1962 c = read(net, sibuf, canread);
1963 }
1964 } else {
1965 c = read(net, sibuf, canread);
1966 }
1967 settimer(didnetreceive);
1968#else /* !defined(SO_OOBINLINE) */
1969 c = read(net, sbp, canread);
1970#endif /* !defined(SO_OOBINLINE) */
1971 if (c < 0 && errno == EWOULDBLOCK) {
1972 c = 0;
1973 } else if (c <= 0) {
1974 return -1;
1975 }
1976 if (netdata) {
1977 Dump('<', sbp, c);
1978 }
1979 scc += c;
1980 returnValue = 1;
1981 }
1982
1983 /*
1984 * Something to read from the tty...
1985 */
1986 if (FD_ISSET(tin, &ibits)) {
1987 FD_CLR(tin, &ibits);
1988 if (tcc == 0) {
1989 tbp = tibuf; /* nothing left, reset */
1990 }
1991 c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
1992 if (c < 0 && errno == EWOULDBLOCK) {
1993 c = 0;
1994 } else {
1995 /* EOF detection for line mode!!!! */
1996 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
1997 /* must be an EOF... */
1998 *tbp = ntc.t_eofc;
1999 c = 1;
2000 }
2001 if (c <= 0) {
2002 tcc = c;
2003 return -1;
2004 }
2005 }
2006 tcc += c;
2007 returnValue = 1; /* did something useful */
2008 }
2009
2010# if defined(TN3270)
2011 if (tcc > 0) {
2012 if (In3270) {
2013 c = DataFromTerminal(tbp, tcc);
2014 if (c) {
2015 returnValue = 1;
2016 }
2017 tcc -= c;
2018 tbp += c;
2019 } else {
58f9a26f 2020# endif /* defined(TN3270) */
3e91ea2a
GM
2021 returnValue = 1;
2022 while (tcc > 0) {
2023 register int sc;
2024
2025 if (NETROOM() < 2) {
2026 flushline = 1;
2027 break;
2028 }
2029 c = *tbp++ & 0xff, sc = strip(c), tcc--;
2030 if (sc == escape) {
2031 command(0);
2032 tcc = 0;
2033 flushline = 1;
2034 break;
2035 } else if (MODE_LINE(globalmode) && (sc == echoc)) {
2036 if (tcc > 0 && strip(*tbp) == echoc) {
2037 tbp++;
2038 tcc--;
2039 } else {
2040 dontlecho = !dontlecho;
2041 settimer(echotoggle);
2042 setconnmode();
2043 tcc = 0;
2044 flushline = 1;
2045 break;
2046 }
2047 }
2048 if (localchars) {
2049 if (sc == ntc.t_intrc) {
2050 intp();
2051 break;
2052 } else if (sc == ntc.t_quitc) {
2053 sendbrk();
2054 break;
2055 } else if (sc == nltc.t_flushc) {
2056 NET2ADD(IAC, AO);
2057 if (autoflush) {
2058 doflush();
2059 }
2060 break;
2061 } else if (MODE_LOCAL_CHARS(globalmode)) {
2062 ;
2063 } else if (sc == nttyb.sg_kill) {
2064 NET2ADD(IAC, EL);
2065 break;
2066 } else if (sc == nttyb.sg_erase) {
2067 NET2ADD(IAC, EC);
2068 break;
2069 }
2070 }
2071 switch (c) {
2072 case '\n':
2073 /*
2074 * If we are in CRMOD mode (\r ==> \n)
2075 * on our local machine, then probably
2076 * a newline (unix) is CRLF (TELNET).
2077 */
2078 if (MODE_LOCAL_CHARS(globalmode)) {
2079 NETADD('\r');
2080 }
2081 NETADD('\n');
2082 flushline = 1;
2083 break;
2084 case '\r':
2085 NET2ADD('\r', '\0');
2086 flushline = 1;
2087 break;
2088 case IAC:
2089 NET2ADD(IAC, IAC);
2090 break;
2091 default:
2092 NETADD(c);
2093 break;
2094 }
2095 }
2096# if defined(TN3270)
2097 }
2098 }
2099# endif /* defined(TN3270) */
2100
2101 if ((!MODE_LINE(globalmode) || flushline) &&
2102 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2103 FD_CLR(net, &obits);
2104 returnValue = netflush();
2105 }
2106 if (scc > 0) {
2107# if !defined(TN3270)
2108 telrcv();
2109 returnValue = 1;
2110# else /* !defined(TN3270) */
2111 returnValue = Push3270();
2112# endif /* !defined(TN3270) */
2113 }
2114 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
2115 FD_CLR(tout, &obits);
2116 returnValue = ttyflush();
2117 }
2118 return returnValue;
2119}
2120\f
2121/*
2122 * Select from tty and network...
2123 */
2124static void
2125telnet()
2126{
2127 int on = 1;
2128#if defined(TN3270) && defined(unix)
2129 int myPid;
2130#endif /* defined(TN3270) */
2131
2132 tout = fileno(stdout);
2133 tin = fileno(stdin);
2134 setconnmode();
2135 scc = 0;
2136 tcc = 0;
2137 FD_ZERO(&ibits);
2138 FD_ZERO(&obits);
2139 FD_ZERO(&xbits);
2140
2141 ioctl(net, FIONBIO, (char *)&on);
2142
2143#if defined(TN3270)
2144#if !defined(DEBUG) /* DBX can't handle! */
2145 ioctl(net, FIOASYNC, (char *)&on); /* hear about input */
2146#endif /* !defined(DEBUG) */
2147
2148#if defined(unix)
2149 myPid = getpid();
2150#if defined(NOT43)
2151 myPid = -myPid;
2152#endif /* defined(NOT43) */
2153 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */
2154#endif /* defined(unix) */
2155
2156#endif /* defined(TN3270) */
2157
2158#if defined(SO_OOBINLINE)
2159 setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
2160#endif /* defined(SO_OOBINLINE) */
2161
58f9a26f 2162# if !defined(TN3270)
3e91ea2a
GM
2163 if (telnetport) {
2164 if (!hisopts[TELOPT_SGA]) {
2165 willoption(TELOPT_SGA, 0);
2166 }
3e91ea2a
GM
2167 if (!myopts[TELOPT_TTYPE]) {
2168 dooption(TELOPT_TTYPE, 0);
2169 }
3e91ea2a 2170 }
58f9a26f 2171# endif /* !defined(TN3270) */
3e91ea2a
GM
2172
2173# if !defined(TN3270)
2174 for (;;) {
2175 if (Scheduler(1) == -1) {
2176 setcommandmode();
2177 return;
2178 }
2179 }
2180# else /* !defined(TN3270) */
2181 for (;;) {
2182 int schedValue;
2183
2184 while (!In3270) {
2185 if (Scheduler(1) == -1) {
2186 setcommandmode();
2187 return;
2188 }
2189 }
2190
2191 while ((schedValue = Scheduler(0)) != 0) {
2192 if (schedValue == -1) {
2193 setcommandmode();
2194 return;
2195 }
2196 }
2197 /* If there is data waiting to go out to terminal, don't
2198 * schedule any more data for the terminal.
2199 */
2200 if (tfrontp-tbackp) {
2201 schedValue = 1;
2202 } else {
2203 schedValue = DoTerminalOutput();
2204 }
2205 if (schedValue) {
2206 if (Scheduler(1) == -1) {
2207 setcommandmode();
2208 return;
2209 }
2210 }
2211 }
2212# endif /* !defined(TN3270) */
2213}
2214\f
2215/*
2216 * The following are data structures and routines for
2217 * the "send" command.
2218 *
2219 */
2220
2221struct sendlist {
2222 char *name; /* How user refers to it (case independent) */
2223 int what; /* Character to be sent (<0 ==> special) */
2224 char *help; /* Help information (0 ==> no help) */
2225#if defined(NOT43)
2226 int (*routine)(); /* Routine to perform (for special ops) */
2227#else /* defined(NOT43) */
2228 void (*routine)(); /* Routine to perform (for special ops) */
2229#endif /* defined(NOT43) */
2230};
2231\f
2232#define SENDQUESTION -1
2233#define SENDESCAPE -3
2234
2235static struct sendlist Sendlist[] = {
2236 { "ao", AO, "Send Telnet Abort output" },
2237 { "ayt", AYT, "Send Telnet 'Are You There'" },
2238 { "brk", BREAK, "Send Telnet Break" },
2239 { "ec", EC, "Send Telnet Erase Character" },
2240 { "el", EL, "Send Telnet Erase Line" },
2241 { "escape", SENDESCAPE, "Send current escape character" },
2242 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2243 { "ip", IP, "Send Telnet Interrupt Process" },
2244 { "nop", NOP, "Send Telnet 'No operation'" },
2245 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2246 { "?", SENDQUESTION, "Display send options" },
2247 { 0 }
2248};
2249
2250static struct sendlist Sendlist2[] = { /* some synonyms */
2251 { "break", BREAK, 0 },
2252
2253 { "intp", IP, 0 },
2254 { "interrupt", IP, 0 },
2255 { "intr", IP, 0 },
2256
2257 { "help", SENDQUESTION, 0 },
2258
2259 { 0 }
2260};
2261
2262static char **
2263getnextsend(name)
2264char *name;
2265{
2266 struct sendlist *c = (struct sendlist *) name;
2267
2268 return (char **) (c+1);
2269}
2270
2271static struct sendlist *
2272getsend(name)
2273char *name;
2274{
2275 struct sendlist *sl;
2276
2277 if ((sl = (struct sendlist *)
2278 genget(name, (char **) Sendlist, getnextsend)) != 0) {
2279 return sl;
2280 } else {
2281 return (struct sendlist *)
2282 genget(name, (char **) Sendlist2, getnextsend);
2283 }
2284}
2285
2286static
2287sendcmd(argc, argv)
2288int argc;
2289char **argv;
2290{
2291 int what; /* what we are sending this time */
2292 int count; /* how many bytes we are going to need to send */
2293 int i;
2294 int question = 0; /* was at least one argument a question */
2295 struct sendlist *s; /* pointer to current command */
2296
2297 if (argc < 2) {
2298 printf("need at least one argument for 'send' command\n");
2299 printf("'send ?' for help\n");
2300 return 0;
2301 }
2302 /*
2303 * First, validate all the send arguments.
2304 * In addition, we see how much space we are going to need, and
2305 * whether or not we will be doing a "SYNCH" operation (which
2306 * flushes the network queue).
2307 */
2308 count = 0;
2309 for (i = 1; i < argc; i++) {
2310 s = getsend(argv[i]);
2311 if (s == 0) {
2312 printf("Unknown send argument '%s'\n'send ?' for help.\n",
2313 argv[i]);
2314 return 0;
2315 } else if (s == Ambiguous(struct sendlist *)) {
2316 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2317 argv[i]);
2318 return 0;
2319 }
2320 switch (s->what) {
2321 case SENDQUESTION:
2322 break;
2323 case SENDESCAPE:
2324 count += 1;
2325 break;
2326 case SYNCH:
2327 count += 2;
2328 break;
2329 default:
2330 count += 2;
2331 break;
2332 }
2333 }
2334 /* Now, do we have enough room? */
2335 if (NETROOM() < count) {
2336 printf("There is not enough room in the buffer TO the network\n");
2337 printf("to process your request. Nothing will be done.\n");
2338 printf("('send synch' will throw away most data in the network\n");
2339 printf("buffer, if this might help.)\n");
2340 return 0;
2341 }
2342 /* OK, they are all OK, now go through again and actually send */
2343 for (i = 1; i < argc; i++) {
2344 if ((s = getsend(argv[i])) == 0) {
2345 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
2346 quit();
2347 /*NOTREACHED*/
2348 }
2349 if (s->routine) {
2350 (*s->routine)(s);
2351 } else {
2352 switch (what = s->what) {
2353 case SYNCH:
2354 dosynch();
2355 break;
2356 case SENDQUESTION:
2357 for (s = Sendlist; s->name; s++) {
2358 if (s->help) {
2359 printf(s->name);
2360 if (s->help) {
2361 printf("\t%s", s->help);
2362 }
2363 printf("\n");
2364 }
2365 }
2366 question = 1;
2367 break;
2368 case SENDESCAPE:
2369 NETADD(escape);
2370 break;
2371 default:
2372 NET2ADD(IAC, what);
2373 break;
2374 }
2375 }
2376 }
2377 return !question;
2378}
2379\f
2380/*
2381 * The following are the routines and data structures referred
2382 * to by the arguments to the "toggle" command.
2383 */
2384
2385static
2386lclchars()
2387{
2388 donelclchars = 1;
2389 return 1;
2390}
2391
2392static
2393togdebug()
2394{
2395#ifndef NOT43
2396 if (net > 0 &&
2397 setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
2398 < 0) {
2399 perror("setsockopt (SO_DEBUG)");
2400 }
58f9a26f 2401#else /* NOT43 */
3e91ea2a
GM
2402 if (debug) {
2403 if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
2404 perror("setsockopt (SO_DEBUG)");
2405 } else
2406 printf("Cannot turn off socket debugging\n");
58f9a26f 2407#endif /* NOT43 */
3e91ea2a
GM
2408 return 1;
2409}
2410
2411
2412
2413extern int togglehelp();
2414
2415struct togglelist {
2416 char *name; /* name of toggle */
2417 char *help; /* help message */
2418 int (*handler)(); /* routine to do actual setting */
2419 int dohelp; /* should we display help information */
2420 int *variable;
2421 char *actionexplanation;
2422};
2423
2424static struct togglelist Togglelist[] = {
2425 { "autoflush",
2426 "toggle flushing of output when sending interrupt characters",
2427 0,
2428 1,
2429 &autoflush,
2430 "flush output when sending interrupt characters" },
2431 { "autosynch",
2432 "toggle automatic sending of interrupt characters in urgent mode",
2433 0,
2434 1,
2435 &autosynch,
2436 "send interrupt characters in urgent mode" },
2437 { "crmod",
2438 "toggle mapping of received carriage returns",
2439 0,
2440 1,
2441 &crmod,
2442 "map carriage return on output" },
2443 { "localchars",
2444 "toggle local recognition of certain control characters",
2445 lclchars,
2446 1,
2447 &localchars,
2448 "recognize certain control characters" },
2449 { " ", "", 0, 1 }, /* empty line */
2450 { "debug",
2451 "(debugging) toggle debugging",
2452 togdebug,
2453 1,
2454 &debug,
2455 "turn on socket level debugging" },
2456 { "netdata",
2457 "(debugging) toggle printing of hexadecimal network data",
2458 0,
2459 1,
2460 &netdata,
2461 "print hexadecimal representation of network traffic" },
2462 { "options",
2463 "(debugging) toggle viewing of options processing",
2464 0,
2465 1,
2466 &showoptions,
2467 "show option processing" },
2468 { " ", "", 0, 1 }, /* empty line */
2469 { "?",
2470 "display help information",
2471 togglehelp,
2472 1 },
2473 { "help",
2474 "display help information",
2475 togglehelp,
2476 0 },
2477 { 0 }
2478};
2479
2480static
2481togglehelp()
2482{
2483 struct togglelist *c;
2484
2485 for (c = Togglelist; c->name; c++) {
2486 if (c->dohelp) {
2487 printf("%s\t%s\n", c->name, c->help);
2488 }
2489 }
2490 return 0;
2491}
2492
2493static char **
2494getnexttoggle(name)
2495char *name;
2496{
2497 struct togglelist *c = (struct togglelist *) name;
2498
2499 return (char **) (c+1);
2500}
2501
2502static struct togglelist *
2503gettoggle(name)
2504char *name;
2505{
2506 return (struct togglelist *)
2507 genget(name, (char **) Togglelist, getnexttoggle);
2508}
2509
2510static
2511toggle(argc, argv)
2512int argc;
2513char *argv[];
2514{
2515 int retval = 1;
2516 char *name;
2517 struct togglelist *c;
2518
2519 if (argc < 2) {
2520 fprintf(stderr,
2521 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
2522 return 0;
2523 }
2524 argc--;
2525 argv++;
2526 while (argc--) {
2527 name = *argv++;
2528 c = gettoggle(name);
2529 if (c == Ambiguous(struct togglelist *)) {
2530 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
2531 name);
2532 return 0;
2533 } else if (c == 0) {
2534 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
2535 name);
2536 return 0;
2537 } else {
2538 if (c->variable) {
2539 *c->variable = !*c->variable; /* invert it */
2540 printf("%s %s.\n", *c->variable? "Will" : "Won't",
2541 c->actionexplanation);
2542 }
2543 if (c->handler) {
2544 retval &= (*c->handler)(c);
2545 }
2546 }
2547 }
2548 return retval;
2549}
2550\f
2551/*
2552 * The following perform the "set" command.
2553 */
2554
2555struct setlist {
2556 char *name; /* name */
2557 char *help; /* help information */
2558 char *charp; /* where it is located at */
2559};
2560
2561static struct setlist Setlist[] = {
2562 { "echo", "character to toggle local echoing on/off", &echoc },
2563 { "escape", "character to escape back to telnet command mode", &escape },
2564 { " ", "" },
2565 { " ", "The following need 'localchars' to be toggled true", 0 },
2566 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
2567 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
2568 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
2569 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
2570 { "quit", "character to cause a Break", &ntc.t_quitc },
2571 { "eof", "character to cause an EOF ", &ntc.t_eofc },
2572 { 0 }
2573};
2574
2575static char **
2576getnextset(name)
2577char *name;
2578{
2579 struct setlist *c = (struct setlist *)name;
2580
2581 return (char **) (c+1);
2582}
2583
2584static struct setlist *
2585getset(name)
2586char *name;
2587{
2588 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
2589}
2590
2591static
2592setcmd(argc, argv)
2593int argc;
2594char *argv[];
2595{
2596 int value;
2597 struct setlist *ct;
2598
2599 /* XXX back we go... sigh */
2600 if (argc != 3) {
2601 if ((argc == 2) &&
2602 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
2603 for (ct = Setlist; ct->name; ct++) {
2604 printf("%s\t%s\n", ct->name, ct->help);
2605 }
2606 printf("?\tdisplay help information\n");
2607 } else {
2608 printf("Format is 'set Name Value'\n'set ?' for help.\n");
2609 }
2610 return 0;
2611 }
2612
2613 ct = getset(argv[1]);
2614 if (ct == 0) {
2615 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
2616 argv[1]);
2617 return 0;
2618 } else if (ct == Ambiguous(struct setlist *)) {
2619 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
2620 argv[1]);
2621 return 0;
2622 } else {
2623 if (strcmp("off", argv[2])) {
2624 value = special(argv[2]);
2625 } else {
2626 value = -1;
2627 }
2628 *(ct->charp) = value;
2629 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
2630 }
2631 return 1;
2632}
2633\f
2634/*
2635 * The following are the data structures and routines for the
2636 * 'mode' command.
2637 */
2638
2639static
2640dolinemode()
2641{
2642 if (hisopts[TELOPT_SGA]) {
2643 wontoption(TELOPT_SGA, 0);
2644 }
2645 if (hisopts[TELOPT_ECHO]) {
2646 wontoption(TELOPT_ECHO, 0);
2647 }
2648 return 1;
2649}
2650
2651static
2652docharmode()
2653{
2654 if (!hisopts[TELOPT_SGA]) {
2655 willoption(TELOPT_SGA, 0);
2656 }
2657 if (!hisopts[TELOPT_ECHO]) {
2658 willoption(TELOPT_ECHO, 0);
2659 }
2660 return 1;
2661}
2662
2663static struct cmd Modelist[] = {
2664 { "character", "character-at-a-time mode", docharmode, 1, 1 },
2665 { "line", "line-by-line mode", dolinemode, 1, 1 },
2666 { 0 },
2667};
2668
2669static char **
2670getnextmode(name)
2671char *name;
2672{
2673 struct cmd *c = (struct cmd *) name;
2674
2675 return (char **) (c+1);
2676}
2677
2678static struct cmd *
2679getmodecmd(name)
2680char *name;
2681{
2682 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
2683}
2684
2685static
2686modecmd(argc, argv)
2687int argc;
2688char *argv[];
2689{
2690 struct cmd *mt;
2691
2692 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
2693 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
2694 for (mt = Modelist; mt->name; mt++) {
2695 printf("%s\t%s\n", mt->name, mt->help);
2696 }
2697 return 0;
2698 }
2699 mt = getmodecmd(argv[1]);
2700 if (mt == 0) {
2701 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
2702 return 0;
2703 } else if (mt == Ambiguous(struct cmd *)) {
2704 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
2705 return 0;
2706 } else {
2707 (*mt->handler)();
2708 }
2709 return 1;
2710}
2711\f
2712/*
2713 * The following data structures and routines implement the
2714 * "display" command.
2715 */
2716
2717static
2718display(argc, argv)
2719int argc;
2720char *argv[];
2721{
2722#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
2723 if (*tl->variable) { \
2724 printf("will"); \
2725 } else { \
2726 printf("won't"); \
2727 } \
2728 printf(" %s.\n", tl->actionexplanation); \
2729 }
2730
2731#define doset(sl) if (sl->name && *sl->name != ' ') { \
2732 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
2733 }
2734
2735 struct togglelist *tl;
2736 struct setlist *sl;
2737
2738 if (argc == 1) {
2739 for (tl = Togglelist; tl->name; tl++) {
2740 dotog(tl);
2741 }
2742 printf("\n");
2743 for (sl = Setlist; sl->name; sl++) {
2744 doset(sl);
2745 }
2746 } else {
2747 int i;
2748
2749 for (i = 1; i < argc; i++) {
2750 sl = getset(argv[i]);
2751 tl = gettoggle(argv[i]);
2752 if ((sl == Ambiguous(struct setlist *)) ||
2753 (tl == Ambiguous(struct togglelist *))) {
2754 printf("?Ambiguous argument '%s'.\n", argv[i]);
2755 return 0;
2756 } else if (!sl && !tl) {
2757 printf("?Unknown argument '%s'.\n", argv[i]);
2758 return 0;
2759 } else {
2760 if (tl) {
2761 dotog(tl);
2762 }
2763 if (sl) {
2764 doset(sl);
2765 }
2766 }
2767 }
2768 }
2769 return 1;
2770#undef doset
2771#undef dotog
2772}
2773\f
2774/*
2775 * The following are the data structures, and many of the routines,
2776 * relating to command processing.
2777 */
2778
2779/*
2780 * Set the escape character.
2781 */
2782static
2783setescape(argc, argv)
2784 int argc;
2785 char *argv[];
2786{
2787 register char *arg;
2788 char buf[50];
2789
2790 printf(
2791 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
2792 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
2793 if (argc > 2)
2794 arg = argv[1];
2795 else {
2796 printf("new escape character: ");
2797 gets(buf);
2798 arg = buf;
2799 }
2800 if (arg[0] != '\0')
2801 escape = arg[0];
2802 if (!In3270) {
2803 printf("Escape character is '%s'.\n", control(escape));
2804 }
2805 fflush(stdout);
2806 return 1;
2807}
2808
2809/*VARARGS*/
2810static
2811togcrmod()
2812{
2813 crmod = !crmod;
2814 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
2815 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
2816 fflush(stdout);
2817 return 1;
2818}
2819
2820/*VARARGS*/
2821suspend()
2822{
2823 setcommandmode();
2824#if defined(unix)
2825 kill(0, SIGTSTP);
2826#endif /* defined(unix) */
2827 /* reget parameters in case they were changed */
2828 ioctl(0, TIOCGETP, (char *)&ottyb);
2829 ioctl(0, TIOCGETC, (char *)&otc);
2830 ioctl(0, TIOCGLTC, (char *)&oltc);
2831 return 1;
2832}
2833
2834/*VARARGS*/
2835static
0d3c45d4
GM
2836bye(argc, argv)
2837int argc; /* Number of arguments */
2838char *argv[]; /* arguments */
3e91ea2a
GM
2839{
2840 if (connected) {
2841 shutdown(net, 2);
2842 printf("Connection closed.\n");
2843 close(net);
2844 connected = 0;
2845 /* reset options */
0d3c45d4 2846 tninit();
3e91ea2a 2847#if defined(TN3270)
0d3c45d4 2848 SetIn3270(); /* Get out of 3270 mode */
3e91ea2a
GM
2849#endif /* defined(TN3270) */
2850 }
0d3c45d4
GM
2851 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
2852 longjmp(toplevel, 1);
2853 /* NOTREACHED */
2854 }
2855 return 1; /* Keep lint, etc., happy */
3e91ea2a
GM
2856}
2857
2858/*VARARGS*/
2859quit()
2860{
0d3c45d4 2861 (void) call(bye, "bye", "fromquit", 0);
3e91ea2a
GM
2862 Exit(0);
2863 /*NOTREACHED*/
2864 return 1; /* just to keep lint happy */
2865}
2866
2867/*
2868 * Print status about the connection.
2869 */
2870static
2871status(argc, argv)
2872int argc;
2873char *argv[];
2874{
2875 if (connected) {
2876 printf("Connected to %s.\n", hostname);
2877 if (argc < 2) {
2878 printf("Operating in %s.\n",
2879 modelist[getconnmode()].modedescriptions);
2880 if (localchars) {
2881 printf("Catching signals locally.\n");
2882 }
2883 }
2884 } else {
2885 printf("No connection.\n");
2886 }
2887# if !defined(TN3270)
2888 printf("Escape character is '%s'.\n", control(escape));
2889 fflush(stdout);
2890# else /* !defined(TN3270) */
2891 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
2892 printf("Escape character is '%s'.\n", control(escape));
2893 }
2894# if defined(unix)
2895 if (In3270 && transcom) {
2896 printf("Transparent mode command is '%s'.\n", transcom);
2897 }
2898# endif /* defined(unix) */
2899 fflush(stdout);
2900 if (In3270) {
2901 return 0;
2902 }
2903# endif /* defined(TN3270) */
2904 return 1;
2905}
2906
2907#if defined(TN3270) && defined(unix)
2908static
2909settranscom(argc, argv)
2910 int argc;
2911 char *argv[];
2912{
2913 int i, len = 0;
2914 char *strcpy(), *strcat();
2915
2916 if (argc == 1 && transcom) {
2917 transcom = 0;
2918 }
2919 if (argc == 1) {
2920 return;
2921 }
2922 for (i = 1; i < argc; ++i) {
2923 len += 1 + strlen(argv[1]);
2924 }
2925 transcom = tline;
2926 (void) strcpy(transcom, argv[1]);
2927 for (i = 2; i < argc; ++i) {
2928 (void) strcat(transcom, " ");
2929 (void) strcat(transcom, argv[i]);
2930 }
2931}
2932#endif /* defined(TN3270) && defined(unix) */
2933
2934
2935static
2936tn(argc, argv)
2937 int argc;
2938 char *argv[];
2939{
2940 register struct hostent *host = 0;
2941#if defined(msdos)
2942 char *cp;
58f9a26f 2943#endif /* defined(msdos) */
3e91ea2a
GM
2944
2945 if (connected) {
2946 printf("?Already connected to %s\n", hostname);
2947 return 0;
2948 }
2949 if (argc < 2) {
2950 (void) strcpy(line, "Connect ");
2951 printf("(to) ");
2952 gets(&line[strlen(line)]);
2953 makeargv();
2954 argc = margc;
2955 argv = margv;
2956 }
2957 if (argc > 3) {
2958 printf("usage: %s host-name [port]\n", argv[0]);
2959 return 0;
2960 }
2961#if defined(msdos)
2962 for (cp = argv[1]; *cp; cp++) {
2963 if (isupper(*cp)) {
2964 *cp = tolower(*cp);
2965 }
2966 }
2967#endif /* defined(msdos) */
2968 sin.sin_addr.s_addr = inet_addr(argv[1]);
2969 if (sin.sin_addr.s_addr != -1) {
2970 sin.sin_family = AF_INET;
2971 (void) strcpy(hnamebuf, argv[1]);
2972 hostname = hnamebuf;
2973 } else {
2974 host = gethostbyname(argv[1]);
2975 if (host) {
2976 sin.sin_family = host->h_addrtype;
2977#if defined(h_addr) /* In 4.3, this is a #define */
2978 bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr, host->h_length);
2979#else /* defined(h_addr) */
2980 bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
2981#endif /* defined(h_addr) */
2982 hostname = host->h_name;
2983 } else {
2984 printf("%s: unknown host\n", argv[1]);
2985 return 0;
2986 }
2987 }
2988 sin.sin_port = sp->s_port;
2989 if (argc == 3) {
2990 sin.sin_port = atoi(argv[2]);
2991 if (sin.sin_port == 0) {
2992 sp = getservbyname(argv[2], "tcp");
2993 if (sp)
2994 sin.sin_port = sp->s_port;
2995 else {
2996 printf("%s: bad port number\n", argv[2]);
2997 return 0;
2998 }
2999 } else {
3000 sin.sin_port = atoi(argv[2]);
3001 sin.sin_port = htons(sin.sin_port);
3002 }
3003 telnetport = 0;
3004 } else {
3005 telnetport = 1;
3006 }
3007#if defined(unix)
3008 signal(SIGINT, intr);
3009 signal(SIGQUIT, intr2);
3010 signal(SIGPIPE, deadpeer);
3011#endif /* defined(unix) */
3012 printf("Trying...\n");
3013 do {
3014 net = socket(AF_INET, SOCK_STREAM, 0);
3015 if (net < 0) {
3016 perror("telnet: socket");
3017 return 0;
3018 }
3019#ifndef NOT43
3020 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG,
3021 (char *)&debug, sizeof(debug)) < 0)
58f9a26f 3022#else /* NOT43 */
3e91ea2a 3023 if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
58f9a26f 3024#endif /* NOT43 */
3e91ea2a
GM
3025 perror("setsockopt (SO_DEBUG)");
3026
3027 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
3028#if defined(h_addr) /* In 4.3, this is a #define */
3029 if (host && host->h_addr_list[1]) {
3030 int oerrno = errno;
3031
3032 fprintf(stderr, "telnet: connect to address %s: ",
3033 inet_ntoa(sin.sin_addr));
3034 errno = oerrno;
3035 perror((char *)0);
3036 host->h_addr_list++;
3037 bcopy(host->h_addr_list[0],
3038 (caddr_t)&sin.sin_addr, host->h_length);
3039 fprintf(stderr, "Trying %s...\n",
3040 inet_ntoa(sin.sin_addr));
3041 (void) close(net);
3042 continue;
3043 }
3044#endif /* defined(h_addr) */
3045 perror("telnet: Unable to connect to remote host");
3046#if defined(unix)
3047 signal(SIGINT, SIG_DFL);
3048 signal(SIGQUIT, SIG_DFL);
58f9a26f 3049#endif /* defined(unix) */
3e91ea2a
GM
3050 return 0;
3051 }
3052 connected++;
3053 } while (connected == 0);
3054 call(status, "status", "notmuch", 0);
3055 if (setjmp(peerdied) == 0)
3056 telnet();
3057 ExitString(stderr, "Connection closed by foreign host.\n",1);
3058 /*NOTREACHED*/
3059}
3060
3061
3062#define HELPINDENT (sizeof ("connect"))
3063
3064static char
3065 openhelp[] = "connect to a site",
3066 closehelp[] = "close current connection",
3067 quithelp[] = "exit telnet",
3068 zhelp[] = "suspend telnet",
3069 statushelp[] = "print status information",
3070 helphelp[] = "print help information",
3071 sendhelp[] = "transmit special characters ('send ?' for more)",
3072 sethelp[] = "set operating parameters ('set ?' for more)",
3073 togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3074 displayhelp[] = "display operating parameters",
3075#if defined(TN3270) && defined(unix)
3076 transcomhelp[] = "specify Unix command for transparent mode pipe",
3077#endif /* defined(TN3270) && defined(unix) */
3078 modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3079
3080extern int help();
3081
3082static struct cmd cmdtab[] = {
3083 { "close", closehelp, bye, 1, 1 },
3084 { "display", displayhelp, display, 1, 0 },
3085 { "mode", modehelp, modecmd, 1, 1 },
3086 { "open", openhelp, tn, 1, 0 },
3087 { "quit", quithelp, quit, 1, 0 },
3088 { "send", sendhelp, sendcmd, 1, 1 },
3089 { "set", sethelp, setcmd, 1, 0 },
3090 { "status", statushelp, status, 1, 0 },
3091 { "toggle", togglestring, toggle, 1, 0 },
3092#if defined(TN3270) && defined(unix)
3093 { "transcom", transcomhelp, settranscom, 1, 0 },
3094#endif /* defined(TN3270) && defined(unix) */
3095 { "z", zhelp, suspend, 1, 0 },
3096 { "?", helphelp, help, 1, 0 },
3097 0
3098};
3099
3100static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
3101static char escapehelp[] = "deprecated command -- use 'set escape' instead";
3102
3103static struct cmd cmdtab2[] = {
3104 { "help", helphelp, help, 0, 0 },
3105 { "escape", escapehelp, setescape, 1, 0 },
3106 { "crmod", crmodhelp, togcrmod, 1, 0 },
3107 0
3108};
3109
3110/*
3111 * Call routine with argc, argv set from args (terminated by 0).
3112 * VARARGS2
3113 */
3114static
3115call(routine, args)
3116 int (*routine)();
3117 char *args;
3118{
3119 register char **argp;
3120 register int argc;
3121
3122 for (argc = 0, argp = &args; *argp++ != 0; argc++)
3123 ;
3124 return (*routine)(argc, &args);
3125}
3126
3127static char **
3128getnextcmd(name)
3129char *name;
3130{
3131 struct cmd *c = (struct cmd *) name;
3132
3133 return (char **) (c+1);
3134}
3135
3136static struct cmd *
3137getcmd(name)
3138char *name;
3139{
3140 struct cmd *cm;
3141
3142 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3143 return cm;
3144 } else {
3145 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3146 }
3147}
3148
3149void
3150command(top)
3151 int top;
3152{
3153 register struct cmd *c;
3154
3155 setcommandmode();
3156 if (!top) {
3157 putchar('\n');
3158 } else {
3159#if defined(unix)
3160 signal(SIGINT, SIG_DFL);
3161 signal(SIGQUIT, SIG_DFL);
3162#endif /* defined(unix) */
3163 }
3164 for (;;) {
3165 printf("%s> ", prompt);
3166 if (gets(line) == NULL) {
3167 if (feof(stdin) || ferror(stdin))
3168 quit();
3169 break;
3170 }
3171 if (line[0] == 0)
3172 break;
3173 makeargv();
3174 c = getcmd(margv[0]);
3175 if (c == Ambiguous(struct cmd *)) {
3176 printf("?Ambiguous command\n");
3177 continue;
3178 }
3179 if (c == 0) {
3180 printf("?Invalid command\n");
3181 continue;
3182 }
3183 if (c->needconnect && !connected) {
3184 printf("?Need to be connected first.\n");
3185 continue;
3186 }
3187 if ((*c->handler)(margc, margv)) {
3188 break;
3189 }
3190 }
3191 if (!top) {
3192 if (!connected) {
3193 longjmp(toplevel, 1);
3194 /*NOTREACHED*/
3195 }
3196 setconnmode();
3197 }
3198}
3199\f
3200/*
3201 * Help command.
3202 */
3203static
3204help(argc, argv)
3205 int argc;
3206 char *argv[];
3207{
3208 register struct cmd *c;
3209
3210 if (argc == 1) {
3211 printf("Commands may be abbreviated. Commands are:\n\n");
3212 for (c = cmdtab; c->name; c++)
3213 if (c->dohelp) {
3214 printf("%-*s\t%s\n", HELPINDENT, c->name,
3215 c->help);
3216 }
3217 return 0;
3218 }
3219 while (--argc > 0) {
3220 register char *arg;
3221 arg = *++argv;
3222 c = getcmd(arg);
3223 if (c == Ambiguous(struct cmd *))
3224 printf("?Ambiguous help command %s\n", arg);
3225 else if (c == (struct cmd *)0)
3226 printf("?Invalid help command %s\n", arg);
3227 else
3228 printf("%s\n", c->help);
3229 }
3230 return 0;
3231}
3232\f
3233/*
3234 * main. Parse arguments, invoke the protocol or command parser.
3235 */
3236
3237
3238void
3239main(argc, argv)
3240 int argc;
3241 char *argv[];
3242{
0d3c45d4
GM
3243 tninit(); /* Clear out things */
3244
3e91ea2a
GM
3245 NetTrace = stdout;
3246 ioctl(0, TIOCGETP, (char *)&ottyb);
3247 ioctl(0, TIOCGETC, (char *)&otc);
3248 ioctl(0, TIOCGLTC, (char *)&oltc);
3249#if defined(LNOFLSH)
3250 ioctl(0, TIOCLGET, (char *)&autoflush);
3251 autoflush = !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */
3252#else /* LNOFLSH */
3253 autoflush = 1;
3254#endif /* LNOFLSH */
3255 ntc = otc;
3256 nltc = oltc;
3257 nttyb = ottyb;
3258 setbuf(stdin, (char *)0);
3259 setbuf(stdout, (char *)0);
3260 prompt = argv[0];
3261 if (argc > 1 && !strcmp(argv[1], "-d")) {
3262 debug = 1;
3263 argv++;
3264 argc--;
3265 }
3266 if (argc > 1 && !strcmp(argv[1], "-n")) {
3267 argv++;
3268 argc--;
3269 if (argc > 1) { /* get file name */
3270 NetTrace = fopen(argv[1], "w");
3271 argv++;
3272 argc--;
3273 if (NetTrace == NULL) {
3274 NetTrace = stdout;
3275 }
3276 }
3277 }
3278#if defined(TN3270) && defined(unix)
3279 if (argc > 1 && !strcmp(argv[1], "-t")) {
3280 argv++;
3281 argc--;
3282 if (argc > 1) { /* get command name */
3283 transcom = tline;
3284 (void) strcpy(transcom, argv[1]);
3285 argv++;
3286 argc--;
3287 }
3288 }
3289#endif /* defined(TN3270) && defined(unix) */
3290 if (argc != 1) {
3291 if (setjmp(toplevel) != 0)
3292 Exit(0);
3293 tn(argc, argv);
3294 }
3295 setjmp(toplevel);
3296 for (;;)
3297 command(1);
3298}