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