Fix core dumps on suns (from sun!nowicki, Bill Nowicki).
[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
1063 longest = 0;
1064 nmatches = 0;
1065 found = 0;
1066 for (c = table; (p = *c) != 0; c = (*next)(c)) {
1067 for (q = name;
1068 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
1069 if (*q == 0) /* exact match? */
1070 return (c);
1071 if (!*q) { /* the name was a prefix */
1072 if (q - name > longest) {
1073 longest = q - name;
1074 nmatches = 1;
1075 found = c;
1076 } else if (q - name == longest)
1077 nmatches++;
1078 }
1079 }
1080 if (nmatches > 1)
1081 return Ambiguous(char **);
1082 return (found);
1083}
1084
1085/*
1086 * Make a character string into a number.
1087 *
1088 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
1089 */
1090
1091static
1092special(s)
1093register char *s;
1094{
1095 register char c;
1096 char b;
1097
1098 switch (*s) {
1099 case '^':
1100 b = *++s;
1101 if (b == '?') {
1102 c = b | 0x40; /* DEL */
1103 } else {
1104 c = b & 0x1f;
1105 }
1106 break;
1107 default:
1108 c = *s;
1109 break;
1110 }
1111 return c;
1112}
1113
1114/*
1115 * Construct a control character sequence
1116 * for a special character.
1117 */
1118static char *
1119control(c)
1120 register int c;
1121{
1122 static char buf[3];
1123
1124 if (c == 0x7f)
1125 return ("^?");
1126 if (c == '\377') {
1127 return "off";
1128 }
1129 if (c >= 0x20) {
1130 buf[0] = c;
1131 buf[1] = 0;
1132 } else {
1133 buf[0] = '^';
1134 buf[1] = '@'+c;
1135 buf[2] = 0;
1136 }
1137 return (buf);
1138}
1139
1140
1141/*
1142 * upcase()
1143 *
1144 * Upcase (in place) the argument.
1145 */
1146
1147static void
1148upcase(argument)
1149register char *argument;
1150{
1151 register int c;
1152
1153 while ((c = *argument) != 0) {
1154 if (islower(c)) {
1155 *argument = toupper(c);
1156 }
1157 argument++;
1158 }
1159}
3a764688
GM
1160
1161/*
1162 * SetSockOpt()
1163 *
1164 * Compensate for differences in 4.2 and 4.3 systems.
1165 */
1166
1167static int
1168SetSockOpt(fd, level, option, yesno)
1169int
1170 fd,
1171 level,
1172 option,
1173 yesno;
1174{
1175#ifndef NOT43
1176 return setsockopt(fd, level, option,
1177 (char *)&yesno, sizeof yesno);
1178#else /* NOT43 */
1179 if (yesno == 0) { /* Can't do that in 4.2! */
1180 fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
1181 option);
1182 return -1;
1183 }
1184 return setsockopt(fd, level, option, 0, 0);
1185#endif /* NOT43 */
1186}
3e91ea2a
GM
1187\f
1188/*
1189 * The following are routines used to print out debugging information.
1190 */
1191
1192
1193static void
1194Dump(direction, buffer, length)
1195char direction;
1196char *buffer;
1197int length;
1198{
1199# define BYTES_PER_LINE 32
1200# define min(x,y) ((x<y)? x:y)
1201 char *pThis;
1202 int offset;
1203
1204 offset = 0;
1205
1206 while (length) {
1207 /* print one line */
1208 fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1209 pThis = buffer;
1210 buffer = buffer+min(length, BYTES_PER_LINE);
1211 while (pThis < buffer) {
1212 fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1213 pThis++;
1214 }
1215 fprintf(NetTrace, "\n");
1216 length -= BYTES_PER_LINE;
1217 offset += BYTES_PER_LINE;
1218 if (length < 0) {
1219 return;
1220 }
1221 /* find next unique line */
1222 }
1223}
1224
1225
1226/*VARARGS*/
1227static void
1228printoption(direction, fmt, option, what)
1229 char *direction, *fmt;
1230 int option, what;
1231{
1232 if (!showoptions)
1233 return;
1234 fprintf(NetTrace, "%s ", direction+1);
1235 if (fmt == doopt)
1236 fmt = "do";
1237 else if (fmt == dont)
1238 fmt = "dont";
1239 else if (fmt == will)
1240 fmt = "will";
1241 else if (fmt == wont)
1242 fmt = "wont";
1243 else
1244 fmt = "???";
1245 if (option < (sizeof telopts/sizeof telopts[0]))
1246 fprintf(NetTrace, "%s %s", fmt, telopts[option]);
1247 else
1248 fprintf(NetTrace, "%s %d", fmt, option);
1249 if (*direction == '<') {
1250 fprintf(NetTrace, "\r\n");
1251 return;
1252 }
1253 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
1254}
1255
1256static void
1257printsub(direction, pointer, length)
1258char *direction, /* "<" or ">" */
1259 *pointer; /* where suboption data sits */
1260int length; /* length of suboption data */
1261{
1262 if (showoptions) {
1263 fprintf(NetTrace, "%s suboption ",
1264 (direction[0] == '<')? "Received":"Sent");
1265 switch (pointer[0]) {
1266 case TELOPT_TTYPE:
1267 fprintf(NetTrace, "Terminal type ");
1268 switch (pointer[1]) {
1269 case TELQUAL_IS:
1270 {
1271 char tmpbuf[sizeof subbuffer];
1272 int minlen = min(length, sizeof tmpbuf);
1273
42cf515f 1274 memcpy(tmpbuf, pointer+2, minlen);
3e91ea2a
GM
1275 tmpbuf[minlen-1] = 0;
1276 fprintf(NetTrace, "is %s.\n", tmpbuf);
1277 }
1278 break;
1279 case TELQUAL_SEND:
1280 fprintf(NetTrace, "- request to send.\n");
1281 break;
1282 default:
1283 fprintf(NetTrace,
1284 "- unknown qualifier %d (0x%x).\n", pointer[1]);
1285 }
1286 break;
1287 default:
1288 fprintf(NetTrace, "Unknown option %d (0x%x)\n",
1289 pointer[0], pointer[0]);
1290 }
1291 }
1292}
1293\f
1294/*
1295 * Check to see if any out-of-band data exists on a socket (for
1296 * Telnet "synch" processing).
1297 */
1298
1299static int
1300stilloob(s)
1301int s; /* socket number */
1302{
1303 static struct timeval timeout = { 0 };
1304 fd_set excepts;
1305 int value;
1306
1307 do {
1308 FD_ZERO(&excepts);
1309 FD_SET(s, &excepts);
1310 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
1311 } while ((value == -1) && (errno == EINTR));
1312
1313 if (value < 0) {
1314 perror("select");
1315 quit();
1316 }
1317 if (FD_ISSET(s, &excepts)) {
1318 return 1;
1319 } else {
1320 return 0;
1321 }
1322}
1323
1324
1325/*
1326 * netflush
1327 * Send as much data as possible to the network,
1328 * handling requests for urgent data.
1329 *
1330 * The return value indicates whether we did any
1331 * useful work.
1332 */
1333
1334
1335int
1336netflush()
1337{
1338 int n;
1339
1340 if ((n = nfrontp - nbackp) > 0) {
1341 if (!neturg) {
42cf515f 1342 n = send(net, nbackp, n, 0); /* normal write */
3e91ea2a
GM
1343 } else {
1344 n = neturg - nbackp;
1345 /*
1346 * In 4.2 (and 4.3) systems, there is some question about
1347 * what byte in a sendOOB operation is the "OOB" data.
1348 * To make ourselves compatible, we only send ONE byte
1349 * out of band, the one WE THINK should be OOB (though
1350 * we really have more the TCP philosophy of urgent data
1351 * rather than the Unix philosophy of OOB data).
1352 */
1353 if (n > 1) {
1354 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
1355 } else {
1356 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
1357 }
1358 }
1359 }
1360 if (n < 0) {
1361 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
1362 setcommandmode();
1363 perror(hostname);
42cf515f 1364 NetClose(net);
3e91ea2a
GM
1365 neturg = 0;
1366 longjmp(peerdied, -1);
1367 /*NOTREACHED*/
1368 }
1369 n = 0;
1370 }
1371 if (netdata && n) {
1372 Dump('>', nbackp, n);
1373 }
1374 nbackp += n;
1375 if (nbackp >= neturg) {
1376 neturg = 0;
1377 }
1378 if (nbackp == nfrontp) {
1379 nbackp = nfrontp = netobuf;
1380 }
1381 return n > 0;
1382}
1383\f
1384/*
1385 * nextitem()
1386 *
1387 * Return the address of the next "item" in the TELNET data
1388 * stream. This will be the address of the next character if
1389 * the current address is a user data character, or it will
1390 * be the address of the character following the TELNET command
1391 * if the current address is a TELNET IAC ("I Am a Command")
1392 * character.
1393 */
1394
1395static char *
1396nextitem(current)
1397char *current;
1398{
1399 if ((*current&0xff) != IAC) {
1400 return current+1;
1401 }
1402 switch (*(current+1)&0xff) {
1403 case DO:
1404 case DONT:
1405 case WILL:
1406 case WONT:
1407 return current+3;
1408 case SB: /* loop forever looking for the SE */
1409 {
1410 register char *look = current+2;
1411
1412 for (;;) {
1413 if ((*look++&0xff) == IAC) {
1414 if ((*look++&0xff) == SE) {
1415 return look;
1416 }
1417 }
1418 }
1419 }
1420 default:
1421 return current+2;
1422 }
1423}
1424/*
1425 * netclear()
1426 *
1427 * We are about to do a TELNET SYNCH operation. Clear
1428 * the path to the network.
1429 *
1430 * Things are a bit tricky since we may have sent the first
1431 * byte or so of a previous TELNET command into the network.
1432 * So, we have to scan the network buffer from the beginning
1433 * until we are up to where we want to be.
1434 *
1435 * A side effect of what we do, just to keep things
1436 * simple, is to clear the urgent data pointer. The principal
1437 * caller should be setting the urgent data pointer AFTER calling
1438 * us in any case.
1439 */
1440
1441static void
1442netclear()
1443{
1444 register char *thisitem, *next;
1445 char *good;
1446#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
1447 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1448
1449 thisitem = netobuf;
1450
1451 while ((next = nextitem(thisitem)) <= nbackp) {
1452 thisitem = next;
1453 }
1454
1455 /* Now, thisitem is first before/at boundary. */
1456
1457 good = netobuf; /* where the good bytes go */
1458
1459 while (nfrontp > thisitem) {
1460 if (wewant(thisitem)) {
1461 int length;
1462
1463 next = thisitem;
1464 do {
1465 next = nextitem(next);
1466 } while (wewant(next) && (nfrontp > next));
1467 length = next-thisitem;
42cf515f 1468 memcpy(good, thisitem, length);
3e91ea2a
GM
1469 good += length;
1470 thisitem = next;
1471 } else {
1472 thisitem = nextitem(thisitem);
1473 }
1474 }
1475
1476 nbackp = netobuf;
1477 nfrontp = good; /* next byte to be sent */
1478 neturg = 0;
1479}
1480\f
1481/*
1482 * These routines add various telnet commands to the data stream.
1483 */
1484
1485#if defined(NOT43)
1486static int
1487#else /* defined(NOT43) */
1488static void
1489#endif /* defined(NOT43) */
1490dosynch()
1491{
1492 netclear(); /* clear the path to the network */
1493 NET2ADD(IAC, DM);
1494 neturg = NETLOC()-1; /* Some systems are off by one XXX */
1495
1496#if defined(NOT43)
1497 return 0;
1498#endif /* defined(NOT43) */
1499}
1500
1501static void
1502doflush()
1503{
1504 NET2ADD(IAC, DO);
1505 NETADD(TELOPT_TM);
1506 flushline = 1;
1507 flushout = 1;
1508 ttyflush();
1509 /* do printoption AFTER flush, otherwise the output gets tossed... */
1510 printoption("<SENT", doopt, TELOPT_TM, 0);
1511}
1512
1513static void
1514intp()
1515{
1516 NET2ADD(IAC, IP);
1517 if (autoflush) {
1518 doflush();
1519 }
1520 if (autosynch) {
1521 dosynch();
1522 }
1523}
1524
1525static void
1526sendbrk()
1527{
1528 NET2ADD(IAC, BREAK);
1529 if (autoflush) {
1530 doflush();
1531 }
1532 if (autosynch) {
1533 dosynch();
1534 }
1535}
1536\f
1537/*
1538 * Send as much data as possible to the terminal.
1539 *
1540 * The return value indicates whether we did any
1541 * useful work.
1542 */
1543
1544
1545static int
1546ttyflush()
1547{
1548 int n;
1549
1550 if ((n = tfrontp - tbackp) > 0) {
1551 if (!(SYNCHing||flushout)) {
42cf515f 1552 n = TerminalWrite(tout, tbackp, n);
3e91ea2a 1553 } else {
3a764688 1554 TerminalFlushOutput();
3e91ea2a
GM
1555 /* we leave 'n' alone! */
1556 }
1557 }
1558 if (n >= 0) {
1559 tbackp += n;
1560 if (tbackp == tfrontp) {
1561 tbackp = tfrontp = ttyobuf;
1562 }
1563 }
1564 return n > 0;
1565}
1566
1567#if defined(TN3270)
1568
1569#if defined(unix)
1570static void
1571inputAvailable()
1572{
1573 HaveInput = 1;
1574}
1575#endif /* defined(unix) */
1576
1577void
1578outputPurge()
1579{
1580 int tmp = flushout;
1581
1582 flushout = 1;
1583
1584 ttyflush();
1585
1586 flushout = tmp;
1587}
1588
1589#endif /* defined(TN3270) */
1590\f
1591#if defined(unix)
1592/*
1593 * Various signal handling routines.
1594 */
1595
1596static void
1597deadpeer()
1598{
1599 setcommandmode();
1600 longjmp(peerdied, -1);
1601}
1602
1603static void
1604intr()
1605{
1606 if (localchars) {
1607 intp();
1608 return;
1609 }
1610 setcommandmode();
1611 longjmp(toplevel, -1);
1612}
1613
1614static void
1615intr2()
1616{
1617 if (localchars) {
1618 sendbrk();
1619 return;
1620 }
1621}
1622
1623static void
1624doescape()
1625{
1626 command(0);
1627}
1628#endif /* defined(unix) */
1629\f
3e91ea2a
GM
1630/*
1631 * These routines decides on what the mode should be (based on the values
1632 * of various global variables).
1633 */
1634
1635
1636static
1637getconnmode()
1638{
1639 static char newmode[16] =
1640 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1641 int modeindex = 0;
1642
1643 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1644 modeindex += 1;
1645 }
1646 if (hisopts[TELOPT_ECHO]) {
1647 modeindex += 2;
1648 }
1649 if (hisopts[TELOPT_SGA]) {
1650 modeindex += 4;
1651 }
1652 if (In3270) {
1653 modeindex += 8;
1654 }
1655 return newmode[modeindex];
1656}
1657
1658void
1659setconnmode()
1660{
3a764688 1661 TerminalNewMode(getconnmode());
3e91ea2a
GM
1662}
1663
1664
1665void
1666setcommandmode()
1667{
3a764688 1668 TerminalNewMode(0);
3e91ea2a
GM
1669}
1670\f
1671static void
1672willoption(option, reply)
1673 int option, reply;
1674{
1675 char *fmt;
1676
1677 switch (option) {
1678
de7a7ce6 1679 case TELOPT_ECHO:
3e91ea2a 1680# if defined(TN3270)
de7a7ce6
GM
1681 /*
1682 * The following is a pain in the rear-end.
1683 * Various IBM servers (some versions of Wiscnet,
1684 * possibly Fibronics/Spartacus, and who knows who
1685 * else) will NOT allow us to send "DO SGA" too early
1686 * in the setup proceedings. On the other hand,
1687 * 4.2 servers (telnetd) won't set SGA correctly.
1688 * So, we are stuck. Empirically (but, based on
1689 * a VERY small sample), the IBM servers don't send
1690 * out anything about ECHO, so we postpone our sending
1691 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
1692 * DO send).
1693 */
1694 {
de7a7ce6
GM
1695 if (askedSGA == 0) {
1696 askedSGA = 1;
1697 if (!hisopts[TELOPT_SGA]) {
1698 willoption(TELOPT_SGA, 0);
1699 }
1700 }
1701 }
1702 /* Fall through */
3e91ea2a
GM
1703 case TELOPT_EOR:
1704 case TELOPT_BINARY:
1705#endif /* defined(TN3270) */
3e91ea2a
GM
1706 case TELOPT_SGA:
1707 settimer(modenegotiated);
1708 hisopts[option] = 1;
1709 fmt = doopt;
1710 setconnmode(); /* possibly set new tty mode */
1711 break;
1712
1713 case TELOPT_TM:
1714 return; /* Never reply to TM will's/wont's */
1715
1716 default:
1717 fmt = dont;
1718 break;
1719 }
1720 sprintf(nfrontp, fmt, option);
1721 nfrontp += sizeof (dont) - 2;
1722 if (reply)
1723 printoption(">SENT", fmt, option, reply);
1724 else
1725 printoption("<SENT", fmt, option, reply);
1726}
1727
1728static void
1729wontoption(option, reply)
1730 int option, reply;
1731{
1732 char *fmt;
1733
1734 switch (option) {
1735
1736 case TELOPT_ECHO:
1737 case TELOPT_SGA:
1738 settimer(modenegotiated);
1739 hisopts[option] = 0;
1740 fmt = dont;
1741 setconnmode(); /* Set new tty mode */
1742 break;
1743
1744 case TELOPT_TM:
1745 return; /* Never reply to TM will's/wont's */
1746
1747 default:
1748 fmt = dont;
1749 }
1750 sprintf(nfrontp, fmt, option);
1751 nfrontp += sizeof (doopt) - 2;
1752 if (reply)
1753 printoption(">SENT", fmt, option, reply);
1754 else
1755 printoption("<SENT", fmt, option, reply);
1756}
1757
1758static void
1759dooption(option)
1760 int option;
1761{
1762 char *fmt;
1763
1764 switch (option) {
1765
1766 case TELOPT_TM:
1767 fmt = will;
1768 break;
1769
1770# if defined(TN3270)
1771 case TELOPT_EOR:
1772 case TELOPT_BINARY:
1773# endif /* defined(TN3270) */
1774 case TELOPT_TTYPE: /* terminal type option */
1775 case TELOPT_SGA: /* no big deal */
1776 fmt = will;
1777 myopts[option] = 1;
1778 break;
1779
1780 case TELOPT_ECHO: /* We're never going to echo... */
1781 default:
1782 fmt = wont;
1783 break;
1784 }
1785 sprintf(nfrontp, fmt, option);
1786 nfrontp += sizeof (doopt) - 2;
1787 printoption(">SENT", fmt, option, 0);
1788}
1789
1790/*
1791 * suboption()
1792 *
1793 * Look at the sub-option buffer, and try to be helpful to the other
1794 * side.
1795 *
1796 * Currently we recognize:
1797 *
1798 * Terminal type, send request.
1799 */
1800
1801static void
1802suboption()
1803{
1804 printsub("<", subbuffer, subend-subbuffer+1);
1805 switch (subbuffer[0]&0xff) {
1806 case TELOPT_TTYPE:
1807 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1808 ;
1809 } else {
1810 char *name;
1811 char namebuf[41];
1812 extern char *getenv();
1813 int len;
1814
1815#if defined(TN3270)
1816 /*
0d3c45d4 1817 * Try to send a 3270 type terminal name. Decide which one based
3e91ea2a
GM
1818 * on the format of our screen, and (in the future) color
1819 * capaiblities.
1820 */
3a764688 1821#if defined(unix)
42cf515f
GM
1822 if (initscr() != ERR) { /* Initialize curses to get line size */
1823 MaxNumberLines = LINES;
1824 MaxNumberColumns = COLS;
1825 }
1826#else /* defined(unix) */
1827 InitTerminal();
1828#endif /* defined(unix) */
1829 if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
3e91ea2a 1830 Sent3270TerminalType = 1;
42cf515f 1831 if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
3e91ea2a
GM
1832 MaxNumberLines = 27;
1833 MaxNumberColumns = 132;
1834 sb_terminal[SBTERMMODEL] = '5';
42cf515f 1835 } else if (MaxNumberLines >= 43) {
3e91ea2a
GM
1836 MaxNumberLines = 43;
1837 MaxNumberColumns = 80;
1838 sb_terminal[SBTERMMODEL] = '4';
42cf515f 1839 } else if (MaxNumberLines >= 32) {
3e91ea2a
GM
1840 MaxNumberLines = 32;
1841 MaxNumberColumns = 80;
1842 sb_terminal[SBTERMMODEL] = '3';
1843 } else {
1844 MaxNumberLines = 24;
1845 MaxNumberColumns = 80;
1846 sb_terminal[SBTERMMODEL] = '2';
1847 }
1848 NumberLines = 24; /* before we start out... */
1849 NumberColumns = 80;
1850 ScreenSize = NumberLines*NumberColumns;
1851 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1852 ExitString(stderr,
1853 "Programming error: MAXSCREENSIZE too small.\n", 1);
1854 /*NOTREACHED*/
1855 }
42cf515f 1856 memcpy(nfrontp, sb_terminal, sizeof sb_terminal);
3e91ea2a
GM
1857 printsub(">", nfrontp+2, sizeof sb_terminal-2);
1858 nfrontp += sizeof sb_terminal;
1859 return;
1860 }
1861#endif /* defined(TN3270) */
1862
1863 name = getenv("TERM");
1864 if ((name == 0) || ((len = strlen(name)) > 40)) {
1865 name = "UNKNOWN";
1866 }
1867 if ((len + 4+2) < NETROOM()) {
1868 strcpy(namebuf, name);
1869 upcase(namebuf);
1870 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1871 TELQUAL_IS, namebuf, IAC, SE);
1872 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1873 nfrontp += 4+strlen(namebuf)+2;
1874 } else {
1875 ExitString(stderr, "No room in buffer for terminal type.\n",
1876 1);
1877 /*NOTREACHED*/
1878 }
1879 }
1880
1881 default:
1882 break;
1883 }
1884}
1885
1886#if defined(TN3270)
1887static void
1888SetIn3270()
1889{
1890 if (Sent3270TerminalType && myopts[TELOPT_BINARY]
4f8919ec 1891 && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
3e91ea2a
GM
1892 if (!In3270) {
1893 In3270 = 1;
0d3c45d4 1894 Init3270(); /* Initialize 3270 functions */
3e91ea2a 1895 /* initialize terminal key mapping */
0d3c45d4 1896 InitTerminal(); /* Start terminal going */
3e91ea2a
GM
1897 setconnmode();
1898 }
1899 } else {
1900 if (In3270) {
1901 StopScreen(1);
1902 In3270 = 0;
3eb3862f 1903 Stop3270(); /* Tell 3270 we aren't here anymore */
3e91ea2a
GM
1904 setconnmode();
1905 }
1906 }
1907}
1908#endif /* defined(TN3270) */
1909\f
3e91ea2a
GM
1910
1911static void
1912telrcv()
1913{
1914 register int c;
04b5f59e 1915 static int telrcv_state = TS_DATA;
3e91ea2a
GM
1916# if defined(TN3270)
1917 register int Scc;
1918 register char *Sbp;
1919# endif /* defined(TN3270) */
1920
1921 while ((scc > 0) && (TTYROOM() > 2)) {
1922 c = *sbp++ & 0xff, scc--;
04b5f59e 1923 switch (telrcv_state) {
3e91ea2a
GM
1924
1925 case TS_CR:
04b5f59e 1926 telrcv_state = TS_DATA;
3e91ea2a
GM
1927 if (c == '\0') {
1928 break; /* Ignore \0 after CR */
1929 } else if (c == '\n') {
1930 if (hisopts[TELOPT_ECHO] && !crmod) {
1931 TTYADD(c);
1932 }
1933 break;
1934 }
1935 /* Else, fall through */
1936
1937 case TS_DATA:
1938 if (c == IAC) {
04b5f59e 1939 telrcv_state = TS_IAC;
3e91ea2a
GM
1940 continue;
1941 }
1942# if defined(TN3270)
1943 if (In3270) {
1944 *Ifrontp++ = c;
1945 Sbp = sbp;
1946 Scc = scc;
1947 while (Scc > 0) {
1948 c = *Sbp++ & 0377, Scc--;
1949 if (c == IAC) {
04b5f59e 1950 telrcv_state = TS_IAC;
3e91ea2a
GM
1951 break;
1952 }
1953 *Ifrontp++ = c;
1954 }
1955 sbp = Sbp;
1956 scc = Scc;
1957 } else
1958# endif /* defined(TN3270) */
1959 /*
1960 * The 'crmod' hack (see following) is needed
1961 * since we can't * set CRMOD on output only.
1962 * Machines like MULTICS like to send \r without
1963 * \n; since we must turn off CRMOD to get proper
1964 * input, the mapping is done here (sigh).
1965 */
1966 if (c == '\r') {
1967 if (scc > 0) {
1968 c = *sbp&0xff;
1969 if (c == 0) {
1970 sbp++, scc--;
1971 /* a "true" CR */
1972 TTYADD('\r');
1973 } else if (!hisopts[TELOPT_ECHO] &&
1974 (c == '\n')) {
1975 sbp++, scc--;
1976 TTYADD('\n');
1977 } else {
1978 TTYADD('\r');
1979 if (crmod) {
1980 TTYADD('\n');
1981 }
1982 }
1983 } else {
04b5f59e 1984 telrcv_state = TS_CR;
3e91ea2a
GM
1985 TTYADD('\r');
1986 if (crmod) {
1987 TTYADD('\n');
1988 }
1989 }
1990 } else {
1991 TTYADD(c);
1992 }
1993 continue;
1994
1995 case TS_IAC:
1996 switch (c) {
1997
1998 case WILL:
04b5f59e 1999 telrcv_state = TS_WILL;
3e91ea2a
GM
2000 continue;
2001
2002 case WONT:
04b5f59e 2003 telrcv_state = TS_WONT;
3e91ea2a
GM
2004 continue;
2005
2006 case DO:
04b5f59e 2007 telrcv_state = TS_DO;
3e91ea2a
GM
2008 continue;
2009
2010 case DONT:
04b5f59e 2011 telrcv_state = TS_DONT;
3e91ea2a
GM
2012 continue;
2013
2014 case DM:
2015 /*
2016 * We may have missed an urgent notification,
2017 * so make sure we flush whatever is in the
2018 * buffer currently.
2019 */
2020 SYNCHing = 1;
2021 ttyflush();
2022 SYNCHing = stilloob(net);
2023 settimer(gotDM);
2024 break;
2025
2026 case NOP:
2027 case GA:
2028 break;
2029
2030 case SB:
2031 SB_CLEAR();
04b5f59e 2032 telrcv_state = TS_SB;
3e91ea2a
GM
2033 continue;
2034
2035# if defined(TN3270)
2036 case EOR:
2037 if (In3270) {
2038 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
2039 if (Ibackp == Ifrontp) {
2040 Ibackp = Ifrontp = Ibuf;
2041 ISend = 0; /* should have been! */
2042 } else {
2043 ISend = 1;
2044 }
2045 }
2046 break;
2047# endif /* defined(TN3270) */
2048
2049 case IAC:
2050# if !defined(TN3270)
2051 TTYADD(IAC);
2052# else /* !defined(TN3270) */
2053 if (In3270) {
2054 *Ifrontp++ = IAC;
2055 } else {
2056 TTYADD(IAC);
2057 }
2058# endif /* !defined(TN3270) */
2059 break;
2060
2061 default:
2062 break;
2063 }
04b5f59e 2064 telrcv_state = TS_DATA;
3e91ea2a
GM
2065 continue;
2066
2067 case TS_WILL:
2068 printoption(">RCVD", will, c, !hisopts[c]);
2069 if (c == TELOPT_TM) {
2070 if (flushout) {
2071 flushout = 0;
2072 }
2073 } else if (!hisopts[c]) {
2074 willoption(c, 1);
2075 }
2076 SetIn3270();
04b5f59e 2077 telrcv_state = TS_DATA;
3e91ea2a
GM
2078 continue;
2079
2080 case TS_WONT:
2081 printoption(">RCVD", wont, c, hisopts[c]);
2082 if (c == TELOPT_TM) {
2083 if (flushout) {
2084 flushout = 0;
2085 }
2086 } else if (hisopts[c]) {
2087 wontoption(c, 1);
2088 }
2089 SetIn3270();
04b5f59e 2090 telrcv_state = TS_DATA;
3e91ea2a
GM
2091 continue;
2092
2093 case TS_DO:
2094 printoption(">RCVD", doopt, c, !myopts[c]);
2095 if (!myopts[c])
2096 dooption(c);
2097 SetIn3270();
04b5f59e 2098 telrcv_state = TS_DATA;
3e91ea2a
GM
2099 continue;
2100
2101 case TS_DONT:
2102 printoption(">RCVD", dont, c, myopts[c]);
2103 if (myopts[c]) {
2104 myopts[c] = 0;
2105 sprintf(nfrontp, wont, c);
2106 nfrontp += sizeof (wont) - 2;
2107 flushline = 1;
2108 setconnmode(); /* set new tty mode (maybe) */
2109 printoption(">SENT", wont, c, 0);
2110 }
2111 SetIn3270();
04b5f59e 2112 telrcv_state = TS_DATA;
3e91ea2a
GM
2113 continue;
2114
2115 case TS_SB:
2116 if (c == IAC) {
04b5f59e 2117 telrcv_state = TS_SE;
3e91ea2a
GM
2118 } else {
2119 SB_ACCUM(c);
2120 }
2121 continue;
2122
2123 case TS_SE:
2124 if (c != SE) {
2125 if (c != IAC) {
2126 SB_ACCUM(IAC);
2127 }
2128 SB_ACCUM(c);
04b5f59e 2129 telrcv_state = TS_SB;
3e91ea2a
GM
2130 } else {
2131 SB_TERM();
2132 suboption(); /* handle sub-option */
2133 SetIn3270();
04b5f59e 2134 telrcv_state = TS_DATA;
3e91ea2a
GM
2135 }
2136 }
2137 }
2138}
2139\f
2140#if defined(TN3270)
2141
2142/*
2143 * The following routines are places where the various tn3270
2144 * routines make calls into telnet.c.
2145 */
2146
2147/* TtyChars() - returns the number of characters in the TTY buffer */
2148TtyChars()
2149{
2150 return(tfrontp-tbackp);
2151}
2152
2153/*
2154 * DataToNetwork - queue up some data to go to network. If "done" is set,
2155 * then when last byte is queued, we add on an IAC EOR sequence (so,
2156 * don't call us with "done" until you want that done...)
2157 *
2158 * We actually do send all the data to the network buffer, since our
2159 * only client needs for us to do that.
2160 */
2161
2162int
2163DataToNetwork(buffer, count, done)
2164register char *buffer; /* where the data is */
2165register int count; /* how much to send */
2166int done; /* is this the last of a logical block */
2167{
2168 register int c;
2169 int origCount;
2170 fd_set o;
2171
2172 origCount = count;
2173 FD_ZERO(&o);
2174
2175 while (count) {
2176 if ((netobuf+sizeof netobuf - nfrontp) < 6) {
2177 netflush();
2178 while ((netobuf+sizeof netobuf - nfrontp) < 6) {
2179 FD_SET(net, &o);
2180 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
2181 (struct timeval *) 0);
2182 netflush();
2183 }
2184 }
2185 c = *buffer++;
2186 count--;
2187 if (c == IAC) {
2188 *nfrontp++ = IAC;
2189 *nfrontp++ = IAC;
2190 } else {
2191 *nfrontp++ = c;
2192 }
2193 }
2194
2195 if (done && !count) {
2196 *nfrontp++ = IAC;
2197 *nfrontp++ = EOR;
2198 netflush(); /* try to move along as quickly as ... */
2199 }
2200 return(origCount - count);
2201}
2202
2203/* DataToTerminal - queue up some data to go to terminal. */
2204
2205int
2206DataToTerminal(buffer, count)
2207register char *buffer; /* where the data is */
2208register int count; /* how much to send */
2209{
2210 int origCount;
42cf515f 2211#if defined(unix)
3e91ea2a
GM
2212 fd_set o;
2213
3e91ea2a 2214 FD_ZERO(&o);
42cf515f
GM
2215#endif /* defined(unix) */
2216 origCount = count;
3e91ea2a
GM
2217
2218 while (count) {
2219 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2220 ttyflush();
2221 while (tfrontp >= ttyobuf+sizeof ttyobuf) {
42cf515f 2222#if defined(unix)
3e91ea2a
GM
2223 FD_SET(tout, &o);
2224 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
2225 (struct timeval *) 0);
42cf515f 2226#endif /* defined(unix) */
3e91ea2a
GM
2227 ttyflush();
2228 }
2229 }
2230 *tfrontp++ = *buffer++;
2231 count--;
2232 }
2233 return(origCount - count);
2234}
2235
2236/* EmptyTerminal - called to make sure that the terminal buffer is empty.
2237 * Note that we consider the buffer to run all the
2238 * way to the kernel (thus the select).
2239 */
2240
2241void
2242EmptyTerminal()
2243{
42cf515f 2244#if defined(unix)
3e91ea2a
GM
2245 fd_set o;
2246
2247 FD_ZERO(&o);
42cf515f 2248#endif /* defined(unix) */
3e91ea2a
GM
2249
2250 if (tfrontp == tbackp) {
42cf515f 2251#if defined(unix)
3e91ea2a
GM
2252 FD_SET(tout, &o);
2253 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2254 (struct timeval *) 0); /* wait for TTLOWAT */
42cf515f 2255#endif /* defined(unix) */
3e91ea2a
GM
2256 } else {
2257 while (tfrontp != tbackp) {
2258 ttyflush();
42cf515f 2259#if defined(unix)
3e91ea2a
GM
2260 FD_SET(tout, &o);
2261 (void) select(tout+1, (int *) 0, &o, (int *) 0,
2262 (struct timeval *) 0); /* wait for TTLOWAT */
42cf515f 2263#endif /* defined(unix) */
3e91ea2a
GM
2264 }
2265 }
2266}
2267
2268/*
2269 * Push3270 - Try to send data along the 3270 output (to screen) direction.
2270 */
2271
2272static int
2273Push3270()
2274{
2275 int save = scc;
2276
2277 if (scc) {
2278 if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
2279 if (Ibackp != Ibuf) {
42cf515f 2280 memcpy(Ibuf, Ibackp, Ifrontp-Ibackp);
3e91ea2a
GM
2281 Ifrontp -= (Ibackp-Ibuf);
2282 Ibackp = Ibuf;
2283 }
2284 }
2285 if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
2286 telrcv();
2287 }
2288 }
2289 return save != scc;
2290}
2291
2292
2293/*
2294 * Finish3270 - get the last dregs of 3270 data out to the terminal
2295 * before quitting.
2296 */
2297
2298static void
2299Finish3270()
2300{
2301 while (Push3270() || !DoTerminalOutput()) {
de3003f6
GM
2302#if defined(unix)
2303 HaveInput = 0;
2304#endif /* defined(unix) */
3e91ea2a
GM
2305 ;
2306 }
2307}
2308
2309
2310
2311/* StringToTerminal - output a null terminated string to the terminal */
2312
2313void
2314StringToTerminal(s)
2315char *s;
2316{
2317 int count;
2318
2319 count = strlen(s);
2320 if (count) {
2321 (void) DataToTerminal(s, count); /* we know it always goes... */
2322 }
2323}
2324
2325
2326#if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
2327/* _putchar - output a single character to the terminal. This name is so that
2328 * curses(3x) can call us to send out data.
2329 */
2330
2331void
2332_putchar(c)
2333char c;
2334{
2335 if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2336 (void) DataToTerminal(&c, 1);
2337 } else {
2338 *tfrontp++ = c; /* optimize if possible. */
2339 }
2340}
2341#endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
2342\f
2343static void
2344SetForExit()
2345{
2346 setconnmode();
2347 if (In3270) {
2348 Finish3270();
2349 }
2350 setcommandmode();
2351 fflush(stdout);
2352 fflush(stderr);
2353 if (In3270) {
2354 StopScreen(1);
2355 }
2356 setconnmode();
2357 setcommandmode();
2358}
2359
2360static void
2361Exit(returnCode)
2362int returnCode;
2363{
2364 SetForExit();
2365 exit(returnCode);
2366}
2367
2368void
2369ExitString(file, string, returnCode)
2370FILE *file;
2371char *string;
2372int returnCode;
2373{
2374 SetForExit();
2375 fwrite(string, 1, strlen(string), file);
2376 exit(returnCode);
2377}
2378
2379void
2380ExitPerror(string, returnCode)
2381char *string;
2382int returnCode;
2383{
2384 SetForExit();
2385 perror(string);
2386 exit(returnCode);
2387}
2388
2389#endif /* defined(TN3270) */
2390\f
3eb3862f
GM
2391/*
2392 * Scheduler()
2393 *
2394 * Try to do something.
2395 *
2396 * If we do something useful, return 1; else return 0.
2397 *
2398 */
2399
2400
2401int
3e91ea2a
GM
2402Scheduler(block)
2403int block; /* should we block in the select ? */
2404{
2405 register int c;
2406 /* One wants to be a bit careful about setting returnValue
2407 * to one, since a one implies we did some useful work,
2408 * and therefore probably won't be called to block next
2409 * time (TN3270 mode only).
2410 */
2411 int returnValue = 0;
2412 static struct timeval TimeValue = { 0 };
2413
2414 if (scc < 0 && tcc < 0) {
2415 return -1;
2416 }
2417
2418 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
2419 FD_SET(net, &obits);
2420 }
42cf515f 2421#if !defined(MSDOS)
3e91ea2a
GM
2422 if (TTYBYTES()) {
2423 FD_SET(tout, &obits);
2424 }
3eb3862f 2425 if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
3e91ea2a
GM
2426 FD_SET(tin, &ibits);
2427 }
42cf515f 2428#endif /* !defined(MSDOS) */
3e91ea2a
GM
2429# if !defined(TN3270)
2430 if (TTYROOM()) {
2431 FD_SET(net, &ibits);
2432 }
2433# else /* !defined(TN3270) */
2434 if (!ISend && TTYROOM()) {
2435 FD_SET(net, &ibits);
2436 }
2437# endif /* !defined(TN3270) */
2438 if (!SYNCHing) {
2439 FD_SET(net, &xbits);
2440 }
2441# if defined(TN3270) && defined(unix)
2442 if (HaveInput) {
2443 HaveInput = 0;
2444 signal(SIGIO, inputAvailable);
2445 }
2446#endif /* defined(TN3270) && defined(unix) */
2447 if ((c = select(16, &ibits, &obits, &xbits,
42cf515f 2448 block? (struct timeval *)0 : &TimeValue)) < 0) {
3e91ea2a
GM
2449 if (c == -1) {
2450 /*
2451 * we can get EINTR if we are in line mode,
2452 * and the user does an escape (TSTP), or
2453 * some other signal generator.
2454 */
2455 if (errno == EINTR) {
2456 return 0;
2457 }
2458# if defined(TN3270)
2459 /*
2460 * we can get EBADF if we were in transparent
2461 * mode, and the transcom process died.
2462 */
2463 if (errno == EBADF) {
2464 /*
2465 * zero the bits (even though kernel does it)
2466 * to make sure we are selecting on the right
2467 * ones.
2468 */
2469 FD_ZERO(&ibits);
2470 FD_ZERO(&obits);
2471 FD_ZERO(&xbits);
2472 return 0;
2473 }
2474# endif /* defined(TN3270) */
2475 /* I don't like this, does it ever happen? */
2476 printf("sleep(5) from telnet, after select\r\n");
2477#if defined(unix)
2478 sleep(5);
2479#endif /* defined(unix) */
2480 }
2481 return 0;
2482 }
2483
2484 /*
2485 * Any urgent data?
2486 */
2487 if (FD_ISSET(net, &xbits)) {
2488 FD_CLR(net, &xbits);
2489 SYNCHing = 1;
2490 ttyflush(); /* flush already enqueued data */
2491 }
2492
2493 /*
2494 * Something to read from the network...
2495 */
2496 if (FD_ISSET(net, &ibits)) {
2497 int canread;
2498
2499 FD_CLR(net, &ibits);
2500 if (scc == 0) {
2501 sbp = sibuf;
2502 }
9c96a9cd 2503 canread = sibuf + sizeof sibuf - (sbp+scc);
3e91ea2a
GM
2504#if !defined(SO_OOBINLINE)
2505 /*
2506 * In 4.2 (and some early 4.3) systems, the
2507 * OOB indication and data handling in the kernel
2508 * is such that if two separate TCP Urgent requests
2509 * come in, one byte of TCP data will be overlaid.
2510 * This is fatal for Telnet, but we try to live
2511 * with it.
2512 *
2513 * In addition, in 4.2 (and...), a special protocol
2514 * is needed to pick up the TCP Urgent data in
2515 * the correct sequence.
2516 *
2517 * What we do is: if we think we are in urgent
2518 * mode, we look to see if we are "at the mark".
2519 * If we are, we do an OOB receive. If we run
2520 * this twice, we will do the OOB receive twice,
2521 * but the second will fail, since the second
2522 * time we were "at the mark", but there wasn't
2523 * any data there (the kernel doesn't reset
2524 * "at the mark" until we do a normal read).
2525 * Once we've read the OOB data, we go ahead
2526 * and do normal reads.
2527 *
2528 * There is also another problem, which is that
2529 * since the OOB byte we read doesn't put us
2530 * out of OOB state, and since that byte is most
2531 * likely the TELNET DM (data mark), we would
2532 * stay in the TELNET SYNCH (SYNCHing) state.
2533 * So, clocks to the rescue. If we've "just"
2534 * received a DM, then we test for the
2535 * presence of OOB data when the receive OOB
2536 * fails (and AFTER we did the normal mode read
2537 * to clear "at the mark").
2538 */
2539 if (SYNCHing) {
2540 int atmark;
2541
2542 ioctl(net, SIOCATMARK, (char *)&atmark);
2543 if (atmark) {
9c96a9cd 2544 c = recv(net, sbp+scc, canread, MSG_OOB);
3e91ea2a 2545 if ((c == -1) && (errno == EINVAL)) {
42cf515f 2546 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2547 if (clocks.didnetreceive < clocks.gotDM) {
2548 SYNCHing = stilloob(net);
2549 }
2550 }
2551 } else {
42cf515f 2552 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2553 }
2554 } else {
42cf515f 2555 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2556 }
2557 settimer(didnetreceive);
2558#else /* !defined(SO_OOBINLINE) */
42cf515f 2559 c = recv(net, sbp+scc, canread, 0);
3e91ea2a
GM
2560#endif /* !defined(SO_OOBINLINE) */
2561 if (c < 0 && errno == EWOULDBLOCK) {
2562 c = 0;
2563 } else if (c <= 0) {
2564 return -1;
2565 }
2566 if (netdata) {
9c96a9cd 2567 Dump('<', sbp+scc, c);
3e91ea2a
GM
2568 }
2569 scc += c;
2570 returnValue = 1;
2571 }
2572
2573 /*
2574 * Something to read from the tty...
2575 */
42cf515f 2576#if defined(MSDOS)
3eb3862f 2577 if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
42cf515f
GM
2578#else /* defined(MSDOS) */
2579 if (FD_ISSET(tin, &ibits))
2580#endif /* defined(MSDOS) */
2581 {
3e91ea2a
GM
2582 FD_CLR(tin, &ibits);
2583 if (tcc == 0) {
2584 tbp = tibuf; /* nothing left, reset */
2585 }
42cf515f 2586 c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp);
3e91ea2a
GM
2587 if (c < 0 && errno == EWOULDBLOCK) {
2588 c = 0;
2589 } else {
3a764688 2590#if defined(unix)
3e91ea2a
GM
2591 /* EOF detection for line mode!!!! */
2592 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
2593 /* must be an EOF... */
2594 *tbp = ntc.t_eofc;
2595 c = 1;
2596 }
3a764688 2597#endif /* defined(unix) */
3e91ea2a
GM
2598 if (c <= 0) {
2599 tcc = c;
2600 return -1;
2601 }
2602 }
2603 tcc += c;
2604 returnValue = 1; /* did something useful */
2605 }
2606
2607# if defined(TN3270)
2608 if (tcc > 0) {
2609 if (In3270) {
2610 c = DataFromTerminal(tbp, tcc);
2611 if (c) {
2612 returnValue = 1;
2613 }
2614 tcc -= c;
2615 tbp += c;
2616 } else {
58f9a26f 2617# endif /* defined(TN3270) */
3e91ea2a
GM
2618 returnValue = 1;
2619 while (tcc > 0) {
2620 register int sc;
2621
2622 if (NETROOM() < 2) {
2623 flushline = 1;
2624 break;
2625 }
2626 c = *tbp++ & 0xff, sc = strip(c), tcc--;
2627 if (sc == escape) {
2628 command(0);
2629 tcc = 0;
2630 flushline = 1;
2631 break;
2632 } else if (MODE_LINE(globalmode) && (sc == echoc)) {
2633 if (tcc > 0 && strip(*tbp) == echoc) {
2634 tbp++;
2635 tcc--;
2636 } else {
2637 dontlecho = !dontlecho;
2638 settimer(echotoggle);
2639 setconnmode();
2640 tcc = 0;
2641 flushline = 1;
2642 break;
2643 }
2644 }
2645 if (localchars) {
42cf515f 2646 if (TerminalSpecialChars(sc) == 0) {
3e91ea2a
GM
2647 break;
2648 }
2649 }
2650 switch (c) {
2651 case '\n':
2652 /*
2653 * If we are in CRMOD mode (\r ==> \n)
2654 * on our local machine, then probably
2655 * a newline (unix) is CRLF (TELNET).
2656 */
2657 if (MODE_LOCAL_CHARS(globalmode)) {
2658 NETADD('\r');
2659 }
2660 NETADD('\n');
2661 flushline = 1;
2662 break;
2663 case '\r':
2664 NET2ADD('\r', '\0');
2665 flushline = 1;
2666 break;
2667 case IAC:
2668 NET2ADD(IAC, IAC);
2669 break;
2670 default:
2671 NETADD(c);
2672 break;
2673 }
2674 }
2675# if defined(TN3270)
2676 }
2677 }
2678# endif /* defined(TN3270) */
2679
2680 if ((!MODE_LINE(globalmode) || flushline) &&
2681 FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2682 FD_CLR(net, &obits);
2683 returnValue = netflush();
2684 }
2685 if (scc > 0) {
2686# if !defined(TN3270)
2687 telrcv();
2688 returnValue = 1;
2689# else /* !defined(TN3270) */
2690 returnValue = Push3270();
2691# endif /* !defined(TN3270) */
2692 }
42cf515f
GM
2693#if defined(MSDOS)
2694 if (TTYBYTES())
2695#else /* defined(MSDOS) */
2696 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
2697#endif /* defined(MSDOS) */
2698 {
3e91ea2a
GM
2699 FD_CLR(tout, &obits);
2700 returnValue = ttyflush();
2701 }
2702 return returnValue;
2703}
2704\f
2705/*
2706 * Select from tty and network...
2707 */
2708static void
2709telnet()
2710{
42cf515f
GM
2711#if defined(MSDOS)
2712#define SCHED_BLOCK 0 /* Don't block in MSDOS */
2713#else /* defined(MSDOS) */
2714#define SCHED_BLOCK 1
2715#endif /* defined(MSDOS) */
2716
3e91ea2a
GM
2717#if defined(TN3270) && defined(unix)
2718 int myPid;
2719#endif /* defined(TN3270) */
2720
2721 tout = fileno(stdout);
2722 tin = fileno(stdin);
2723 setconnmode();
2724 scc = 0;
2725 tcc = 0;
2726 FD_ZERO(&ibits);
2727 FD_ZERO(&obits);
2728 FD_ZERO(&xbits);
2729
3a764688 2730 NetNonblockingIO(net, 1);
3e91ea2a
GM
2731
2732#if defined(TN3270)
3dd51ee4
GM
2733 if (noasynch == 0) { /* DBX can't handle! */
2734 NetSigIO(net, 1);
2735 }
3a764688 2736 NetSetPgrp(net);
3e91ea2a
GM
2737#endif /* defined(TN3270) */
2738
3a764688
GM
2739
2740#if defined(SO_OOBINLINE) && !defined(MSDOS)
2741 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
2742#endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */
3e91ea2a 2743
58f9a26f 2744# if !defined(TN3270)
3e91ea2a
GM
2745 if (telnetport) {
2746 if (!hisopts[TELOPT_SGA]) {
2747 willoption(TELOPT_SGA, 0);
2748 }
3e91ea2a
GM
2749 if (!myopts[TELOPT_TTYPE]) {
2750 dooption(TELOPT_TTYPE, 0);
2751 }
3e91ea2a 2752 }
58f9a26f 2753# endif /* !defined(TN3270) */
3e91ea2a
GM
2754
2755# if !defined(TN3270)
2756 for (;;) {
42cf515f 2757 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2758 setcommandmode();
2759 return;
2760 }
2761 }
2762# else /* !defined(TN3270) */
2763 for (;;) {
2764 int schedValue;
2765
4f8919ec 2766 while (!In3270 && !shell_active) {
42cf515f 2767 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2768 setcommandmode();
2769 return;
2770 }
2771 }
2772
2773 while ((schedValue = Scheduler(0)) != 0) {
2774 if (schedValue == -1) {
2775 setcommandmode();
2776 return;
2777 }
2778 }
2779 /* If there is data waiting to go out to terminal, don't
2780 * schedule any more data for the terminal.
2781 */
2782 if (tfrontp-tbackp) {
2783 schedValue = 1;
2784 } else {
3eb3862f 2785 if (shell_active) {
3eb3862f
GM
2786 if (shell_continue() == 0) {
2787 ConnectScreen();
3eb3862f 2788 }
4f8919ec 2789 } else if (In3270) {
3eb3862f
GM
2790 schedValue = DoTerminalOutput();
2791 }
3e91ea2a 2792 }
3eb3862f 2793 if (schedValue && (shell_active == 0)) {
42cf515f 2794 if (Scheduler(SCHED_BLOCK) == -1) {
3e91ea2a
GM
2795 setcommandmode();
2796 return;
2797 }
2798 }
2799 }
2800# endif /* !defined(TN3270) */
2801}
2802\f
2803/*
2804 * The following are data structures and routines for
2805 * the "send" command.
2806 *
2807 */
2808
2809struct sendlist {
2810 char *name; /* How user refers to it (case independent) */
2811 int what; /* Character to be sent (<0 ==> special) */
2812 char *help; /* Help information (0 ==> no help) */
2813#if defined(NOT43)
2814 int (*routine)(); /* Routine to perform (for special ops) */
2815#else /* defined(NOT43) */
2816 void (*routine)(); /* Routine to perform (for special ops) */
2817#endif /* defined(NOT43) */
2818};
2819\f
2820#define SENDQUESTION -1
2821#define SENDESCAPE -3
2822
2823static struct sendlist Sendlist[] = {
2824 { "ao", AO, "Send Telnet Abort output" },
2825 { "ayt", AYT, "Send Telnet 'Are You There'" },
2826 { "brk", BREAK, "Send Telnet Break" },
2827 { "ec", EC, "Send Telnet Erase Character" },
2828 { "el", EL, "Send Telnet Erase Line" },
2829 { "escape", SENDESCAPE, "Send current escape character" },
2830 { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2831 { "ip", IP, "Send Telnet Interrupt Process" },
2832 { "nop", NOP, "Send Telnet 'No operation'" },
2833 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2834 { "?", SENDQUESTION, "Display send options" },
2835 { 0 }
2836};
2837
2838static struct sendlist Sendlist2[] = { /* some synonyms */
2839 { "break", BREAK, 0 },
2840
2841 { "intp", IP, 0 },
2842 { "interrupt", IP, 0 },
2843 { "intr", IP, 0 },
2844
2845 { "help", SENDQUESTION, 0 },
2846
2847 { 0 }
2848};
2849
2850static char **
2851getnextsend(name)
2852char *name;
2853{
2854 struct sendlist *c = (struct sendlist *) name;
2855
2856 return (char **) (c+1);
2857}
2858
2859static struct sendlist *
2860getsend(name)
2861char *name;
2862{
2863 struct sendlist *sl;
2864
2865 if ((sl = (struct sendlist *)
2866 genget(name, (char **) Sendlist, getnextsend)) != 0) {
2867 return sl;
2868 } else {
2869 return (struct sendlist *)
2870 genget(name, (char **) Sendlist2, getnextsend);
2871 }
2872}
2873
2874static
2875sendcmd(argc, argv)
2876int argc;
2877char **argv;
2878{
2879 int what; /* what we are sending this time */
2880 int count; /* how many bytes we are going to need to send */
2881 int i;
2882 int question = 0; /* was at least one argument a question */
2883 struct sendlist *s; /* pointer to current command */
2884
2885 if (argc < 2) {
2886 printf("need at least one argument for 'send' command\n");
2887 printf("'send ?' for help\n");
2888 return 0;
2889 }
2890 /*
2891 * First, validate all the send arguments.
2892 * In addition, we see how much space we are going to need, and
2893 * whether or not we will be doing a "SYNCH" operation (which
2894 * flushes the network queue).
2895 */
2896 count = 0;
2897 for (i = 1; i < argc; i++) {
2898 s = getsend(argv[i]);
2899 if (s == 0) {
2900 printf("Unknown send argument '%s'\n'send ?' for help.\n",
2901 argv[i]);
2902 return 0;
2903 } else if (s == Ambiguous(struct sendlist *)) {
2904 printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2905 argv[i]);
2906 return 0;
2907 }
2908 switch (s->what) {
2909 case SENDQUESTION:
2910 break;
2911 case SENDESCAPE:
2912 count += 1;
2913 break;
2914 case SYNCH:
2915 count += 2;
2916 break;
2917 default:
2918 count += 2;
2919 break;
2920 }
2921 }
2922 /* Now, do we have enough room? */
2923 if (NETROOM() < count) {
2924 printf("There is not enough room in the buffer TO the network\n");
2925 printf("to process your request. Nothing will be done.\n");
2926 printf("('send synch' will throw away most data in the network\n");
2927 printf("buffer, if this might help.)\n");
2928 return 0;
2929 }
2930 /* OK, they are all OK, now go through again and actually send */
2931 for (i = 1; i < argc; i++) {
2932 if ((s = getsend(argv[i])) == 0) {
2933 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
2934 quit();
2935 /*NOTREACHED*/
2936 }
2937 if (s->routine) {
2938 (*s->routine)(s);
2939 } else {
2940 switch (what = s->what) {
2941 case SYNCH:
2942 dosynch();
2943 break;
2944 case SENDQUESTION:
2945 for (s = Sendlist; s->name; s++) {
2946 if (s->help) {
2947 printf(s->name);
2948 if (s->help) {
2949 printf("\t%s", s->help);
2950 }
2951 printf("\n");
2952 }
2953 }
2954 question = 1;
2955 break;
2956 case SENDESCAPE:
2957 NETADD(escape);
2958 break;
2959 default:
2960 NET2ADD(IAC, what);
2961 break;
2962 }
2963 }
2964 }
2965 return !question;
2966}
2967\f
2968/*
2969 * The following are the routines and data structures referred
2970 * to by the arguments to the "toggle" command.
2971 */
2972
2973static
2974lclchars()
2975{
2976 donelclchars = 1;
2977 return 1;
2978}
2979
2980static
2981togdebug()
2982{
2983#ifndef NOT43
2984 if (net > 0 &&
3a764688 2985 (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
3e91ea2a
GM
2986 perror("setsockopt (SO_DEBUG)");
2987 }
58f9a26f 2988#else /* NOT43 */
3e91ea2a 2989 if (debug) {
3a764688 2990 if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
3e91ea2a
GM
2991 perror("setsockopt (SO_DEBUG)");
2992 } else
2993 printf("Cannot turn off socket debugging\n");
58f9a26f 2994#endif /* NOT43 */
3e91ea2a
GM
2995 return 1;
2996}
2997
2998
4f8919ec
GM
2999static int
3000togbinary()
3001{
3002 donebinarytoggle = 1;
3003
3004 if (myopts[TELOPT_BINARY] == 0) { /* Go into binary mode */
3005 NET2ADD(IAC, DO);
3006 NETADD(TELOPT_BINARY);
3007 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3008 NET2ADD(IAC, WILL);
3009 NETADD(TELOPT_BINARY);
3010 printoption("<SENT", doopt, TELOPT_BINARY, 0);
3011 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
3012 printf("Negotiating binary mode with remote host.\n");
3013 } else { /* Turn off binary mode */
3014 NET2ADD(IAC, DONT);
3015 NETADD(TELOPT_BINARY);
3016 printoption("<SENT", dont, TELOPT_BINARY, 0);
3017 NET2ADD(IAC, DONT);
3018 NETADD(TELOPT_BINARY);
3019 printoption("<SENT", dont, TELOPT_BINARY, 0);
3020 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
3021 printf("Negotiating network ascii mode with remote host.\n");
3022 }
3023 return 1;
3024}
3025
3026
3e91ea2a
GM
3027
3028extern int togglehelp();
3029
3030struct togglelist {
3031 char *name; /* name of toggle */
3032 char *help; /* help message */
3033 int (*handler)(); /* routine to do actual setting */
3034 int dohelp; /* should we display help information */
3035 int *variable;
3036 char *actionexplanation;
3037};
3038
3039static struct togglelist Togglelist[] = {
3040 { "autoflush",
3041 "toggle flushing of output when sending interrupt characters",
3042 0,
3043 1,
3044 &autoflush,
3045 "flush output when sending interrupt characters" },
3046 { "autosynch",
3047 "toggle automatic sending of interrupt characters in urgent mode",
3048 0,
3049 1,
3050 &autosynch,
3051 "send interrupt characters in urgent mode" },
4f8919ec
GM
3052 { "binary",
3053 "toggle sending and receiving of binary data",
3054 togbinary,
3055 1,
3056 0,
3057 "send and receive network data in binary mode" },
3e91ea2a
GM
3058 { "crmod",
3059 "toggle mapping of received carriage returns",
3060 0,
3061 1,
3062 &crmod,
3063 "map carriage return on output" },
3064 { "localchars",
3065 "toggle local recognition of certain control characters",
3066 lclchars,
3067 1,
3068 &localchars,
3069 "recognize certain control characters" },
3070 { " ", "", 0, 1 }, /* empty line */
3071 { "debug",
3072 "(debugging) toggle debugging",
3073 togdebug,
3074 1,
3075 &debug,
3076 "turn on socket level debugging" },
3077 { "netdata",
3078 "(debugging) toggle printing of hexadecimal network data",
3079 0,
3080 1,
3081 &netdata,
3082 "print hexadecimal representation of network traffic" },
3083 { "options",
3084 "(debugging) toggle viewing of options processing",
3085 0,
3086 1,
3087 &showoptions,
3088 "show option processing" },
3089 { " ", "", 0, 1 }, /* empty line */
3090 { "?",
3091 "display help information",
3092 togglehelp,
3093 1 },
3094 { "help",
3095 "display help information",
3096 togglehelp,
3097 0 },
3098 { 0 }
3099};
3100
3101static
3102togglehelp()
3103{
3104 struct togglelist *c;
3105
3106 for (c = Togglelist; c->name; c++) {
3107 if (c->dohelp) {
3108 printf("%s\t%s\n", c->name, c->help);
3109 }
3110 }
3111 return 0;
3112}
3113
3114static char **
3115getnexttoggle(name)
3116char *name;
3117{
3118 struct togglelist *c = (struct togglelist *) name;
3119
3120 return (char **) (c+1);
3121}
3122
3123static struct togglelist *
3124gettoggle(name)
3125char *name;
3126{
3127 return (struct togglelist *)
3128 genget(name, (char **) Togglelist, getnexttoggle);
3129}
3130
3131static
3132toggle(argc, argv)
3133int argc;
3134char *argv[];
3135{
3136 int retval = 1;
3137 char *name;
3138 struct togglelist *c;
3139
3140 if (argc < 2) {
3141 fprintf(stderr,
3142 "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
3143 return 0;
3144 }
3145 argc--;
3146 argv++;
3147 while (argc--) {
3148 name = *argv++;
3149 c = gettoggle(name);
3150 if (c == Ambiguous(struct togglelist *)) {
3151 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
3152 name);
3153 return 0;
3154 } else if (c == 0) {
3155 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
3156 name);
3157 return 0;
3158 } else {
3159 if (c->variable) {
3160 *c->variable = !*c->variable; /* invert it */
3161 printf("%s %s.\n", *c->variable? "Will" : "Won't",
3162 c->actionexplanation);
3163 }
3164 if (c->handler) {
3165 retval &= (*c->handler)(c);
3166 }
3167 }
3168 }
3169 return retval;
3170}
3171\f
3172/*
3173 * The following perform the "set" command.
3174 */
3175
3176struct setlist {
3177 char *name; /* name */
3178 char *help; /* help information */
3179 char *charp; /* where it is located at */
3180};
3181
3182static struct setlist Setlist[] = {
3183 { "echo", "character to toggle local echoing on/off", &echoc },
3184 { "escape", "character to escape back to telnet command mode", &escape },
3185 { " ", "" },
3186 { " ", "The following need 'localchars' to be toggled true", 0 },
3a764688 3187#if defined(unix)
3e91ea2a
GM
3188 { "erase", "character to cause an Erase Character", &nttyb.sg_erase },
3189 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
3190 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
3191 { "kill", "character to cause an Erase Line", &nttyb.sg_kill },
3192 { "quit", "character to cause a Break", &ntc.t_quitc },
3193 { "eof", "character to cause an EOF ", &ntc.t_eofc },
3a764688
GM
3194#endif /* defined(unix) */
3195#if defined(MSDOS)
3196 { "erase", "character to cause an Erase Character", &termEraseChar },
3197 { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
3198 { "interrupt", "character to cause an Interrupt Process", &termIntChar },
3199 { "kill", "character to cause an Erase Line", &termKillChar },
3200 { "quit", "character to cause a Break", &termQuitChar },
3201 { "eof", "character to cause an EOF ", &termEofChar },
3202#endif /* defined(MSDOS) */
3e91ea2a
GM
3203 { 0 }
3204};
3205
3206static char **
3207getnextset(name)
3208char *name;
3209{
3210 struct setlist *c = (struct setlist *)name;
3211
3212 return (char **) (c+1);
3213}
3214
3215static struct setlist *
3216getset(name)
3217char *name;
3218{
3219 return (struct setlist *) genget(name, (char **) Setlist, getnextset);
3220}
3221
3222static
3223setcmd(argc, argv)
3224int argc;
3225char *argv[];
3226{
3227 int value;
3228 struct setlist *ct;
3229
3230 /* XXX back we go... sigh */
3231 if (argc != 3) {
3232 if ((argc == 2) &&
3233 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
3234 for (ct = Setlist; ct->name; ct++) {
3235 printf("%s\t%s\n", ct->name, ct->help);
3236 }
3237 printf("?\tdisplay help information\n");
3238 } else {
3239 printf("Format is 'set Name Value'\n'set ?' for help.\n");
3240 }
3241 return 0;
3242 }
3243
3244 ct = getset(argv[1]);
3245 if (ct == 0) {
3246 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
3247 argv[1]);
3248 return 0;
3249 } else if (ct == Ambiguous(struct setlist *)) {
3250 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
3251 argv[1]);
3252 return 0;
3253 } else {
3254 if (strcmp("off", argv[2])) {
3255 value = special(argv[2]);
3256 } else {
3257 value = -1;
3258 }
3259 *(ct->charp) = value;
3260 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
3261 }
3262 return 1;
3263}
3264\f
3265/*
3266 * The following are the data structures and routines for the
3267 * 'mode' command.
3268 */
3269
3270static
3271dolinemode()
3272{
3273 if (hisopts[TELOPT_SGA]) {
3274 wontoption(TELOPT_SGA, 0);
3275 }
3276 if (hisopts[TELOPT_ECHO]) {
3277 wontoption(TELOPT_ECHO, 0);
3278 }
3279 return 1;
3280}
3281
3282static
3283docharmode()
3284{
3285 if (!hisopts[TELOPT_SGA]) {
3286 willoption(TELOPT_SGA, 0);
3287 }
3288 if (!hisopts[TELOPT_ECHO]) {
3289 willoption(TELOPT_ECHO, 0);
3290 }
3291 return 1;
3292}
3293
3294static struct cmd Modelist[] = {
3295 { "character", "character-at-a-time mode", docharmode, 1, 1 },
3296 { "line", "line-by-line mode", dolinemode, 1, 1 },
3297 { 0 },
3298};
3299
3300static char **
3301getnextmode(name)
3302char *name;
3303{
3304 struct cmd *c = (struct cmd *) name;
3305
3306 return (char **) (c+1);
3307}
3308
3309static struct cmd *
3310getmodecmd(name)
3311char *name;
3312{
3313 return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
3314}
3315
3316static
3317modecmd(argc, argv)
3318int argc;
3319char *argv[];
3320{
3321 struct cmd *mt;
3322
3323 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
3324 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
3325 for (mt = Modelist; mt->name; mt++) {
3326 printf("%s\t%s\n", mt->name, mt->help);
3327 }
3328 return 0;
3329 }
3330 mt = getmodecmd(argv[1]);
3331 if (mt == 0) {
3332 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
3333 return 0;
3334 } else if (mt == Ambiguous(struct cmd *)) {
3335 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
3336 return 0;
3337 } else {
3338 (*mt->handler)();
3339 }
3340 return 1;
3341}
3342\f
3343/*
3344 * The following data structures and routines implement the
3345 * "display" command.
3346 */
3347
3348static
3349display(argc, argv)
3350int argc;
3351char *argv[];
3352{
3353#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
3354 if (*tl->variable) { \
3355 printf("will"); \
3356 } else { \
3357 printf("won't"); \
3358 } \
3359 printf(" %s.\n", tl->actionexplanation); \
3360 }
3361
3362#define doset(sl) if (sl->name && *sl->name != ' ') { \
3363 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
3364 }
3365
3366 struct togglelist *tl;
3367 struct setlist *sl;
3368
3369 if (argc == 1) {
3370 for (tl = Togglelist; tl->name; tl++) {
3371 dotog(tl);
3372 }
3373 printf("\n");
3374 for (sl = Setlist; sl->name; sl++) {
3375 doset(sl);
3376 }
3377 } else {
3378 int i;
3379
3380 for (i = 1; i < argc; i++) {
3381 sl = getset(argv[i]);
3382 tl = gettoggle(argv[i]);
3383 if ((sl == Ambiguous(struct setlist *)) ||
3384 (tl == Ambiguous(struct togglelist *))) {
3385 printf("?Ambiguous argument '%s'.\n", argv[i]);
3386 return 0;
3387 } else if (!sl && !tl) {
3388 printf("?Unknown argument '%s'.\n", argv[i]);
3389 return 0;
3390 } else {
3391 if (tl) {
3392 dotog(tl);
3393 }
3394 if (sl) {
3395 doset(sl);
3396 }
3397 }
3398 }
3399 }
3400 return 1;
3401#undef doset
3402#undef dotog
3403}
3404\f
3405/*
3406 * The following are the data structures, and many of the routines,
3407 * relating to command processing.
3408 */
3409
3410/*
3411 * Set the escape character.
3412 */
3413static
3414setescape(argc, argv)
3415 int argc;
3416 char *argv[];
3417{
3418 register char *arg;
3419 char buf[50];
3420
3421 printf(
3422 "Deprecated usage - please use 'set escape%s%s' in the future.\n",
3423 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
3424 if (argc > 2)
3425 arg = argv[1];
3426 else {
3427 printf("new escape character: ");
3428 gets(buf);
3429 arg = buf;
3430 }
3431 if (arg[0] != '\0')
3432 escape = arg[0];
3433 if (!In3270) {
3434 printf("Escape character is '%s'.\n", control(escape));
3435 }
3436 fflush(stdout);
3437 return 1;
3438}
3439
3440/*VARARGS*/
3441static
3442togcrmod()
3443{
3444 crmod = !crmod;
3445 printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
3446 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
3447 fflush(stdout);
3448 return 1;
3449}
3450
3451/*VARARGS*/
3452suspend()
3453{
3454 setcommandmode();
3455#if defined(unix)
3456 kill(0, SIGTSTP);
3eb3862f 3457#endif /* defined(unix) */
3e91ea2a 3458 /* reget parameters in case they were changed */
3a764688 3459 TerminalSaveState();
3eb3862f 3460 setconnmode();
3e91ea2a
GM
3461 return 1;
3462}
3463
3464/*VARARGS*/
3465static
0d3c45d4
GM
3466bye(argc, argv)
3467int argc; /* Number of arguments */
3468char *argv[]; /* arguments */
3e91ea2a
GM
3469{
3470 if (connected) {
3471 shutdown(net, 2);
3472 printf("Connection closed.\n");
42cf515f 3473 NetClose(net);
3e91ea2a
GM
3474 connected = 0;
3475 /* reset options */
0d3c45d4 3476 tninit();
3e91ea2a 3477#if defined(TN3270)
0d3c45d4 3478 SetIn3270(); /* Get out of 3270 mode */
3e91ea2a
GM
3479#endif /* defined(TN3270) */
3480 }
0d3c45d4
GM
3481 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
3482 longjmp(toplevel, 1);
3483 /* NOTREACHED */
3484 }
3485 return 1; /* Keep lint, etc., happy */
3e91ea2a
GM
3486}
3487
3488/*VARARGS*/
3489quit()
3490{
0d3c45d4 3491 (void) call(bye, "bye", "fromquit", 0);
3e91ea2a
GM
3492 Exit(0);
3493 /*NOTREACHED*/
3494 return 1; /* just to keep lint happy */
3495}
3496
3497/*
3498 * Print status about the connection.
3499 */
3500static
3501status(argc, argv)
3502int argc;
3503char *argv[];
3504{
3505 if (connected) {
3506 printf("Connected to %s.\n", hostname);
3507 if (argc < 2) {
3508 printf("Operating in %s.\n",
3509 modelist[getconnmode()].modedescriptions);
3510 if (localchars) {
3511 printf("Catching signals locally.\n");
3512 }
3513 }
3514 } else {
3515 printf("No connection.\n");
3516 }
3517# if !defined(TN3270)
3518 printf("Escape character is '%s'.\n", control(escape));
3519 fflush(stdout);
3520# else /* !defined(TN3270) */
3521 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
3522 printf("Escape character is '%s'.\n", control(escape));
3523 }
3524# if defined(unix)
3525 if (In3270 && transcom) {
3526 printf("Transparent mode command is '%s'.\n", transcom);
3527 }
3528# endif /* defined(unix) */
3529 fflush(stdout);
3530 if (In3270) {
3531 return 0;
3532 }
3533# endif /* defined(TN3270) */
3534 return 1;
3535}
3536
3537#if defined(TN3270) && defined(unix)
3538static
3539settranscom(argc, argv)
3540 int argc;
3541 char *argv[];
3542{
3543 int i, len = 0;
3544 char *strcpy(), *strcat();
3545
3546 if (argc == 1 && transcom) {
3547 transcom = 0;
3548 }
3549 if (argc == 1) {
3550 return;
3551 }
3552 for (i = 1; i < argc; ++i) {
3553 len += 1 + strlen(argv[1]);
3554 }
3555 transcom = tline;
3556 (void) strcpy(transcom, argv[1]);
3557 for (i = 2; i < argc; ++i) {
3558 (void) strcat(transcom, " ");
3559 (void) strcat(transcom, argv[i]);
3560 }
3561}
3562#endif /* defined(TN3270) && defined(unix) */
3563
3564
7a29e53d 3565
3e91ea2a
GM
3566static
3567tn(argc, argv)
3568 int argc;
3569 char *argv[];
3570{
3571 register struct hostent *host = 0;
42cf515f 3572#if defined(MSDOS)
3e91ea2a 3573 char *cp;
42cf515f 3574#endif /* defined(MSDOS) */
3e91ea2a
GM
3575
3576 if (connected) {
3577 printf("?Already connected to %s\n", hostname);
3578 return 0;
3579 }
3580 if (argc < 2) {
3581 (void) strcpy(line, "Connect ");
3582 printf("(to) ");
3583 gets(&line[strlen(line)]);
3584 makeargv();
3585 argc = margc;
3586 argv = margv;
3587 }
3588 if (argc > 3) {
3589 printf("usage: %s host-name [port]\n", argv[0]);
3590 return 0;
3591 }
42cf515f 3592#if defined(MSDOS)
3e91ea2a
GM
3593 for (cp = argv[1]; *cp; cp++) {
3594 if (isupper(*cp)) {
3595 *cp = tolower(*cp);
3596 }
3597 }
42cf515f 3598#endif /* defined(MSDOS) */
3e91ea2a
GM
3599 sin.sin_addr.s_addr = inet_addr(argv[1]);
3600 if (sin.sin_addr.s_addr != -1) {
3601 sin.sin_family = AF_INET;
3602 (void) strcpy(hnamebuf, argv[1]);
3603 hostname = hnamebuf;
3604 } else {
3605 host = gethostbyname(argv[1]);
3606 if (host) {
3607 sin.sin_family = host->h_addrtype;
3608#if defined(h_addr) /* In 4.3, this is a #define */
42cf515f
GM
3609 memcpy((caddr_t)&sin.sin_addr,
3610 host->h_addr_list[0], host->h_length);
3e91ea2a 3611#else /* defined(h_addr) */
42cf515f 3612 memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
3e91ea2a
GM
3613#endif /* defined(h_addr) */
3614 hostname = host->h_name;
3615 } else {
3616 printf("%s: unknown host\n", argv[1]);
3617 return 0;
3618 }
3619 }
3620 sin.sin_port = sp->s_port;
3621 if (argc == 3) {
3622 sin.sin_port = atoi(argv[2]);
3623 if (sin.sin_port == 0) {
3624 sp = getservbyname(argv[2], "tcp");
3625 if (sp)
3626 sin.sin_port = sp->s_port;
3627 else {
3628 printf("%s: bad port number\n", argv[2]);
3629 return 0;
3630 }
3631 } else {
3632 sin.sin_port = atoi(argv[2]);
3633 sin.sin_port = htons(sin.sin_port);
3634 }
3635 telnetport = 0;
3636 } else {
3637 telnetport = 1;
3638 }
3639#if defined(unix)
3640 signal(SIGINT, intr);
3641 signal(SIGQUIT, intr2);
3642 signal(SIGPIPE, deadpeer);
3643#endif /* defined(unix) */
3644 printf("Trying...\n");
3645 do {
3646 net = socket(AF_INET, SOCK_STREAM, 0);
3647 if (net < 0) {
3648 perror("telnet: socket");
3649 return 0;
3650 }
3a764688 3651 if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
3e91ea2a 3652 perror("setsockopt (SO_DEBUG)");
3a764688 3653 }
3e91ea2a
GM
3654
3655 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
3656#if defined(h_addr) /* In 4.3, this is a #define */
3657 if (host && host->h_addr_list[1]) {
3658 int oerrno = errno;
3659
3660 fprintf(stderr, "telnet: connect to address %s: ",
3661 inet_ntoa(sin.sin_addr));
3662 errno = oerrno;
3663 perror((char *)0);
3664 host->h_addr_list++;
42cf515f
GM
3665 memcpy((caddr_t)&sin.sin_addr,
3666 host->h_addr_list[0], host->h_length);
3e91ea2a
GM
3667 fprintf(stderr, "Trying %s...\n",
3668 inet_ntoa(sin.sin_addr));
42cf515f 3669 (void) NetClose(net);
3e91ea2a
GM
3670 continue;
3671 }
3672#endif /* defined(h_addr) */
3673 perror("telnet: Unable to connect to remote host");
3674#if defined(unix)
3675 signal(SIGINT, SIG_DFL);
3676 signal(SIGQUIT, SIG_DFL);
58f9a26f 3677#endif /* defined(unix) */
3e91ea2a
GM
3678 return 0;
3679 }
3680 connected++;
3681 } while (connected == 0);
3682 call(status, "status", "notmuch", 0);
3683 if (setjmp(peerdied) == 0)
3684 telnet();
42cf515f 3685 NetClose(net);
3e91ea2a
GM
3686 ExitString(stderr, "Connection closed by foreign host.\n",1);
3687 /*NOTREACHED*/
3688}
3689
3690
3691#define HELPINDENT (sizeof ("connect"))
3692
3693static char
3694 openhelp[] = "connect to a site",
3695 closehelp[] = "close current connection",
3696 quithelp[] = "exit telnet",
3e91ea2a
GM
3697 statushelp[] = "print status information",
3698 helphelp[] = "print help information",
3699 sendhelp[] = "transmit special characters ('send ?' for more)",
3700 sethelp[] = "set operating parameters ('set ?' for more)",
3701 togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3702 displayhelp[] = "display operating parameters",
3703#if defined(TN3270) && defined(unix)
3704 transcomhelp[] = "specify Unix command for transparent mode pipe",
3705#endif /* defined(TN3270) && defined(unix) */
7a29e53d
GM
3706#if defined(unix)
3707 zhelp[] = "suspend telnet",
3708#endif /* defined(unix */
3dd51ee4 3709#if defined(TN3270)
7a29e53d 3710 shellhelp[] = "invoke a subshell",
3dd51ee4 3711#endif /* defined(TN3270) */
3e91ea2a
GM
3712 modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3713
7a29e53d 3714extern int help(), shell();
3e91ea2a
GM
3715
3716static struct cmd cmdtab[] = {
3717 { "close", closehelp, bye, 1, 1 },
3718 { "display", displayhelp, display, 1, 0 },
3719 { "mode", modehelp, modecmd, 1, 1 },
3720 { "open", openhelp, tn, 1, 0 },
3721 { "quit", quithelp, quit, 1, 0 },
3722 { "send", sendhelp, sendcmd, 1, 1 },
3723 { "set", sethelp, setcmd, 1, 0 },
3724 { "status", statushelp, status, 1, 0 },
3725 { "toggle", togglestring, toggle, 1, 0 },
3726#if defined(TN3270) && defined(unix)
3727 { "transcom", transcomhelp, settranscom, 1, 0 },
3728#endif /* defined(TN3270) && defined(unix) */
7a29e53d 3729#if defined(unix)
3e91ea2a 3730 { "z", zhelp, suspend, 1, 0 },
7a29e53d 3731#endif /* defined(unix) */
3dd51ee4
GM
3732#if defined(TN3270)
3733 { "!", shellhelp, shell, 1, 1 },
3734#endif /* defined(TN3270) */
3e91ea2a
GM
3735 { "?", helphelp, help, 1, 0 },
3736 0
3737};
3738
3739static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
3740static char escapehelp[] = "deprecated command -- use 'set escape' instead";
3741
3742static struct cmd cmdtab2[] = {
3743 { "help", helphelp, help, 0, 0 },
3744 { "escape", escapehelp, setescape, 1, 0 },
3745 { "crmod", crmodhelp, togcrmod, 1, 0 },
3746 0
3747};
3748
3749/*
3750 * Call routine with argc, argv set from args (terminated by 0).
3751 * VARARGS2
3752 */
3753static
3754call(routine, args)
3755 int (*routine)();
3756 char *args;
3757{
3758 register char **argp;
3759 register int argc;
3760
3761 for (argc = 0, argp = &args; *argp++ != 0; argc++)
3762 ;
3763 return (*routine)(argc, &args);
3764}
3765
3766static char **
3767getnextcmd(name)
3768char *name;
3769{
3770 struct cmd *c = (struct cmd *) name;
3771
3772 return (char **) (c+1);
3773}
3774
3775static struct cmd *
3776getcmd(name)
3777char *name;
3778{
3779 struct cmd *cm;
3780
3781 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3782 return cm;
3783 } else {
3784 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3785 }
3786}
3787
3788void
3789command(top)
3790 int top;
3791{
3dd51ee4 3792 register struct cmd *c;
3e91ea2a 3793
3dd51ee4
GM
3794 setcommandmode();
3795 if (!top) {
3796 putchar('\n');
3797 } else {
3e91ea2a 3798#if defined(unix)
3dd51ee4
GM
3799 signal(SIGINT, SIG_DFL);
3800 signal(SIGQUIT, SIG_DFL);
3e91ea2a 3801#endif /* defined(unix) */
3dd51ee4
GM
3802 }
3803 for (;;) {
3804 printf("%s> ", prompt);
3805 if (gets(line) == NULL) {
3806 if (feof(stdin) || ferror(stdin))
3807 quit();
3808 break;
3e91ea2a 3809 }
3dd51ee4
GM
3810 if (line[0] == 0)
3811 break;
3812 makeargv();
3813 c = getcmd(margv[0]);
3814 if (c == Ambiguous(struct cmd *)) {
3815 printf("?Ambiguous command\n");
3816 continue;
3e91ea2a 3817 }
3dd51ee4
GM
3818 if (c == 0) {
3819 printf("?Invalid command\n");
3820 continue;
3821 }
3822 if (c->needconnect && !connected) {
3823 printf("?Need to be connected first.\n");
3824 continue;
3825 }
3826 if ((*c->handler)(margc, margv)) {
3827 break;
3828 }
3829 }
3830 if (!top) {
3831 if (!connected) {
3832 longjmp(toplevel, 1);
3833 /*NOTREACHED*/
3e91ea2a 3834 }
3dd51ee4
GM
3835 if (shell_active == 0) {
3836 setconnmode();
3837 }
3838 }
3e91ea2a
GM
3839}
3840\f
3841/*
3842 * Help command.
3843 */
3844static
3845help(argc, argv)
3846 int argc;
3847 char *argv[];
3848{
3849 register struct cmd *c;
3850
3851 if (argc == 1) {
3852 printf("Commands may be abbreviated. Commands are:\n\n");
3853 for (c = cmdtab; c->name; c++)
3854 if (c->dohelp) {
3855 printf("%-*s\t%s\n", HELPINDENT, c->name,
3856 c->help);
3857 }
3858 return 0;
3859 }
3860 while (--argc > 0) {
3861 register char *arg;
3862 arg = *++argv;
3863 c = getcmd(arg);
3864 if (c == Ambiguous(struct cmd *))
3865 printf("?Ambiguous help command %s\n", arg);
3866 else if (c == (struct cmd *)0)
3867 printf("?Invalid help command %s\n", arg);
3868 else
3869 printf("%s\n", c->help);
3870 }
3871 return 0;
3872}
3873\f
3874/*
3875 * main. Parse arguments, invoke the protocol or command parser.
3876 */
3877
3878
3879void
3880main(argc, argv)
3881 int argc;
3882 char *argv[];
3883{
0d3c45d4
GM
3884 tninit(); /* Clear out things */
3885
3e91ea2a 3886 NetTrace = stdout;
3a764688
GM
3887 TerminalSaveState();
3888 autoflush = TerminalAutoFlush();
3889
3e91ea2a 3890 prompt = argv[0];
3dd51ee4
GM
3891 while ((argc > 1) && (argv[1][0] == '-')) {
3892 if (!strcmp(argv[1], "-d")) {
3893 debug = 1;
3894 } else if (!strcmp(argv[1], "-n")) {
c71df935
GM
3895 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3896 NetTrace = fopen(argv[2], "w");
3dd51ee4
GM
3897 argv++;
3898 argc--;
3899 if (NetTrace == NULL) {
3900 NetTrace = stdout;
3901 }
3e91ea2a 3902 }
3dd51ee4 3903 } else {
3e91ea2a 3904#if defined(TN3270) && defined(unix)
3dd51ee4
GM
3905 if (!strcmp(argv[1], "-t")) {
3906 if ((argc > 1) && (argv[1][0] != '-')) { /* get file name */
3907 transcom = tline;
3908 (void) strcpy(transcom, argv[1]);
3909 argv++;
3910 argc--;
3911 }
3912 } else if (!strcmp(argv[1], "-noasynch")) {
3913 noasynch = 1;
3914 } else
3915#endif /* defined(TN3270) && defined(unix) */
3916 if (argv[1][1] != '\0') {
3917 fprintf(stderr, "Unknown option *%s*.\n", argv[1]);
3918 }
3e91ea2a 3919 }
3dd51ee4
GM
3920 argc--;
3921 argv++;
3e91ea2a 3922 }
3e91ea2a
GM
3923 if (argc != 1) {
3924 if (setjmp(toplevel) != 0)
3925 Exit(0);
3926 tn(argc, argv);
3927 }
3928 setjmp(toplevel);
3eb3862f
GM
3929 for (;;) {
3930#if !defined(TN3270)
3e91ea2a 3931 command(1);
3eb3862f
GM
3932#else /* !defined(TN3270) */
3933 if (!shell_active) {
3934 command(1);
3935 } else {
3dd51ee4 3936#if defined(TN3270)
3eb3862f 3937 shell_continue();
3dd51ee4 3938#endif /* defined(TN3270) */
3eb3862f
GM
3939 }
3940#endif /* !defined(TN3270) */
3941 }
3e91ea2a 3942}