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