xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 45232)
133685Sbostic /*
2*45232Sborman  * 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*45232Sborman static char sccsid[] = "@(#)telnet.c	5.51 (Berkeley) 09/14/90";
1033685Sbostic #endif /* not lint */
1121580Sdist 
129217Ssam #include <sys/types.h>
139217Ssam 
1443319Skfall #ifdef	KERBEROS
1543319Skfall #include <sys/socket.h>
1643319Skfall #include <netinet/in.h>
1743319Skfall #include <kerberosIV/des.h>
1843319Skfall #include <kerberosIV/krb.h>
1943319Skfall #include "krb4-proto.h"
2043319Skfall #endif
2143319Skfall 
2232377Sminshall #if	defined(unix)
2333804Sminshall #include <signal.h>
2432377Sminshall /* By the way, we need to include curses.h before telnet.h since,
2532377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2632377Sminshall  * declared in curses.h.
2732377Sminshall  */
2832377Sminshall #endif	/* defined(unix) */
2932377Sminshall 
3012212Ssam #include <arpa/telnet.h>
3132377Sminshall 
3244360Sborman #if	defined(unix)
3344360Sborman #include <strings.h>
3444360Sborman #else	/* defined(unix) */
3532377Sminshall #include <string.h>
3644360Sborman #endif	/* defined(unix) */
379217Ssam 
3838908Sborman #include <ctype.h>
3938908Sborman 
4032381Sminshall #include "ring.h"
4132381Sminshall 
4232377Sminshall #include "defines.h"
4332377Sminshall #include "externs.h"
4432377Sminshall #include "types.h"
4532377Sminshall #include "general.h"
4627178Sminshall 
4727178Sminshall 
4827228Sminshall #define	strip(x)	((x)&0x7f)
496000Sroot 
5044360Sborman extern char *env_getvalue();
5127088Sminshall 
5232377Sminshall static char	subbuffer[SUBBUFSIZE],
5332377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
5427676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
5527676Sminshall #define	SB_TERM()	subend = subpointer;
5627676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5727676Sminshall 				*subpointer++ = (c); \
5827676Sminshall 			}
5927676Sminshall 
6037226Sminshall char	options[256];		/* The combined options */
6138689Sborman char	do_dont_resp[256];
6238689Sborman char	will_wont_resp[256];
636000Sroot 
6432377Sminshall int
6532377Sminshall 	connected,
6632377Sminshall 	showoptions,
6732377Sminshall 	In3270,		/* Are we in 3270 mode? */
6832377Sminshall 	ISend,		/* trying to send network data in */
6943319Skfall #ifdef	KERBEROS
7043319Skfall 	kerberized = 0,	/* Are we using Kerberos authentication ? */
7143319Skfall #endif
7232377Sminshall 	debug = 0,
7332377Sminshall 	crmod,
7432377Sminshall 	netdata,	/* Print out network data flow */
7532377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7634848Sminshall #if	defined(TN3270)
7736241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
7836241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
7932377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
8034848Sminshall #endif	/* defined(TN3270) */
8133286Sminshall 	telnetport,
8232531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
8332531Sminshall 	flushout,	/* flush output */
8432531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
8532531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
8637219Sminshall 	localflow,	/* we handle flow control locally */
8732531Sminshall 	localchars,	/* we recognize interrupt/quit */
8832531Sminshall 	donelclchars,	/* the user has set "localchars" */
8932531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
9032531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
9132531Sminshall 	globalmode;
9227088Sminshall 
9344360Sborman char *prompt = 0;
946000Sroot 
9544360Sborman cc_t escape;
9644360Sborman #ifdef	KLUDGELINEMODE
9744360Sborman cc_t echoc;
9844360Sborman #endif
9927186Sminshall 
10027186Sminshall /*
1016000Sroot  * Telnet receiver states for fsm
1026000Sroot  */
1036000Sroot #define	TS_DATA		0
1046000Sroot #define	TS_IAC		1
1056000Sroot #define	TS_WILL		2
1066000Sroot #define	TS_WONT		3
1076000Sroot #define	TS_DO		4
1086000Sroot #define	TS_DONT		5
10927021Sminshall #define	TS_CR		6
11027676Sminshall #define	TS_SB		7		/* sub-option collection */
11127676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1126000Sroot 
11332377Sminshall static int	telrcv_state;
1146000Sroot 
11532377Sminshall jmp_buf	toplevel = { 0 };
11632377Sminshall jmp_buf	peerdied;
1176000Sroot 
11832377Sminshall int	flushline;
11938811Sborman int	linemode;
12027021Sminshall 
12138689Sborman #ifdef	KLUDGELINEMODE
12238689Sborman int	kludgelinemode = 1;
12338689Sborman #endif
12438689Sborman 
12532377Sminshall /*
12632377Sminshall  * The following are some clocks used to decide how to interpret
12732377Sminshall  * the relationship between various variables.
12832377Sminshall  */
1296000Sroot 
13032377Sminshall Clocks clocks;
13132377Sminshall 
13238689Sborman #ifdef	notdef
13332377Sminshall Modelist modelist[] = {
13432377Sminshall 	{ "telnet command mode", COMMAND_LINE },
13532377Sminshall 	{ "character-at-a-time mode", 0 },
13632377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
13732377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
13832377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13932377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
14032377Sminshall 	{ "3270 mode", 0 },
14132377Sminshall };
14238689Sborman #endif
1436000Sroot 
14432377Sminshall 
14532377Sminshall /*
14632377Sminshall  * Initialize telnet environment.
14732377Sminshall  */
1486000Sroot 
14932377Sminshall init_telnet()
15032377Sminshall {
15144360Sborman     env_init();
15244360Sborman 
15332377Sminshall     SB_CLEAR();
15437226Sminshall     ClearArray(options);
1556000Sroot 
15637219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
1576000Sroot 
15832377Sminshall     SYNCHing = 0;
1596000Sroot 
16032377Sminshall     /* Don't change NetTrace */
1616000Sroot 
16232377Sminshall     escape = CONTROL(']');
16344360Sborman #ifdef	KLUDGELINEMODE
16432377Sminshall     echoc = CONTROL('E');
16544360Sborman #endif
1666000Sroot 
16732377Sminshall     flushline = 1;
16832377Sminshall     telrcv_state = TS_DATA;
16932377Sminshall }
17032554Sminshall 
1716000Sroot 
17244360Sborman #ifdef	notdef
17332554Sminshall #include <varargs.h>
1746000Sroot 
17534848Sminshall /*VARARGS*/
17632554Sminshall static void
17732554Sminshall printring(va_alist)
17832554Sminshall va_dcl
17932554Sminshall {
18032554Sminshall     va_list ap;
18132554Sminshall     char buffer[100];		/* where things go */
18232554Sminshall     char *ptr;
18332554Sminshall     char *format;
18432554Sminshall     char *string;
18532554Sminshall     Ring *ring;
18632554Sminshall     int i;
18732554Sminshall 
18832554Sminshall     va_start(ap);
18932554Sminshall 
19032554Sminshall     ring = va_arg(ap, Ring *);
19132554Sminshall     format = va_arg(ap, char *);
19232554Sminshall     ptr = buffer;
19332554Sminshall 
19432554Sminshall     while ((i = *format++) != 0) {
19532554Sminshall 	if (i == '%') {
19632554Sminshall 	    i = *format++;
19732554Sminshall 	    switch (i) {
19832554Sminshall 	    case 'c':
19932554Sminshall 		*ptr++ = va_arg(ap, int);
20032554Sminshall 		break;
20132554Sminshall 	    case 's':
20232554Sminshall 		string = va_arg(ap, char *);
20332554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
20432554Sminshall 		ring_supply_data(ring, string, strlen(string));
20532554Sminshall 		ptr = buffer;
20632554Sminshall 		break;
20732554Sminshall 	    case 0:
20832554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20932554Sminshall 		/*NOTREACHED*/
21032554Sminshall 	    default:
21132554Sminshall 		ExitString("printring: unknown format character.\n", 1);
21232554Sminshall 		/*NOTREACHED*/
21332554Sminshall 	    }
21432554Sminshall 	} else {
21532554Sminshall 	    *ptr++ = i;
21632554Sminshall 	}
21732554Sminshall     }
21832554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21932554Sminshall }
22044360Sborman #endif
22132554Sminshall 
22237226Sminshall /*
22337226Sminshall  * These routines are in charge of sending option negotiations
22437226Sminshall  * to the other side.
22537226Sminshall  *
22637226Sminshall  * The basic idea is that we send the negotiation if either side
22737226Sminshall  * is in disagreement as to what the current state should be.
22837226Sminshall  */
22932554Sminshall 
23038689Sborman send_do(c, init)
23138689Sborman register int c, init;
2326000Sroot {
23338689Sborman     if (init) {
23438689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
23538689Sborman 				my_want_state_is_do(c))
23638689Sborman 	    return;
23738689Sborman 	set_my_want_state_do(c);
23838689Sborman 	do_dont_resp[c]++;
23937226Sminshall     }
24038689Sborman     NET2ADD(IAC, DO);
24138689Sborman     NETADD(c);
24238689Sborman     printoption("SENT", "do", c);
24337226Sminshall }
24437226Sminshall 
24537226Sminshall void
24638689Sborman send_dont(c, init)
24738689Sborman register int c, init;
24837226Sminshall {
24938689Sborman     if (init) {
25038689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
25138689Sborman 				my_want_state_is_dont(c))
25238689Sborman 	    return;
25338689Sborman 	set_my_want_state_dont(c);
25438689Sborman 	do_dont_resp[c]++;
25537226Sminshall     }
25638689Sborman     NET2ADD(IAC, DONT);
25738689Sborman     NETADD(c);
25838689Sborman     printoption("SENT", "dont", c);
25937226Sminshall }
26037226Sminshall 
26137226Sminshall void
26238689Sborman send_will(c, init)
26338689Sborman register int c, init;
26437226Sminshall {
26538689Sborman     if (init) {
26638689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
26738689Sborman 				my_want_state_is_will(c))
26838689Sborman 	    return;
26938689Sborman 	set_my_want_state_will(c);
27038689Sborman 	will_wont_resp[c]++;
27137226Sminshall     }
27238689Sborman     NET2ADD(IAC, WILL);
27338689Sborman     NETADD(c);
27438689Sborman     printoption("SENT", "will", c);
27537226Sminshall }
27637226Sminshall 
27737226Sminshall void
27838689Sborman send_wont(c, init)
27938689Sborman register int c, init;
28037226Sminshall {
28138689Sborman     if (init) {
28238689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
28338689Sborman 				my_want_state_is_wont(c))
28438689Sborman 	    return;
28538689Sborman 	set_my_want_state_wont(c);
28638689Sborman 	will_wont_resp[c]++;
28737226Sminshall     }
28838689Sborman     NET2ADD(IAC, WONT);
28938689Sborman     NETADD(c);
29038689Sborman     printoption("SENT", "wont", c);
29137226Sminshall }
29237226Sminshall 
29337226Sminshall 
29437226Sminshall void
29537226Sminshall willoption(option)
29637226Sminshall 	int option;
29737226Sminshall {
29838689Sborman 	int new_state_ok = 0;
2996000Sroot 
30038689Sborman 	if (do_dont_resp[option]) {
30138689Sborman 	    --do_dont_resp[option];
30238689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
30338689Sborman 		--do_dont_resp[option];
30438689Sborman 	}
30537226Sminshall 
30638689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3076000Sroot 
30838689Sborman 	    switch (option) {
30938689Sborman 
31038689Sborman 	    case TELOPT_ECHO:
31138689Sborman #	    if defined(TN3270)
31238689Sborman 		/*
31338689Sborman 		 * The following is a pain in the rear-end.
31438689Sborman 		 * Various IBM servers (some versions of Wiscnet,
31538689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
31638689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
31738689Sborman 		 * in the setup proceedings.  On the other hand,
31838689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
31938689Sborman 		 * So, we are stuck.  Empirically (but, based on
32038689Sborman 		 * a VERY small sample), the IBM servers don't send
32138689Sborman 		 * out anything about ECHO, so we postpone our sending
32238689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
32338689Sborman 		 * DO send).
32438689Sborman 		  */
32538689Sborman 		{
32638689Sborman 		    if (askedSGA == 0) {
32738689Sborman 			askedSGA = 1;
32838689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
32938689Sborman 			    send_do(TELOPT_SGA, 1);
33032377Sminshall 		    }
33132377Sminshall 		}
33238689Sborman 		    /* Fall through */
33338689Sborman 	    case TELOPT_EOR:
33438908Sborman #endif	    /* defined(TN3270) */
33538689Sborman 	    case TELOPT_BINARY:
33638689Sborman 	    case TELOPT_SGA:
33727110Sminshall 		settimer(modenegotiated);
33838908Sborman 		/* FALL THROUGH */
33938908Sborman 	    case TELOPT_STATUS:
34038689Sborman 		new_state_ok = 1;
3416000Sroot 		break;
3426000Sroot 
34338689Sborman 	    case TELOPT_TM:
34438689Sborman 		if (flushout)
34538689Sborman 		    flushout = 0;
34638689Sborman 		/*
34738689Sborman 		 * Special case for TM.  If we get back a WILL,
34838689Sborman 		 * pretend we got back a WONT.
34938689Sborman 		 */
35038689Sborman 		set_my_want_state_dont(option);
35138689Sborman 		set_my_state_dont(option);
35227110Sminshall 		return;			/* Never reply to TM will's/wont's */
3536000Sroot 
35438689Sborman 	    case TELOPT_LINEMODE:
35538689Sborman 	    default:
3566000Sroot 		break;
35738689Sborman 	    }
35838689Sborman 
35938689Sborman 	    if (new_state_ok) {
36038689Sborman 		set_my_want_state_do(option);
36138689Sborman 		send_do(option, 0);
36238689Sborman 		setconnmode(0);		/* possibly set new tty mode */
36338689Sborman 	    } else {
36438689Sborman 		do_dont_resp[option]++;
36538689Sborman 		send_dont(option, 0);
36638689Sborman 	    }
3676000Sroot 	}
36838689Sborman 	set_my_state_do(option);
3696000Sroot }
3706000Sroot 
37132377Sminshall void
37237226Sminshall wontoption(option)
37337226Sminshall 	int option;
3746000Sroot {
37538689Sborman 	if (do_dont_resp[option]) {
37638689Sborman 	    --do_dont_resp[option];
37738689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
37838689Sborman 		--do_dont_resp[option];
37938689Sborman 	}
38037226Sminshall 
38138689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3826000Sroot 
38338689Sborman 	    switch (option) {
38438689Sborman 
38538689Sborman #ifdef	KLUDGELINEMODE
38638689Sborman 	    case TELOPT_SGA:
38738689Sborman 		if (!kludgelinemode)
38838689Sborman 		    break;
38938689Sborman 		/* FALL THROUGH */
39038689Sborman #endif
39138689Sborman 	    case TELOPT_ECHO:
39227110Sminshall 		settimer(modenegotiated);
3936000Sroot 		break;
3946000Sroot 
39538689Sborman 	    case TELOPT_TM:
39638689Sborman 		if (flushout)
39738689Sborman 		    flushout = 0;
39838689Sborman 		set_my_want_state_dont(option);
39938689Sborman 		set_my_state_dont(option);
40027110Sminshall 		return;		/* Never reply to TM will's/wont's */
40127110Sminshall 
40238689Sborman 	    default:
40338689Sborman 		break;
40438689Sborman 	    }
40538689Sborman 	    set_my_want_state_dont(option);
40644360Sborman 	    if (my_state_is_do(option))
40744360Sborman 		send_dont(option, 0);
40838689Sborman 	    setconnmode(0);			/* Set new tty mode */
40938689Sborman 	} else if (option == TELOPT_TM) {
41038689Sborman 	    /*
41138689Sborman 	     * Special case for TM.
41238689Sborman 	     */
41338689Sborman 	    if (flushout)
41438689Sborman 		flushout = 0;
41538689Sborman 	    set_my_want_state_dont(option);
4166000Sroot 	}
41738689Sborman 	set_my_state_dont(option);
4186000Sroot }
4196000Sroot 
42032377Sminshall static void
4216000Sroot dooption(option)
4226000Sroot 	int option;
4236000Sroot {
42438689Sborman 	int new_state_ok = 0;
4256000Sroot 
42638689Sborman 	if (will_wont_resp[option]) {
42738689Sborman 	    --will_wont_resp[option];
42838689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
42938689Sborman 		--will_wont_resp[option];
43038689Sborman 	}
43137226Sminshall 
43238689Sborman 	if (will_wont_resp[option] == 0) {
43338689Sborman 	  if (my_want_state_is_wont(option)) {
4346000Sroot 
43538689Sborman 	    switch (option) {
43638689Sborman 
43738689Sborman 	    case TELOPT_TM:
43838689Sborman 		/*
43938689Sborman 		 * Special case for TM.  We send a WILL, but pretend
44038689Sborman 		 * we sent WONT.
44138689Sborman 		 */
44238689Sborman 		send_will(option, 0);
44338689Sborman 		set_my_want_state_wont(TELOPT_TM);
44438689Sborman 		set_my_state_wont(TELOPT_TM);
44538689Sborman 		return;
44638689Sborman 
44743319Skfall #ifdef	KERBEROS
44843319Skfall 	    case TELOPT_AUTHENTICATION:
44943319Skfall 		if (kerberized)
45043319Skfall 			new_state_ok = 1;
45143319Skfall 		break;
45243319Skfall #endif
45332377Sminshall #	if defined(TN3270)
45438689Sborman 	    case TELOPT_EOR:		/* end of record */
45538908Sborman #	endif	/* defined(TN3270) */
45638689Sborman 	    case TELOPT_BINARY:		/* binary mode */
45738689Sborman 	    case TELOPT_NAWS:		/* window size */
45838689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
45938689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
46038689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
46138689Sborman 	    case TELOPT_SGA:		/* no big deal */
46244360Sborman 	    case TELOPT_ENVIRON:	/* environment variable option */
46338689Sborman 		new_state_ok = 1;
4646000Sroot 		break;
4656000Sroot 
46644360Sborman 	    case TELOPT_XDISPLOC:	/* X Display location */
46744360Sborman 		if (env_getvalue("DISPLAY"))
46844360Sborman 		    new_state_ok = 1;
46944360Sborman 		break;
47044360Sborman 
47138689Sborman 	    case TELOPT_LINEMODE:
47238689Sborman #ifdef	KLUDGELINEMODE
47338689Sborman 		kludgelinemode = 0;
47444360Sborman 		send_do(TELOPT_SGA, 1);
47538689Sborman #endif
47638689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
47738689Sborman 		send_will(option, 0);
47838689Sborman 		set_my_state_will(TELOPT_LINEMODE);
47938689Sborman 		slc_init();
48038689Sborman 		return;
48138689Sborman 
48238689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
48338689Sborman 	    default:
4846000Sroot 		break;
48538689Sborman 	    }
48638689Sborman 
48738689Sborman 	    if (new_state_ok) {
48838689Sborman 		set_my_want_state_will(option);
48938689Sborman 		send_will(option, 0);
490*45232Sborman 		setconnmode(0);			/* Set new tty mode */
49138689Sborman 	    } else {
49238689Sborman 		will_wont_resp[option]++;
49338689Sborman 		send_wont(option, 0);
49438689Sborman 	    }
49538689Sborman 	  } else {
49638689Sborman 	    /*
49738689Sborman 	     * Handle options that need more things done after the
49838689Sborman 	     * other side has acknowledged the option.
49938689Sborman 	     */
50038689Sborman 	    switch (option) {
50138689Sborman 	    case TELOPT_LINEMODE:
50238689Sborman #ifdef	KLUDGELINEMODE
50338689Sborman 		kludgelinemode = 0;
50444360Sborman 		send_do(TELOPT_SGA, 1);
50538689Sborman #endif
50638689Sborman 		set_my_state_will(option);
50738689Sborman 		slc_init();
50844360Sborman 		send_do(TELOPT_SGA, 0);
50938689Sborman 		return;
51038689Sborman 	    }
51138689Sborman 	  }
5126000Sroot 	}
51338689Sborman 	set_my_state_will(option);
5146000Sroot }
51527676Sminshall 
51638689Sborman static void
51738689Sborman dontoption(option)
51838689Sborman 	int option;
51938689Sborman {
52038689Sborman 
52138689Sborman 	if (will_wont_resp[option]) {
52238689Sborman 	    --will_wont_resp[option];
52338689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
52438689Sborman 		--will_wont_resp[option];
52538689Sborman 	}
52638689Sborman 
52738689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
52838811Sborman 	    switch (option) {
52938811Sborman 	    case TELOPT_LINEMODE:
53038811Sborman 		linemode = 0;	/* put us back to the default state */
53138811Sborman 		break;
53238811Sborman 	    }
53338689Sborman 	    /* we always accept a DONT */
53438689Sborman 	    set_my_want_state_wont(option);
53544360Sborman 	    if (my_state_is_will(option))
53644360Sborman 		send_wont(option, 0);
53739529Sborman 	    setconnmode(0);			/* Set new tty mode */
53838689Sborman 	}
53938689Sborman 	set_my_state_wont(option);
54038689Sborman }
54138689Sborman 
54227676Sminshall /*
54338908Sborman  * Given a buffer returned by tgetent(), this routine will turn
54438908Sborman  * the pipe seperated list of names in the buffer into an array
54538908Sborman  * of pointers to null terminated names.  We toss out any bad,
54638908Sborman  * duplicate, or verbose names (names with spaces).
54738908Sborman  */
54838908Sborman 
54938908Sborman static char *unknown[] = { "UNKNOWN", 0 };
55038908Sborman 
55138908Sborman char **
55238908Sborman mklist(buf, name)
55338908Sborman char *buf, *name;
55438908Sborman {
55538908Sborman 	register int n;
55638908Sborman 	register char c, *cp, **argvp, *cp2, **argv;
55738908Sborman 	char *malloc();
55838908Sborman 
55938908Sborman 	if (name) {
56038908Sborman 		if (strlen(name) > 40)
56138908Sborman 			name = 0;
56238908Sborman 		else {
56338908Sborman 			unknown[0] = name;
56438908Sborman 			upcase(name);
56538908Sborman 		}
56638908Sborman 	}
56738908Sborman 	/*
56838908Sborman 	 * Count up the number of names.
56938908Sborman 	 */
57038908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
57138908Sborman 		if (*cp == '|')
57238908Sborman 			n++;
57338908Sborman 	}
57438908Sborman 	/*
57538908Sborman 	 * Allocate an array to put the name pointers into
57638908Sborman 	 */
57738908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
57838908Sborman 	if (argv == 0)
57938908Sborman 		return(unknown);
58038908Sborman 
58138908Sborman 	/*
58238908Sborman 	 * Fill up the array of pointers to names.
58338908Sborman 	 */
58438908Sborman 	*argv = 0;
58538908Sborman 	argvp = argv+1;
58638908Sborman 	n = 0;
58738908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
58838908Sborman 		if (c == '|' || c == ':') {
58938908Sborman 			*cp++ = '\0';
59038908Sborman 			/*
59138908Sborman 			 * Skip entries that have spaces or are over 40
59238908Sborman 			 * characters long.  If this is our environment
59338908Sborman 			 * name, then put it up front.  Otherwise, as
59438908Sborman 			 * long as this is not a duplicate name (case
59538908Sborman 			 * insensitive) add it to the list.
59638908Sborman 			 */
59738908Sborman 			if (n || (cp - cp2 > 41))
59838908Sborman 				;
59938908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
60038908Sborman 				*argv = cp2;
60138908Sborman 			else if (is_unique(cp2, argv+1, argvp))
60238908Sborman 				*argvp++ = cp2;
60338908Sborman 			if (c == ':')
60438908Sborman 				break;
60538908Sborman 			/*
60638908Sborman 			 * Skip multiple delimiters. Reset cp2 to
60738908Sborman 			 * the beginning of the next name. Reset n,
60838908Sborman 			 * the flag for names with spaces.
60938908Sborman 			 */
61038908Sborman 			while ((c = *cp) == '|')
61138908Sborman 				cp++;
61238908Sborman 			cp2 = cp;
61338908Sborman 			n = 0;
61438908Sborman 		}
61538908Sborman 		/*
61638908Sborman 		 * Skip entries with spaces or non-ascii values.
61738908Sborman 		 * Convert lower case letters to upper case.
61838908Sborman 		 */
61938908Sborman 		if ((c == ' ') || !isascii(c))
62038908Sborman 			n = 1;
62138908Sborman 		else if (islower(c))
62238908Sborman 			*cp = toupper(c);
62338908Sborman 	}
62438908Sborman 
62538908Sborman 	/*
62638908Sborman 	 * Check for an old V6 2 character name.  If the second
62738908Sborman 	 * name points to the beginning of the buffer, and is
62838908Sborman 	 * only 2 characters long, move it to the end of the array.
62938908Sborman 	 */
63038908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
63138908Sborman 		*argvp++ = buf;
63238908Sborman 		cp = *argv++;
63338908Sborman 		*argv = cp;
63438908Sborman 	}
63538908Sborman 
63638908Sborman 	/*
63738908Sborman 	 * Duplicate last name, for TTYPE option, and null
63838908Sborman 	 * terminate the array.  If we didn't find a match on
63938908Sborman 	 * our terminal name, put that name at the beginning.
64038908Sborman 	 */
64138908Sborman 	cp = *(argvp-1);
64238908Sborman 	*argvp++ = cp;
64338908Sborman 	*argvp = 0;
64438908Sborman 
64538908Sborman 	if (*argv == 0) {
64638908Sborman 		if (name)
64738908Sborman 			*argv = name;
64838908Sborman 		else
64938908Sborman 			argv++;
65038908Sborman 	}
65138908Sborman 	if (*argv)
65238908Sborman 		return(argv);
65338908Sborman 	else
65438908Sborman 		return(unknown);
65538908Sborman }
65638908Sborman 
65738908Sborman is_unique(name, as, ae)
65838908Sborman register char *name, **as, **ae;
65938908Sborman {
66038908Sborman 	register char **ap;
66138908Sborman 	register int n;
66238908Sborman 
66338908Sborman 	n = strlen(name) + 1;
66438908Sborman 	for (ap = as; ap < ae; ap++)
66538908Sborman 		if (strncasecmp(*ap, name, n) == 0)
66638908Sborman 			return(0);
66738908Sborman 	return (1);
66838908Sborman }
66938908Sborman 
67038908Sborman #ifdef	TERMCAP
67139529Sborman char termbuf[1024];
67244360Sborman /*ARGSUSED*/
67338908Sborman setupterm(tname, fd, errp)
67438908Sborman char *tname;
67538908Sborman int fd, *errp;
67638908Sborman {
67739529Sborman 	if (tgetent(termbuf, tname) == 1) {
67839529Sborman 		termbuf[1023] = '\0';
67938908Sborman 		if (errp)
68038908Sborman 			*errp = 1;
68138908Sborman 		return(0);
68238908Sborman 	}
68338908Sborman 	if (errp)
68438908Sborman 		*errp = 0;
68538908Sborman 	return(-1);
68638908Sborman }
68739529Sborman #else
68839529Sborman #define	termbuf	ttytype
68939529Sborman extern char ttytype[];
69038908Sborman #endif
69138908Sborman 
69238908Sborman char *
69338908Sborman gettermname()
69438908Sborman {
69538908Sborman 	char *tname;
69638908Sborman 	static int first = 1;
69738908Sborman 	static char **tnamep;
69838908Sborman 	static char **next;
69938908Sborman 	int err;
70038908Sborman 
70138908Sborman 	if (first) {
70238908Sborman 		first = 0;
70344360Sborman 		if ((tname = env_getvalue("TERM")) &&
70438908Sborman 				(setupterm(tname, 1, &err) == 0)) {
70539529Sborman 			tnamep = mklist(termbuf, tname);
70638908Sborman 		} else {
70738908Sborman 			if (tname && (strlen(tname) <= 40)) {
70838908Sborman 				unknown[0] = tname;
70938908Sborman 				upcase(tname);
71038908Sborman 			}
71138908Sborman 			tnamep = unknown;
71238908Sborman 		}
71338908Sborman 		next = tnamep;
71438908Sborman 	}
71538908Sborman 	if (*next == 0)
71638908Sborman 		next = tnamep;
71738908Sborman 	return(*next++);
71838908Sborman }
71938908Sborman /*
72027676Sminshall  * suboption()
72127676Sminshall  *
72227676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
72327676Sminshall  * side.
72427676Sminshall  *
72527676Sminshall  *	Currently we recognize:
72627676Sminshall  *
72727676Sminshall  *		Terminal type, send request.
72837219Sminshall  *		Terminal speed (send request).
72937219Sminshall  *		Local flow control (is request).
73038689Sborman  *		Linemode
73127676Sminshall  */
73227676Sminshall 
73332377Sminshall static void
73427676Sminshall suboption()
73527676Sminshall {
73638689Sborman     printsub('<', subbuffer, subend-subbuffer+2);
73727676Sminshall     switch (subbuffer[0]&0xff) {
73827676Sminshall     case TELOPT_TTYPE:
73938689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
74038689Sborman 	    return;
74127676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
74227676Sminshall 	    ;
74327676Sminshall 	} else {
74427676Sminshall 	    char *name;
74538908Sborman 	    char temp[50];
74627676Sminshall 	    int len;
74727676Sminshall 
74832377Sminshall #if	defined(TN3270)
74932531Sminshall 	    if (tn3270_ttype()) {
75032377Sminshall 		return;
75132377Sminshall 	    }
75232377Sminshall #endif	/* defined(TN3270) */
75338908Sborman 	    name = gettermname();
75438908Sborman 	    len = strlen(name) + 4 + 2;
75538908Sborman 	    if (len < NETROOM()) {
75638908Sborman 		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
75738908Sborman 				TELQUAL_IS, name, IAC, SE);
75838689Sborman 		ring_supply_data(&netoring, temp, len);
75938908Sborman 		printsub('>', &temp[2], len-2);
76032377Sminshall 	    } else {
76137226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
76232377Sminshall 		/*NOTREACHED*/
76327676Sminshall 	    }
76427676Sminshall 	}
76537219Sminshall 	break;
76637219Sminshall     case TELOPT_TSPEED:
76738689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
76838689Sborman 	    return;
76937219Sminshall 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
770*45232Sborman 	    long ospeed, ispeed;
77138689Sborman 	    char temp[50];
77237219Sminshall 	    int len;
77327676Sminshall 
77437219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
77537219Sminshall 
77638689Sborman 	    sprintf(temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
77738689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
77838689Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
77937219Sminshall 
78038689Sborman 	    if (len < NETROOM()) {
78138689Sborman 		ring_supply_data(&netoring, temp, len);
78238689Sborman 		printsub('>', temp+2, len - 2);
78337219Sminshall 	    }
78444360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
78537219Sminshall 	}
78637219Sminshall 	break;
78737219Sminshall     case TELOPT_LFLOW:
78838689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
78938689Sborman 	    return;
79037219Sminshall 	if ((subbuffer[1]&0xff) == 1) {
79137219Sminshall 	    localflow = 1;
79237219Sminshall 	} else if ((subbuffer[1]&0xff) == 0) {
79337219Sminshall 	    localflow = 0;
79437219Sminshall 	}
79537219Sminshall 	setcommandmode();
79638689Sborman 	setconnmode(0);
79737219Sminshall 	break;
79838689Sborman 
79938689Sborman     case TELOPT_LINEMODE:
80038689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
80138689Sborman 	    return;
80238689Sborman 	switch (subbuffer[1]&0xff) {
80338689Sborman 	case WILL:
80438689Sborman 	    lm_will(&subbuffer[2], subend - &subbuffer[2]);
80538689Sborman 	    break;
80638689Sborman 	case WONT:
80738689Sborman 	    lm_wont(&subbuffer[2], subend - &subbuffer[2]);
80838689Sborman 	    break;
80938689Sborman 	case DO:
81038689Sborman 	    lm_do(&subbuffer[2], subend - &subbuffer[2]);
81138689Sborman 	    break;
81238689Sborman 	case DONT:
81338689Sborman 	    lm_dont(&subbuffer[2], subend - &subbuffer[2]);
81438689Sborman 	    break;
81538689Sborman 	case LM_SLC:
81638689Sborman 	    slc(&subbuffer[2], subend - &subbuffer[2]);
81738689Sborman 	    break;
81838689Sborman 	case LM_MODE:
81938689Sborman 	    lm_mode(&subbuffer[2], subend - &subbuffer[2], 0);
82038689Sborman 	    break;
82138689Sborman 	default:
82244360Sborman 	    break;
82344360Sborman 	}
82444360Sborman 	break;
82544360Sborman 
82644360Sborman     case TELOPT_ENVIRON:
82744360Sborman 	switch(subbuffer[1]&0xff) {
82844360Sborman 	case TELQUAL_IS:
82944360Sborman 	case TELQUAL_INFO:
83044360Sborman 	    if (my_want_state_is_dont(TELOPT_ENVIRON))
83144360Sborman 		return;
83244360Sborman 	    break;
83344360Sborman 	case TELQUAL_SEND:
83444360Sborman 	    if (my_want_state_is_wont(TELOPT_ENVIRON)) {
83544360Sborman 		return;
83644360Sborman 	    }
83744360Sborman 	    break;
83844360Sborman 	default:
83944360Sborman 	    return;
84044360Sborman 	}
84144360Sborman 	env_opt(&subbuffer[1], subend - &subbuffer[1]);
84244360Sborman 	break;
84344360Sborman 
84444360Sborman     case TELOPT_XDISPLOC:
84544360Sborman 	if (my_want_state_is_wont(TELOPT_XDISPLOC))
84644360Sborman 	    return;
84744360Sborman 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
84844360Sborman 	    char temp[50], *dp;
84944360Sborman 	    int len;
85044360Sborman 
85144360Sborman 	    if ((dp = env_getvalue("DISPLAY")) == NULL) {
85244360Sborman 		/*
85344360Sborman 		 * Something happened, we no longer have a DISPLAY
85444360Sborman 		 * variable.  So, turn off the option.
85544360Sborman 		 */
85644360Sborman 		send_wont(TELOPT_XDISPLOC, 1);
85738689Sborman 		break;
85844360Sborman 	    }
85944360Sborman 	    sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
86044360Sborman 		    TELQUAL_IS, dp, IAC, SE);
86144360Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
86244360Sborman 
86344360Sborman 	    if (len < NETROOM()) {
86444360Sborman 		ring_supply_data(&netoring, temp, len);
86544360Sborman 		printsub('>', temp+2, len - 2);
86644360Sborman 	    }
86744360Sborman /*@*/	    else printf("lm_will: not enough room in buffer\n");
86838689Sborman 	}
86944360Sborman 	break;
87043319Skfall 
87143319Skfall #ifdef	KERBEROS
87243319Skfall     case TELOPT_AUTHENTICATION:
87343319Skfall 	if ((subbuffer[1] & 0xff) == TELQUAL_SEND) {
87443319Skfall 		register char *cp = &subbuffer[2];
87543320Skfall 		char tmp[256];
87643319Skfall 		int dokrb4 = 0, unknowntypes = 0, noresponse = 1;
87743319Skfall 
87843319Skfall 		while (cp < subend) {
879*45232Sborman 			switch (*cp&0xff) {
88043319Skfall 			case TELQUAL_AUTHTYPE_KERBEROS_V4:
88143319Skfall 				dokrb4 = 1;
88243319Skfall 				break;
88343319Skfall 			default:
88443319Skfall 				unknowntypes++;
88543319Skfall 			}
88643319Skfall 			cp++;
88743319Skfall 		}
88843319Skfall 
88943319Skfall 		if (noresponse && dokrb4) {
89043319Skfall 			register unsigned char *ucp = (unsigned char *)cp;
89143319Skfall 			char *krb_realm;
89243319Skfall 			char hst_inst[INST_SZ];
89343319Skfall 			KTEXT_ST authent_st;
89443319Skfall 			int space = 0;
89543319Skfall 			int retval;
89643319Skfall 			extern char *krb_realmofhost(), *krb_get_phost();
89743319Skfall 
89843319Skfall 			fprintf(stderr,
89943319Skfall 				"[Trying Kerberos V4 authentication]\n");
90043319Skfall 
90143319Skfall 			krb_realm = krb_get_phost(hostname);
90243319Skfall 			bzero(hst_inst, sizeof(hst_inst));
90343319Skfall 			if (krb_realm)
90443319Skfall 			    strncpy(hst_inst, krb_realm, sizeof(hst_inst));
90543319Skfall 			hst_inst[sizeof(hst_inst)-1] = '\0';
90643319Skfall 			if (!(krb_realm = krb_realmofhost(hst_inst))) {
90743319Skfall 			    fprintf(stderr, "no realm for %s\n", hostname);
90843319Skfall 			    goto cantsend4;
90943319Skfall 			}
91043319Skfall 			if (retval = krb_mk_req(&authent_st, "rcmd", hst_inst,
91143319Skfall 			    krb_realm, 0L)) {
91243319Skfall 				fprintf(stderr, "mk_req failed: %s\n",
91343319Skfall 				    krb_err_txt[retval]);
91443319Skfall 				goto cantsend4;
91543319Skfall 			}
91643319Skfall 			space = authent_st.length;
91743319Skfall 			for (ucp = authent_st.dat; ucp < authent_st.dat +
91843319Skfall 			     authent_st.length; ucp++) {
91943319Skfall 				if (*ucp == IAC)
92043319Skfall 					space++;
92143319Skfall 			}
92243320Skfall 			if (NETROOM() < 6 + 1 + 2 +
92343319Skfall 			    space + 2) {
92443319Skfall 				fprintf(stderr,
92543319Skfall 				   "no room to send V4 ticket/authenticator\n");
92643319Skfall cantsend4:
92743319Skfall 			    if (7 < NETROOM()) {
92843319Skfall 				printring(&netoring, "%c%c%c%c%c%c%c", IAC, SB,
92943319Skfall 				  TELOPT_AUTHENTICATION,
93043319Skfall 				  TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
93143319Skfall 				sprintf(tmp, "%c%c%c%c%c", TELOPT_AUTHENTICATION,
93243319Skfall 					TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
93343319Skfall 				printsub(">", tmp, 4+2-2-2);
93443319Skfall 			    } else
93543319Skfall 				exit(1);
93643319Skfall 			} else {
93743319Skfall #ifdef notdef
93843320Skfall 		    printring(&netoring, "%c%c%c%c%c%c", IAC, SB,
93943319Skfall 			      TELOPT_AUTHENTICATION,
94043319Skfall 			      TELQUAL_IS, TELQUAL_AUTHTYPE_KERBEROS,
94143320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4);
94243320Skfall 		    sprintf(tmp, "%c%c%c%c%c%c", TELOPT_AUTHENTICATION,
94343319Skfall 			    TELQUAL_IS, TELQUAL_AUTHTYPE_KERBEROS,
94443320Skfall 			    TELQUAL_AUTHTYPE_KERBEROS_V4, IAC, SE);
94543319Skfall #else
94643320Skfall 			    printring(&netoring, "%c%c%c%c%c", IAC, SB,
94743319Skfall 			      TELOPT_AUTHENTICATION,
94843319Skfall 			      TELQUAL_IS,
94943320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4);
95043320Skfall 			    sprintf(tmp, "%c%c%c%c%c", TELOPT_AUTHENTICATION,
95143319Skfall 			      TELQUAL_IS,
95243320Skfall 			      TELQUAL_AUTHTYPE_KERBEROS_V4, IAC, SE);
95343319Skfall #endif
95443320Skfall 			    printsub(">", tmp, 4+2-2-2);
95543319Skfall 			    ring_supply_bindata(&netoring,
95643319Skfall 			        (char *)authent_st.dat, authent_st.length, IAC);
95743319Skfall 			    printring(&netoring, "%c%c", IAC, SE);
95843319Skfall 			}
95943319Skfall 			noresponse = 0;
96043319Skfall 	    	}
96143319Skfall 	    	if (noresponse) {
96243319Skfall 			if (NETROOM() < 7) {
96343319Skfall 			    ExitString("not enough room to reject unhandled authtype\n", 1);
96443319Skfall 			} else {
96543319Skfall 			    fprintf(stderr,"[Sending empty auth info in response to request for %d unknown type(s):\n\t", unknowntypes);
96643319Skfall #ifdef notdef
96743319Skfall 			    cp = &subbuffer[3];
96843319Skfall #else
96943319Skfall 			    cp = &subbuffer[2];
97043319Skfall #endif
97143319Skfall 			    while (cp < subend) {
972*45232Sborman 				switch (*cp&0xff) {
97343319Skfall 				case TELQUAL_AUTHTYPE_KERBEROS_V4:
97443319Skfall 			    		break;
97543319Skfall 				default:
97643319Skfall 			    		fprintf(stderr, "%d,", *cp);
97743319Skfall 			    		break;
97843319Skfall 				}
97943319Skfall 				cp++;
98043319Skfall 		    	    }
98143319Skfall 		    	    fputs("]\n", stderr);
98243319Skfall 		    	    printring(&netoring, "%c%c%c%c%c%c%c", IAC, SB,
98343319Skfall 			      TELOPT_AUTHENTICATION,
98443319Skfall 			      TELQUAL_IS, TELQUAL_AUTHTYPE_NONE, IAC, SE);
98543319Skfall 			}
98643319Skfall 	    	}
98743319Skfall 	}
98838689Sborman 	break;
98943319Skfall #endif /* KERBEROS */
99044360Sborman 
99127676Sminshall     default:
99227676Sminshall 	break;
99327676Sminshall     }
99427676Sminshall }
99538689Sborman 
99638689Sborman static char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
99738689Sborman 
99838689Sborman lm_will(cmd, len)
999*45232Sborman unsigned char *cmd;
100038689Sborman {
100144360Sborman     if (len < 1) {
100244360Sborman /*@*/	printf("lm_will: no command!!!\n");	/* Should not happen... */
100344360Sborman 	return;
100444360Sborman     }
100538689Sborman     switch(cmd[0]) {
100638689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
100738689Sborman     default:
100838689Sborman 	str_lm[3] = DONT;
100938689Sborman 	str_lm[4] = cmd[0];
101038689Sborman 	if (NETROOM() > sizeof(str_lm)) {
101138689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
101238689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
101338689Sborman 	}
101438689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
101538689Sborman 	break;
101638689Sborman     }
101738689Sborman }
101838689Sborman 
101938689Sborman lm_wont(cmd, len)
1020*45232Sborman unsigned char *cmd;
102138689Sborman {
102244360Sborman     if (len < 1) {
102344360Sborman /*@*/	printf("lm_wont: no command!!!\n");	/* Should not happen... */
102444360Sborman 	return;
102544360Sborman     }
102638689Sborman     switch(cmd[0]) {
102738689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
102838689Sborman     default:
102938689Sborman 	/* We are always DONT, so don't respond */
103038689Sborman 	return;
103138689Sborman     }
103238689Sborman }
103338689Sborman 
103438689Sborman lm_do(cmd, len)
1035*45232Sborman unsigned char *cmd;
103638689Sborman {
103744360Sborman     if (len < 1) {
103844360Sborman /*@*/	printf("lm_do: no command!!!\n");	/* Should not happen... */
103944360Sborman 	return;
104044360Sborman     }
104138689Sborman     switch(cmd[0]) {
104238689Sborman     case LM_FORWARDMASK:
104338689Sborman     default:
104438689Sborman 	str_lm[3] = WONT;
104538689Sborman 	str_lm[4] = cmd[0];
104638689Sborman 	if (NETROOM() > sizeof(str_lm)) {
104738689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
104838689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
104938689Sborman 	}
105038689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
105138689Sborman 	break;
105238689Sborman     }
105338689Sborman }
105438689Sborman 
105538689Sborman lm_dont(cmd, len)
1056*45232Sborman unsigned char *cmd;
105738689Sborman {
105844360Sborman     if (len < 1) {
105944360Sborman /*@*/	printf("lm_dont: no command!!!\n");	/* Should not happen... */
106044360Sborman 	return;
106144360Sborman     }
106238689Sborman     switch(cmd[0]) {
106338689Sborman     case LM_FORWARDMASK:
106438689Sborman     default:
106538689Sborman 	/* we are always WONT, so don't respond */
106638689Sborman 	break;
106738689Sborman     }
106838689Sborman }
106938689Sborman 
107038689Sborman static char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE };
107138689Sborman 
107238689Sborman lm_mode(cmd, len, init)
107338689Sborman char *cmd;
107438689Sborman int len, init;
107538689Sborman {
107638689Sborman 	if (len != 1)
107738689Sborman 		return;
107844360Sborman 	if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
107938689Sborman 		return;
108038689Sborman 	if (*cmd&MODE_ACK)
108138689Sborman 		return;
108244360Sborman 	linemode = *cmd&(MODE_MASK&~MODE_ACK);
108338689Sborman 	str_lm_mode[4] = linemode;
108438689Sborman 	if (!init)
108538689Sborman 	    str_lm_mode[4] |= MODE_ACK;
108638689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
108738689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
108838689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
108938689Sborman 	}
109038689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
109138689Sborman 	setconnmode(0);	/* set changed mode */
109238689Sborman }
109338689Sborman 
109432377Sminshall 
109527088Sminshall 
109638689Sborman /*
109738689Sborman  * slc()
109838689Sborman  * Handle special character suboption of LINEMODE.
109938689Sborman  */
110038689Sborman 
110138689Sborman struct spc {
110240245Sborman 	cc_t val;
110340245Sborman 	cc_t *valp;
110438689Sborman 	char flags;	/* Current flags & level */
110538689Sborman 	char mylevel;	/* Maximum level & flags */
110638689Sborman } spc_data[NSLC+1];
110738689Sborman 
110838689Sborman #define SLC_IMPORT	0
110938689Sborman #define	SLC_EXPORT	1
111038689Sborman #define SLC_RVALUE	2
111138689Sborman static int slc_mode = SLC_EXPORT;
111238689Sborman 
111338689Sborman slc_init()
111438689Sborman {
111538689Sborman 	register struct spc *spcp;
111640245Sborman 	extern cc_t *tcval();
111738689Sborman 
111838689Sborman 	localchars = 1;
111938689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
112038689Sborman 		spcp->val = 0;
112138689Sborman 		spcp->valp = 0;
112238689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
112338689Sborman 	}
112438689Sborman 
112538689Sborman #define	initfunc(func, flags) { \
112638689Sborman 					spcp = &spc_data[func]; \
112738689Sborman 					if (spcp->valp = tcval(func)) { \
112838689Sborman 					    spcp->val = *spcp->valp; \
112938689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
113038689Sborman 					} else { \
113138689Sborman 					    spcp->val = 0; \
113238689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
113338689Sborman 					} \
113438689Sborman 				    }
113538689Sborman 
113638689Sborman 	initfunc(SLC_SYNCH, 0);
113738689Sborman 	/* No BRK */
113838689Sborman 	initfunc(SLC_AO, 0);
113938689Sborman 	initfunc(SLC_AYT, 0);
114038689Sborman 	/* No EOR */
114138689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
114238689Sborman 	initfunc(SLC_EOF, 0);
114339529Sborman #ifndef	SYSV_TERMIO
114438689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
114538689Sborman #endif
114638689Sborman 	initfunc(SLC_EC, 0);
114738689Sborman 	initfunc(SLC_EL, 0);
114839529Sborman #ifndef	SYSV_TERMIO
114938689Sborman 	initfunc(SLC_EW, 0);
115038689Sborman 	initfunc(SLC_RP, 0);
115138689Sborman 	initfunc(SLC_LNEXT, 0);
115238689Sborman #endif
115338689Sborman 	initfunc(SLC_XON, 0);
115438689Sborman 	initfunc(SLC_XOFF, 0);
115539529Sborman #ifdef	SYSV_TERMIO
115638689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
115738689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
115838689Sborman #endif
115944360Sborman 	initfunc(SLC_FORW1, 0);
116044360Sborman #ifdef	USE_TERMIO
116144360Sborman 	initfunc(SLC_FORW2, 0);
116238689Sborman 	/* No FORW2 */
116344360Sborman #endif
116438689Sborman 
116538689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
116638689Sborman #undef	initfunc
116738689Sborman 
116838689Sborman 	if (slc_mode == SLC_EXPORT)
116938689Sborman 		slc_export();
117038689Sborman 	else
117138689Sborman 		slc_import(1);
117238689Sborman 
117338689Sborman }
117438689Sborman 
117538689Sborman slcstate()
117638689Sborman {
117738689Sborman     printf("Special characters are %s values\n",
117838689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
117938689Sborman 		slc_mode == SLC_EXPORT ? "local" :
118038689Sborman 					 "remote");
118138689Sborman }
118238689Sborman 
118338689Sborman slc_mode_export()
118438689Sborman {
118538689Sborman     slc_mode = SLC_EXPORT;
118638689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
118738689Sborman 	slc_export();
118838689Sborman }
118938689Sborman 
119038689Sborman slc_mode_import(def)
119138689Sborman {
119238689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
119338689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
119438689Sborman 	slc_import(def);
119538689Sborman }
119638689Sborman 
119738689Sborman char slc_import_val[] = {
119838689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
119938689Sborman };
120038689Sborman char slc_import_def[] = {
120138689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
120238689Sborman };
120338689Sborman 
120438689Sborman slc_import(def)
120538689Sborman int def;
120638689Sborman {
120738689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
120838689Sborman 	if (def) {
120938689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
121038689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
121138689Sborman 	} else {
121238689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
121338689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
121438689Sborman 	}
121538689Sborman     }
121638689Sborman /*@*/ else printf("slc_import: not enough room\n");
121738689Sborman }
121838689Sborman 
121938689Sborman slc_export()
122038689Sborman {
122138689Sborman     register struct spc *spcp;
122238689Sborman 
122338689Sborman     TerminalDefaultChars();
122438689Sborman 
122538689Sborman     slc_start_reply();
122638689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
122738689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
1228*45232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
1229*45232Sborman 		spcp->flags = SLC_NOSUPPORT;
1230*45232Sborman 	    else
1231*45232Sborman 		spcp->flags = spcp->mylevel;
123238689Sborman 	    if (spcp->valp)
123338689Sborman 		spcp->val = *spcp->valp;
1234*45232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
123538689Sborman 	}
123638689Sborman     }
123738689Sborman     slc_end_reply();
123838689Sborman     if (slc_update())
123938689Sborman 	setconnmode(1);	/* set the  new character values */
124038689Sborman }
124138689Sborman 
124238689Sborman slc(cp, len)
124338689Sborman register char *cp;
124438689Sborman int len;
124538689Sborman {
124638689Sborman 	register struct spc *spcp;
124738689Sborman 	register int func,level;
124838689Sborman 
124938689Sborman 	slc_start_reply();
125038689Sborman 
125138689Sborman 	for (; len >= 3; len -=3, cp +=3) {
125238689Sborman 
125338689Sborman 		func = cp[SLC_FUNC];
125438689Sborman 
125538689Sborman 		if (func == 0) {
125638689Sborman 			/*
125738689Sborman 			 * Client side: always ignore 0 function.
125838689Sborman 			 */
125938689Sborman 			continue;
126038689Sborman 		}
126138689Sborman 		if (func > NSLC) {
1262*45232Sborman 			if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
126338689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
126438689Sborman 			continue;
126538689Sborman 		}
126638689Sborman 
126738689Sborman 		spcp = &spc_data[func];
126838689Sborman 
126938689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
127038689Sborman 
127140245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
127238689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
127338689Sborman 			continue;
127438689Sborman 		}
127538689Sborman 
127638689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
127738689Sborman 			/*
127838689Sborman 			 * This is an error condition, the SLC_ACK
127938689Sborman 			 * bit should never be set for the SLC_DEFAULT
128038689Sborman 			 * level.  Our best guess to recover is to
128138689Sborman 			 * ignore the SLC_ACK bit.
128238689Sborman 			 */
128338689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
128438689Sborman 		}
128538689Sborman 
128638689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
128740245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
128838689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
128938689Sborman 			continue;
129038689Sborman 		}
129138689Sborman 
129238689Sborman 		level &= ~SLC_ACK;
129338689Sborman 
129438689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
129538689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
129640245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
129738689Sborman 		}
129838689Sborman 		if (level == SLC_DEFAULT) {
129938689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
130038689Sborman 				spcp->flags = spcp->mylevel;
130138689Sborman 			else
130238689Sborman 				spcp->flags = SLC_NOSUPPORT;
130338689Sborman 		}
130438689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
130538689Sborman 	}
130638689Sborman 	slc_end_reply();
130738689Sborman 	if (slc_update())
130838689Sborman 		setconnmode(1);	/* set the  new character values */
130938689Sborman }
131038689Sborman 
131138689Sborman slc_check()
131238689Sborman {
131338689Sborman     register struct spc *spcp;
131438689Sborman 
131538689Sborman     slc_start_reply();
131638689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
131738689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
131838689Sborman 	    spcp->val = *spcp->valp;
1319*45232Sborman 	    if (spcp->val == (cc_t)(_POSIX_VDISABLE))
1320*45232Sborman 		spcp->flags = SLC_NOSUPPORT;
1321*45232Sborman 	    else
1322*45232Sborman 		spcp->flags = spcp->mylevel;
1323*45232Sborman 	    slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
132438689Sborman 	}
132538689Sborman     }
132638689Sborman     slc_end_reply();
132738689Sborman     setconnmode(1);
132838689Sborman }
132938689Sborman 
133038689Sborman 
133138689Sborman unsigned char slc_reply[128];
133238689Sborman unsigned char *slc_replyp;
133338689Sborman slc_start_reply()
133438689Sborman {
133538689Sborman 	slc_replyp = slc_reply;
133638689Sborman 	*slc_replyp++ = IAC;
133738689Sborman 	*slc_replyp++ = SB;
133838689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
133938689Sborman 	*slc_replyp++ = LM_SLC;
134038689Sborman }
134138689Sborman 
134238689Sborman slc_add_reply(func, flags, value)
134338689Sborman char func;
134438689Sborman char flags;
134540245Sborman cc_t value;
134638689Sborman {
134738689Sborman 	if ((*slc_replyp++ = func) == IAC)
134838689Sborman 		*slc_replyp++ = IAC;
134938689Sborman 	if ((*slc_replyp++ = flags) == IAC)
135038689Sborman 		*slc_replyp++ = IAC;
135140245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
135238689Sborman 		*slc_replyp++ = IAC;
135338689Sborman }
135438689Sborman 
135538689Sborman slc_end_reply()
135638689Sborman {
135738689Sborman     register int len;
135838689Sborman 
135938689Sborman     *slc_replyp++ = IAC;
136038689Sborman     *slc_replyp++ = SE;
136138689Sborman     len = slc_replyp - slc_reply;
136238689Sborman     if (len <= 6)
136338689Sborman 	return;
136438689Sborman     if (NETROOM() > len) {
136538689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
136638689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
136738689Sborman     }
136838689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
136938689Sborman }
137038689Sborman 
137138689Sborman slc_update()
137238689Sborman {
137338689Sborman 	register struct spc *spcp;
137438689Sborman 	int need_update = 0;
137538689Sborman 
137638689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
137738689Sborman 		if (!(spcp->flags&SLC_ACK))
137838689Sborman 			continue;
137938689Sborman 		spcp->flags &= ~SLC_ACK;
138038689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
138138689Sborman 			*spcp->valp = spcp->val;
138238689Sborman 			need_update = 1;
138338689Sborman 		}
138438689Sborman 	}
138538689Sborman 	return(need_update);
138638689Sborman }
138738689Sborman 
138844360Sborman env_opt(buf, len)
138944360Sborman register char *buf;
139044360Sborman register int len;
139144360Sborman {
139244360Sborman 	register char *ep = 0, *epc = 0;
139344360Sborman 	register int i;
139444360Sborman 
1395*45232Sborman 	switch(buf[0]&0xff) {
139644360Sborman 	case TELQUAL_SEND:
139744360Sborman 		env_opt_start();
139844360Sborman 		if (len == 1) {
139944360Sborman 			env_opt_add(NULL);
140044360Sborman 		} else for (i = 1; i < len; i++) {
1401*45232Sborman 			switch (buf[i]&0xff) {
140244360Sborman 			case ENV_VALUE:
140344360Sborman 				if (ep) {
140444360Sborman 					*epc = 0;
140544360Sborman 					env_opt_add(ep);
140644360Sborman 				}
140744360Sborman 				ep = epc = &buf[i+1];
140844360Sborman 				break;
140944360Sborman 			case ENV_ESC:
141044360Sborman 				i++;
141144360Sborman 				/*FALL THROUGH*/
141244360Sborman 			default:
141344360Sborman 				if (epc)
141444360Sborman 					*epc++ = buf[i];
141544360Sborman 				break;
141644360Sborman 			}
141744360Sborman 			if (ep) {
141844360Sborman 				*epc = 0;
141944360Sborman 				env_opt_add(ep);
142044360Sborman 			}
142144360Sborman 		}
142244360Sborman 		env_opt_end(1);
142344360Sborman 		break;
142444360Sborman 
142544360Sborman 	case TELQUAL_IS:
142644360Sborman 	case TELQUAL_INFO:
142744360Sborman 		/* Ignore for now.  We shouldn't get it anyway. */
142844360Sborman 		break;
142944360Sborman 
143044360Sborman 	default:
143144360Sborman 		break;
143244360Sborman 	}
143344360Sborman }
143444360Sborman 
143544360Sborman #define	OPT_REPLY_SIZE	256
143644360Sborman unsigned char *opt_reply;
143744360Sborman unsigned char *opt_replyp;
143844360Sborman unsigned char *opt_replyend;
143944360Sborman 
144044360Sborman env_opt_start()
144144360Sborman {
144244360Sborman 	extern char *realloc();
144344360Sborman 	extern char *malloc();
144444360Sborman 
144544360Sborman 	if (opt_reply)
144644360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
144744360Sborman 	else
144844360Sborman 		opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
144944360Sborman 	if (opt_reply == NULL) {
145044360Sborman /*@*/		printf("env_opt_start: malloc()/realloc() failed!!!\n");
145144360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
145244360Sborman 		return;
145344360Sborman 	}
145444360Sborman 	opt_replyp = opt_reply;
145544360Sborman 	opt_replyend = opt_reply + OPT_REPLY_SIZE;
145644360Sborman 	*opt_replyp++ = IAC;
145744360Sborman 	*opt_replyp++ = SB;
145844360Sborman 	*opt_replyp++ = TELOPT_ENVIRON;
145944360Sborman 	*opt_replyp++ = TELQUAL_IS;
146044360Sborman }
146144360Sborman 
146244360Sborman env_opt_start_info()
146344360Sborman {
146444360Sborman 	env_opt_start();
146544360Sborman 	if (opt_replyp)
146644360Sborman 	    opt_replyp[-1] = TELQUAL_INFO;
146744360Sborman }
146844360Sborman 
146944360Sborman env_opt_add(ep)
147044360Sborman register char *ep;
147144360Sborman {
147244360Sborman 	register char *vp, c;
147344360Sborman 	extern char *realloc();
147444360Sborman 	extern char *env_default();
147544360Sborman 
147644360Sborman 	if (opt_reply == NULL)		/*XXX*/
147744360Sborman 		return;			/*XXX*/
147844360Sborman 
147944360Sborman 	if (ep == NULL || *ep == '\0') {
148044360Sborman 		env_default(1);
148144360Sborman 		while (ep = env_default(0))
148244360Sborman 			env_opt_add(ep);
148344360Sborman 		return;
148444360Sborman 	}
148544360Sborman 	vp = env_getvalue(ep);
148644360Sborman 	if (opt_replyp + (vp?strlen(vp):0) + strlen(ep) + 6 > opt_replyend) {
148744360Sborman 		register int len;
148844360Sborman 		opt_replyend += OPT_REPLY_SIZE;
148944360Sborman 		len = opt_replyend - opt_reply;
149044360Sborman 		opt_reply = (unsigned char *)realloc(opt_reply, len);
149144360Sborman 		if (opt_reply == NULL) {
149244360Sborman /*@*/			printf("env_opt_add: realloc() failed!!!\n");
149344360Sborman 			opt_reply = opt_replyp = opt_replyend = NULL;
149444360Sborman 			return;
149544360Sborman 		}
149644360Sborman 		opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
149744360Sborman 		opt_replyend = opt_reply + len;
149844360Sborman 	}
149944360Sborman 	*opt_replyp++ = ENV_VAR;
150044360Sborman 	for (;;) {
150144360Sborman 		while (c = *ep++) {
1502*45232Sborman 			switch(c&0xff) {
150344360Sborman 			case IAC:
150444360Sborman 				*opt_replyp++ = IAC;
150544360Sborman 				break;
150644360Sborman 			case ENV_VALUE:
150744360Sborman 			case ENV_VAR:
150844360Sborman 			case ENV_ESC:
150944360Sborman 				*opt_replyp++ = ENV_ESC;
151044360Sborman 				break;
151144360Sborman 			}
151244360Sborman 			*opt_replyp++ = c;
151344360Sborman 		}
151444360Sborman 		if (ep = vp) {
151544360Sborman 			*opt_replyp++ = ENV_VALUE;
151644360Sborman 			vp = NULL;
151744360Sborman 		} else
151844360Sborman 			break;
151944360Sborman 	}
152044360Sborman }
152144360Sborman 
152244360Sborman env_opt_end(emptyok)
152344360Sborman register int emptyok;
152444360Sborman {
152544360Sborman 	register int len;
152644360Sborman 
152744360Sborman 	len = opt_replyp - opt_reply + 2;
152844360Sborman 	if (emptyok || len > 6) {
152944360Sborman 		*opt_replyp++ = IAC;
153044360Sborman 		*opt_replyp++ = SE;
153144360Sborman 		if (NETROOM() > len) {
153244360Sborman 			ring_supply_data(&netoring, opt_reply, len);
153344360Sborman 			printsub('>', &opt_reply[2], len - 2);
153444360Sborman 		}
153544360Sborman /*@*/		else printf("slc_end_reply: not enough room\n");
153644360Sborman 	}
153744360Sborman 	if (opt_reply) {
153844360Sborman 		free(opt_reply);
153944360Sborman 		opt_reply = opt_replyp = opt_replyend = NULL;
154044360Sborman 	}
154144360Sborman }
154244360Sborman 
154338689Sborman 
154438689Sborman 
154533804Sminshall int
154632377Sminshall telrcv()
154727110Sminshall {
154832377Sminshall     register int c;
154932385Sminshall     register int scc;
155032385Sminshall     register char *sbp;
155132385Sminshall     int count;
155232385Sminshall     int returnValue = 0;
155327088Sminshall 
155432385Sminshall     scc = 0;
155532385Sminshall     count = 0;
155632385Sminshall     while (TTYROOM() > 2) {
155732385Sminshall 	if (scc == 0) {
155832385Sminshall 	    if (count) {
155932528Sminshall 		ring_consumed(&netiring, count);
156032385Sminshall 		returnValue = 1;
156132385Sminshall 		count = 0;
156232385Sminshall 	    }
156332528Sminshall 	    sbp = netiring.consume;
156432528Sminshall 	    scc = ring_full_consecutive(&netiring);
156532385Sminshall 	    if (scc == 0) {
156632385Sminshall 		/* No more data coming in */
156732385Sminshall 		break;
156832385Sminshall 	    }
156932385Sminshall 	}
157032385Sminshall 
157132385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
157232385Sminshall 
157332377Sminshall 	switch (telrcv_state) {
157427110Sminshall 
157532377Sminshall 	case TS_CR:
157632377Sminshall 	    telrcv_state = TS_DATA;
157735518Sminshall 	    if (c == '\0') {
157835518Sminshall 		break;	/* Ignore \0 after CR */
157939529Sborman 	    }
158039529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
158135518Sminshall 		TTYADD(c);
158235518Sminshall 		break;
158332377Sminshall 	    }
158435518Sminshall 	    /* Else, fall through */
158527088Sminshall 
158632377Sminshall 	case TS_DATA:
158732377Sminshall 	    if (c == IAC) {
158832377Sminshall 		telrcv_state = TS_IAC;
158933804Sminshall 		break;
159032377Sminshall 	    }
159132377Sminshall #	    if defined(TN3270)
159232377Sminshall 	    if (In3270) {
159332377Sminshall 		*Ifrontp++ = c;
159432385Sminshall 		while (scc > 0) {
159532385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
159632377Sminshall 		    if (c == IAC) {
159732377Sminshall 			telrcv_state = TS_IAC;
159834304Sminshall 			break;
159932377Sminshall 		    }
160032377Sminshall 		    *Ifrontp++ = c;
160132377Sminshall 		}
160232377Sminshall 	    } else
160332377Sminshall #	    endif /* defined(TN3270) */
160435518Sminshall 		    /*
160535518Sminshall 		     * The 'crmod' hack (see following) is needed
160635518Sminshall 		     * since we can't * set CRMOD on output only.
160735518Sminshall 		     * Machines like MULTICS like to send \r without
160835518Sminshall 		     * \n; since we must turn off CRMOD to get proper
160935518Sminshall 		     * input, the mapping is done here (sigh).
161035518Sminshall 		     */
161138689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
161235518Sminshall 		if (scc > 0) {
161335518Sminshall 		    c = *sbp&0xff;
161435518Sminshall 		    if (c == 0) {
161535518Sminshall 			sbp++, scc--; count++;
161635518Sminshall 			/* a "true" CR */
161732377Sminshall 			TTYADD('\r');
161838689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
161935518Sminshall 					(c == '\n')) {
162035518Sminshall 			sbp++, scc--; count++;
162132377Sminshall 			TTYADD('\n');
162235518Sminshall 		    } else {
162335518Sminshall 			TTYADD('\r');
162435518Sminshall 			if (crmod) {
162535518Sminshall 				TTYADD('\n');
162632377Sminshall 			}
162732377Sminshall 		    }
162835518Sminshall 		} else {
162935518Sminshall 		    telrcv_state = TS_CR;
163035518Sminshall 		    TTYADD('\r');
163135518Sminshall 		    if (crmod) {
163235518Sminshall 			    TTYADD('\n');
163335518Sminshall 		    }
163432377Sminshall 		}
163532377Sminshall 	    } else {
163632377Sminshall 		TTYADD(c);
163732377Sminshall 	    }
163832377Sminshall 	    continue;
163927088Sminshall 
164032377Sminshall 	case TS_IAC:
164138689Sborman process_iac:
164232377Sminshall 	    switch (c) {
164332377Sminshall 
164432377Sminshall 	    case WILL:
164532377Sminshall 		telrcv_state = TS_WILL;
164632377Sminshall 		continue;
164727261Sminshall 
164832377Sminshall 	    case WONT:
164932377Sminshall 		telrcv_state = TS_WONT;
165032377Sminshall 		continue;
165127261Sminshall 
165232377Sminshall 	    case DO:
165332377Sminshall 		telrcv_state = TS_DO;
165432377Sminshall 		continue;
165527261Sminshall 
165632377Sminshall 	    case DONT:
165732377Sminshall 		telrcv_state = TS_DONT;
165832377Sminshall 		continue;
165927261Sminshall 
166032377Sminshall 	    case DM:
166132377Sminshall 		    /*
166232377Sminshall 		     * We may have missed an urgent notification,
166332377Sminshall 		     * so make sure we flush whatever is in the
166432377Sminshall 		     * buffer currently.
166532377Sminshall 		     */
1666*45232Sborman 		printoption("RCVD", "IAC", DM);
166732377Sminshall 		SYNCHing = 1;
166844360Sborman 		(void) ttyflush(1);
166932554Sminshall 		SYNCHing = stilloob();
167032377Sminshall 		settimer(gotDM);
167132377Sminshall 		break;
167227088Sminshall 
167332377Sminshall 	    case SB:
167432377Sminshall 		SB_CLEAR();
167532377Sminshall 		telrcv_state = TS_SB;
167638689Sborman 		printoption("RCVD", "IAC", SB);
167732377Sminshall 		continue;
167827261Sminshall 
167932377Sminshall #	    if defined(TN3270)
168032377Sminshall 	    case EOR:
168132377Sminshall 		if (In3270) {
168232377Sminshall 		    if (Ibackp == Ifrontp) {
168332377Sminshall 			Ibackp = Ifrontp = Ibuf;
168432377Sminshall 			ISend = 0;	/* should have been! */
168532377Sminshall 		    } else {
168644360Sborman 			Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
168732377Sminshall 			ISend = 1;
168827088Sminshall 		    }
168927088Sminshall 		}
1690*45232Sborman 		printoption("RCVD", "IAC", EOR);
169127088Sminshall 		break;
169232377Sminshall #	    endif /* defined(TN3270) */
169332377Sminshall 
169432377Sminshall 	    case IAC:
169532377Sminshall #	    if !defined(TN3270)
169632377Sminshall 		TTYADD(IAC);
169732377Sminshall #	    else /* !defined(TN3270) */
169832377Sminshall 		if (In3270) {
169932377Sminshall 		    *Ifrontp++ = IAC;
170032377Sminshall 		} else {
170132377Sminshall 		    TTYADD(IAC);
170232377Sminshall 		}
170332377Sminshall #	    endif /* !defined(TN3270) */
170427088Sminshall 		break;
170532377Sminshall 
170644360Sborman 	    case NOP:
170744360Sborman 	    case GA:
170827088Sminshall 	    default:
170944360Sborman 		printoption("RCVD", "IAC", c);
171027088Sminshall 		break;
171127088Sminshall 	    }
171232377Sminshall 	    telrcv_state = TS_DATA;
171332377Sminshall 	    continue;
171427088Sminshall 
171532377Sminshall 	case TS_WILL:
171637226Sminshall 	    printoption("RCVD", "will", c);
171738689Sborman 	    willoption(c);
171832377Sminshall 	    SetIn3270();
171932377Sminshall 	    telrcv_state = TS_DATA;
172032377Sminshall 	    continue;
172127110Sminshall 
172232377Sminshall 	case TS_WONT:
172337226Sminshall 	    printoption("RCVD", "wont", c);
172438689Sborman 	    wontoption(c);
172532377Sminshall 	    SetIn3270();
172632377Sminshall 	    telrcv_state = TS_DATA;
172732377Sminshall 	    continue;
172827088Sminshall 
172932377Sminshall 	case TS_DO:
173037226Sminshall 	    printoption("RCVD", "do", c);
173137226Sminshall 	    dooption(c);
173232377Sminshall 	    SetIn3270();
173337219Sminshall 	    if (c == TELOPT_NAWS) {
173437219Sminshall 		sendnaws();
173537219Sminshall 	    } else if (c == TELOPT_LFLOW) {
173637219Sminshall 		localflow = 1;
173737219Sminshall 		setcommandmode();
173838689Sborman 		setconnmode(0);
173937219Sminshall 	    }
174032377Sminshall 	    telrcv_state = TS_DATA;
174132377Sminshall 	    continue;
174227088Sminshall 
174332377Sminshall 	case TS_DONT:
174437226Sminshall 	    printoption("RCVD", "dont", c);
174538689Sborman 	    dontoption(c);
174637226Sminshall 	    flushline = 1;
174738689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
174832377Sminshall 	    SetIn3270();
174932377Sminshall 	    telrcv_state = TS_DATA;
175032377Sminshall 	    continue;
175127088Sminshall 
175232377Sminshall 	case TS_SB:
175332377Sminshall 	    if (c == IAC) {
175432377Sminshall 		telrcv_state = TS_SE;
175532377Sminshall 	    } else {
175632377Sminshall 		SB_ACCUM(c);
175732377Sminshall 	    }
175832377Sminshall 	    continue;
175927088Sminshall 
176032377Sminshall 	case TS_SE:
176132377Sminshall 	    if (c != SE) {
176232377Sminshall 		if (c != IAC) {
176338689Sborman 		    /*
176438689Sborman 		     * This is an error.  We only expect to get
176538689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
176638689Sborman 		     * have happend.  An IAC was not doubled, the
176738689Sborman 		     * IAC SE was left off, or another option got
176838689Sborman 		     * inserted into the suboption are all possibilities.
176938689Sborman 		     * If we assume that the IAC was not doubled,
177038689Sborman 		     * and really the IAC SE was left off, we could
177138689Sborman 		     * get into an infinate loop here.  So, instead,
177238689Sborman 		     * we terminate the suboption, and process the
177338689Sborman 		     * partial suboption if we can.
177438689Sborman 		     */
177538689Sborman 		    SB_TERM();
177632377Sminshall 		    SB_ACCUM(IAC);
177738689Sborman 		    SB_ACCUM(c);
177838689Sborman 		    printoption("In SUBOPTION processing, RCVD", "IAC", c);
177938689Sborman 		    suboption();	/* handle sub-option */
178038689Sborman 		    SetIn3270();
178138689Sborman 		    telrcv_state = TS_IAC;
178238689Sborman 		    goto process_iac;
178332377Sminshall 		}
178432377Sminshall 		SB_ACCUM(c);
178532377Sminshall 		telrcv_state = TS_SB;
178632377Sminshall 	    } else {
178732377Sminshall 		SB_TERM();
178838689Sborman 		SB_ACCUM(IAC);
178938689Sborman 		SB_ACCUM(SE);
179032377Sminshall 		suboption();	/* handle sub-option */
179132377Sminshall 		SetIn3270();
179232377Sminshall 		telrcv_state = TS_DATA;
179332377Sminshall 	    }
179427088Sminshall 	}
179527088Sminshall     }
179632667Sminshall     if (count)
179732667Sminshall 	ring_consumed(&netiring, count);
179832385Sminshall     return returnValue||count;
179927088Sminshall }
180032385Sminshall 
180132385Sminshall static int
180232554Sminshall telsnd()
180332385Sminshall {
180432385Sminshall     int tcc;
180532385Sminshall     int count;
180632385Sminshall     int returnValue = 0;
180732385Sminshall     char *tbp;
180832385Sminshall 
180932385Sminshall     tcc = 0;
181032385Sminshall     count = 0;
181132385Sminshall     while (NETROOM() > 2) {
181232385Sminshall 	register int sc;
181332385Sminshall 	register int c;
181432385Sminshall 
181532385Sminshall 	if (tcc == 0) {
181632385Sminshall 	    if (count) {
181732528Sminshall 		ring_consumed(&ttyiring, count);
181832385Sminshall 		returnValue = 1;
181932385Sminshall 		count = 0;
182032385Sminshall 	    }
182132528Sminshall 	    tbp = ttyiring.consume;
182232528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
182332385Sminshall 	    if (tcc == 0) {
182432385Sminshall 		break;
182532385Sminshall 	    }
182632385Sminshall 	}
182732385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
182832385Sminshall 	if (sc == escape) {
182938689Sborman 	    /*
183038689Sborman 	     * Double escape is a pass through of a single escape character.
183138689Sborman 	     */
183238689Sborman 	    if (tcc && strip(*tbp) == escape) {
183338689Sborman 		tbp++;
183438689Sborman 		tcc--;
183538689Sborman 		count++;
183638689Sborman 	    } else {
183738689Sborman 		command(0, tbp, tcc);
183838689Sborman 		count += tcc;
183938689Sborman 		tcc = 0;
184038689Sborman 		flushline = 1;
184138689Sborman 		break;
184238689Sborman 	    }
184338689Sborman 	}
184438689Sborman #ifdef	KLUDGELINEMODE
184538689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
184632385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
184732385Sminshall 		tcc--; tbp++; count++;
184832385Sminshall 	    } else {
184932385Sminshall 		dontlecho = !dontlecho;
185032385Sminshall 		settimer(echotoggle);
185138689Sborman 		setconnmode(0);
185232385Sminshall 		flushline = 1;
185332385Sminshall 		break;
185432385Sminshall 	    }
185532385Sminshall 	}
185638689Sborman #endif
185738689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
185832385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
185932385Sminshall 		break;
186032385Sminshall 	    }
186132385Sminshall 	}
186238689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
186332385Sminshall 	    switch (c) {
186432385Sminshall 	    case '\n':
186532385Sminshall 		    /*
186632385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
186732385Sminshall 		     * on our local machine, then probably
186832385Sminshall 		     * a newline (unix) is CRLF (TELNET).
186932385Sminshall 		     */
187032385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
187132385Sminshall 		    NETADD('\r');
187232385Sminshall 		}
187332385Sminshall 		NETADD('\n');
187432385Sminshall 		flushline = 1;
187532385Sminshall 		break;
187632385Sminshall 	    case '\r':
187732385Sminshall 		if (!crlf) {
187832385Sminshall 		    NET2ADD('\r', '\0');
187932385Sminshall 		} else {
188032385Sminshall 		    NET2ADD('\r', '\n');
188132385Sminshall 		}
188232385Sminshall 		flushline = 1;
188332385Sminshall 		break;
188432385Sminshall 	    case IAC:
188532385Sminshall 		NET2ADD(IAC, IAC);
188632385Sminshall 		break;
188732385Sminshall 	    default:
188832385Sminshall 		NETADD(c);
188932385Sminshall 		break;
189032385Sminshall 	    }
189132385Sminshall 	} else if (c == IAC) {
189232385Sminshall 	    NET2ADD(IAC, IAC);
189332385Sminshall 	} else {
189432385Sminshall 	    NETADD(c);
189532385Sminshall 	}
189632385Sminshall     }
189732667Sminshall     if (count)
189832667Sminshall 	ring_consumed(&ttyiring, count);
189932385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
190032385Sminshall }
190132377Sminshall 
190227088Sminshall /*
190332377Sminshall  * Scheduler()
190432377Sminshall  *
190532377Sminshall  * Try to do something.
190632377Sminshall  *
190732377Sminshall  * If we do something useful, return 1; else return 0.
190832377Sminshall  *
190927110Sminshall  */
191027110Sminshall 
191127110Sminshall 
191232377Sminshall int
191332377Sminshall Scheduler(block)
191432377Sminshall int	block;			/* should we block in the select ? */
191527110Sminshall {
191632377Sminshall 		/* One wants to be a bit careful about setting returnValue
191732377Sminshall 		 * to one, since a one implies we did some useful work,
191832377Sminshall 		 * and therefore probably won't be called to block next
191932377Sminshall 		 * time (TN3270 mode only).
192032377Sminshall 		 */
192132531Sminshall     int returnValue;
192232531Sminshall     int netin, netout, netex, ttyin, ttyout;
192327110Sminshall 
192432531Sminshall     /* Decide which rings should be processed */
192532531Sminshall 
192632531Sminshall     netout = ring_full_count(&netoring) &&
192738689Sborman 	    (flushline ||
192838689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
192938689Sborman #ifdef	KLUDGELINEMODE
193038689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
193138689Sborman #endif
193238689Sborman 		) ||
193338689Sborman 			my_want_state_is_will(TELOPT_BINARY));
193432531Sminshall     ttyout = ring_full_count(&ttyoring);
193532531Sminshall 
193632377Sminshall #if	defined(TN3270)
193732531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
193832377Sminshall #else	/* defined(TN3270) */
193932531Sminshall     ttyin = ring_empty_count(&ttyiring);
194032377Sminshall #endif	/* defined(TN3270) */
194132531Sminshall 
194232531Sminshall #if	defined(TN3270)
194332531Sminshall     netin = ring_empty_count(&netiring);
194432377Sminshall #   else /* !defined(TN3270) */
194532531Sminshall     netin = !ISend && ring_empty_count(&netiring);
194632377Sminshall #   endif /* !defined(TN3270) */
194732531Sminshall 
194832531Sminshall     netex = !SYNCHing;
194932531Sminshall 
195032531Sminshall     /* If we have seen a signal recently, reset things */
195132377Sminshall #   if defined(TN3270) && defined(unix)
195232377Sminshall     if (HaveInput) {
195332377Sminshall 	HaveInput = 0;
195444360Sborman 	(void) signal(SIGIO, inputAvailable);
195532377Sminshall     }
195632377Sminshall #endif	/* defined(TN3270) && defined(unix) */
195732377Sminshall 
195832531Sminshall     /* Call to system code to process rings */
195927178Sminshall 
196032531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
196127178Sminshall 
196232531Sminshall     /* Now, look at the input rings, looking for work to do. */
196332377Sminshall 
196432531Sminshall     if (ring_full_count(&ttyiring)) {
196532377Sminshall #   if defined(TN3270)
196632377Sminshall 	if (In3270) {
196734848Sminshall 	    int c;
196834848Sminshall 
196933804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
197032528Sminshall 					ring_full_consecutive(&ttyiring));
197132377Sminshall 	    if (c) {
197232377Sminshall 		returnValue = 1;
197332667Sminshall 	        ring_consumed(&ttyiring, c);
197432377Sminshall 	    }
197532377Sminshall 	} else {
197632377Sminshall #   endif /* defined(TN3270) */
197732554Sminshall 	    returnValue |= telsnd();
197832377Sminshall #   if defined(TN3270)
197927178Sminshall 	}
198032531Sminshall #   endif /* defined(TN3270) */
198127178Sminshall     }
198232377Sminshall 
198332528Sminshall     if (ring_full_count(&netiring)) {
198432377Sminshall #	if !defined(TN3270)
198532385Sminshall 	returnValue |= telrcv();
198632377Sminshall #	else /* !defined(TN3270) */
198732377Sminshall 	returnValue = Push3270();
198832377Sminshall #	endif /* !defined(TN3270) */
198932377Sminshall     }
199032377Sminshall     return returnValue;
199127178Sminshall }
199227178Sminshall 
199327178Sminshall /*
199432377Sminshall  * Select from tty and network...
199527088Sminshall  */
199632377Sminshall void
199732377Sminshall telnet()
199827088Sminshall {
199932531Sminshall     sys_telnet_init();
200027088Sminshall 
200132377Sminshall #   if !defined(TN3270)
200232377Sminshall     if (telnetport) {
200338689Sborman 	send_do(TELOPT_SGA, 1);
200438689Sborman 	send_will(TELOPT_TTYPE, 1);
200538689Sborman 	send_will(TELOPT_NAWS, 1);
200638689Sborman 	send_will(TELOPT_TSPEED, 1);
200738689Sborman 	send_will(TELOPT_LFLOW, 1);
200838689Sborman 	send_will(TELOPT_LINEMODE, 1);
200943319Skfall #ifdef	KERBEROS
201043319Skfall 	if (kerberized)
201143319Skfall 		send_will(TELOPT_AUTHENTICATION, 1);
201243319Skfall #endif
201338908Sborman 	send_do(TELOPT_STATUS, 1);
201444360Sborman 	if (env_getvalue("DISPLAY"))
201544360Sborman 	    send_will(TELOPT_XDISPLOC, 1);
201644360Sborman 	send_will(TELOPT_ENVIRON, 1);
201727178Sminshall     }
201832377Sminshall #   endif /* !defined(TN3270) */
201927088Sminshall 
202032377Sminshall #   if !defined(TN3270)
202132377Sminshall     for (;;) {
202232385Sminshall 	int schedValue;
202332385Sminshall 
202432385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
202532385Sminshall 	    if (schedValue == -1) {
202632385Sminshall 		setcommandmode();
202732385Sminshall 		return;
202832385Sminshall 	    }
202932385Sminshall 	}
203032385Sminshall 
203132531Sminshall 	if (Scheduler(1) == -1) {
203232377Sminshall 	    setcommandmode();
203332377Sminshall 	    return;
203432377Sminshall 	}
203532377Sminshall     }
203632377Sminshall #   else /* !defined(TN3270) */
203732377Sminshall     for (;;) {
203832377Sminshall 	int schedValue;
203927088Sminshall 
204032377Sminshall 	while (!In3270 && !shell_active) {
204132531Sminshall 	    if (Scheduler(1) == -1) {
204232377Sminshall 		setcommandmode();
204332377Sminshall 		return;
204432377Sminshall 	    }
204527088Sminshall 	}
204632377Sminshall 
204732377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
204832377Sminshall 	    if (schedValue == -1) {
204932377Sminshall 		setcommandmode();
205032377Sminshall 		return;
205132377Sminshall 	    }
205227088Sminshall 	}
205332377Sminshall 		/* If there is data waiting to go out to terminal, don't
205432377Sminshall 		 * schedule any more data for the terminal.
205532377Sminshall 		 */
205634304Sminshall 	if (ring_full_count(&ttyoring)) {
205732377Sminshall 	    schedValue = 1;
205827088Sminshall 	} else {
205932377Sminshall 	    if (shell_active) {
206032377Sminshall 		if (shell_continue() == 0) {
206132377Sminshall 		    ConnectScreen();
206227088Sminshall 		}
206332377Sminshall 	    } else if (In3270) {
206432377Sminshall 		schedValue = DoTerminalOutput();
206532377Sminshall 	    }
206627088Sminshall 	}
206732377Sminshall 	if (schedValue && (shell_active == 0)) {
206832531Sminshall 	    if (Scheduler(1) == -1) {
206932377Sminshall 		setcommandmode();
207032377Sminshall 		return;
207132377Sminshall 	    }
207227088Sminshall 	}
207332377Sminshall     }
207432377Sminshall #   endif /* !defined(TN3270) */
207527088Sminshall }
207632377Sminshall 
207734848Sminshall #if	0	/* XXX - this not being in is a bug */
207827088Sminshall /*
207932554Sminshall  * nextitem()
208032554Sminshall  *
208132554Sminshall  *	Return the address of the next "item" in the TELNET data
208232554Sminshall  * stream.  This will be the address of the next character if
208332554Sminshall  * the current address is a user data character, or it will
208432554Sminshall  * be the address of the character following the TELNET command
208532554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
208632554Sminshall  * character.
208732554Sminshall  */
208832554Sminshall 
208932554Sminshall static char *
209032554Sminshall nextitem(current)
209132554Sminshall char	*current;
209232554Sminshall {
209332554Sminshall     if ((*current&0xff) != IAC) {
209432554Sminshall 	return current+1;
209532554Sminshall     }
209632554Sminshall     switch (*(current+1)&0xff) {
209732554Sminshall     case DO:
209832554Sminshall     case DONT:
209932554Sminshall     case WILL:
210032554Sminshall     case WONT:
210132554Sminshall 	return current+3;
210232554Sminshall     case SB:		/* loop forever looking for the SE */
210332554Sminshall 	{
210432554Sminshall 	    register char *look = current+2;
210532554Sminshall 
210632554Sminshall 	    for (;;) {
210732554Sminshall 		if ((*look++&0xff) == IAC) {
210832554Sminshall 		    if ((*look++&0xff) == SE) {
210932554Sminshall 			return look;
211032554Sminshall 		    }
211132554Sminshall 		}
211232554Sminshall 	    }
211332554Sminshall 	}
211432554Sminshall     default:
211532554Sminshall 	return current+2;
211632554Sminshall     }
211732554Sminshall }
211834848Sminshall #endif	/* 0 */
211932554Sminshall 
212032554Sminshall /*
212132554Sminshall  * netclear()
212232554Sminshall  *
212332554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
212432554Sminshall  * the path to the network.
212532554Sminshall  *
212632554Sminshall  *	Things are a bit tricky since we may have sent the first
212732554Sminshall  * byte or so of a previous TELNET command into the network.
212832554Sminshall  * So, we have to scan the network buffer from the beginning
212932554Sminshall  * until we are up to where we want to be.
213032554Sminshall  *
213132554Sminshall  *	A side effect of what we do, just to keep things
213232554Sminshall  * simple, is to clear the urgent data pointer.  The principal
213332554Sminshall  * caller should be setting the urgent data pointer AFTER calling
213432554Sminshall  * us in any case.
213532554Sminshall  */
213632554Sminshall 
213732554Sminshall static void
213832554Sminshall netclear()
213932554Sminshall {
214032554Sminshall #if	0	/* XXX */
214132554Sminshall     register char *thisitem, *next;
214232554Sminshall     char *good;
214332554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
214432554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
214532554Sminshall 
214632554Sminshall     thisitem = netobuf;
214732554Sminshall 
214832554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
214932554Sminshall 	thisitem = next;
215032554Sminshall     }
215132554Sminshall 
215232554Sminshall     /* Now, thisitem is first before/at boundary. */
215332554Sminshall 
215432554Sminshall     good = netobuf;	/* where the good bytes go */
215532554Sminshall 
215632554Sminshall     while (netoring.add > thisitem) {
215732554Sminshall 	if (wewant(thisitem)) {
215832554Sminshall 	    int length;
215932554Sminshall 
216032554Sminshall 	    next = thisitem;
216132554Sminshall 	    do {
216232554Sminshall 		next = nextitem(next);
216332554Sminshall 	    } while (wewant(next) && (nfrontp > next));
216432554Sminshall 	    length = next-thisitem;
216532554Sminshall 	    memcpy(good, thisitem, length);
216632554Sminshall 	    good += length;
216732554Sminshall 	    thisitem = next;
216832554Sminshall 	} else {
216932554Sminshall 	    thisitem = nextitem(thisitem);
217032554Sminshall 	}
217132554Sminshall     }
217232554Sminshall 
217332554Sminshall #endif	/* 0 */
217432554Sminshall }
217532554Sminshall 
217632554Sminshall /*
217732377Sminshall  * These routines add various telnet commands to the data stream.
217827088Sminshall  */
217932377Sminshall 
218032554Sminshall static void
218132554Sminshall doflush()
218232554Sminshall {
218332554Sminshall     NET2ADD(IAC, DO);
218432554Sminshall     NETADD(TELOPT_TM);
218532554Sminshall     flushline = 1;
218632554Sminshall     flushout = 1;
218744360Sborman     (void) ttyflush(1);			/* Flush/drop output */
218832554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
218937226Sminshall     printoption("SENT", "do", TELOPT_TM);
219032554Sminshall }
219132554Sminshall 
219232377Sminshall void
219332377Sminshall xmitAO()
219427088Sminshall {
219532377Sminshall     NET2ADD(IAC, AO);
219638908Sborman     printoption("SENT", "IAC", AO);
219732377Sminshall     if (autoflush) {
219832377Sminshall 	doflush();
219932377Sminshall     }
220032377Sminshall }
220127088Sminshall 
220232377Sminshall 
220332377Sminshall void
220432377Sminshall xmitEL()
220527088Sminshall {
220632377Sminshall     NET2ADD(IAC, EL);
220738908Sborman     printoption("SENT", "IAC", EL);
220827088Sminshall }
220927088Sminshall 
221032377Sminshall void
221132377Sminshall xmitEC()
221227088Sminshall {
221332377Sminshall     NET2ADD(IAC, EC);
221438908Sborman     printoption("SENT", "IAC", EC);
221527088Sminshall }
221627088Sminshall 
221732377Sminshall 
221832377Sminshall #if	defined(NOT43)
221932377Sminshall int
222032377Sminshall #else	/* defined(NOT43) */
222132377Sminshall void
222232377Sminshall #endif	/* defined(NOT43) */
222332377Sminshall dosynch()
222427088Sminshall {
222532377Sminshall     netclear();			/* clear the path to the network */
222633294Sminshall     NETADD(IAC);
222733294Sminshall     setneturg();
222833294Sminshall     NETADD(DM);
222938908Sborman     printoption("SENT", "IAC", DM);
223027088Sminshall 
223132377Sminshall #if	defined(NOT43)
223232377Sminshall     return 0;
223332377Sminshall #endif	/* defined(NOT43) */
223427088Sminshall }
223527088Sminshall 
223632377Sminshall void
223738908Sborman get_status()
223838908Sborman {
223938908Sborman     char tmp[16];
224038908Sborman     register char *cp;
224138908Sborman 
224238908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
224338908Sborman 	printf("Remote side does not support STATUS option\n");
224438908Sborman 	return;
224538908Sborman     }
224638908Sborman     if (!showoptions)
224738908Sborman 	printf("You will not see the response unless you set \"options\"\n");
224838908Sborman 
224938908Sborman     cp = tmp;
225038908Sborman 
225138908Sborman     *cp++ = IAC;
225238908Sborman     *cp++ = SB;
225338908Sborman     *cp++ = TELOPT_STATUS;
225438908Sborman     *cp++ = TELQUAL_SEND;
225538908Sborman     *cp++ = IAC;
225638908Sborman     *cp++ = SE;
225738908Sborman     if (NETROOM() >= cp - tmp) {
225838908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
225938908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
226038908Sborman     }
226138908Sborman }
226238908Sborman 
226338908Sborman void
226432377Sminshall intp()
226527088Sminshall {
226632377Sminshall     NET2ADD(IAC, IP);
226738908Sborman     printoption("SENT", "IAC", IP);
226832377Sminshall     flushline = 1;
226932377Sminshall     if (autoflush) {
227032377Sminshall 	doflush();
227132377Sminshall     }
227232377Sminshall     if (autosynch) {
227332377Sminshall 	dosynch();
227432377Sminshall     }
227527088Sminshall }
227627186Sminshall 
227732377Sminshall void
227832377Sminshall sendbrk()
227927186Sminshall {
228032377Sminshall     NET2ADD(IAC, BREAK);
228138908Sborman     printoption("SENT", "IAC", BREAK);
228232377Sminshall     flushline = 1;
228332377Sminshall     if (autoflush) {
228432377Sminshall 	doflush();
228532377Sminshall     }
228632377Sminshall     if (autosynch) {
228732377Sminshall 	dosynch();
228832377Sminshall     }
228927186Sminshall }
229038689Sborman 
229138689Sborman void
229238689Sborman sendabort()
229338689Sborman {
229438689Sborman     NET2ADD(IAC, ABORT);
229538908Sborman     printoption("SENT", "IAC", ABORT);
229638689Sborman     flushline = 1;
229738689Sborman     if (autoflush) {
229838689Sborman 	doflush();
229938689Sborman     }
230038689Sborman     if (autosynch) {
230138689Sborman 	dosynch();
230238689Sborman     }
230338689Sborman }
230438689Sborman 
230538689Sborman void
230638689Sborman sendsusp()
230738689Sborman {
230838689Sborman     NET2ADD(IAC, SUSP);
230938908Sborman     printoption("SENT", "IAC", SUSP);
231038689Sborman     flushline = 1;
231138689Sborman     if (autoflush) {
231238689Sborman 	doflush();
231338689Sborman     }
231438689Sborman     if (autosynch) {
231538689Sborman 	dosynch();
231638689Sborman     }
231738689Sborman }
231838689Sborman 
231938689Sborman void
232038689Sborman sendeof()
232138689Sborman {
232238908Sborman     NET2ADD(IAC, xEOF);
232338908Sborman     printoption("SENT", "IAC", xEOF);
232438689Sborman }
232538689Sborman 
2326*45232Sborman void
2327*45232Sborman sendayt()
2328*45232Sborman {
2329*45232Sborman     NET2ADD(IAC, AYT);
2330*45232Sborman     printoption("SENT", "IAC", AYT);
2331*45232Sborman }
2332*45232Sborman 
233337219Sminshall /*
233437219Sminshall  * Send a window size update to the remote system.
233537219Sminshall  */
233637219Sminshall 
233737219Sminshall void
233837219Sminshall sendnaws()
233937219Sminshall {
234037219Sminshall     long rows, cols;
234138689Sborman     unsigned char tmp[16];
234238689Sborman     register unsigned char *cp;
234337219Sminshall 
234438689Sborman     if (my_state_is_wont(TELOPT_NAWS))
234538689Sborman 	return;
234637219Sminshall 
234738689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
234838689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
234938689Sborman 
235037219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
235137219Sminshall 	return;
235237219Sminshall     }
235337219Sminshall 
235438689Sborman     cp = tmp;
235538689Sborman 
235638689Sborman     *cp++ = IAC;
235738689Sborman     *cp++ = SB;
235838689Sborman     *cp++ = TELOPT_NAWS;
235938689Sborman     PUTSHORT(cp, cols);
236038689Sborman     PUTSHORT(cp, rows);
236138689Sborman     *cp++ = IAC;
236238689Sborman     *cp++ = SE;
236338689Sborman     if (NETROOM() >= cp - tmp) {
236438689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
236538689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
236637219Sminshall     }
236737219Sminshall }
236837226Sminshall 
236938908Sborman tel_enter_binary(rw)
237038908Sborman int rw;
237137226Sminshall {
237238908Sborman     if (rw&1)
237338908Sborman 	send_do(TELOPT_BINARY, 1);
237438908Sborman     if (rw&2)
237538908Sborman 	send_will(TELOPT_BINARY, 1);
237637226Sminshall }
237737226Sminshall 
237838908Sborman tel_leave_binary(rw)
237938908Sborman int rw;
238037226Sminshall {
238138908Sborman     if (rw&1)
238238908Sborman 	send_dont(TELOPT_BINARY, 1);
238338908Sborman     if (rw&2)
238438908Sborman 	send_wont(TELOPT_BINARY, 1);
238537226Sminshall }
2386