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