xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 46808)
133685Sbostic /*
245232Sborman  * Copyright (c) 1988, 1990 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
633685Sbostic  */
711758Ssam 
821580Sdist #ifndef lint
9*46808Sdab static char sccsid[] = "@(#)telnet.c	5.52 (Berkeley) 03/01/91";
1033685Sbostic #endif /* not lint */
1121580Sdist 
129217Ssam #include <sys/types.h>
139217Ssam 
1432377Sminshall #if	defined(unix)
1533804Sminshall #include <signal.h>
1632377Sminshall /* By the way, we need to include curses.h before telnet.h since,
1732377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
1832377Sminshall  * declared in curses.h.
1932377Sminshall  */
2032377Sminshall #endif	/* defined(unix) */
2132377Sminshall 
2212212Ssam #include <arpa/telnet.h>
2332377Sminshall 
2438908Sborman #include <ctype.h>
2538908Sborman 
2632381Sminshall #include "ring.h"
2732381Sminshall 
2832377Sminshall #include "defines.h"
2932377Sminshall #include "externs.h"
3032377Sminshall #include "types.h"
3132377Sminshall #include "general.h"
3227178Sminshall 
3327178Sminshall 
3427228Sminshall #define	strip(x)	((x)&0x7f)
356000Sroot 
36*46808Sdab static unsigned char	subbuffer[SUBBUFSIZE],
37*46808Sdab 			*subpointer, *subend;	 /* buffer for sub-options */
3827676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
39*46808Sdab #define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }
4027676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4127676Sminshall 				*subpointer++ = (c); \
4227676Sminshall 			}
4327676Sminshall 
44*46808Sdab #define	SB_GET()	((*subpointer++)&0xff)
45*46808Sdab #define	SB_PEEK()	((*subpointer)&0xff)
46*46808Sdab #define	SB_EOF()	(subpointer >= subend)
47*46808Sdab #define	SB_LEN()	(subend - subpointer)
48*46808Sdab 
4937226Sminshall char	options[256];		/* The combined options */
5038689Sborman char	do_dont_resp[256];
5138689Sborman char	will_wont_resp[256];
526000Sroot 
5332377Sminshall int
54*46808Sdab 	eight = 0,
55*46808Sdab 	autologin = 0,	/* Autologin anyone? */
5632377Sminshall 	connected,
5732377Sminshall 	showoptions,
5832377Sminshall 	In3270,		/* Are we in 3270 mode? */
5932377Sminshall 	ISend,		/* trying to send network data in */
6032377Sminshall 	debug = 0,
6132377Sminshall 	crmod,
6232377Sminshall 	netdata,	/* Print out network data flow */
6332377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6434848Sminshall #if	defined(TN3270)
6536241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
6636241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
6732377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
6834848Sminshall #endif	/* defined(TN3270) */
6933286Sminshall 	telnetport,
7032531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
7132531Sminshall 	flushout,	/* flush output */
7232531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
7332531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
7437219Sminshall 	localflow,	/* we handle flow control locally */
7532531Sminshall 	localchars,	/* we recognize interrupt/quit */
7632531Sminshall 	donelclchars,	/* the user has set "localchars" */
7732531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
7832531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
7932531Sminshall 	globalmode;
8027088Sminshall 
8144360Sborman char *prompt = 0;
826000Sroot 
8344360Sborman cc_t escape;
84*46808Sdab cc_t rlogin;
8544360Sborman #ifdef	KLUDGELINEMODE
8644360Sborman cc_t echoc;
8744360Sborman #endif
8827186Sminshall 
8927186Sminshall /*
906000Sroot  * Telnet receiver states for fsm
916000Sroot  */
926000Sroot #define	TS_DATA		0
936000Sroot #define	TS_IAC		1
946000Sroot #define	TS_WILL		2
956000Sroot #define	TS_WONT		3
966000Sroot #define	TS_DO		4
976000Sroot #define	TS_DONT		5
9827021Sminshall #define	TS_CR		6
9927676Sminshall #define	TS_SB		7		/* sub-option collection */
10027676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1016000Sroot 
10232377Sminshall static int	telrcv_state;
1036000Sroot 
10432377Sminshall jmp_buf	toplevel = { 0 };
10532377Sminshall jmp_buf	peerdied;
1066000Sroot 
10732377Sminshall int	flushline;
10838811Sborman int	linemode;
10927021Sminshall 
11038689Sborman #ifdef	KLUDGELINEMODE
11138689Sborman int	kludgelinemode = 1;
11238689Sborman #endif
11338689Sborman 
11432377Sminshall /*
11532377Sminshall  * The following are some clocks used to decide how to interpret
11632377Sminshall  * the relationship between various variables.
11732377Sminshall  */
1186000Sroot 
11932377Sminshall Clocks clocks;
12032377Sminshall 
12138689Sborman #ifdef	notdef
12232377Sminshall Modelist modelist[] = {
12332377Sminshall 	{ "telnet command mode", COMMAND_LINE },
12432377Sminshall 	{ "character-at-a-time mode", 0 },
12532377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
12632377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
12732377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
12832377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
12932377Sminshall 	{ "3270 mode", 0 },
13032377Sminshall };
13138689Sborman #endif
1326000Sroot 
13332377Sminshall 
13432377Sminshall /*
13532377Sminshall  * Initialize telnet environment.
13632377Sminshall  */
1376000Sroot 
138*46808Sdab     void
13932377Sminshall init_telnet()
14032377Sminshall {
14144360Sborman     env_init();
14244360Sborman 
14332377Sminshall     SB_CLEAR();
14437226Sminshall     ClearArray(options);
1456000Sroot 
14637219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
147*46808Sdab #if	defined(ENCRYPT) || defined(AUTHENTICATE)
148*46808Sdab     auth_encrypt_connect(connected);
149*46808Sdab #endif
1506000Sroot 
15132377Sminshall     SYNCHing = 0;
1526000Sroot 
15332377Sminshall     /* Don't change NetTrace */
1546000Sroot 
15532377Sminshall     escape = CONTROL(']');
156*46808Sdab     rlogin = _POSIX_VDISABLE;
15744360Sborman #ifdef	KLUDGELINEMODE
15832377Sminshall     echoc = CONTROL('E');
15944360Sborman #endif
1606000Sroot 
16132377Sminshall     flushline = 1;
16232377Sminshall     telrcv_state = TS_DATA;
16332377Sminshall }
16432554Sminshall 
1656000Sroot 
16644360Sborman #ifdef	notdef
16732554Sminshall #include <varargs.h>
1686000Sroot 
169*46808Sdab     /*VARARGS*/
170*46808Sdab     static void
17132554Sminshall printring(va_alist)
172*46808Sdab     va_dcl
17332554Sminshall {
17432554Sminshall     va_list ap;
17532554Sminshall     char buffer[100];		/* where things go */
17632554Sminshall     char *ptr;
17732554Sminshall     char *format;
17832554Sminshall     char *string;
17932554Sminshall     Ring *ring;
18032554Sminshall     int i;
18132554Sminshall 
18232554Sminshall     va_start(ap);
18332554Sminshall 
18432554Sminshall     ring = va_arg(ap, Ring *);
18532554Sminshall     format = va_arg(ap, char *);
18632554Sminshall     ptr = buffer;
18732554Sminshall 
18832554Sminshall     while ((i = *format++) != 0) {
18932554Sminshall 	if (i == '%') {
19032554Sminshall 	    i = *format++;
19132554Sminshall 	    switch (i) {
19232554Sminshall 	    case 'c':
19332554Sminshall 		*ptr++ = va_arg(ap, int);
19432554Sminshall 		break;
19532554Sminshall 	    case 's':
19632554Sminshall 		string = va_arg(ap, char *);
19732554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
19832554Sminshall 		ring_supply_data(ring, string, strlen(string));
19932554Sminshall 		ptr = buffer;
20032554Sminshall 		break;
20132554Sminshall 	    case 0:
20232554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20332554Sminshall 		/*NOTREACHED*/
20432554Sminshall 	    default:
20532554Sminshall 		ExitString("printring: unknown format character.\n", 1);
20632554Sminshall 		/*NOTREACHED*/
20732554Sminshall 	    }
20832554Sminshall 	} else {
20932554Sminshall 	    *ptr++ = i;
21032554Sminshall 	}
21132554Sminshall     }
21232554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21332554Sminshall }
21444360Sborman #endif
21532554Sminshall 
21637226Sminshall /*
21737226Sminshall  * These routines are in charge of sending option negotiations
21837226Sminshall  * to the other side.
21937226Sminshall  *
22037226Sminshall  * The basic idea is that we send the negotiation if either side
22137226Sminshall  * is in disagreement as to what the current state should be.
22237226Sminshall  */
22332554Sminshall 
224*46808Sdab     void
22538689Sborman send_do(c, init)
226*46808Sdab     register int c, init;
2276000Sroot {
22838689Sborman     if (init) {
22938689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
23038689Sborman 				my_want_state_is_do(c))
23138689Sborman 	    return;
23238689Sborman 	set_my_want_state_do(c);
23338689Sborman 	do_dont_resp[c]++;
23437226Sminshall     }
23538689Sborman     NET2ADD(IAC, DO);
23638689Sborman     NETADD(c);
237*46808Sdab     printoption("SENT", DO, c);
23837226Sminshall }
23937226Sminshall 
240*46808Sdab     void
24138689Sborman send_dont(c, init)
242*46808Sdab     register int c, init;
24337226Sminshall {
24438689Sborman     if (init) {
24538689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
24638689Sborman 				my_want_state_is_dont(c))
24738689Sborman 	    return;
24838689Sborman 	set_my_want_state_dont(c);
24938689Sborman 	do_dont_resp[c]++;
25037226Sminshall     }
25138689Sborman     NET2ADD(IAC, DONT);
25238689Sborman     NETADD(c);
253*46808Sdab     printoption("SENT", DONT, c);
25437226Sminshall }
25537226Sminshall 
256*46808Sdab     void
25738689Sborman send_will(c, init)
258*46808Sdab     register int c, init;
25937226Sminshall {
26038689Sborman     if (init) {
26138689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
26238689Sborman 				my_want_state_is_will(c))
26338689Sborman 	    return;
26438689Sborman 	set_my_want_state_will(c);
26538689Sborman 	will_wont_resp[c]++;
26637226Sminshall     }
26738689Sborman     NET2ADD(IAC, WILL);
26838689Sborman     NETADD(c);
269*46808Sdab     printoption("SENT", WILL, c);
27037226Sminshall }
27137226Sminshall 
272*46808Sdab     void
27338689Sborman send_wont(c, init)
274*46808Sdab     register int c, init;
27537226Sminshall {
27638689Sborman     if (init) {
27738689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
27838689Sborman 				my_want_state_is_wont(c))
27938689Sborman 	    return;
28038689Sborman 	set_my_want_state_wont(c);
28138689Sborman 	will_wont_resp[c]++;
28237226Sminshall     }
28338689Sborman     NET2ADD(IAC, WONT);
28438689Sborman     NETADD(c);
285*46808Sdab     printoption("SENT", WONT, c);
28637226Sminshall }
28737226Sminshall 
28837226Sminshall 
289*46808Sdab 	void
29037226Sminshall willoption(option)
29137226Sminshall 	int option;
29237226Sminshall {
29338689Sborman 	int new_state_ok = 0;
2946000Sroot 
29538689Sborman 	if (do_dont_resp[option]) {
29638689Sborman 	    --do_dont_resp[option];
29738689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
29838689Sborman 		--do_dont_resp[option];
29938689Sborman 	}
30037226Sminshall 
30138689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3026000Sroot 
30338689Sborman 	    switch (option) {
30438689Sborman 
30538689Sborman 	    case TELOPT_ECHO:
30638689Sborman #	    if defined(TN3270)
30738689Sborman 		/*
30838689Sborman 		 * The following is a pain in the rear-end.
30938689Sborman 		 * Various IBM servers (some versions of Wiscnet,
31038689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
31138689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
31238689Sborman 		 * in the setup proceedings.  On the other hand,
31338689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
31438689Sborman 		 * So, we are stuck.  Empirically (but, based on
31538689Sborman 		 * a VERY small sample), the IBM servers don't send
31638689Sborman 		 * out anything about ECHO, so we postpone our sending
31738689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
31838689Sborman 		 * DO send).
31938689Sborman 		  */
32038689Sborman 		{
32138689Sborman 		    if (askedSGA == 0) {
32238689Sborman 			askedSGA = 1;
32338689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
32438689Sborman 			    send_do(TELOPT_SGA, 1);
32532377Sminshall 		    }
32632377Sminshall 		}
32738689Sborman 		    /* Fall through */
32838689Sborman 	    case TELOPT_EOR:
32938908Sborman #endif	    /* defined(TN3270) */
33038689Sborman 	    case TELOPT_BINARY:
33138689Sborman 	    case TELOPT_SGA:
33227110Sminshall 		settimer(modenegotiated);
33338908Sborman 		/* FALL THROUGH */
33438908Sborman 	    case TELOPT_STATUS:
335*46808Sdab #if	defined(AUTHENTICATE)
336*46808Sdab 	    case TELOPT_AUTHENTICATION:
337*46808Sdab #endif
338*46808Sdab #if	defined(ENCRYPT)
339*46808Sdab 	    case TELOPT_ENCRYPT:
340*46808Sdab #endif
34138689Sborman 		new_state_ok = 1;
3426000Sroot 		break;
3436000Sroot 
34438689Sborman 	    case TELOPT_TM:
34538689Sborman 		if (flushout)
34638689Sborman 		    flushout = 0;
34738689Sborman 		/*
34838689Sborman 		 * Special case for TM.  If we get back a WILL,
34938689Sborman 		 * pretend we got back a WONT.
35038689Sborman 		 */
35138689Sborman 		set_my_want_state_dont(option);
35238689Sborman 		set_my_state_dont(option);
35327110Sminshall 		return;			/* Never reply to TM will's/wont's */
3546000Sroot 
35538689Sborman 	    case TELOPT_LINEMODE:
35638689Sborman 	    default:
3576000Sroot 		break;
35838689Sborman 	    }
35938689Sborman 
36038689Sborman 	    if (new_state_ok) {
36138689Sborman 		set_my_want_state_do(option);
36238689Sborman 		send_do(option, 0);
36338689Sborman 		setconnmode(0);		/* possibly set new tty mode */
36438689Sborman 	    } else {
36538689Sborman 		do_dont_resp[option]++;
36638689Sborman 		send_dont(option, 0);
36738689Sborman 	    }
3686000Sroot 	}
36938689Sborman 	set_my_state_do(option);
370*46808Sdab #if	defined(ENCRYPT)
371*46808Sdab 	if (option == TELOPT_ENCRYPT)
372*46808Sdab 		encrypt_send_support();
373*46808Sdab #endif
3746000Sroot }
3756000Sroot 
376*46808Sdab 	void
37737226Sminshall wontoption(option)
37837226Sminshall 	int option;
3796000Sroot {
38038689Sborman 	if (do_dont_resp[option]) {
38138689Sborman 	    --do_dont_resp[option];
38238689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
38338689Sborman 		--do_dont_resp[option];
38438689Sborman 	}
38537226Sminshall 
38638689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3876000Sroot 
38838689Sborman 	    switch (option) {
38938689Sborman 
39038689Sborman #ifdef	KLUDGELINEMODE
39138689Sborman 	    case TELOPT_SGA:
39238689Sborman 		if (!kludgelinemode)
39338689Sborman 		    break;
39438689Sborman 		/* FALL THROUGH */
39538689Sborman #endif
39638689Sborman 	    case TELOPT_ECHO:
39727110Sminshall 		settimer(modenegotiated);
3986000Sroot 		break;
3996000Sroot 
40038689Sborman 	    case TELOPT_TM:
40138689Sborman 		if (flushout)
40238689Sborman 		    flushout = 0;
40338689Sborman 		set_my_want_state_dont(option);
40438689Sborman 		set_my_state_dont(option);
40527110Sminshall 		return;		/* Never reply to TM will's/wont's */
40627110Sminshall 
40738689Sborman 	    default:
40838689Sborman 		break;
40938689Sborman 	    }
41038689Sborman 	    set_my_want_state_dont(option);
41144360Sborman 	    if (my_state_is_do(option))
41244360Sborman 		send_dont(option, 0);
41338689Sborman 	    setconnmode(0);			/* Set new tty mode */
41438689Sborman 	} else if (option == TELOPT_TM) {
41538689Sborman 	    /*
41638689Sborman 	     * Special case for TM.
41738689Sborman 	     */
41838689Sborman 	    if (flushout)
41938689Sborman 		flushout = 0;
42038689Sborman 	    set_my_want_state_dont(option);
4216000Sroot 	}
42238689Sborman 	set_my_state_dont(option);
4236000Sroot }
4246000Sroot 
425*46808Sdab 	static void
4266000Sroot dooption(option)
4276000Sroot 	int option;
4286000Sroot {
42938689Sborman 	int new_state_ok = 0;
4306000Sroot 
43138689Sborman 	if (will_wont_resp[option]) {
43238689Sborman 	    --will_wont_resp[option];
43338689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
43438689Sborman 		--will_wont_resp[option];
43538689Sborman 	}
43637226Sminshall 
43738689Sborman 	if (will_wont_resp[option] == 0) {
43838689Sborman 	  if (my_want_state_is_wont(option)) {
4396000Sroot 
44038689Sborman 	    switch (option) {
44138689Sborman 
44238689Sborman 	    case TELOPT_TM:
44338689Sborman 		/*
44438689Sborman 		 * Special case for TM.  We send a WILL, but pretend
44538689Sborman 		 * we sent WONT.
44638689Sborman 		 */
44738689Sborman 		send_will(option, 0);
44838689Sborman 		set_my_want_state_wont(TELOPT_TM);
44938689Sborman 		set_my_state_wont(TELOPT_TM);
45038689Sborman 		return;
45138689Sborman 
45232377Sminshall #	if defined(TN3270)
45338689Sborman 	    case TELOPT_EOR:		/* end of record */
45438908Sborman #	endif	/* defined(TN3270) */
45538689Sborman 	    case TELOPT_BINARY:		/* binary mode */
45638689Sborman 	    case TELOPT_NAWS:		/* window size */
45738689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
45838689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
45938689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
46038689Sborman 	    case TELOPT_SGA:		/* no big deal */
46144360Sborman 	    case TELOPT_ENVIRON:	/* environment variable option */
462*46808Sdab #if	defined(ENCRYPT)
463*46808Sdab 	    case TELOPT_ENCRYPT:	/* encryption variable option */
464*46808Sdab #endif
46538689Sborman 		new_state_ok = 1;
4666000Sroot 		break;
467*46808Sdab #if	defined(AUTHENTICATE)
468*46808Sdab 	    case TELOPT_AUTHENTICATION:
469*46808Sdab 		if (autologin)
470*46808Sdab 			new_state_ok = 1;
471*46808Sdab 		break;
472*46808Sdab #endif
4736000Sroot 
47444360Sborman 	    case TELOPT_XDISPLOC:	/* X Display location */
475*46808Sdab 		if (env_getvalue((unsigned char *)"DISPLAY"))
47644360Sborman 		    new_state_ok = 1;
47744360Sborman 		break;
47844360Sborman 
47938689Sborman 	    case TELOPT_LINEMODE:
48038689Sborman #ifdef	KLUDGELINEMODE
48138689Sborman 		kludgelinemode = 0;
48244360Sborman 		send_do(TELOPT_SGA, 1);
48338689Sborman #endif
48438689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
48538689Sborman 		send_will(option, 0);
48638689Sborman 		set_my_state_will(TELOPT_LINEMODE);
48738689Sborman 		slc_init();
48838689Sborman 		return;
48938689Sborman 
49038689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
49138689Sborman 	    default:
4926000Sroot 		break;
49338689Sborman 	    }
49438689Sborman 
49538689Sborman 	    if (new_state_ok) {
49638689Sborman 		set_my_want_state_will(option);
49738689Sborman 		send_will(option, 0);
49845232Sborman 		setconnmode(0);			/* Set new tty mode */
49938689Sborman 	    } else {
50038689Sborman 		will_wont_resp[option]++;
50138689Sborman 		send_wont(option, 0);
50238689Sborman 	    }
50338689Sborman 	  } else {
50438689Sborman 	    /*
50538689Sborman 	     * Handle options that need more things done after the
50638689Sborman 	     * other side has acknowledged the option.
50738689Sborman 	     */
50838689Sborman 	    switch (option) {
50938689Sborman 	    case TELOPT_LINEMODE:
51038689Sborman #ifdef	KLUDGELINEMODE
51138689Sborman 		kludgelinemode = 0;
51244360Sborman 		send_do(TELOPT_SGA, 1);
51338689Sborman #endif
51438689Sborman 		set_my_state_will(option);
51538689Sborman 		slc_init();
51644360Sborman 		send_do(TELOPT_SGA, 0);
51738689Sborman 		return;
51838689Sborman 	    }
51938689Sborman 	  }
5206000Sroot 	}
52138689Sborman 	set_my_state_will(option);
5226000Sroot }
52327676Sminshall 
524*46808Sdab 	static void
52538689Sborman dontoption(option)
52638689Sborman 	int option;
52738689Sborman {
52838689Sborman 
52938689Sborman 	if (will_wont_resp[option]) {
53038689Sborman 	    --will_wont_resp[option];
53138689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
53238689Sborman 		--will_wont_resp[option];
53338689Sborman 	}
53438689Sborman 
53538689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
53638811Sborman 	    switch (option) {
53738811Sborman 	    case TELOPT_LINEMODE:
53838811Sborman 		linemode = 0;	/* put us back to the default state */
53938811Sborman 		break;
54038811Sborman 	    }
54138689Sborman 	    /* we always accept a DONT */
54238689Sborman 	    set_my_want_state_wont(option);
54344360Sborman 	    if (my_state_is_will(option))
54444360Sborman 		send_wont(option, 0);
54539529Sborman 	    setconnmode(0);			/* Set new tty mode */
54638689Sborman 	}
54738689Sborman 	set_my_state_wont(option);
54838689Sborman }
54938689Sborman 
55027676Sminshall /*
55138908Sborman  * Given a buffer returned by tgetent(), this routine will turn
55238908Sborman  * the pipe seperated list of names in the buffer into an array
55338908Sborman  * of pointers to null terminated names.  We toss out any bad,
55438908Sborman  * duplicate, or verbose names (names with spaces).
55538908Sborman  */
55638908Sborman 
557*46808Sdab static char *name_unknown = "UNKNOWN";
558*46808Sdab static char *unknown[] = { 0, 0 };
55938908Sborman 
560*46808Sdab 	char **
56138908Sborman mklist(buf, name)
562*46808Sdab 	char *buf, *name;
56338908Sborman {
56438908Sborman 	register int n;
565*46808Sdab 	register char c, *cp, **argvp, *cp2, **argv, **avt;
56638908Sborman 
56738908Sborman 	if (name) {
568*46808Sdab 		if (strlen(name) > 40) {
56938908Sborman 			name = 0;
570*46808Sdab 			unknown[0] = name_unknown;
571*46808Sdab 		} else {
57238908Sborman 			unknown[0] = name;
57338908Sborman 			upcase(name);
57438908Sborman 		}
575*46808Sdab 	} else
576*46808Sdab 		unknown[0] = name_unknown;
57738908Sborman 	/*
57838908Sborman 	 * Count up the number of names.
57938908Sborman 	 */
58038908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
58138908Sborman 		if (*cp == '|')
58238908Sborman 			n++;
58338908Sborman 	}
58438908Sborman 	/*
58538908Sborman 	 * Allocate an array to put the name pointers into
58638908Sborman 	 */
58738908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
58838908Sborman 	if (argv == 0)
58938908Sborman 		return(unknown);
59038908Sborman 
59138908Sborman 	/*
59238908Sborman 	 * Fill up the array of pointers to names.
59338908Sborman 	 */
59438908Sborman 	*argv = 0;
59538908Sborman 	argvp = argv+1;
59638908Sborman 	n = 0;
59738908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
59838908Sborman 		if (c == '|' || c == ':') {
59938908Sborman 			*cp++ = '\0';
60038908Sborman 			/*
60138908Sborman 			 * Skip entries that have spaces or are over 40
60238908Sborman 			 * characters long.  If this is our environment
60338908Sborman 			 * name, then put it up front.  Otherwise, as
60438908Sborman 			 * long as this is not a duplicate name (case
60538908Sborman 			 * insensitive) add it to the list.
60638908Sborman 			 */
60738908Sborman 			if (n || (cp - cp2 > 41))
60838908Sborman 				;
60938908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
61038908Sborman 				*argv = cp2;
61138908Sborman 			else if (is_unique(cp2, argv+1, argvp))
61238908Sborman 				*argvp++ = cp2;
61338908Sborman 			if (c == ':')
61438908Sborman 				break;
61538908Sborman 			/*
61638908Sborman 			 * Skip multiple delimiters. Reset cp2 to
61738908Sborman 			 * the beginning of the next name. Reset n,
61838908Sborman 			 * the flag for names with spaces.
61938908Sborman 			 */
62038908Sborman 			while ((c = *cp) == '|')
62138908Sborman 				cp++;
62238908Sborman 			cp2 = cp;
62338908Sborman 			n = 0;
62438908Sborman 		}
62538908Sborman 		/*
62638908Sborman 		 * Skip entries with spaces or non-ascii values.
62738908Sborman 		 * Convert lower case letters to upper case.
62838908Sborman 		 */
62938908Sborman 		if ((c == ' ') || !isascii(c))
63038908Sborman 			n = 1;
63138908Sborman 		else if (islower(c))
63238908Sborman 			*cp = toupper(c);
63338908Sborman 	}
63438908Sborman 
63538908Sborman 	/*
63638908Sborman 	 * Check for an old V6 2 character name.  If the second
63738908Sborman 	 * name points to the beginning of the buffer, and is
63838908Sborman 	 * only 2 characters long, move it to the end of the array.
63938908Sborman 	 */
64038908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
641*46808Sdab 		--argvp;
642*46808Sdab 		for (avt = &argv[1]; avt < argvp; avt++)
643*46808Sdab 			*avt = *(avt+1);
64438908Sborman 		*argvp++ = buf;
64538908Sborman 	}
64638908Sborman 
64738908Sborman 	/*
64838908Sborman 	 * Duplicate last name, for TTYPE option, and null
64938908Sborman 	 * terminate the array.  If we didn't find a match on
65038908Sborman 	 * our terminal name, put that name at the beginning.
65138908Sborman 	 */
65238908Sborman 	cp = *(argvp-1);
65338908Sborman 	*argvp++ = cp;
65438908Sborman 	*argvp = 0;
65538908Sborman 
65638908Sborman 	if (*argv == 0) {
65738908Sborman 		if (name)
65838908Sborman 			*argv = name;
659*46808Sdab 		else {
660*46808Sdab 			--argvp;
661*46808Sdab 			for (avt = argv; avt < argvp; avt++)
662*46808Sdab 				*avt = *(avt+1);
663*46808Sdab 		}
66438908Sborman 	}
66538908Sborman 	if (*argv)
66638908Sborman 		return(argv);
66738908Sborman 	else
66838908Sborman 		return(unknown);
66938908Sborman }
67038908Sborman 
671*46808Sdab 	int
67238908Sborman is_unique(name, as, ae)
673*46808Sdab 	register char *name, **as, **ae;
67438908Sborman {
67538908Sborman 	register char **ap;
67638908Sborman 	register int n;
67738908Sborman 
67838908Sborman 	n = strlen(name) + 1;
67938908Sborman 	for (ap = as; ap < ae; ap++)
68038908Sborman 		if (strncasecmp(*ap, name, n) == 0)
68138908Sborman 			return(0);
68238908Sborman 	return (1);
68338908Sborman }
68438908Sborman 
68538908Sborman #ifdef	TERMCAP
68639529Sborman char termbuf[1024];
687*46808Sdab 
688*46808Sdab 	/*ARGSUSED*/
689*46808Sdab 	int
69038908Sborman setupterm(tname, fd, errp)
691*46808Sdab 	char *tname;
692*46808Sdab 	int fd, *errp;
69338908Sborman {
69439529Sborman 	if (tgetent(termbuf, tname) == 1) {
69539529Sborman 		termbuf[1023] = '\0';
69638908Sborman 		if (errp)
69738908Sborman 			*errp = 1;
69838908Sborman 		return(0);
69938908Sborman 	}
70038908Sborman 	if (errp)
70138908Sborman 		*errp = 0;
70238908Sborman 	return(-1);
70338908Sborman }
70439529Sborman #else
70539529Sborman #define	termbuf	ttytype
70639529Sborman extern char ttytype[];
70738908Sborman #endif
70838908Sborman 
709*46808Sdab int resettermname = 1;
710*46808Sdab 
711*46808Sdab 	char *
71238908Sborman gettermname()
71338908Sborman {
71438908Sborman 	char *tname;
715*46808Sdab 	static char **tnamep = 0;
71638908Sborman 	static char **next;
71738908Sborman 	int err;
71838908Sborman 
719*46808Sdab 	if (resettermname) {
720*46808Sdab 		resettermname = 0;
721*46808Sdab 		if (tnamep && tnamep != unknown)
722*46808Sdab 			free(tnamep);
723*46808Sdab 		if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
72438908Sborman 				(setupterm(tname, 1, &err) == 0)) {
72539529Sborman 			tnamep = mklist(termbuf, tname);
72638908Sborman 		} else {
72738908Sborman 			if (tname && (strlen(tname) <= 40)) {
72838908Sborman 				unknown[0] = tname;
72938908Sborman 				upcase(tname);
730*46808Sdab 			} else
731*46808Sdab 				unknown[0] = name_unknown;
73238908Sborman 			tnamep = unknown;
73338908Sborman 		}
73438908Sborman 		next = tnamep;
73538908Sborman 	}
73638908Sborman 	if (*next == 0)
73738908Sborman 		next = tnamep;
73838908Sborman 	return(*next++);
73938908Sborman }
74038908Sborman /*
74127676Sminshall  * suboption()
74227676Sminshall  *
74327676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
74427676Sminshall  * side.
74527676Sminshall  *
74627676Sminshall  *	Currently we recognize:
74727676Sminshall  *
74827676Sminshall  *		Terminal type, send request.
74937219Sminshall  *		Terminal speed (send request).
75037219Sminshall  *		Local flow control (is request).
75138689Sborman  *		Linemode
75227676Sminshall  */
75327676Sminshall 
754*46808Sdab     static void
75527676Sminshall suboption()
75627676Sminshall {
757*46808Sdab     printsub('<', subbuffer, SB_LEN()+2);
758*46808Sdab     switch (SB_GET()) {
75927676Sminshall     case TELOPT_TTYPE:
76038689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
76138689Sborman 	    return;
762*46808Sdab 	if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
763*46808Sdab 	    return;
76427676Sminshall 	} else {
76527676Sminshall 	    char *name;
766*46808Sdab 	    unsigned char temp[50];
76727676Sminshall 	    int len;
76827676Sminshall 
76932377Sminshall #if	defined(TN3270)
77032531Sminshall 	    if (tn3270_ttype()) {
77132377Sminshall 		return;
77232377Sminshall 	    }
77332377Sminshall #endif	/* defined(TN3270) */
77438908Sborman 	    name = gettermname();
77538908Sborman 	    len = strlen(name) + 4 + 2;
77638908Sborman 	    if (len < NETROOM()) {
777*46808Sdab 		sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
77838908Sborman 				TELQUAL_IS, name, IAC, SE);
77938689Sborman 		ring_supply_data(&netoring, temp, len);
78038908Sborman 		printsub('>', &temp[2], len-2);
78132377Sminshall 	    } else {
78237226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
78332377Sminshall 		/*NOTREACHED*/
78427676Sminshall 	    }
78527676Sminshall 	}
78637219Sminshall 	break;
78737219Sminshall     case TELOPT_TSPEED:
78838689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
78938689Sborman 	    return;
790*46808Sdab 	if (SB_EOF())
791*46808Sdab 	    return;
792*46808Sdab 	if (SB_GET() == TELQUAL_SEND) {
79345232Sborman 	    long ospeed, ispeed;
794*46808Sdab 	    unsigned char temp[50];
79537219Sminshall 	    int len;
79627676Sminshall 
79737219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
79837219Sminshall 
799*46808Sdab 	    sprintf((char *)temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
80038689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
801*46808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
80237219Sminshall 
80338689Sborman 	    if (len < NETROOM()) {
80438689Sborman 		ring_supply_data(&netoring, temp, len);
80538689Sborman 		printsub('>', temp+2, len - 2);
80637219Sminshall 	    }
80744360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
80837219Sminshall 	}
80937219Sminshall 	break;
81037219Sminshall     case TELOPT_LFLOW:
81138689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
81238689Sborman 	    return;
813*46808Sdab 	if (SB_EOF())
814*46808Sdab 	    return;
815*46808Sdab 	switch(SB_GET()) {
816*46808Sdab 	case 1:
81737219Sminshall 	    localflow = 1;
818*46808Sdab 	    break;
819*46808Sdab 	case 0:
82037219Sminshall 	    localflow = 0;
821*46808Sdab 	    break;
822*46808Sdab 	default:
823*46808Sdab 	    return;
82437219Sminshall 	}
82537219Sminshall 	setcommandmode();
82638689Sborman 	setconnmode(0);
82737219Sminshall 	break;
82838689Sborman 
82938689Sborman     case TELOPT_LINEMODE:
83038689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
83138689Sborman 	    return;
832*46808Sdab 	if (SB_EOF())
833*46808Sdab 	    return;
834*46808Sdab 	switch (SB_GET()) {
83538689Sborman 	case WILL:
836*46808Sdab 	    lm_will(subpointer, SB_LEN());
83738689Sborman 	    break;
83838689Sborman 	case WONT:
839*46808Sdab 	    lm_wont(subpointer, SB_LEN());
84038689Sborman 	    break;
84138689Sborman 	case DO:
842*46808Sdab 	    lm_do(subpointer, SB_LEN());
84338689Sborman 	    break;
84438689Sborman 	case DONT:
845*46808Sdab 	    lm_dont(subpointer, SB_LEN());
84638689Sborman 	    break;
84738689Sborman 	case LM_SLC:
848*46808Sdab 	    slc(subpointer, SB_LEN());
84938689Sborman 	    break;
85038689Sborman 	case LM_MODE:
851*46808Sdab 	    lm_mode(subpointer, SB_LEN(), 0);
85238689Sborman 	    break;
85338689Sborman 	default:
85444360Sborman 	    break;
85544360Sborman 	}
85644360Sborman 	break;
85744360Sborman 
85844360Sborman     case TELOPT_ENVIRON:
859*46808Sdab 	if (SB_EOF())
860*46808Sdab 	    return;
861*46808Sdab 	switch(SB_PEEK()) {
86244360Sborman 	case TELQUAL_IS:
86344360Sborman 	case TELQUAL_INFO:
86444360Sborman 	    if (my_want_state_is_dont(TELOPT_ENVIRON))
86544360Sborman 		return;
86644360Sborman 	    break;
86744360Sborman 	case TELQUAL_SEND:
86844360Sborman 	    if (my_want_state_is_wont(TELOPT_ENVIRON)) {
86944360Sborman 		return;
87044360Sborman 	    }
87144360Sborman 	    break;
87244360Sborman 	default:
87344360Sborman 	    return;
87444360Sborman 	}
875*46808Sdab 	env_opt(subpointer, SB_LEN());
87644360Sborman 	break;
87744360Sborman 
87844360Sborman     case TELOPT_XDISPLOC:
87944360Sborman 	if (my_want_state_is_wont(TELOPT_XDISPLOC))
88044360Sborman 	    return;
881*46808Sdab 	if (SB_EOF())
882*46808Sdab 	    return;
883*46808Sdab 	if (SB_GET() == TELQUAL_SEND) {
884*46808Sdab 	    unsigned char temp[50], *dp;
88544360Sborman 	    int len;
88644360Sborman 
887*46808Sdab 	    if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
88844360Sborman 		/*
88944360Sborman 		 * Something happened, we no longer have a DISPLAY
89044360Sborman 		 * variable.  So, turn off the option.
89144360Sborman 		 */
89244360Sborman 		send_wont(TELOPT_XDISPLOC, 1);
89338689Sborman 		break;
89444360Sborman 	    }
895*46808Sdab 	    sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
89644360Sborman 		    TELQUAL_IS, dp, IAC, SE);
897*46808Sdab 	    len = strlen((char *)temp+4) + 4;	/* temp[3] is 0 ... */
89844360Sborman 
89944360Sborman 	    if (len < NETROOM()) {
90044360Sborman 		ring_supply_data(&netoring, temp, len);
90144360Sborman 		printsub('>', temp+2, len - 2);
90244360Sborman 	    }
90344360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
90438689Sborman 	}
90544360Sborman 	break;
90643319Skfall 
907*46808Sdab #if	defined(AUTHENTICATE)
908*46808Sdab 	case TELOPT_AUTHENTICATION: {
909*46808Sdab 		if (!autologin)
910*46808Sdab 			break;
911*46808Sdab 		if (SB_EOF())
912*46808Sdab 			return;
913*46808Sdab 		switch(SB_GET()) {
914*46808Sdab 		case TELQUAL_IS:
915*46808Sdab 			if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
916*46808Sdab 				return;
917*46808Sdab 			auth_is(subpointer, SB_LEN());
918*46808Sdab 			break;
919*46808Sdab 		case TELQUAL_SEND:
920*46808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
921*46808Sdab 				return;
922*46808Sdab 			auth_send(subpointer, SB_LEN());
923*46808Sdab 			break;
924*46808Sdab 		case TELQUAL_REPLY:
925*46808Sdab 			if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
926*46808Sdab 				return;
927*46808Sdab 			auth_reply(subpointer, SB_LEN());
928*46808Sdab 			break;
92943319Skfall 		}
930*46808Sdab 	}
931*46808Sdab 	break;
93243319Skfall #endif
933*46808Sdab #if	defined(ENCRYPT)
934*46808Sdab 	case TELOPT_ENCRYPT:
935*46808Sdab 		if (SB_EOF())
936*46808Sdab 			return;
937*46808Sdab 		switch(SB_GET()) {
938*46808Sdab 		case ENCRYPT_START:
939*46808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
940*46808Sdab 				return;
941*46808Sdab 			encrypt_start();
942*46808Sdab 			break;
943*46808Sdab 		case ENCRYPT_END:
944*46808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
945*46808Sdab 				return;
946*46808Sdab 			encrypt_end();
947*46808Sdab 			break;
948*46808Sdab 		case ENCRYPT_SUPPORT:
949*46808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
950*46808Sdab 				return;
951*46808Sdab 			encrypt_support(subpointer, SB_LEN());
952*46808Sdab 			break;
953*46808Sdab 		case ENCRYPT_REQSTART:
954*46808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
955*46808Sdab 				return;
956*46808Sdab 			encrypt_request_start();
957*46808Sdab 			break;
958*46808Sdab 		case ENCRYPT_REQEND:
959*46808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
960*46808Sdab 				return;
961*46808Sdab 			/*
962*46808Sdab 			 * We can always send an REQEND so that we cannot
963*46808Sdab 			 * get stuck encrypting.  We should only get this
964*46808Sdab 			 * if we have been able to get in the correct mode
965*46808Sdab 			 * anyhow.
966*46808Sdab 			 */
967*46808Sdab 			encrypt_request_end();
968*46808Sdab 			break;
969*46808Sdab 		case ENCRYPT_IS:
970*46808Sdab 			if (my_want_state_is_dont(TELOPT_ENCRYPT))
971*46808Sdab 				return;
972*46808Sdab 			encrypt_is(subpointer, SB_LEN());
973*46808Sdab 			break;
974*46808Sdab 		case ENCRYPT_REPLY:
975*46808Sdab 			if (my_want_state_is_wont(TELOPT_ENCRYPT))
976*46808Sdab 				return;
977*46808Sdab 			encrypt_reply(subpointer, SB_LEN());
978*46808Sdab 			break;
979*46808Sdab 		default:
980*46808Sdab 			break;
981*46808Sdab 		}
982*46808Sdab 		break;
98343319Skfall #endif
98427676Sminshall     default:
98527676Sminshall 	break;
98627676Sminshall     }
98727676Sminshall }
98838689Sborman 
989*46808Sdab static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
99038689Sborman 
991*46808Sdab     void
99238689Sborman lm_will(cmd, len)
993*46808Sdab     unsigned char *cmd;
994*46808Sdab     int len;
99538689Sborman {
99644360Sborman     if (len < 1) {
99744360Sborman /*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
99844360Sborman 	return;
99944360Sborman     }
100038689Sborman     switch(cmd[0]) {
100138689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
100238689Sborman     default:
100338689Sborman 	str_lm[3] = DONT;
100438689Sborman 	str_lm[4] = cmd[0];
100538689Sborman 	if (NETROOM() > sizeof(str_lm)) {
100638689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
100738689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
100838689Sborman 	}
100938689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
101038689Sborman 	break;
101138689Sborman     }
101238689Sborman }
101338689Sborman 
1014*46808Sdab     void
101538689Sborman lm_wont(cmd, len)
1016*46808Sdab     unsigned char *cmd;
1017*46808Sdab     int len;
101838689Sborman {
101944360Sborman     if (len < 1) {
102044360Sborman /*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
102144360Sborman 	return;
102244360Sborman     }
102338689Sborman     switch(cmd[0]) {
102438689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
102538689Sborman     default:
102638689Sborman 	/* We are always DONT, so don't respond */
102738689Sborman 	return;
102838689Sborman     }
102938689Sborman }
103038689Sborman 
1031*46808Sdab     void
103238689Sborman lm_do(cmd, len)
1033*46808Sdab     unsigned char *cmd;
1034*46808Sdab     int len;
103538689Sborman {
103644360Sborman     if (len < 1) {
103744360Sborman /*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
103844360Sborman 	return;
103944360Sborman     }
104038689Sborman     switch(cmd[0]) {
104138689Sborman     case LM_FORWARDMASK:
104238689Sborman     default:
104338689Sborman 	str_lm[3] = WONT;
104438689Sborman 	str_lm[4] = cmd[0];
104538689Sborman 	if (NETROOM() > sizeof(str_lm)) {
104638689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
104738689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
104838689Sborman 	}
104938689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
105038689Sborman 	break;
105138689Sborman     }
105238689Sborman }
105338689Sborman 
1054*46808Sdab     void
105538689Sborman lm_dont(cmd, len)
1056*46808Sdab     unsigned char *cmd;
1057*46808Sdab     int len;
105838689Sborman {
105944360Sborman     if (len < 1) {
106044360Sborman /*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
106144360Sborman 	return;
106244360Sborman     }
106338689Sborman     switch(cmd[0]) {
106438689Sborman     case LM_FORWARDMASK:
106538689Sborman     default:
106638689Sborman 	/* we are always WONT, so don't respond */
106738689Sborman 	break;
106838689Sborman     }
106938689Sborman }
107038689Sborman 
1071*46808Sdab static unsigned char str_lm_mode[] = {
1072*46808Sdab 	IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
1073*46808Sdab };
107438689Sborman 
1075*46808Sdab 	void
107638689Sborman lm_mode(cmd, len, init)
1077*46808Sdab 	unsigned char *cmd;
1078*46808Sdab 	int len, init;
107938689Sborman {
108038689Sborman 	if (len != 1)
108138689Sborman 		return;
108244360Sborman 	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
108338689Sborman 		return;
108438689Sborman 	if (*cmd&MODE_ACK)
108538689Sborman 		return;
108644360Sborman 	linemode = *cmd&(MODE_MASK&~MODE_ACK);
108738689Sborman 	str_lm_mode[4] = linemode;
108838689Sborman 	if (!init)
108938689Sborman 	    str_lm_mode[4] |= MODE_ACK;
109038689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
109138689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
109238689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
109338689Sborman 	}
109438689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
109538689Sborman 	setconnmode(0);	/* set changed mode */
109638689Sborman }
109738689Sborman 
109832377Sminshall 
109927088Sminshall 
110038689Sborman /*
110138689Sborman  * slc()
110238689Sborman  * Handle special character suboption of LINEMODE.
110338689Sborman  */
110438689Sborman 
110538689Sborman struct spc {
110640245Sborman 	cc_t val;
110740245Sborman 	cc_t *valp;
110838689Sborman 	char flags;	/* Current flags & level */
110938689Sborman 	char mylevel;	/* Maximum level & flags */
111038689Sborman } spc_data[NSLC+1];
111138689Sborman 
111238689Sborman #define SLC_IMPORT	0
111338689Sborman #define	SLC_EXPORT	1
111438689Sborman #define SLC_RVALUE	2
111538689Sborman static int slc_mode = SLC_EXPORT;
111638689Sborman 
1117*46808Sdab 	void
111838689Sborman slc_init()
111938689Sborman {
112038689Sborman 	register struct spc *spcp;
112138689Sborman 
112238689Sborman 	localchars = 1;
112338689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
112438689Sborman 		spcp->val = 0;
112538689Sborman 		spcp->valp = 0;
112638689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
112738689Sborman 	}
112838689Sborman 
112938689Sborman #define	initfunc(func, flags) { \
113038689Sborman 					spcp = &spc_data[func]; \
113138689Sborman 					if (spcp->valp = tcval(func)) { \
113238689Sborman 					    spcp->val = *spcp->valp; \
113338689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
113438689Sborman 					} else { \
113538689Sborman 					    spcp->val = 0; \
113638689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
113738689Sborman 					} \
113838689Sborman 				    }
113938689Sborman 
114038689Sborman 	initfunc(SLC_SYNCH, 0);
114138689Sborman 	/* No BRK */
114238689Sborman 	initfunc(SLC_AO, 0);
114338689Sborman 	initfunc(SLC_AYT, 0);
114438689Sborman 	/* No EOR */
114538689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
114638689Sborman 	initfunc(SLC_EOF, 0);
114739529Sborman #ifndef	SYSV_TERMIO
114838689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
114938689Sborman #endif
115038689Sborman 	initfunc(SLC_EC, 0);
115138689Sborman 	initfunc(SLC_EL, 0);
115239529Sborman #ifndef	SYSV_TERMIO
115338689Sborman 	initfunc(SLC_EW, 0);
115438689Sborman 	initfunc(SLC_RP, 0);
115538689Sborman 	initfunc(SLC_LNEXT, 0);
115638689Sborman #endif
115738689Sborman 	initfunc(SLC_XON, 0);
115838689Sborman 	initfunc(SLC_XOFF, 0);
115939529Sborman #ifdef	SYSV_TERMIO
116038689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
116138689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
116238689Sborman #endif
116344360Sborman 	initfunc(SLC_FORW1, 0);
116444360Sborman #ifdef	USE_TERMIO
116544360Sborman 	initfunc(SLC_FORW2, 0);
116638689Sborman 	/* No FORW2 */
116744360Sborman #endif
116838689Sborman 
116938689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
117038689Sborman #undef	initfunc
117138689Sborman 
117238689Sborman 	if (slc_mode == SLC_EXPORT)
117338689Sborman 		slc_export();
117438689Sborman 	else
117538689Sborman 		slc_import(1);
117638689Sborman 
117738689Sborman }
117838689Sborman 
1179*46808Sdab     void
118038689Sborman slcstate()
118138689Sborman {
118238689Sborman     printf("Special characters are %s values\n",
118338689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
118438689Sborman 		slc_mode == SLC_EXPORT ? "local" :
118538689Sborman 					 "remote");
118638689Sborman }
118738689Sborman 
1188*46808Sdab     void
118938689Sborman slc_mode_export()
119038689Sborman {
119138689Sborman     slc_mode = SLC_EXPORT;
119238689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
119338689Sborman 	slc_export();
119438689Sborman }
119538689Sborman 
1196*46808Sdab     void
119738689Sborman slc_mode_import(def)
1198*46808Sdab     int def;
119938689Sborman {
120038689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
120138689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
120238689Sborman 	slc_import(def);
120338689Sborman }
120438689Sborman 
1205*46808Sdab unsigned char slc_import_val[] = {
120638689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
120738689Sborman };
1208*46808Sdab unsigned char slc_import_def[] = {
120938689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
121038689Sborman };
121138689Sborman 
1212*46808Sdab     void
121338689Sborman slc_import(def)
1214*46808Sdab     int def;
121538689Sborman {
121638689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
121738689Sborman 	if (def) {
121838689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
121938689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
122038689Sborman 	} else {
122138689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
122238689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
122338689Sborman 	}
122438689Sborman     }
122538689Sborman /*@*/ else printf("slc_import: not enough room\n");
122638689Sborman }
122738689Sborman 
1228*46808Sdab     void
122938689Sborman slc_export()
123038689Sborman {
123138689Sborman     register struct spc *spcp;
123238689Sborman 
123338689Sborman     TerminalDefaultChars();
123438689Sborman 
123538689Sborman     slc_start_reply();
123638689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
123738689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
123845232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
123945232Sborman 		spcp->flags = SLC_NOSUPPORT;
124045232Sborman 	    else
124145232Sborman 		spcp->flags = spcp->mylevel;
124238689Sborman 	    if (spcp->valp)
124338689Sborman 		spcp->val = *spcp->valp;
124445232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
124538689Sborman 	}
124638689Sborman     }
124738689Sborman     slc_end_reply();
1248*46808Sdab     (void)slc_update();
1249*46808Sdab     setconnmode(1);	/* Make sure the character values are set */
125038689Sborman }
125138689Sborman 
1252*46808Sdab 	void
125338689Sborman slc(cp, len)
1254*46808Sdab 	register unsigned char *cp;
1255*46808Sdab 	int len;
125638689Sborman {
125738689Sborman 	register struct spc *spcp;
125838689Sborman 	register int func,level;
125938689Sborman 
126038689Sborman 	slc_start_reply();
126138689Sborman 
126238689Sborman 	for (; len >= 3; len -=3, cp +=3) {
126338689Sborman 
126438689Sborman 		func = cp[SLC_FUNC];
126538689Sborman 
126638689Sborman 		if (func == 0) {
126738689Sborman 			/*
126838689Sborman 			 * Client side: always ignore 0 function.
126938689Sborman 			 */
127038689Sborman 			continue;
127138689Sborman 		}
127238689Sborman 		if (func > NSLC) {
127345232Sborman 			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
127438689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
127538689Sborman 			continue;
127638689Sborman 		}
127738689Sborman 
127838689Sborman 		spcp = &spc_data[func];
127938689Sborman 
128038689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
128138689Sborman 
128240245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
128338689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
128438689Sborman 			continue;
128538689Sborman 		}
128638689Sborman 
128738689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
128838689Sborman 			/*
128938689Sborman 			 * This is an error condition, the SLC_ACK
129038689Sborman 			 * bit should never be set for the SLC_DEFAULT
129138689Sborman 			 * level.  Our best guess to recover is to
129238689Sborman 			 * ignore the SLC_ACK bit.
129338689Sborman 			 */
129438689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
129538689Sborman 		}
129638689Sborman 
129738689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
129840245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
129938689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
130038689Sborman 			continue;
130138689Sborman 		}
130238689Sborman 
130338689Sborman 		level &= ~SLC_ACK;
130438689Sborman 
130538689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
130638689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
130740245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
130838689Sborman 		}
130938689Sborman 		if (level == SLC_DEFAULT) {
131038689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
131138689Sborman 				spcp->flags = spcp->mylevel;
131238689Sborman 			else
131338689Sborman 				spcp->flags = SLC_NOSUPPORT;
131438689Sborman 		}
131538689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
131638689Sborman 	}
131738689Sborman 	slc_end_reply();
131838689Sborman 	if (slc_update())
131938689Sborman 		setconnmode(1);	/* set the  new character values */
132038689Sborman }
132138689Sborman 
1322*46808Sdab     void
132338689Sborman slc_check()
132438689Sborman {
132538689Sborman     register struct spc *spcp;
132638689Sborman 
132738689Sborman     slc_start_reply();
132838689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
132938689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
133038689Sborman 	    spcp->val = *spcp->valp;
133145232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
133245232Sborman 		spcp->flags = SLC_NOSUPPORT;
133345232Sborman 	    else
133445232Sborman 		spcp->flags = spcp->mylevel;
133545232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
133638689Sborman 	}
133738689Sborman     }
133838689Sborman     slc_end_reply();
133938689Sborman     setconnmode(1);
134038689Sborman }
134138689Sborman 
134238689Sborman 
134338689Sborman unsigned char slc_reply[128];
134438689Sborman unsigned char *slc_replyp;
1345*46808Sdab 
1346*46808Sdab 	void
134738689Sborman slc_start_reply()
134838689Sborman {
134938689Sborman 	slc_replyp = slc_reply;
135038689Sborman 	*slc_replyp++ = IAC;
135138689Sborman 	*slc_replyp++ = SB;
135238689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
135338689Sborman 	*slc_replyp++ = LM_SLC;
135438689Sborman }
135538689Sborman 
1356*46808Sdab 	void
135738689Sborman slc_add_reply(func, flags, value)
1358*46808Sdab 	unsigned char func;
1359*46808Sdab 	unsigned char flags;
1360*46808Sdab 	cc_t value;
136138689Sborman {
136238689Sborman 	if ((*slc_replyp++ = func) == IAC)
136338689Sborman 		*slc_replyp++ = IAC;
136438689Sborman 	if ((*slc_replyp++ = flags) == IAC)
136538689Sborman 		*slc_replyp++ = IAC;
136640245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
136738689Sborman 		*slc_replyp++ = IAC;
136838689Sborman }
136938689Sborman 
1370*46808Sdab     void
137138689Sborman slc_end_reply()
137238689Sborman {
137338689Sborman     register int len;
137438689Sborman 
137538689Sborman     *slc_replyp++ = IAC;
137638689Sborman     *slc_replyp++ = SE;
137738689Sborman     len = slc_replyp - slc_reply;
137838689Sborman     if (len <= 6)
137938689Sborman 	return;
138038689Sborman     if (NETROOM() > len) {
138138689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
138238689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
138338689Sborman     }
138438689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
138538689Sborman }
138638689Sborman 
1387*46808Sdab 	int
138838689Sborman slc_update()
138938689Sborman {
139038689Sborman 	register struct spc *spcp;
139138689Sborman 	int need_update = 0;
139238689Sborman 
139338689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
139438689Sborman 		if (!(spcp->flags&SLC_ACK))
139538689Sborman 			continue;
139638689Sborman 		spcp->flags &= ~SLC_ACK;
139738689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
139838689Sborman 			*spcp->valp = spcp->val;
139938689Sborman 			need_update = 1;
140038689Sborman 		}
140138689Sborman 	}
140238689Sborman 	return(need_update);
140338689Sborman }
140438689Sborman 
1405*46808Sdab 	void
140644360Sborman env_opt(buf, len)
1407*46808Sdab 	register unsigned char *buf;
1408*46808Sdab 	register int len;
140944360Sborman {
1410*46808Sdab 	register unsigned char *ep = 0, *epc = 0;
141144360Sborman 	register int i;
141244360Sborman 
141345232Sborman 	switch(buf[0]&0xff) {
141444360Sborman 	case TELQUAL_SEND:
141544360Sborman 		env_opt_start();
141644360Sborman 		if (len == 1) {
141744360Sborman 			env_opt_add(NULL);
141844360Sborman 		} else for (i = 1; i < len; i++) {
141945232Sborman 			switch (buf[i]&0xff) {
142044360Sborman 			case ENV_VALUE:
142144360Sborman 				if (ep) {
142244360Sborman 					*epc = 0;
142344360Sborman 					env_opt_add(ep);
142444360Sborman 				}
142544360Sborman 				ep = epc = &buf[i+1];
142644360Sborman 				break;
142744360Sborman 			case ENV_ESC:
142844360Sborman 				i++;
142944360Sborman 				/*FALL THROUGH*/
143044360Sborman 			default:
143144360Sborman 				if (epc)
143244360Sborman 					*epc++ = buf[i];
143344360Sborman 				break;
143444360Sborman 			}
143544360Sborman 			if (ep) {
143644360Sborman 				*epc = 0;
143744360Sborman 				env_opt_add(ep);
143844360Sborman 			}
143944360Sborman 		}
144044360Sborman 		env_opt_end(1);
144144360Sborman 		break;
144244360Sborman 
144344360Sborman 	case TELQUAL_IS:
144444360Sborman 	case TELQUAL_INFO:
144544360Sborman 		/* Ignore for now.  We shouldn't get it anyway. */
144644360Sborman 		break;
144744360Sborman 
144844360Sborman 	default:
144944360Sborman 		break;
145044360Sborman 	}
145144360Sborman }
145244360Sborman 
145344360Sborman #define	OPT_REPLY_SIZE	256
145444360Sborman unsigned char *opt_reply;
145544360Sborman unsigned char *opt_replyp;
145644360Sborman unsigned char *opt_replyend;
145744360Sborman 
1458*46808Sdab 	void
145944360Sborman env_opt_start()
146044360Sborman {
146144360Sborman 	if (opt_reply)
146244360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
146344360Sborman 	else
146444360Sborman 		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
146544360Sborman 	if (opt_reply == NULL) {
146644360Sborman /*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
146744360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
146844360Sborman 		return;
146944360Sborman 	}
147044360Sborman 	opt_replyp = opt_reply;
147144360Sborman 	opt_replyend = opt_reply + OPT_REPLY_SIZE;
147244360Sborman 	*opt_replyp++ = IAC;
147344360Sborman 	*opt_replyp++ = SB;
147444360Sborman 	*opt_replyp++ = TELOPT_ENVIRON;
147544360Sborman 	*opt_replyp++ = TELQUAL_IS;
147644360Sborman }
147744360Sborman 
1478*46808Sdab 	void
147944360Sborman env_opt_start_info()
148044360Sborman {
148144360Sborman 	env_opt_start();
148244360Sborman 	if (opt_replyp)
148344360Sborman 	    opt_replyp[-1] = TELQUAL_INFO;
148444360Sborman }
148544360Sborman 
1486*46808Sdab 	void
148744360Sborman env_opt_add(ep)
1488*46808Sdab 	register unsigned char *ep;
148944360Sborman {
1490*46808Sdab 	register unsigned char *vp, c;
149144360Sborman 
149244360Sborman 	if (opt_reply == NULL)		/*XXX*/
149344360Sborman 		return;			/*XXX*/
149444360Sborman 
149544360Sborman 	if (ep == NULL || *ep == '\0') {
149644360Sborman 		env_default(1);
149744360Sborman 		while (ep = env_default(0))
149844360Sborman 			env_opt_add(ep);
149944360Sborman 		return;
150044360Sborman 	}
150144360Sborman 	vp = env_getvalue(ep);
1502*46808Sdab 	if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
1503*46808Sdab 				strlen((char *)ep) + 6 > opt_replyend)
1504*46808Sdab 	{
150544360Sborman 		register int len;
150644360Sborman 		opt_replyend += OPT_REPLY_SIZE;
150744360Sborman 		len = opt_replyend - opt_reply;
150844360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, len);
150944360Sborman 		if (opt_reply == NULL) {
151044360Sborman /*@*/			printf("env_opt_add: realloc() failed!!!\n");
151144360Sborman 			opt_reply = opt_replyp = opt_replyend = NULL;
151244360Sborman 			return;
151344360Sborman 		}
151444360Sborman 		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
151544360Sborman 		opt_replyend = opt_reply + len;
151644360Sborman 	}
151744360Sborman 	*opt_replyp++ = ENV_VAR;
151844360Sborman 	for (;;) {
151944360Sborman 		while (c = *ep++) {
152045232Sborman 			switch(c&0xff) {
152144360Sborman 			case IAC:
152244360Sborman 				*opt_replyp++ = IAC;
152344360Sborman 				break;
152444360Sborman 			case ENV_VALUE:
152544360Sborman 			case ENV_VAR:
152644360Sborman 			case ENV_ESC:
152744360Sborman 				*opt_replyp++ = ENV_ESC;
152844360Sborman 				break;
152944360Sborman 			}
153044360Sborman 			*opt_replyp++ = c;
153144360Sborman 		}
153244360Sborman 		if (ep = vp) {
153344360Sborman 			*opt_replyp++ = ENV_VALUE;
153444360Sborman 			vp = NULL;
153544360Sborman 		} else
153644360Sborman 			break;
153744360Sborman 	}
153844360Sborman }
153944360Sborman 
1540*46808Sdab 	void
154144360Sborman env_opt_end(emptyok)
1542*46808Sdab 	register int emptyok;
154344360Sborman {
154444360Sborman 	register int len;
154544360Sborman 
154644360Sborman 	len = opt_replyp - opt_reply + 2;
154744360Sborman 	if (emptyok || len > 6) {
154844360Sborman 		*opt_replyp++ = IAC;
154944360Sborman 		*opt_replyp++ = SE;
155044360Sborman 		if (NETROOM() > len) {
155144360Sborman 			ring_supply_data(&netoring, opt_reply, len);
155244360Sborman 			printsub('>', &opt_reply[2], len - 2);
155344360Sborman 		}
155444360Sborman /*@*/		else printf("slc_end_reply: not enough room\n");
155544360Sborman 	}
155644360Sborman 	if (opt_reply) {
155744360Sborman 		free(opt_reply);
155844360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
155944360Sborman 	}
156044360Sborman }
156144360Sborman 
156238689Sborman 
156338689Sborman 
1564*46808Sdab     int
156532377Sminshall telrcv()
156627110Sminshall {
156732377Sminshall     register int c;
156832385Sminshall     register int scc;
1569*46808Sdab     register unsigned char *sbp;
157032385Sminshall     int count;
157132385Sminshall     int returnValue = 0;
157227088Sminshall 
157332385Sminshall     scc = 0;
157432385Sminshall     count = 0;
157532385Sminshall     while (TTYROOM() > 2) {
157632385Sminshall 	if (scc == 0) {
157732385Sminshall 	    if (count) {
157832528Sminshall 		ring_consumed(&netiring, count);
157932385Sminshall 		returnValue = 1;
158032385Sminshall 		count = 0;
158132385Sminshall 	    }
158232528Sminshall 	    sbp = netiring.consume;
158332528Sminshall 	    scc = ring_full_consecutive(&netiring);
158432385Sminshall 	    if (scc == 0) {
158532385Sminshall 		/* No more data coming in */
158632385Sminshall 		break;
158732385Sminshall 	    }
158832385Sminshall 	}
158932385Sminshall 
159032385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
1591*46808Sdab #if	defined(ENCRYPT)
1592*46808Sdab 	if (decrypt_input)
1593*46808Sdab 		c = (*decrypt_input)(c);
1594*46808Sdab #endif
159532385Sminshall 
159632377Sminshall 	switch (telrcv_state) {
159727110Sminshall 
159832377Sminshall 	case TS_CR:
159932377Sminshall 	    telrcv_state = TS_DATA;
160035518Sminshall 	    if (c == '\0') {
160135518Sminshall 		break;	/* Ignore \0 after CR */
160239529Sborman 	    }
160339529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
160435518Sminshall 		TTYADD(c);
160535518Sminshall 		break;
160632377Sminshall 	    }
160735518Sminshall 	    /* Else, fall through */
160827088Sminshall 
160932377Sminshall 	case TS_DATA:
161032377Sminshall 	    if (c == IAC) {
161132377Sminshall 		telrcv_state = TS_IAC;
161233804Sminshall 		break;
161332377Sminshall 	    }
161432377Sminshall #	    if defined(TN3270)
161532377Sminshall 	    if (In3270) {
161632377Sminshall 		*Ifrontp++ = c;
161732385Sminshall 		while (scc > 0) {
161832385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
1619*46808Sdab #if	defined(ENCRYPT)
1620*46808Sdab 		    if (decrypt_input)
1621*46808Sdab 			c = (*decrypt_input)(c);
1622*46808Sdab #endif
162332377Sminshall 		    if (c == IAC) {
162432377Sminshall 			telrcv_state = TS_IAC;
162534304Sminshall 			break;
162632377Sminshall 		    }
162732377Sminshall 		    *Ifrontp++ = c;
162832377Sminshall 		}
162932377Sminshall 	    } else
163032377Sminshall #	    endif /* defined(TN3270) */
163135518Sminshall 		    /*
163235518Sminshall 		     * The 'crmod' hack (see following) is needed
163335518Sminshall 		     * since we can't * set CRMOD on output only.
163435518Sminshall 		     * Machines like MULTICS like to send \r without
163535518Sminshall 		     * \n; since we must turn off CRMOD to get proper
163635518Sminshall 		     * input, the mapping is done here (sigh).
163735518Sminshall 		     */
163838689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
163935518Sminshall 		if (scc > 0) {
164035518Sminshall 		    c = *sbp&0xff;
1641*46808Sdab #if	defined(ENCRYPT)
1642*46808Sdab 		    if (decrypt_input)
1643*46808Sdab 			c = (*decrypt_input)(c);
1644*46808Sdab #endif
164535518Sminshall 		    if (c == 0) {
164635518Sminshall 			sbp++, scc--; count++;
164735518Sminshall 			/* a "true" CR */
164832377Sminshall 			TTYADD('\r');
164938689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
165035518Sminshall 					(c == '\n')) {
165135518Sminshall 			sbp++, scc--; count++;
165232377Sminshall 			TTYADD('\n');
165335518Sminshall 		    } else {
1654*46808Sdab #if	defined(ENCRYPT)
1655*46808Sdab 		        if (decrypt_input)
1656*46808Sdab 			    (*decrypt_input)(-1);
1657*46808Sdab #endif
1658*46808Sdab 
165935518Sminshall 			TTYADD('\r');
166035518Sminshall 			if (crmod) {
166135518Sminshall 				TTYADD('\n');
166232377Sminshall 			}
166332377Sminshall 		    }
166435518Sminshall 		} else {
166535518Sminshall 		    telrcv_state = TS_CR;
166635518Sminshall 		    TTYADD('\r');
166735518Sminshall 		    if (crmod) {
166835518Sminshall 			    TTYADD('\n');
166935518Sminshall 		    }
167032377Sminshall 		}
167132377Sminshall 	    } else {
167232377Sminshall 		TTYADD(c);
167332377Sminshall 	    }
167432377Sminshall 	    continue;
167527088Sminshall 
167632377Sminshall 	case TS_IAC:
167738689Sborman process_iac:
167832377Sminshall 	    switch (c) {
167932377Sminshall 
168032377Sminshall 	    case WILL:
168132377Sminshall 		telrcv_state = TS_WILL;
168232377Sminshall 		continue;
168327261Sminshall 
168432377Sminshall 	    case WONT:
168532377Sminshall 		telrcv_state = TS_WONT;
168632377Sminshall 		continue;
168727261Sminshall 
168832377Sminshall 	    case DO:
168932377Sminshall 		telrcv_state = TS_DO;
169032377Sminshall 		continue;
169127261Sminshall 
169232377Sminshall 	    case DONT:
169332377Sminshall 		telrcv_state = TS_DONT;
169432377Sminshall 		continue;
169527261Sminshall 
169632377Sminshall 	    case DM:
169732377Sminshall 		    /*
169832377Sminshall 		     * We may have missed an urgent notification,
169932377Sminshall 		     * so make sure we flush whatever is in the
170032377Sminshall 		     * buffer currently.
170132377Sminshall 		     */
1702*46808Sdab 		printoption("RCVD", IAC, DM);
170332377Sminshall 		SYNCHing = 1;
170444360Sborman 		(void) ttyflush(1);
170532554Sminshall 		SYNCHing = stilloob();
170632377Sminshall 		settimer(gotDM);
170732377Sminshall 		break;
170827088Sminshall 
170932377Sminshall 	    case SB:
171032377Sminshall 		SB_CLEAR();
171132377Sminshall 		telrcv_state = TS_SB;
171232377Sminshall 		continue;
171327261Sminshall 
171432377Sminshall #	    if defined(TN3270)
171532377Sminshall 	    case EOR:
171632377Sminshall 		if (In3270) {
171732377Sminshall 		    if (Ibackp == Ifrontp) {
171832377Sminshall 			Ibackp = Ifrontp = Ibuf;
171932377Sminshall 			ISend = 0;	/* should have been! */
172032377Sminshall 		    } else {
172144360Sborman 			Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
172232377Sminshall 			ISend = 1;
172327088Sminshall 		    }
172427088Sminshall 		}
1725*46808Sdab 		printoption("RCVD", IAC, EOR);
172627088Sminshall 		break;
172732377Sminshall #	    endif /* defined(TN3270) */
172832377Sminshall 
172932377Sminshall 	    case IAC:
173032377Sminshall #	    if !defined(TN3270)
173132377Sminshall 		TTYADD(IAC);
173232377Sminshall #	    else /* !defined(TN3270) */
173332377Sminshall 		if (In3270) {
173432377Sminshall 		    *Ifrontp++ = IAC;
173532377Sminshall 		} else {
173632377Sminshall 		    TTYADD(IAC);
173732377Sminshall 		}
173832377Sminshall #	    endif /* !defined(TN3270) */
173927088Sminshall 		break;
174032377Sminshall 
174144360Sborman 	    case NOP:
174244360Sborman 	    case GA:
174327088Sminshall 	    default:
1744*46808Sdab 		printoption("RCVD", IAC, c);
174527088Sminshall 		break;
174627088Sminshall 	    }
174732377Sminshall 	    telrcv_state = TS_DATA;
174832377Sminshall 	    continue;
174927088Sminshall 
175032377Sminshall 	case TS_WILL:
1751*46808Sdab 	    printoption("RCVD", WILL, c);
175238689Sborman 	    willoption(c);
175332377Sminshall 	    SetIn3270();
175432377Sminshall 	    telrcv_state = TS_DATA;
175532377Sminshall 	    continue;
175627110Sminshall 
175732377Sminshall 	case TS_WONT:
1758*46808Sdab 	    printoption("RCVD", WONT, c);
175938689Sborman 	    wontoption(c);
176032377Sminshall 	    SetIn3270();
176132377Sminshall 	    telrcv_state = TS_DATA;
176232377Sminshall 	    continue;
176327088Sminshall 
176432377Sminshall 	case TS_DO:
1765*46808Sdab 	    printoption("RCVD", DO, c);
176637226Sminshall 	    dooption(c);
176732377Sminshall 	    SetIn3270();
176837219Sminshall 	    if (c == TELOPT_NAWS) {
176937219Sminshall 		sendnaws();
177037219Sminshall 	    } else if (c == TELOPT_LFLOW) {
177137219Sminshall 		localflow = 1;
177237219Sminshall 		setcommandmode();
177338689Sborman 		setconnmode(0);
177437219Sminshall 	    }
177532377Sminshall 	    telrcv_state = TS_DATA;
177632377Sminshall 	    continue;
177727088Sminshall 
177832377Sminshall 	case TS_DONT:
1779*46808Sdab 	    printoption("RCVD", DONT, c);
178038689Sborman 	    dontoption(c);
178137226Sminshall 	    flushline = 1;
178238689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
178332377Sminshall 	    SetIn3270();
178432377Sminshall 	    telrcv_state = TS_DATA;
178532377Sminshall 	    continue;
178627088Sminshall 
178732377Sminshall 	case TS_SB:
178832377Sminshall 	    if (c == IAC) {
178932377Sminshall 		telrcv_state = TS_SE;
179032377Sminshall 	    } else {
179132377Sminshall 		SB_ACCUM(c);
179232377Sminshall 	    }
179332377Sminshall 	    continue;
179427088Sminshall 
179532377Sminshall 	case TS_SE:
179632377Sminshall 	    if (c != SE) {
179732377Sminshall 		if (c != IAC) {
179838689Sborman 		    /*
179938689Sborman 		     * This is an error.  We only expect to get
180038689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
180138689Sborman 		     * have happend.  An IAC was not doubled, the
180238689Sborman 		     * IAC SE was left off, or another option got
180338689Sborman 		     * inserted into the suboption are all possibilities.
180438689Sborman 		     * If we assume that the IAC was not doubled,
180538689Sborman 		     * and really the IAC SE was left off, we could
180638689Sborman 		     * get into an infinate loop here.  So, instead,
180738689Sborman 		     * we terminate the suboption, and process the
180838689Sborman 		     * partial suboption if we can.
180938689Sborman 		     */
181032377Sminshall 		    SB_ACCUM(IAC);
181138689Sborman 		    SB_ACCUM(c);
1812*46808Sdab 		    subpointer -= 2;
1813*46808Sdab 		    SB_TERM();
1814*46808Sdab 
1815*46808Sdab 		    printoption("In SUBOPTION processing, RCVD", IAC, c);
181638689Sborman 		    suboption();	/* handle sub-option */
181738689Sborman 		    SetIn3270();
181838689Sborman 		    telrcv_state = TS_IAC;
181938689Sborman 		    goto process_iac;
182032377Sminshall 		}
182132377Sminshall 		SB_ACCUM(c);
182232377Sminshall 		telrcv_state = TS_SB;
182332377Sminshall 	    } else {
182438689Sborman 		SB_ACCUM(IAC);
182538689Sborman 		SB_ACCUM(SE);
1826*46808Sdab 		subpointer -= 2;
1827*46808Sdab 		SB_TERM();
182832377Sminshall 		suboption();	/* handle sub-option */
182932377Sminshall 		SetIn3270();
183032377Sminshall 		telrcv_state = TS_DATA;
183132377Sminshall 	    }
183227088Sminshall 	}
183327088Sminshall     }
183432667Sminshall     if (count)
183532667Sminshall 	ring_consumed(&netiring, count);
183632385Sminshall     return returnValue||count;
183727088Sminshall }
183832385Sminshall 
1839*46808Sdab static int bol = 1, local = 0;
1840*46808Sdab 
1841*46808Sdab     int
1842*46808Sdab rlogin_susp()
1843*46808Sdab {
1844*46808Sdab     if (local) {
1845*46808Sdab 	local = 0;
1846*46808Sdab 	bol = 1;
1847*46808Sdab 	command(0, "z\n", 2);
1848*46808Sdab 	return(1);
1849*46808Sdab     }
1850*46808Sdab     return(0);
1851*46808Sdab }
1852*46808Sdab 
1853*46808Sdab     static int
185432554Sminshall telsnd()
185532385Sminshall {
185632385Sminshall     int tcc;
185732385Sminshall     int count;
185832385Sminshall     int returnValue = 0;
1859*46808Sdab     unsigned char *tbp;
186032385Sminshall 
186132385Sminshall     tcc = 0;
186232385Sminshall     count = 0;
186332385Sminshall     while (NETROOM() > 2) {
186432385Sminshall 	register int sc;
186532385Sminshall 	register int c;
186632385Sminshall 
186732385Sminshall 	if (tcc == 0) {
186832385Sminshall 	    if (count) {
186932528Sminshall 		ring_consumed(&ttyiring, count);
187032385Sminshall 		returnValue = 1;
187132385Sminshall 		count = 0;
187232385Sminshall 	    }
187332528Sminshall 	    tbp = ttyiring.consume;
187432528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
187532385Sminshall 	    if (tcc == 0) {
187632385Sminshall 		break;
187732385Sminshall 	    }
187832385Sminshall 	}
187932385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
1880*46808Sdab 	if (rlogin != _POSIX_VDISABLE) {
1881*46808Sdab 		if (bol) {
1882*46808Sdab 			bol = 0;
1883*46808Sdab 			if (sc == rlogin) {
1884*46808Sdab 				local = 1;
1885*46808Sdab 				continue;
1886*46808Sdab 			}
1887*46808Sdab 		} else if (local) {
1888*46808Sdab 			local = 0;
1889*46808Sdab 			if (sc == '.' || c == termEofChar) {
1890*46808Sdab 				bol = 1;
1891*46808Sdab 				command(0, "close\n", 6);
1892*46808Sdab 				continue;
1893*46808Sdab 			}
1894*46808Sdab 			if (sc == termSuspChar) {
1895*46808Sdab 				bol = 1;
1896*46808Sdab 				command(0, "z\n", 2);
1897*46808Sdab 				continue;
1898*46808Sdab 			}
1899*46808Sdab 			if (sc == escape) {
1900*46808Sdab 				command(0, (char *)tbp, tcc);
1901*46808Sdab 				bol = 1;
1902*46808Sdab 				count += tcc;
1903*46808Sdab 				tcc = 0;
1904*46808Sdab 				flushline = 1;
1905*46808Sdab 				break;
1906*46808Sdab 			}
1907*46808Sdab 			if (sc != rlogin) {
1908*46808Sdab 				++tcc;
1909*46808Sdab 				--tbp;
1910*46808Sdab 				--count;
1911*46808Sdab 				c = sc = rlogin;
1912*46808Sdab 			}
1913*46808Sdab 		}
1914*46808Sdab 		if ((sc == '\n') || (sc == '\r'))
1915*46808Sdab 			bol = 1;
1916*46808Sdab 	} else if (sc == escape) {
191738689Sborman 	    /*
191838689Sborman 	     * Double escape is a pass through of a single escape character.
191938689Sborman 	     */
192038689Sborman 	    if (tcc && strip(*tbp) == escape) {
192138689Sborman 		tbp++;
192238689Sborman 		tcc--;
192338689Sborman 		count++;
1924*46808Sdab 		bol = 0;
192538689Sborman 	    } else {
1926*46808Sdab 		command(0, (char *)tbp, tcc);
1927*46808Sdab 		bol = 1;
192838689Sborman 		count += tcc;
192938689Sborman 		tcc = 0;
193038689Sborman 		flushline = 1;
193138689Sborman 		break;
193238689Sborman 	    }
1933*46808Sdab 	} else
1934*46808Sdab 	    bol = 0;
193538689Sborman #ifdef	KLUDGELINEMODE
193638689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
193732385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
193832385Sminshall 		tcc--; tbp++; count++;
193932385Sminshall 	    } else {
194032385Sminshall 		dontlecho = !dontlecho;
194132385Sminshall 		settimer(echotoggle);
194238689Sborman 		setconnmode(0);
194332385Sminshall 		flushline = 1;
194432385Sminshall 		break;
194532385Sminshall 	    }
194632385Sminshall 	}
194738689Sborman #endif
194838689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
194932385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
1950*46808Sdab 		bol = 1;
195132385Sminshall 		break;
195232385Sminshall 	    }
195332385Sminshall 	}
195438689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
195532385Sminshall 	    switch (c) {
195632385Sminshall 	    case '\n':
195732385Sminshall 		    /*
195832385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
195932385Sminshall 		     * on our local machine, then probably
196032385Sminshall 		     * a newline (unix) is CRLF (TELNET).
196132385Sminshall 		     */
196232385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
196332385Sminshall 		    NETADD('\r');
196432385Sminshall 		}
196532385Sminshall 		NETADD('\n');
1966*46808Sdab 		bol = flushline = 1;
196732385Sminshall 		break;
196832385Sminshall 	    case '\r':
196932385Sminshall 		if (!crlf) {
197032385Sminshall 		    NET2ADD('\r', '\0');
197132385Sminshall 		} else {
197232385Sminshall 		    NET2ADD('\r', '\n');
197332385Sminshall 		}
1974*46808Sdab 		bol = flushline = 1;
197532385Sminshall 		break;
197632385Sminshall 	    case IAC:
197732385Sminshall 		NET2ADD(IAC, IAC);
197832385Sminshall 		break;
197932385Sminshall 	    default:
198032385Sminshall 		NETADD(c);
198132385Sminshall 		break;
198232385Sminshall 	    }
198332385Sminshall 	} else if (c == IAC) {
198432385Sminshall 	    NET2ADD(IAC, IAC);
198532385Sminshall 	} else {
198632385Sminshall 	    NETADD(c);
198732385Sminshall 	}
198832385Sminshall     }
198932667Sminshall     if (count)
199032667Sminshall 	ring_consumed(&ttyiring, count);
199132385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
199232385Sminshall }
199332377Sminshall 
199427088Sminshall /*
199532377Sminshall  * Scheduler()
199632377Sminshall  *
199732377Sminshall  * Try to do something.
199832377Sminshall  *
199932377Sminshall  * If we do something useful, return 1; else return 0.
200032377Sminshall  *
200127110Sminshall  */
200227110Sminshall 
200327110Sminshall 
2004*46808Sdab     int
200532377Sminshall Scheduler(block)
2006*46808Sdab     int	block;			/* should we block in the select ? */
200727110Sminshall {
200832377Sminshall 		/* One wants to be a bit careful about setting returnValue
200932377Sminshall 		 * to one, since a one implies we did some useful work,
201032377Sminshall 		 * and therefore probably won't be called to block next
201132377Sminshall 		 * time (TN3270 mode only).
201232377Sminshall 		 */
201332531Sminshall     int returnValue;
201432531Sminshall     int netin, netout, netex, ttyin, ttyout;
201527110Sminshall 
201632531Sminshall     /* Decide which rings should be processed */
201732531Sminshall 
201832531Sminshall     netout = ring_full_count(&netoring) &&
201938689Sborman 	    (flushline ||
202038689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
202138689Sborman #ifdef	KLUDGELINEMODE
202238689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
202338689Sborman #endif
202438689Sborman 		) ||
202538689Sborman 			my_want_state_is_will(TELOPT_BINARY));
202632531Sminshall     ttyout = ring_full_count(&ttyoring);
202732531Sminshall 
202832377Sminshall #if	defined(TN3270)
202932531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
203032377Sminshall #else	/* defined(TN3270) */
203132531Sminshall     ttyin = ring_empty_count(&ttyiring);
203232377Sminshall #endif	/* defined(TN3270) */
203332531Sminshall 
203432531Sminshall #if	defined(TN3270)
203532531Sminshall     netin = ring_empty_count(&netiring);
203632377Sminshall #   else /* !defined(TN3270) */
203732531Sminshall     netin = !ISend && ring_empty_count(&netiring);
203832377Sminshall #   endif /* !defined(TN3270) */
203932531Sminshall 
204032531Sminshall     netex = !SYNCHing;
204132531Sminshall 
204232531Sminshall     /* If we have seen a signal recently, reset things */
204332377Sminshall #   if defined(TN3270) && defined(unix)
204432377Sminshall     if (HaveInput) {
204532377Sminshall 	HaveInput = 0;
204644360Sborman 	(void) signal(SIGIO, inputAvailable);
204732377Sminshall     }
204832377Sminshall #endif	/* defined(TN3270) && defined(unix) */
204932377Sminshall 
205032531Sminshall     /* Call to system code to process rings */
205127178Sminshall 
205232531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
205327178Sminshall 
205432531Sminshall     /* Now, look at the input rings, looking for work to do. */
205532377Sminshall 
205632531Sminshall     if (ring_full_count(&ttyiring)) {
205732377Sminshall #   if defined(TN3270)
205832377Sminshall 	if (In3270) {
205934848Sminshall 	    int c;
206034848Sminshall 
206133804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
206232528Sminshall 					ring_full_consecutive(&ttyiring));
206332377Sminshall 	    if (c) {
206432377Sminshall 		returnValue = 1;
206532667Sminshall 	        ring_consumed(&ttyiring, c);
206632377Sminshall 	    }
206732377Sminshall 	} else {
206832377Sminshall #   endif /* defined(TN3270) */
206932554Sminshall 	    returnValue |= telsnd();
207032377Sminshall #   if defined(TN3270)
207127178Sminshall 	}
207232531Sminshall #   endif /* defined(TN3270) */
207327178Sminshall     }
207432377Sminshall 
207532528Sminshall     if (ring_full_count(&netiring)) {
207632377Sminshall #	if !defined(TN3270)
207732385Sminshall 	returnValue |= telrcv();
207832377Sminshall #	else /* !defined(TN3270) */
207932377Sminshall 	returnValue = Push3270();
208032377Sminshall #	endif /* !defined(TN3270) */
208132377Sminshall     }
208232377Sminshall     return returnValue;
208327178Sminshall }
208427178Sminshall 
208527178Sminshall /*
208632377Sminshall  * Select from tty and network...
208727088Sminshall  */
2088*46808Sdab     void
2089*46808Sdab telnet(user)
2090*46808Sdab     char *user;
209127088Sminshall {
209232531Sminshall     sys_telnet_init();
209327088Sminshall 
2094*46808Sdab #if defined(ENCRYPT) || defined(AUTHENTICATE)
2095*46808Sdab     {
2096*46808Sdab 	static char local_host[256] = { 0 };
2097*46808Sdab 	int len = sizeof(local_host);
2098*46808Sdab 
2099*46808Sdab 	if (!local_host[0]) {
2100*46808Sdab 		gethostname(local_host, &len);
2101*46808Sdab 		local_host[sizeof(local_host)-1] = 0;
2102*46808Sdab 	}
2103*46808Sdab 	auth_encrypt_init(local_host, hostname, "TELNET", 0);
2104*46808Sdab 	auth_encrypt_user(user);
2105*46808Sdab     }
2106*46808Sdab #endif
210732377Sminshall #   if !defined(TN3270)
210832377Sminshall     if (telnetport) {
2109*46808Sdab #if	defined(AUTHENTICATE)
2110*46808Sdab 	if (autologin)
2111*46808Sdab 		send_will(TELOPT_AUTHENTICATION, 1);
2112*46808Sdab #endif
2113*46808Sdab #if	defined(ENCRYPT)
2114*46808Sdab 	send_do(TELOPT_ENCRYPT, 1);
2115*46808Sdab 	send_will(TELOPT_ENCRYPT, 1);
2116*46808Sdab #endif
211738689Sborman 	send_do(TELOPT_SGA, 1);
211838689Sborman 	send_will(TELOPT_TTYPE, 1);
211938689Sborman 	send_will(TELOPT_NAWS, 1);
212038689Sborman 	send_will(TELOPT_TSPEED, 1);
212138689Sborman 	send_will(TELOPT_LFLOW, 1);
212238689Sborman 	send_will(TELOPT_LINEMODE, 1);
2123*46808Sdab 	send_will(TELOPT_ENVIRON, 1);
212438908Sborman 	send_do(TELOPT_STATUS, 1);
2125*46808Sdab 	if (env_getvalue((unsigned char *)"DISPLAY"))
212644360Sborman 	    send_will(TELOPT_XDISPLOC, 1);
2127*46808Sdab 	if (eight)
2128*46808Sdab 	    tel_enter_binary(eight);
212927178Sminshall     }
213032377Sminshall #   endif /* !defined(TN3270) */
213127088Sminshall 
213232377Sminshall #   if !defined(TN3270)
213332377Sminshall     for (;;) {
213432385Sminshall 	int schedValue;
213532385Sminshall 
213632385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
213732385Sminshall 	    if (schedValue == -1) {
213832385Sminshall 		setcommandmode();
213932385Sminshall 		return;
214032385Sminshall 	    }
214132385Sminshall 	}
214232385Sminshall 
214332531Sminshall 	if (Scheduler(1) == -1) {
214432377Sminshall 	    setcommandmode();
214532377Sminshall 	    return;
214632377Sminshall 	}
214732377Sminshall     }
214832377Sminshall #   else /* !defined(TN3270) */
214932377Sminshall     for (;;) {
215032377Sminshall 	int schedValue;
215127088Sminshall 
215232377Sminshall 	while (!In3270 && !shell_active) {
215332531Sminshall 	    if (Scheduler(1) == -1) {
215432377Sminshall 		setcommandmode();
215532377Sminshall 		return;
215632377Sminshall 	    }
215727088Sminshall 	}
215832377Sminshall 
215932377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
216032377Sminshall 	    if (schedValue == -1) {
216132377Sminshall 		setcommandmode();
216232377Sminshall 		return;
216332377Sminshall 	    }
216427088Sminshall 	}
216532377Sminshall 		/* If there is data waiting to go out to terminal, don't
216632377Sminshall 		 * schedule any more data for the terminal.
216732377Sminshall 		 */
216834304Sminshall 	if (ring_full_count(&ttyoring)) {
216932377Sminshall 	    schedValue = 1;
217027088Sminshall 	} else {
217132377Sminshall 	    if (shell_active) {
217232377Sminshall 		if (shell_continue() == 0) {
217332377Sminshall 		    ConnectScreen();
217427088Sminshall 		}
217532377Sminshall 	    } else if (In3270) {
217632377Sminshall 		schedValue = DoTerminalOutput();
217732377Sminshall 	    }
217827088Sminshall 	}
217932377Sminshall 	if (schedValue && (shell_active == 0)) {
218032531Sminshall 	    if (Scheduler(1) == -1) {
218132377Sminshall 		setcommandmode();
218232377Sminshall 		return;
218332377Sminshall 	    }
218427088Sminshall 	}
218532377Sminshall     }
218632377Sminshall #   endif /* !defined(TN3270) */
218727088Sminshall }
218832377Sminshall 
218934848Sminshall #if	0	/* XXX - this not being in is a bug */
219027088Sminshall /*
219132554Sminshall  * nextitem()
219232554Sminshall  *
219332554Sminshall  *	Return the address of the next "item" in the TELNET data
219432554Sminshall  * stream.  This will be the address of the next character if
219532554Sminshall  * the current address is a user data character, or it will
219632554Sminshall  * be the address of the character following the TELNET command
219732554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
219832554Sminshall  * character.
219932554Sminshall  */
220032554Sminshall 
2201*46808Sdab     static char *
220232554Sminshall nextitem(current)
2203*46808Sdab     char *current;
220432554Sminshall {
220532554Sminshall     if ((*current&0xff) != IAC) {
220632554Sminshall 	return current+1;
220732554Sminshall     }
220832554Sminshall     switch (*(current+1)&0xff) {
220932554Sminshall     case DO:
221032554Sminshall     case DONT:
221132554Sminshall     case WILL:
221232554Sminshall     case WONT:
221332554Sminshall 	return current+3;
221432554Sminshall     case SB:		/* loop forever looking for the SE */
221532554Sminshall 	{
221632554Sminshall 	    register char *look = current+2;
221732554Sminshall 
221832554Sminshall 	    for (;;) {
221932554Sminshall 		if ((*look++&0xff) == IAC) {
222032554Sminshall 		    if ((*look++&0xff) == SE) {
222132554Sminshall 			return look;
222232554Sminshall 		    }
222332554Sminshall 		}
222432554Sminshall 	    }
222532554Sminshall 	}
222632554Sminshall     default:
222732554Sminshall 	return current+2;
222832554Sminshall     }
222932554Sminshall }
223034848Sminshall #endif	/* 0 */
223132554Sminshall 
223232554Sminshall /*
223332554Sminshall  * netclear()
223432554Sminshall  *
223532554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
223632554Sminshall  * the path to the network.
223732554Sminshall  *
223832554Sminshall  *	Things are a bit tricky since we may have sent the first
223932554Sminshall  * byte or so of a previous TELNET command into the network.
224032554Sminshall  * So, we have to scan the network buffer from the beginning
224132554Sminshall  * until we are up to where we want to be.
224232554Sminshall  *
224332554Sminshall  *	A side effect of what we do, just to keep things
224432554Sminshall  * simple, is to clear the urgent data pointer.  The principal
224532554Sminshall  * caller should be setting the urgent data pointer AFTER calling
224632554Sminshall  * us in any case.
224732554Sminshall  */
224832554Sminshall 
2249*46808Sdab     static void
225032554Sminshall netclear()
225132554Sminshall {
225232554Sminshall #if	0	/* XXX */
225332554Sminshall     register char *thisitem, *next;
225432554Sminshall     char *good;
225532554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
225632554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
225732554Sminshall 
225832554Sminshall     thisitem = netobuf;
225932554Sminshall 
226032554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
226132554Sminshall 	thisitem = next;
226232554Sminshall     }
226332554Sminshall 
226432554Sminshall     /* Now, thisitem is first before/at boundary. */
226532554Sminshall 
226632554Sminshall     good = netobuf;	/* where the good bytes go */
226732554Sminshall 
226832554Sminshall     while (netoring.add > thisitem) {
226932554Sminshall 	if (wewant(thisitem)) {
227032554Sminshall 	    int length;
227132554Sminshall 
227232554Sminshall 	    next = thisitem;
227332554Sminshall 	    do {
227432554Sminshall 		next = nextitem(next);
227532554Sminshall 	    } while (wewant(next) && (nfrontp > next));
227632554Sminshall 	    length = next-thisitem;
227732554Sminshall 	    memcpy(good, thisitem, length);
227832554Sminshall 	    good += length;
227932554Sminshall 	    thisitem = next;
228032554Sminshall 	} else {
228132554Sminshall 	    thisitem = nextitem(thisitem);
228232554Sminshall 	}
228332554Sminshall     }
228432554Sminshall 
228532554Sminshall #endif	/* 0 */
228632554Sminshall }
228732554Sminshall 
228832554Sminshall /*
228932377Sminshall  * These routines add various telnet commands to the data stream.
229027088Sminshall  */
229132377Sminshall 
2292*46808Sdab     static void
229332554Sminshall doflush()
229432554Sminshall {
229532554Sminshall     NET2ADD(IAC, DO);
229632554Sminshall     NETADD(TELOPT_TM);
229732554Sminshall     flushline = 1;
229832554Sminshall     flushout = 1;
229944360Sborman     (void) ttyflush(1);			/* Flush/drop output */
230032554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
2301*46808Sdab     printoption("SENT", DO, TELOPT_TM);
230232554Sminshall }
230332554Sminshall 
2304*46808Sdab     void
230532377Sminshall xmitAO()
230627088Sminshall {
230732377Sminshall     NET2ADD(IAC, AO);
2308*46808Sdab     printoption("SENT", IAC, AO);
230932377Sminshall     if (autoflush) {
231032377Sminshall 	doflush();
231132377Sminshall     }
231232377Sminshall }
231327088Sminshall 
231432377Sminshall 
2315*46808Sdab     void
231632377Sminshall xmitEL()
231727088Sminshall {
231832377Sminshall     NET2ADD(IAC, EL);
2319*46808Sdab     printoption("SENT", IAC, EL);
232027088Sminshall }
232127088Sminshall 
2322*46808Sdab     void
232332377Sminshall xmitEC()
232427088Sminshall {
232532377Sminshall     NET2ADD(IAC, EC);
2326*46808Sdab     printoption("SENT", IAC, EC);
232727088Sminshall }
232827088Sminshall 
232932377Sminshall 
2330*46808Sdab     int
233132377Sminshall dosynch()
233227088Sminshall {
233332377Sminshall     netclear();			/* clear the path to the network */
233433294Sminshall     NETADD(IAC);
233533294Sminshall     setneturg();
233633294Sminshall     NETADD(DM);
2337*46808Sdab     printoption("SENT", IAC, DM);
2338*46808Sdab     return 1;
233927088Sminshall }
234027088Sminshall 
2341*46808Sdab int want_status_response = 0;
2342*46808Sdab 
2343*46808Sdab     int
234438908Sborman get_status()
234538908Sborman {
2346*46808Sdab     unsigned char tmp[16];
2347*46808Sdab     register unsigned char *cp;
234838908Sborman 
234938908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
235038908Sborman 	printf("Remote side does not support STATUS option\n");
2351*46808Sdab 	return 0;
235238908Sborman     }
235338908Sborman     cp = tmp;
235438908Sborman 
235538908Sborman     *cp++ = IAC;
235638908Sborman     *cp++ = SB;
235738908Sborman     *cp++ = TELOPT_STATUS;
235838908Sborman     *cp++ = TELQUAL_SEND;
235938908Sborman     *cp++ = IAC;
236038908Sborman     *cp++ = SE;
236138908Sborman     if (NETROOM() >= cp - tmp) {
236238908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
236338908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
236438908Sborman     }
2365*46808Sdab     ++want_status_response;
2366*46808Sdab     return 1;
236738908Sborman }
236838908Sborman 
2369*46808Sdab     void
237032377Sminshall intp()
237127088Sminshall {
237232377Sminshall     NET2ADD(IAC, IP);
2373*46808Sdab     printoption("SENT", IAC, IP);
237432377Sminshall     flushline = 1;
237532377Sminshall     if (autoflush) {
237632377Sminshall 	doflush();
237732377Sminshall     }
237832377Sminshall     if (autosynch) {
237932377Sminshall 	dosynch();
238032377Sminshall     }
238127088Sminshall }
238227186Sminshall 
2383*46808Sdab     void
238432377Sminshall sendbrk()
238527186Sminshall {
238632377Sminshall     NET2ADD(IAC, BREAK);
2387*46808Sdab     printoption("SENT", IAC, BREAK);
238832377Sminshall     flushline = 1;
238932377Sminshall     if (autoflush) {
239032377Sminshall 	doflush();
239132377Sminshall     }
239232377Sminshall     if (autosynch) {
239332377Sminshall 	dosynch();
239432377Sminshall     }
239527186Sminshall }
239638689Sborman 
2397*46808Sdab     void
239838689Sborman sendabort()
239938689Sborman {
240038689Sborman     NET2ADD(IAC, ABORT);
2401*46808Sdab     printoption("SENT", IAC, ABORT);
240238689Sborman     flushline = 1;
240338689Sborman     if (autoflush) {
240438689Sborman 	doflush();
240538689Sborman     }
240638689Sborman     if (autosynch) {
240738689Sborman 	dosynch();
240838689Sborman     }
240938689Sborman }
241038689Sborman 
2411*46808Sdab     void
241238689Sborman sendsusp()
241338689Sborman {
241438689Sborman     NET2ADD(IAC, SUSP);
2415*46808Sdab     printoption("SENT", IAC, SUSP);
241638689Sborman     flushline = 1;
241738689Sborman     if (autoflush) {
241838689Sborman 	doflush();
241938689Sborman     }
242038689Sborman     if (autosynch) {
242138689Sborman 	dosynch();
242238689Sborman     }
242338689Sborman }
242438689Sborman 
2425*46808Sdab     void
242638689Sborman sendeof()
242738689Sborman {
242838908Sborman     NET2ADD(IAC, xEOF);
2429*46808Sdab     printoption("SENT", IAC, xEOF);
243038689Sborman }
243138689Sborman 
2432*46808Sdab     void
243345232Sborman sendayt()
243445232Sborman {
243545232Sborman     NET2ADD(IAC, AYT);
2436*46808Sdab     printoption("SENT", IAC, AYT);
243745232Sborman }
243845232Sborman 
243937219Sminshall /*
244037219Sminshall  * Send a window size update to the remote system.
244137219Sminshall  */
244237219Sminshall 
2443*46808Sdab     void
244437219Sminshall sendnaws()
244537219Sminshall {
244637219Sminshall     long rows, cols;
244738689Sborman     unsigned char tmp[16];
244838689Sborman     register unsigned char *cp;
244937219Sminshall 
245038689Sborman     if (my_state_is_wont(TELOPT_NAWS))
245138689Sborman 	return;
245237219Sminshall 
245338689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
245438689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
245538689Sborman 
245637219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
245737219Sminshall 	return;
245837219Sminshall     }
245937219Sminshall 
246038689Sborman     cp = tmp;
246138689Sborman 
246238689Sborman     *cp++ = IAC;
246338689Sborman     *cp++ = SB;
246438689Sborman     *cp++ = TELOPT_NAWS;
246538689Sborman     PUTSHORT(cp, cols);
246638689Sborman     PUTSHORT(cp, rows);
246738689Sborman     *cp++ = IAC;
246838689Sborman     *cp++ = SE;
246938689Sborman     if (NETROOM() >= cp - tmp) {
247038689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
247138689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
247237219Sminshall     }
247337219Sminshall }
247437226Sminshall 
2475*46808Sdab     void
247638908Sborman tel_enter_binary(rw)
2477*46808Sdab     int rw;
247837226Sminshall {
247938908Sborman     if (rw&1)
248038908Sborman 	send_do(TELOPT_BINARY, 1);
248138908Sborman     if (rw&2)
248238908Sborman 	send_will(TELOPT_BINARY, 1);
248337226Sminshall }
248437226Sminshall 
2485*46808Sdab     void
248638908Sborman tel_leave_binary(rw)
2487*46808Sdab     int rw;
248837226Sminshall {
248938908Sborman     if (rw&1)
249038908Sborman 	send_dont(TELOPT_BINARY, 1);
249138908Sborman     if (rw&2)
249238908Sborman 	send_wont(TELOPT_BINARY, 1);
249337226Sminshall }
2494