xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 42770)
133685Sbostic /*
233685Sbostic  * Copyright (c) 1988 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
5*42770Sbostic  * %sccs.include.redist.c%
633685Sbostic  */
711758Ssam 
821580Sdist #ifndef lint
9*42770Sbostic static char sccsid[] = "@(#)telnet.c	5.47 (Berkeley) 06/01/90";
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 
2432377Sminshall #include <string.h>
259217Ssam 
2638908Sborman #include <ctype.h>
2738908Sborman 
2832381Sminshall #include "ring.h"
2932381Sminshall 
3032377Sminshall #include "defines.h"
3132377Sminshall #include "externs.h"
3232377Sminshall #include "types.h"
3332377Sminshall #include "general.h"
3427178Sminshall 
3527178Sminshall 
3627228Sminshall #define	strip(x)	((x)&0x7f)
376000Sroot 
3827088Sminshall 
3932377Sminshall static char	subbuffer[SUBBUFSIZE],
4032377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
4127676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
4227676Sminshall #define	SB_TERM()	subend = subpointer;
4327676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4427676Sminshall 				*subpointer++ = (c); \
4527676Sminshall 			}
4627676Sminshall 
4737226Sminshall char	options[256];		/* The combined options */
4838689Sborman char	do_dont_resp[256];
4938689Sborman char	will_wont_resp[256];
506000Sroot 
5132377Sminshall int
5232377Sminshall 	connected,
5332377Sminshall 	showoptions,
5432377Sminshall 	In3270,		/* Are we in 3270 mode? */
5532377Sminshall 	ISend,		/* trying to send network data in */
5632377Sminshall 	debug = 0,
5732377Sminshall 	crmod,
5832377Sminshall 	netdata,	/* Print out network data flow */
5932377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6034848Sminshall #if	defined(TN3270)
6136241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
6236241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
6332377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
6434848Sminshall #endif	/* defined(TN3270) */
6533286Sminshall 	telnetport,
6632531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
6732531Sminshall 	flushout,	/* flush output */
6832531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
6932531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
7037219Sminshall 	localflow,	/* we handle flow control locally */
7132531Sminshall 	localchars,	/* we recognize interrupt/quit */
7232531Sminshall 	donelclchars,	/* the user has set "localchars" */
7332531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
7432531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
7532531Sminshall 	globalmode;
7627088Sminshall 
7732377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
786000Sroot 
7940245Sborman unsigned char *prompt = 0;
8027186Sminshall 
8140245Sborman cc_t escape, echoc;
8240245Sborman 
8327186Sminshall /*
846000Sroot  * Telnet receiver states for fsm
856000Sroot  */
866000Sroot #define	TS_DATA		0
876000Sroot #define	TS_IAC		1
886000Sroot #define	TS_WILL		2
896000Sroot #define	TS_WONT		3
906000Sroot #define	TS_DO		4
916000Sroot #define	TS_DONT		5
9227021Sminshall #define	TS_CR		6
9327676Sminshall #define	TS_SB		7		/* sub-option collection */
9427676Sminshall #define	TS_SE		8		/* looking for sub-option end */
956000Sroot 
9632377Sminshall static int	telrcv_state;
976000Sroot 
9832377Sminshall jmp_buf	toplevel = { 0 };
9932377Sminshall jmp_buf	peerdied;
1006000Sroot 
10132377Sminshall int	flushline;
10238811Sborman int	linemode;
10327021Sminshall 
10438689Sborman #ifdef	KLUDGELINEMODE
10538689Sborman int	kludgelinemode = 1;
10638689Sborman #endif
10738689Sborman 
10832377Sminshall /*
10932377Sminshall  * The following are some clocks used to decide how to interpret
11032377Sminshall  * the relationship between various variables.
11132377Sminshall  */
1126000Sroot 
11332377Sminshall Clocks clocks;
11432377Sminshall 
11538689Sborman #ifdef	notdef
11632377Sminshall Modelist modelist[] = {
11732377Sminshall 	{ "telnet command mode", COMMAND_LINE },
11832377Sminshall 	{ "character-at-a-time mode", 0 },
11932377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
12032377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
12132377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
12232377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
12332377Sminshall 	{ "3270 mode", 0 },
12432377Sminshall };
12538689Sborman #endif
1266000Sroot 
12732377Sminshall 
12832377Sminshall /*
12932377Sminshall  * Initialize telnet environment.
13032377Sminshall  */
1316000Sroot 
13232377Sminshall init_telnet()
13332377Sminshall {
13432377Sminshall     SB_CLEAR();
13537226Sminshall     ClearArray(options);
1366000Sroot 
13737219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
1386000Sroot 
13932377Sminshall     SYNCHing = 0;
1406000Sroot 
14132377Sminshall     /* Don't change NetTrace */
1426000Sroot 
14332377Sminshall     escape = CONTROL(']');
14432377Sminshall     echoc = CONTROL('E');
1456000Sroot 
14632377Sminshall     flushline = 1;
14732377Sminshall     telrcv_state = TS_DATA;
14832377Sminshall }
14932554Sminshall 
1506000Sroot 
15132554Sminshall #include <varargs.h>
1526000Sroot 
15334848Sminshall /*VARARGS*/
15432554Sminshall static void
15532554Sminshall printring(va_alist)
15632554Sminshall va_dcl
15732554Sminshall {
15832554Sminshall     va_list ap;
15932554Sminshall     char buffer[100];		/* where things go */
16032554Sminshall     char *ptr;
16132554Sminshall     char *format;
16232554Sminshall     char *string;
16332554Sminshall     Ring *ring;
16432554Sminshall     int i;
16532554Sminshall 
16632554Sminshall     va_start(ap);
16732554Sminshall 
16832554Sminshall     ring = va_arg(ap, Ring *);
16932554Sminshall     format = va_arg(ap, char *);
17032554Sminshall     ptr = buffer;
17132554Sminshall 
17232554Sminshall     while ((i = *format++) != 0) {
17332554Sminshall 	if (i == '%') {
17432554Sminshall 	    i = *format++;
17532554Sminshall 	    switch (i) {
17632554Sminshall 	    case 'c':
17732554Sminshall 		*ptr++ = va_arg(ap, int);
17832554Sminshall 		break;
17932554Sminshall 	    case 's':
18032554Sminshall 		string = va_arg(ap, char *);
18132554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
18232554Sminshall 		ring_supply_data(ring, string, strlen(string));
18332554Sminshall 		ptr = buffer;
18432554Sminshall 		break;
18532554Sminshall 	    case 0:
18632554Sminshall 		ExitString("printring: trailing %%.\n", 1);
18732554Sminshall 		/*NOTREACHED*/
18832554Sminshall 	    default:
18932554Sminshall 		ExitString("printring: unknown format character.\n", 1);
19032554Sminshall 		/*NOTREACHED*/
19132554Sminshall 	    }
19232554Sminshall 	} else {
19332554Sminshall 	    *ptr++ = i;
19432554Sminshall 	}
19532554Sminshall     }
19632554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
19732554Sminshall }
19832554Sminshall 
19937226Sminshall /*
20037226Sminshall  * These routines are in charge of sending option negotiations
20137226Sminshall  * to the other side.
20237226Sminshall  *
20337226Sminshall  * The basic idea is that we send the negotiation if either side
20437226Sminshall  * is in disagreement as to what the current state should be.
20537226Sminshall  */
20632554Sminshall 
20738689Sborman send_do(c, init)
20838689Sborman register int c, init;
2096000Sroot {
21038689Sborman     if (init) {
21138689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
21238689Sborman 				my_want_state_is_do(c))
21338689Sborman 	    return;
21438689Sborman 	set_my_want_state_do(c);
21538689Sborman 	do_dont_resp[c]++;
21637226Sminshall     }
21738689Sborman     NET2ADD(IAC, DO);
21838689Sborman     NETADD(c);
21938689Sborman     printoption("SENT", "do", c);
22037226Sminshall }
22137226Sminshall 
22237226Sminshall void
22338689Sborman send_dont(c, init)
22438689Sborman register int c, init;
22537226Sminshall {
22638689Sborman     if (init) {
22738689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
22838689Sborman 				my_want_state_is_dont(c))
22938689Sborman 	    return;
23038689Sborman 	set_my_want_state_dont(c);
23138689Sborman 	do_dont_resp[c]++;
23237226Sminshall     }
23338689Sborman     NET2ADD(IAC, DONT);
23438689Sborman     NETADD(c);
23538689Sborman     printoption("SENT", "dont", c);
23637226Sminshall }
23737226Sminshall 
23837226Sminshall void
23938689Sborman send_will(c, init)
24038689Sborman register int c, init;
24137226Sminshall {
24238689Sborman     if (init) {
24338689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
24438689Sborman 				my_want_state_is_will(c))
24538689Sborman 	    return;
24638689Sborman 	set_my_want_state_will(c);
24738689Sborman 	will_wont_resp[c]++;
24837226Sminshall     }
24938689Sborman     NET2ADD(IAC, WILL);
25038689Sborman     NETADD(c);
25138689Sborman     printoption("SENT", "will", c);
25237226Sminshall }
25337226Sminshall 
25437226Sminshall void
25538689Sborman send_wont(c, init)
25638689Sborman register int c, init;
25737226Sminshall {
25838689Sborman     if (init) {
25938689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
26038689Sborman 				my_want_state_is_wont(c))
26138689Sborman 	    return;
26238689Sborman 	set_my_want_state_wont(c);
26338689Sborman 	will_wont_resp[c]++;
26437226Sminshall     }
26538689Sborman     NET2ADD(IAC, WONT);
26638689Sborman     NETADD(c);
26738689Sborman     printoption("SENT", "wont", c);
26837226Sminshall }
26937226Sminshall 
27037226Sminshall 
27137226Sminshall void
27237226Sminshall willoption(option)
27337226Sminshall 	int option;
27437226Sminshall {
2756000Sroot 	char *fmt;
27638689Sborman 	int new_state_ok = 0;
2776000Sroot 
27838689Sborman 	if (do_dont_resp[option]) {
27938689Sborman 	    --do_dont_resp[option];
28038689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
28138689Sborman 		--do_dont_resp[option];
28238689Sborman 	}
28337226Sminshall 
28438689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
2856000Sroot 
28638689Sborman 	    switch (option) {
28738689Sborman 
28838689Sborman 	    case TELOPT_ECHO:
28938689Sborman #	    if defined(TN3270)
29038689Sborman 		/*
29138689Sborman 		 * The following is a pain in the rear-end.
29238689Sborman 		 * Various IBM servers (some versions of Wiscnet,
29338689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
29438689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
29538689Sborman 		 * in the setup proceedings.  On the other hand,
29638689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
29738689Sborman 		 * So, we are stuck.  Empirically (but, based on
29838689Sborman 		 * a VERY small sample), the IBM servers don't send
29938689Sborman 		 * out anything about ECHO, so we postpone our sending
30038689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
30138689Sborman 		 * DO send).
30238689Sborman 		  */
30338689Sborman 		{
30438689Sborman 		    if (askedSGA == 0) {
30538689Sborman 			askedSGA = 1;
30638689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
30738689Sborman 			    send_do(TELOPT_SGA, 1);
30832377Sminshall 		    }
30932377Sminshall 		}
31038689Sborman 		    /* Fall through */
31138689Sborman 	    case TELOPT_EOR:
31238908Sborman #endif	    /* defined(TN3270) */
31338689Sborman 	    case TELOPT_BINARY:
31438689Sborman 	    case TELOPT_SGA:
31527110Sminshall 		settimer(modenegotiated);
31638908Sborman 		/* FALL THROUGH */
31738908Sborman 	    case TELOPT_STATUS:
31838689Sborman 		new_state_ok = 1;
3196000Sroot 		break;
3206000Sroot 
32138689Sborman 	    case TELOPT_TM:
32238689Sborman 		if (flushout)
32338689Sborman 		    flushout = 0;
32438689Sborman 		/*
32538689Sborman 		 * Special case for TM.  If we get back a WILL,
32638689Sborman 		 * pretend we got back a WONT.
32738689Sborman 		 */
32838689Sborman 		set_my_want_state_dont(option);
32938689Sborman 		set_my_state_dont(option);
33027110Sminshall 		return;			/* Never reply to TM will's/wont's */
3316000Sroot 
33238689Sborman 	    case TELOPT_LINEMODE:
33338689Sborman 	    default:
3346000Sroot 		break;
33538689Sborman 	    }
33638689Sborman 
33738689Sborman 	    if (new_state_ok) {
33838689Sborman 		set_my_want_state_do(option);
33938689Sborman 		send_do(option, 0);
34038689Sborman 		setconnmode(0);		/* possibly set new tty mode */
34138689Sborman 	    } else {
34238689Sborman 		do_dont_resp[option]++;
34338689Sborman 		send_dont(option, 0);
34438689Sborman 	    }
3456000Sroot 	}
34638689Sborman 	set_my_state_do(option);
3476000Sroot }
3486000Sroot 
34932377Sminshall void
35037226Sminshall wontoption(option)
35137226Sminshall 	int option;
3526000Sroot {
3536000Sroot 	char *fmt;
3546000Sroot 
35538689Sborman 	if (do_dont_resp[option]) {
35638689Sborman 	    --do_dont_resp[option];
35738689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
35838689Sborman 		--do_dont_resp[option];
35938689Sborman 	}
36037226Sminshall 
36138689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3626000Sroot 
36338689Sborman 	    switch (option) {
36438689Sborman 
36538689Sborman #ifdef	KLUDGELINEMODE
36638689Sborman 	    case TELOPT_SGA:
36738689Sborman 		if (!kludgelinemode)
36838689Sborman 		    break;
36938689Sborman 		/* FALL THROUGH */
37038689Sborman #endif
37138689Sborman 	    case TELOPT_ECHO:
37227110Sminshall 		settimer(modenegotiated);
3736000Sroot 		break;
3746000Sroot 
37538689Sborman 	    case TELOPT_TM:
37638689Sborman 		if (flushout)
37738689Sborman 		    flushout = 0;
37838689Sborman 		set_my_want_state_dont(option);
37938689Sborman 		set_my_state_dont(option);
38027110Sminshall 		return;		/* Never reply to TM will's/wont's */
38127110Sminshall 
38238689Sborman 	    default:
38338689Sborman 		break;
38438689Sborman 	    }
38538689Sborman 	    set_my_want_state_dont(option);
38638689Sborman 	    send_dont(option, 0);
38738689Sborman 	    setconnmode(0);			/* Set new tty mode */
38838689Sborman 	} else if (option == TELOPT_TM) {
38938689Sborman 	    /*
39038689Sborman 	     * Special case for TM.
39138689Sborman 	     */
39238689Sborman 	    if (flushout)
39338689Sborman 		flushout = 0;
39438689Sborman 	    set_my_want_state_dont(option);
3956000Sroot 	}
39638689Sborman 	set_my_state_dont(option);
3976000Sroot }
3986000Sroot 
39932377Sminshall static void
4006000Sroot dooption(option)
4016000Sroot 	int option;
4026000Sroot {
4036000Sroot 	char *fmt;
40438689Sborman 	int new_state_ok = 0;
4056000Sroot 
40638689Sborman 	if (will_wont_resp[option]) {
40738689Sborman 	    --will_wont_resp[option];
40838689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
40938689Sborman 		--will_wont_resp[option];
41038689Sborman 	}
41137226Sminshall 
41238689Sborman 	if (will_wont_resp[option] == 0) {
41338689Sborman 	  if (my_want_state_is_wont(option)) {
4146000Sroot 
41538689Sborman 	    switch (option) {
41638689Sborman 
41738689Sborman 	    case TELOPT_TM:
41838689Sborman 		/*
41938689Sborman 		 * Special case for TM.  We send a WILL, but pretend
42038689Sborman 		 * we sent WONT.
42138689Sborman 		 */
42238689Sborman 		send_will(option, 0);
42338689Sborman 		set_my_want_state_wont(TELOPT_TM);
42438689Sborman 		set_my_state_wont(TELOPT_TM);
42538689Sborman 		return;
42638689Sborman 
42732377Sminshall #	if defined(TN3270)
42838689Sborman 	    case TELOPT_EOR:		/* end of record */
42938908Sborman #	endif	/* defined(TN3270) */
43038689Sborman 	    case TELOPT_BINARY:		/* binary mode */
43138689Sborman 	    case TELOPT_NAWS:		/* window size */
43238689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
43338689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
43438689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
43538689Sborman 	    case TELOPT_SGA:		/* no big deal */
43638689Sborman 		new_state_ok = 1;
4376000Sroot 		break;
4386000Sroot 
43938689Sborman 	    case TELOPT_LINEMODE:
44038689Sborman #ifdef	KLUDGELINEMODE
44138689Sborman 		kludgelinemode = 0;
44238689Sborman #endif
44338689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
44438689Sborman 		send_will(option, 0);
44538689Sborman 		set_my_state_will(TELOPT_LINEMODE);
44638689Sborman 		slc_init();
44738689Sborman 		return;
44838689Sborman 
44938689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
45038689Sborman 	    default:
4516000Sroot 		break;
45238689Sborman 	    }
45338689Sborman 
45438689Sborman 	    if (new_state_ok) {
45538689Sborman 		set_my_want_state_will(option);
45638689Sborman 		send_will(option, 0);
45738689Sborman 	    } else {
45838689Sborman 		will_wont_resp[option]++;
45938689Sborman 		send_wont(option, 0);
46038689Sborman 	    }
46138689Sborman 	  } else {
46238689Sborman 	    /*
46338689Sborman 	     * Handle options that need more things done after the
46438689Sborman 	     * other side has acknowledged the option.
46538689Sborman 	     */
46638689Sborman 	    switch (option) {
46738689Sborman 	    case TELOPT_LINEMODE:
46838689Sborman #ifdef	KLUDGELINEMODE
46938689Sborman 		kludgelinemode = 0;
47038689Sborman #endif
47138689Sborman 		set_my_state_will(option);
47238689Sborman 		slc_init();
47338689Sborman 		return;
47438689Sborman 	    }
47538689Sborman 	  }
4766000Sroot 	}
47738689Sborman 	set_my_state_will(option);
4786000Sroot }
47927676Sminshall 
48038689Sborman static void
48138689Sborman dontoption(option)
48238689Sborman 	int option;
48338689Sborman {
48438689Sborman 
48538689Sborman 	if (will_wont_resp[option]) {
48638689Sborman 	    --will_wont_resp[option];
48738689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
48838689Sborman 		--will_wont_resp[option];
48938689Sborman 	}
49038689Sborman 
49138689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
49238811Sborman 	    switch (option) {
49338811Sborman 	    case TELOPT_LINEMODE:
49438811Sborman 		linemode = 0;	/* put us back to the default state */
49538811Sborman 		break;
49638811Sborman 	    }
49738689Sborman 	    /* we always accept a DONT */
49838689Sborman 	    set_my_want_state_wont(option);
49938689Sborman 	    send_wont(option, 0);
50039529Sborman 	    setconnmode(0);			/* Set new tty mode */
50138689Sborman 	}
50238689Sborman 	set_my_state_wont(option);
50338689Sborman }
50438689Sborman 
50527676Sminshall /*
50638908Sborman  * Given a buffer returned by tgetent(), this routine will turn
50738908Sborman  * the pipe seperated list of names in the buffer into an array
50838908Sborman  * of pointers to null terminated names.  We toss out any bad,
50938908Sborman  * duplicate, or verbose names (names with spaces).
51038908Sborman  */
51138908Sborman 
51238908Sborman static char *unknown[] = { "UNKNOWN", 0 };
51338908Sborman 
51438908Sborman char **
51538908Sborman mklist(buf, name)
51638908Sborman char *buf, *name;
51738908Sborman {
51838908Sborman 	register int n;
51938908Sborman 	register char c, *cp, **argvp, *cp2, **argv;
52038908Sborman 	char *malloc();
52138908Sborman 
52238908Sborman 	if (name) {
52338908Sborman 		if (strlen(name) > 40)
52438908Sborman 			name = 0;
52538908Sborman 		else {
52638908Sborman 			unknown[0] = name;
52738908Sborman 			upcase(name);
52838908Sborman 		}
52938908Sborman 	}
53038908Sborman 	/*
53138908Sborman 	 * Count up the number of names.
53238908Sborman 	 */
53338908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
53438908Sborman 		if (*cp == '|')
53538908Sborman 			n++;
53638908Sborman 	}
53738908Sborman 	/*
53838908Sborman 	 * Allocate an array to put the name pointers into
53938908Sborman 	 */
54038908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
54138908Sborman 	if (argv == 0)
54238908Sborman 		return(unknown);
54338908Sborman 
54438908Sborman 	/*
54538908Sborman 	 * Fill up the array of pointers to names.
54638908Sborman 	 */
54738908Sborman 	*argv = 0;
54838908Sborman 	argvp = argv+1;
54938908Sborman 	n = 0;
55038908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
55138908Sborman 		if (c == '|' || c == ':') {
55238908Sborman 			*cp++ = '\0';
55338908Sborman 			/*
55438908Sborman 			 * Skip entries that have spaces or are over 40
55538908Sborman 			 * characters long.  If this is our environment
55638908Sborman 			 * name, then put it up front.  Otherwise, as
55738908Sborman 			 * long as this is not a duplicate name (case
55838908Sborman 			 * insensitive) add it to the list.
55938908Sborman 			 */
56038908Sborman 			if (n || (cp - cp2 > 41))
56138908Sborman 				;
56238908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
56338908Sborman 				*argv = cp2;
56438908Sborman 			else if (is_unique(cp2, argv+1, argvp))
56538908Sborman 				*argvp++ = cp2;
56638908Sborman 			if (c == ':')
56738908Sborman 				break;
56838908Sborman 			/*
56938908Sborman 			 * Skip multiple delimiters. Reset cp2 to
57038908Sborman 			 * the beginning of the next name. Reset n,
57138908Sborman 			 * the flag for names with spaces.
57238908Sborman 			 */
57338908Sborman 			while ((c = *cp) == '|')
57438908Sborman 				cp++;
57538908Sborman 			cp2 = cp;
57638908Sborman 			n = 0;
57738908Sborman 		}
57838908Sborman 		/*
57938908Sborman 		 * Skip entries with spaces or non-ascii values.
58038908Sborman 		 * Convert lower case letters to upper case.
58138908Sborman 		 */
58238908Sborman 		if ((c == ' ') || !isascii(c))
58338908Sborman 			n = 1;
58438908Sborman 		else if (islower(c))
58538908Sborman 			*cp = toupper(c);
58638908Sborman 	}
58738908Sborman 
58838908Sborman 	/*
58938908Sborman 	 * Check for an old V6 2 character name.  If the second
59038908Sborman 	 * name points to the beginning of the buffer, and is
59138908Sborman 	 * only 2 characters long, move it to the end of the array.
59238908Sborman 	 */
59338908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
59438908Sborman 		*argvp++ = buf;
59538908Sborman 		cp = *argv++;
59638908Sborman 		*argv = cp;
59738908Sborman 	}
59838908Sborman 
59938908Sborman 	/*
60038908Sborman 	 * Duplicate last name, for TTYPE option, and null
60138908Sborman 	 * terminate the array.  If we didn't find a match on
60238908Sborman 	 * our terminal name, put that name at the beginning.
60338908Sborman 	 */
60438908Sborman 	cp = *(argvp-1);
60538908Sborman 	*argvp++ = cp;
60638908Sborman 	*argvp = 0;
60738908Sborman 
60838908Sborman 	if (*argv == 0) {
60938908Sborman 		if (name)
61038908Sborman 			*argv = name;
61138908Sborman 		else
61238908Sborman 			argv++;
61338908Sborman 	}
61438908Sborman 	if (*argv)
61538908Sborman 		return(argv);
61638908Sborman 	else
61738908Sborman 		return(unknown);
61838908Sborman }
61938908Sborman 
62038908Sborman is_unique(name, as, ae)
62138908Sborman register char *name, **as, **ae;
62238908Sborman {
62338908Sborman 	register char **ap;
62438908Sborman 	register int n;
62538908Sborman 
62638908Sborman 	n = strlen(name) + 1;
62738908Sborman 	for (ap = as; ap < ae; ap++)
62838908Sborman 		if (strncasecmp(*ap, name, n) == 0)
62938908Sborman 			return(0);
63038908Sborman 	return (1);
63138908Sborman }
63238908Sborman 
63338908Sborman #ifdef	TERMCAP
63439529Sborman char termbuf[1024];
63538908Sborman setupterm(tname, fd, errp)
63638908Sborman char *tname;
63738908Sborman int fd, *errp;
63838908Sborman {
63939529Sborman 	if (tgetent(termbuf, tname) == 1) {
64039529Sborman 		termbuf[1023] = '\0';
64138908Sborman 		if (errp)
64238908Sborman 			*errp = 1;
64338908Sborman 		return(0);
64438908Sborman 	}
64538908Sborman 	if (errp)
64638908Sborman 		*errp = 0;
64738908Sborman 	return(-1);
64838908Sborman }
64939529Sborman #else
65039529Sborman #define	termbuf	ttytype
65139529Sborman extern char ttytype[];
65238908Sborman #endif
65338908Sborman 
65438908Sborman char *
65538908Sborman gettermname()
65638908Sborman {
65738908Sborman 	char *tname;
65838908Sborman 	static int first = 1;
65938908Sborman 	static char **tnamep;
66038908Sborman 	static char **next;
66138908Sborman 	char *getenv();
66238908Sborman 	int err;
66338908Sborman 
66438908Sborman 	if (first) {
66538908Sborman 		first = 0;
66638908Sborman 		if ((tname = getenv("TERM")) &&
66738908Sborman 				(setupterm(tname, 1, &err) == 0)) {
66839529Sborman 			tnamep = mklist(termbuf, tname);
66938908Sborman 		} else {
67038908Sborman 			if (tname && (strlen(tname) <= 40)) {
67138908Sborman 				unknown[0] = tname;
67238908Sborman 				upcase(tname);
67338908Sborman 			}
67438908Sborman 			tnamep = unknown;
67538908Sborman 		}
67638908Sborman 		next = tnamep;
67738908Sborman 	}
67838908Sborman 	if (*next == 0)
67938908Sborman 		next = tnamep;
68038908Sborman 	return(*next++);
68138908Sborman }
68238908Sborman /*
68327676Sminshall  * suboption()
68427676Sminshall  *
68527676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
68627676Sminshall  * side.
68727676Sminshall  *
68827676Sminshall  *	Currently we recognize:
68927676Sminshall  *
69027676Sminshall  *		Terminal type, send request.
69137219Sminshall  *		Terminal speed (send request).
69237219Sminshall  *		Local flow control (is request).
69338689Sborman  *		Linemode
69427676Sminshall  */
69527676Sminshall 
69632377Sminshall static void
69727676Sminshall suboption()
69827676Sminshall {
69938689Sborman     printsub('<', subbuffer, subend-subbuffer+2);
70027676Sminshall     switch (subbuffer[0]&0xff) {
70127676Sminshall     case TELOPT_TTYPE:
70238689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
70338689Sborman 	    return;
70427676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
70527676Sminshall 	    ;
70627676Sminshall 	} else {
70727676Sminshall 	    char *name;
70832377Sminshall 	    extern char *getenv();
70938908Sborman 	    char temp[50];
71027676Sminshall 	    int len;
71127676Sminshall 
71232377Sminshall #if	defined(TN3270)
71332531Sminshall 	    if (tn3270_ttype()) {
71432377Sminshall 		return;
71532377Sminshall 	    }
71632377Sminshall #endif	/* defined(TN3270) */
71738908Sborman 	    name = gettermname();
71838908Sborman 	    len = strlen(name) + 4 + 2;
71938908Sborman 	    if (len < NETROOM()) {
72038908Sborman 		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
72138908Sborman 				TELQUAL_IS, name, IAC, SE);
72238689Sborman 		ring_supply_data(&netoring, temp, len);
72338908Sborman 		printsub('>', &temp[2], len-2);
72432377Sminshall 	    } else {
72537226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
72632377Sminshall 		/*NOTREACHED*/
72727676Sminshall 	    }
72827676Sminshall 	}
72937219Sminshall 	break;
73037219Sminshall     case TELOPT_TSPEED:
73138689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
73238689Sborman 	    return;
73337219Sminshall 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
73438689Sborman 	    long ospeed,ispeed;
73538689Sborman 	    char temp[50];
73637219Sminshall 	    int len;
73727676Sminshall 
73837219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
73937219Sminshall 
74038689Sborman 	    sprintf(temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
74138689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
74238689Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
74337219Sminshall 
74438689Sborman 	    if (len < NETROOM()) {
74538689Sborman 		ring_supply_data(&netoring, temp, len);
74638689Sborman 		printsub('>', temp+2, len - 2);
74737219Sminshall 	    }
74837219Sminshall 	}
74937219Sminshall 	break;
75037219Sminshall     case TELOPT_LFLOW:
75138689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
75238689Sborman 	    return;
75337219Sminshall 	if ((subbuffer[1]&0xff) == 1) {
75437219Sminshall 	    localflow = 1;
75537219Sminshall 	} else if ((subbuffer[1]&0xff) == 0) {
75637219Sminshall 	    localflow = 0;
75737219Sminshall 	}
75837219Sminshall 	setcommandmode();
75938689Sborman 	setconnmode(0);
76037219Sminshall 	break;
76138689Sborman 
76238689Sborman     case TELOPT_LINEMODE:
76338689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
76438689Sborman 	    return;
76538689Sborman 	switch (subbuffer[1]&0xff) {
76638689Sborman 	case WILL:
76738689Sborman 	    lm_will(&subbuffer[2], subend - &subbuffer[2]);
76838689Sborman 	    break;
76938689Sborman 	case WONT:
77038689Sborman 	    lm_wont(&subbuffer[2], subend - &subbuffer[2]);
77138689Sborman 	    break;
77238689Sborman 	case DO:
77338689Sborman 	    lm_do(&subbuffer[2], subend - &subbuffer[2]);
77438689Sborman 	    break;
77538689Sborman 	case DONT:
77638689Sborman 	    lm_dont(&subbuffer[2], subend - &subbuffer[2]);
77738689Sborman 	    break;
77838689Sborman 	case LM_SLC:
77938689Sborman 	    slc(&subbuffer[2], subend - &subbuffer[2]);
78038689Sborman 	    break;
78138689Sborman 	case LM_MODE:
78238689Sborman 	    lm_mode(&subbuffer[2], subend - &subbuffer[2], 0);
78338689Sborman 	    break;
78438689Sborman 	default:
78538689Sborman 		break;
78638689Sborman 	}
78738689Sborman 	break;
78827676Sminshall     default:
78927676Sminshall 	break;
79027676Sminshall     }
79127676Sminshall }
79238689Sborman 
79338689Sborman static char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
79438689Sborman 
79538689Sborman lm_will(cmd, len)
79638689Sborman char *cmd;
79738689Sborman {
79838689Sborman     switch(cmd[0]) {
79938689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
80038689Sborman     default:
80138689Sborman 	str_lm[3] = DONT;
80238689Sborman 	str_lm[4] = cmd[0];
80338689Sborman 	if (NETROOM() > sizeof(str_lm)) {
80438689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
80538689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
80638689Sborman 	}
80738689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
80838689Sborman 	break;
80938689Sborman     }
81038689Sborman }
81138689Sborman 
81238689Sborman lm_wont(cmd, len)
81338689Sborman char *cmd;
81438689Sborman {
81538689Sborman     switch(cmd[0]) {
81638689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
81738689Sborman     default:
81838689Sborman 	/* We are always DONT, so don't respond */
81938689Sborman 	return;
82038689Sborman     }
82138689Sborman }
82238689Sborman 
82338689Sborman lm_do(cmd, len)
82438689Sborman char *cmd;
82538689Sborman {
82638689Sborman     switch(cmd[0]) {
82738689Sborman     case LM_FORWARDMASK:
82838689Sborman     default:
82938689Sborman 	str_lm[3] = WONT;
83038689Sborman 	str_lm[4] = cmd[0];
83138689Sborman 	if (NETROOM() > sizeof(str_lm)) {
83238689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
83338689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
83438689Sborman 	}
83538689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
83638689Sborman 	break;
83738689Sborman     }
83838689Sborman }
83938689Sborman 
84038689Sborman lm_dont(cmd, len)
84138689Sborman char *cmd;
84238689Sborman {
84338689Sborman     switch(cmd[0]) {
84438689Sborman     case LM_FORWARDMASK:
84538689Sborman     default:
84638689Sborman 	/* we are always WONT, so don't respond */
84738689Sborman 	break;
84838689Sborman     }
84938689Sborman }
85038689Sborman 
85138689Sborman static char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE };
85238689Sborman 
85338689Sborman lm_mode(cmd, len, init)
85438689Sborman char *cmd;
85538689Sborman int len, init;
85638689Sborman {
85738689Sborman 	if (len != 1)
85838689Sborman 		return;
85938689Sborman 	if ((linemode&(MODE_EDIT|MODE_TRAPSIG)) == *cmd)
86038689Sborman 		return;
86138689Sborman 	if (*cmd&MODE_ACK)
86238689Sborman 		return;
86338689Sborman 	linemode = (*cmd&(MODE_EDIT|MODE_TRAPSIG));
86438689Sborman 	str_lm_mode[4] = linemode;
86538689Sborman 	if (!init)
86638689Sborman 	    str_lm_mode[4] |= MODE_ACK;
86738689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
86838689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
86938689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
87038689Sborman 	}
87138689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
87238689Sborman 	setconnmode(0);	/* set changed mode */
87338689Sborman }
87438689Sborman 
87532377Sminshall 
87627088Sminshall 
87738689Sborman /*
87838689Sborman  * slc()
87938689Sborman  * Handle special character suboption of LINEMODE.
88038689Sborman  */
88138689Sborman 
88238689Sborman struct spc {
88340245Sborman 	cc_t val;
88440245Sborman 	cc_t *valp;
88538689Sborman 	char flags;	/* Current flags & level */
88638689Sborman 	char mylevel;	/* Maximum level & flags */
88738689Sborman } spc_data[NSLC+1];
88838689Sborman 
88938689Sborman #define SLC_IMPORT	0
89038689Sborman #define	SLC_EXPORT	1
89138689Sborman #define SLC_RVALUE	2
89238689Sborman static int slc_mode = SLC_EXPORT;
89338689Sborman 
89438689Sborman slc_init()
89538689Sborman {
89638689Sborman 	register struct spc *spcp;
89740245Sborman 	extern cc_t *tcval();
89838689Sborman 
89938689Sborman 	localchars = 1;
90038689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
90138689Sborman 		spcp->val = 0;
90238689Sborman 		spcp->valp = 0;
90338689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
90438689Sborman 	}
90538689Sborman 
90638689Sborman #define	initfunc(func, flags) { \
90738689Sborman 					spcp = &spc_data[func]; \
90838689Sborman 					if (spcp->valp = tcval(func)) { \
90938689Sborman 					    spcp->val = *spcp->valp; \
91038689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
91138689Sborman 					} else { \
91238689Sborman 					    spcp->val = 0; \
91338689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
91438689Sborman 					} \
91538689Sborman 				    }
91638689Sborman 
91738689Sborman 	initfunc(SLC_SYNCH, 0);
91838689Sborman 	/* No BRK */
91938689Sborman 	initfunc(SLC_AO, 0);
92038689Sborman 	initfunc(SLC_AYT, 0);
92138689Sborman 	/* No EOR */
92238689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
92338689Sborman 	initfunc(SLC_EOF, 0);
92439529Sborman #ifndef	SYSV_TERMIO
92538689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
92638689Sborman #endif
92738689Sborman 	initfunc(SLC_EC, 0);
92838689Sborman 	initfunc(SLC_EL, 0);
92939529Sborman #ifndef	SYSV_TERMIO
93038689Sborman 	initfunc(SLC_EW, 0);
93138689Sborman 	initfunc(SLC_RP, 0);
93238689Sborman 	initfunc(SLC_LNEXT, 0);
93338689Sborman #endif
93438689Sborman 	initfunc(SLC_XON, 0);
93538689Sborman 	initfunc(SLC_XOFF, 0);
93639529Sborman #ifdef	SYSV_TERMIO
93738689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
93838689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
93938689Sborman #endif
94038689Sborman 	/* No FORW1 */
94138689Sborman 	/* No FORW2 */
94238689Sborman 
94338689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
94438689Sborman #undef	initfunc
94538689Sborman 
94638689Sborman 	if (slc_mode == SLC_EXPORT)
94738689Sborman 		slc_export();
94838689Sborman 	else
94938689Sborman 		slc_import(1);
95038689Sborman 
95138689Sborman }
95238689Sborman 
95338689Sborman slcstate()
95438689Sborman {
95538689Sborman     printf("Special characters are %s values\n",
95638689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
95738689Sborman 		slc_mode == SLC_EXPORT ? "local" :
95838689Sborman 					 "remote");
95938689Sborman }
96038689Sborman 
96138689Sborman slc_mode_export()
96238689Sborman {
96338689Sborman     slc_mode = SLC_EXPORT;
96438689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
96538689Sborman 	slc_export();
96638689Sborman }
96738689Sborman 
96838689Sborman slc_mode_import(def)
96938689Sborman {
97038689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
97138689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
97238689Sborman 	slc_import(def);
97338689Sborman }
97438689Sborman 
97538689Sborman char slc_import_val[] = {
97638689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
97738689Sborman };
97838689Sborman char slc_import_def[] = {
97938689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
98038689Sborman };
98138689Sborman 
98238689Sborman slc_import(def)
98338689Sborman int def;
98438689Sborman {
98538689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
98638689Sborman 	if (def) {
98738689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
98838689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
98938689Sborman 	} else {
99038689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
99138689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
99238689Sborman 	}
99338689Sborman     }
99438689Sborman /*@*/ else printf("slc_import: not enough room\n");
99538689Sborman }
99638689Sborman 
99738689Sborman slc_export()
99838689Sborman {
99938689Sborman     register struct spc *spcp;
100038689Sborman 
100138689Sborman     TerminalDefaultChars();
100238689Sborman 
100338689Sborman     slc_start_reply();
100438689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
100538689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
100638689Sborman 	    spcp->flags = spcp->mylevel;
100738689Sborman 	    if (spcp->valp)
100838689Sborman 		spcp->val = *spcp->valp;
100938689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
101038689Sborman 	}
101138689Sborman     }
101238689Sborman     slc_end_reply();
101338689Sborman     if (slc_update())
101438689Sborman 	setconnmode(1);	/* set the  new character values */
101538689Sborman }
101638689Sborman 
101738689Sborman slc(cp, len)
101838689Sborman register char *cp;
101938689Sborman int len;
102038689Sborman {
102138689Sborman 	register struct spc *spcp;
102238689Sborman 	register int func,level;
102338689Sborman 
102438689Sborman 	slc_start_reply();
102538689Sborman 
102638689Sborman 	for (; len >= 3; len -=3, cp +=3) {
102738689Sborman 
102838689Sborman 		func = cp[SLC_FUNC];
102938689Sborman 
103038689Sborman 		if (func == 0) {
103138689Sborman 			/*
103238689Sborman 			 * Client side: always ignore 0 function.
103338689Sborman 			 */
103438689Sborman 			continue;
103538689Sborman 		}
103638689Sborman 		if (func > NSLC) {
103738689Sborman 			if (cp[SLC_FLAGS] & SLC_LEVELBITS != SLC_NOSUPPORT)
103838689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
103938689Sborman 			continue;
104038689Sborman 		}
104138689Sborman 
104238689Sborman 		spcp = &spc_data[func];
104338689Sborman 
104438689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
104538689Sborman 
104640245Sborman 		if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
104738689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
104838689Sborman 			continue;
104938689Sborman 		}
105038689Sborman 
105138689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
105238689Sborman 			/*
105338689Sborman 			 * This is an error condition, the SLC_ACK
105438689Sborman 			 * bit should never be set for the SLC_DEFAULT
105538689Sborman 			 * level.  Our best guess to recover is to
105638689Sborman 			 * ignore the SLC_ACK bit.
105738689Sborman 			 */
105838689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
105938689Sborman 		}
106038689Sborman 
106138689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
106240245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
106338689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
106438689Sborman 			continue;
106538689Sborman 		}
106638689Sborman 
106738689Sborman 		level &= ~SLC_ACK;
106838689Sborman 
106938689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
107038689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
107140245Sborman 			spcp->val = (cc_t)cp[SLC_VALUE];
107238689Sborman 		}
107338689Sborman 		if (level == SLC_DEFAULT) {
107438689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
107538689Sborman 				spcp->flags = spcp->mylevel;
107638689Sborman 			else
107738689Sborman 				spcp->flags = SLC_NOSUPPORT;
107838689Sborman 		}
107938689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
108038689Sborman 	}
108138689Sborman 	slc_end_reply();
108238689Sborman 	if (slc_update())
108338689Sborman 		setconnmode(1);	/* set the  new character values */
108438689Sborman }
108538689Sborman 
108638689Sborman slc_check()
108738689Sborman {
108838689Sborman     register struct spc *spcp;
108938689Sborman 
109038689Sborman     slc_start_reply();
109138689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
109238689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
109338689Sborman 	    spcp->val = *spcp->valp;
109438689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
109538689Sborman 	}
109638689Sborman     }
109738689Sborman     slc_end_reply();
109838689Sborman     setconnmode(1);
109938689Sborman }
110038689Sborman 
110138689Sborman 
110238689Sborman unsigned char slc_reply[128];
110338689Sborman unsigned char *slc_replyp;
110438689Sborman slc_start_reply()
110538689Sborman {
110638689Sborman 	slc_replyp = slc_reply;
110738689Sborman 	*slc_replyp++ = IAC;
110838689Sborman 	*slc_replyp++ = SB;
110938689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
111038689Sborman 	*slc_replyp++ = LM_SLC;
111138689Sborman }
111238689Sborman 
111338689Sborman slc_add_reply(func, flags, value)
111438689Sborman char func;
111538689Sborman char flags;
111640245Sborman cc_t value;
111738689Sborman {
111838689Sborman 	if ((*slc_replyp++ = func) == IAC)
111938689Sborman 		*slc_replyp++ = IAC;
112038689Sborman 	if ((*slc_replyp++ = flags) == IAC)
112138689Sborman 		*slc_replyp++ = IAC;
112240245Sborman 	if ((*slc_replyp++ = (unsigned char)value) == IAC)
112338689Sborman 		*slc_replyp++ = IAC;
112438689Sborman }
112538689Sborman 
112638689Sborman slc_end_reply()
112738689Sborman {
112838689Sborman     register char *cp;
112938689Sborman     register int len;
113038689Sborman 
113138689Sborman     *slc_replyp++ = IAC;
113238689Sborman     *slc_replyp++ = SE;
113338689Sborman     len = slc_replyp - slc_reply;
113438689Sborman     if (len <= 6)
113538689Sborman 	return;
113638689Sborman     if (NETROOM() > len) {
113738689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
113838689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
113938689Sborman     }
114038689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
114138689Sborman }
114238689Sborman 
114338689Sborman slc_update()
114438689Sborman {
114538689Sborman 	register struct spc *spcp;
114638689Sborman 	int need_update = 0;
114738689Sborman 
114838689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
114938689Sborman 		if (!(spcp->flags&SLC_ACK))
115038689Sborman 			continue;
115138689Sborman 		spcp->flags &= ~SLC_ACK;
115238689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
115338689Sborman 			*spcp->valp = spcp->val;
115438689Sborman 			need_update = 1;
115538689Sborman 		}
115638689Sborman 	}
115738689Sborman 	return(need_update);
115838689Sborman }
115938689Sborman 
116038689Sborman 
116138689Sborman 
116233804Sminshall int
116332377Sminshall telrcv()
116427110Sminshall {
116532377Sminshall     register int c;
116632385Sminshall     register int scc;
116732385Sminshall     register char *sbp;
116832385Sminshall     int count;
116932385Sminshall     int returnValue = 0;
117027088Sminshall 
117132385Sminshall     scc = 0;
117232385Sminshall     count = 0;
117332385Sminshall     while (TTYROOM() > 2) {
117432385Sminshall 	if (scc == 0) {
117532385Sminshall 	    if (count) {
117632528Sminshall 		ring_consumed(&netiring, count);
117732385Sminshall 		returnValue = 1;
117832385Sminshall 		count = 0;
117932385Sminshall 	    }
118032528Sminshall 	    sbp = netiring.consume;
118132528Sminshall 	    scc = ring_full_consecutive(&netiring);
118232385Sminshall 	    if (scc == 0) {
118332385Sminshall 		/* No more data coming in */
118432385Sminshall 		break;
118532385Sminshall 	    }
118632385Sminshall 	}
118732385Sminshall 
118832385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
118932385Sminshall 
119032377Sminshall 	switch (telrcv_state) {
119127110Sminshall 
119232377Sminshall 	case TS_CR:
119332377Sminshall 	    telrcv_state = TS_DATA;
119435518Sminshall 	    if (c == '\0') {
119535518Sminshall 		break;	/* Ignore \0 after CR */
119639529Sborman 	    }
119739529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
119835518Sminshall 		TTYADD(c);
119935518Sminshall 		break;
120032377Sminshall 	    }
120135518Sminshall 	    /* Else, fall through */
120227088Sminshall 
120332377Sminshall 	case TS_DATA:
120432377Sminshall 	    if (c == IAC) {
120532377Sminshall 		telrcv_state = TS_IAC;
120633804Sminshall 		break;
120732377Sminshall 	    }
120832377Sminshall #	    if defined(TN3270)
120932377Sminshall 	    if (In3270) {
121032377Sminshall 		*Ifrontp++ = c;
121132385Sminshall 		while (scc > 0) {
121232385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
121332377Sminshall 		    if (c == IAC) {
121432377Sminshall 			telrcv_state = TS_IAC;
121534304Sminshall 			break;
121632377Sminshall 		    }
121732377Sminshall 		    *Ifrontp++ = c;
121832377Sminshall 		}
121932377Sminshall 	    } else
122032377Sminshall #	    endif /* defined(TN3270) */
122135518Sminshall 		    /*
122235518Sminshall 		     * The 'crmod' hack (see following) is needed
122335518Sminshall 		     * since we can't * set CRMOD on output only.
122435518Sminshall 		     * Machines like MULTICS like to send \r without
122535518Sminshall 		     * \n; since we must turn off CRMOD to get proper
122635518Sminshall 		     * input, the mapping is done here (sigh).
122735518Sminshall 		     */
122838689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
122935518Sminshall 		if (scc > 0) {
123035518Sminshall 		    c = *sbp&0xff;
123135518Sminshall 		    if (c == 0) {
123235518Sminshall 			sbp++, scc--; count++;
123335518Sminshall 			/* a "true" CR */
123432377Sminshall 			TTYADD('\r');
123538689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
123635518Sminshall 					(c == '\n')) {
123735518Sminshall 			sbp++, scc--; count++;
123832377Sminshall 			TTYADD('\n');
123935518Sminshall 		    } else {
124035518Sminshall 			TTYADD('\r');
124135518Sminshall 			if (crmod) {
124235518Sminshall 				TTYADD('\n');
124332377Sminshall 			}
124432377Sminshall 		    }
124535518Sminshall 		} else {
124635518Sminshall 		    telrcv_state = TS_CR;
124735518Sminshall 		    TTYADD('\r');
124835518Sminshall 		    if (crmod) {
124935518Sminshall 			    TTYADD('\n');
125035518Sminshall 		    }
125132377Sminshall 		}
125232377Sminshall 	    } else {
125332377Sminshall 		TTYADD(c);
125432377Sminshall 	    }
125532377Sminshall 	    continue;
125627088Sminshall 
125732377Sminshall 	case TS_IAC:
125838689Sborman process_iac:
125932377Sminshall 	    switch (c) {
126032377Sminshall 
126132377Sminshall 	    case WILL:
126232377Sminshall 		telrcv_state = TS_WILL;
126332377Sminshall 		continue;
126427261Sminshall 
126532377Sminshall 	    case WONT:
126632377Sminshall 		telrcv_state = TS_WONT;
126732377Sminshall 		continue;
126827261Sminshall 
126932377Sminshall 	    case DO:
127032377Sminshall 		telrcv_state = TS_DO;
127132377Sminshall 		continue;
127227261Sminshall 
127332377Sminshall 	    case DONT:
127432377Sminshall 		telrcv_state = TS_DONT;
127532377Sminshall 		continue;
127627261Sminshall 
127732377Sminshall 	    case DM:
127832377Sminshall 		    /*
127932377Sminshall 		     * We may have missed an urgent notification,
128032377Sminshall 		     * so make sure we flush whatever is in the
128132377Sminshall 		     * buffer currently.
128232377Sminshall 		     */
128332377Sminshall 		SYNCHing = 1;
128432377Sminshall 		ttyflush(1);
128532554Sminshall 		SYNCHing = stilloob();
128632377Sminshall 		settimer(gotDM);
128732377Sminshall 		break;
128827088Sminshall 
128932377Sminshall 	    case NOP:
129032377Sminshall 	    case GA:
129132377Sminshall 		break;
129227088Sminshall 
129332377Sminshall 	    case SB:
129432377Sminshall 		SB_CLEAR();
129532377Sminshall 		telrcv_state = TS_SB;
129638689Sborman 		printoption("RCVD", "IAC", SB);
129732377Sminshall 		continue;
129827261Sminshall 
129932377Sminshall #	    if defined(TN3270)
130032377Sminshall 	    case EOR:
130132377Sminshall 		if (In3270) {
130232377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
130332377Sminshall 		    if (Ibackp == Ifrontp) {
130432377Sminshall 			Ibackp = Ifrontp = Ibuf;
130532377Sminshall 			ISend = 0;	/* should have been! */
130632377Sminshall 		    } else {
130732377Sminshall 			ISend = 1;
130827088Sminshall 		    }
130927088Sminshall 		}
131027088Sminshall 		break;
131132377Sminshall #	    endif /* defined(TN3270) */
131232377Sminshall 
131332377Sminshall 	    case IAC:
131432377Sminshall #	    if !defined(TN3270)
131532377Sminshall 		TTYADD(IAC);
131632377Sminshall #	    else /* !defined(TN3270) */
131732377Sminshall 		if (In3270) {
131832377Sminshall 		    *Ifrontp++ = IAC;
131932377Sminshall 		} else {
132032377Sminshall 		    TTYADD(IAC);
132132377Sminshall 		}
132232377Sminshall #	    endif /* !defined(TN3270) */
132327088Sminshall 		break;
132432377Sminshall 
132527088Sminshall 	    default:
132627088Sminshall 		break;
132727088Sminshall 	    }
132832377Sminshall 	    telrcv_state = TS_DATA;
132932377Sminshall 	    continue;
133027088Sminshall 
133132377Sminshall 	case TS_WILL:
133237226Sminshall 	    printoption("RCVD", "will", c);
133338689Sborman 	    willoption(c);
133432377Sminshall 	    SetIn3270();
133532377Sminshall 	    telrcv_state = TS_DATA;
133632377Sminshall 	    continue;
133727110Sminshall 
133832377Sminshall 	case TS_WONT:
133937226Sminshall 	    printoption("RCVD", "wont", c);
134038689Sborman 	    wontoption(c);
134132377Sminshall 	    SetIn3270();
134232377Sminshall 	    telrcv_state = TS_DATA;
134332377Sminshall 	    continue;
134427088Sminshall 
134532377Sminshall 	case TS_DO:
134637226Sminshall 	    printoption("RCVD", "do", c);
134737226Sminshall 	    dooption(c);
134832377Sminshall 	    SetIn3270();
134937219Sminshall 	    if (c == TELOPT_NAWS) {
135037219Sminshall 		sendnaws();
135137219Sminshall 	    } else if (c == TELOPT_LFLOW) {
135237219Sminshall 		localflow = 1;
135337219Sminshall 		setcommandmode();
135438689Sborman 		setconnmode(0);
135537219Sminshall 	    }
135632377Sminshall 	    telrcv_state = TS_DATA;
135732377Sminshall 	    continue;
135827088Sminshall 
135932377Sminshall 	case TS_DONT:
136037226Sminshall 	    printoption("RCVD", "dont", c);
136138689Sborman 	    dontoption(c);
136237226Sminshall 	    flushline = 1;
136338689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
136432377Sminshall 	    SetIn3270();
136532377Sminshall 	    telrcv_state = TS_DATA;
136632377Sminshall 	    continue;
136727088Sminshall 
136832377Sminshall 	case TS_SB:
136932377Sminshall 	    if (c == IAC) {
137032377Sminshall 		telrcv_state = TS_SE;
137132377Sminshall 	    } else {
137232377Sminshall 		SB_ACCUM(c);
137332377Sminshall 	    }
137432377Sminshall 	    continue;
137527088Sminshall 
137632377Sminshall 	case TS_SE:
137732377Sminshall 	    if (c != SE) {
137832377Sminshall 		if (c != IAC) {
137938689Sborman 		    /*
138038689Sborman 		     * This is an error.  We only expect to get
138138689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
138238689Sborman 		     * have happend.  An IAC was not doubled, the
138338689Sborman 		     * IAC SE was left off, or another option got
138438689Sborman 		     * inserted into the suboption are all possibilities.
138538689Sborman 		     * If we assume that the IAC was not doubled,
138638689Sborman 		     * and really the IAC SE was left off, we could
138738689Sborman 		     * get into an infinate loop here.  So, instead,
138838689Sborman 		     * we terminate the suboption, and process the
138938689Sborman 		     * partial suboption if we can.
139038689Sborman 		     */
139138689Sborman 		    SB_TERM();
139232377Sminshall 		    SB_ACCUM(IAC);
139338689Sborman 		    SB_ACCUM(c);
139438689Sborman 		    printoption("In SUBOPTION processing, RCVD", "IAC", c);
139538689Sborman 		    suboption();	/* handle sub-option */
139638689Sborman 		    SetIn3270();
139738689Sborman 		    telrcv_state = TS_IAC;
139838689Sborman 		    goto process_iac;
139932377Sminshall 		}
140032377Sminshall 		SB_ACCUM(c);
140132377Sminshall 		telrcv_state = TS_SB;
140232377Sminshall 	    } else {
140332377Sminshall 		SB_TERM();
140438689Sborman 		SB_ACCUM(IAC);
140538689Sborman 		SB_ACCUM(SE);
140632377Sminshall 		suboption();	/* handle sub-option */
140732377Sminshall 		SetIn3270();
140832377Sminshall 		telrcv_state = TS_DATA;
140932377Sminshall 	    }
141027088Sminshall 	}
141127088Sminshall     }
141232667Sminshall     if (count)
141332667Sminshall 	ring_consumed(&netiring, count);
141432385Sminshall     return returnValue||count;
141527088Sminshall }
141632385Sminshall 
141732385Sminshall static int
141832554Sminshall telsnd()
141932385Sminshall {
142032385Sminshall     int tcc;
142132385Sminshall     int count;
142232385Sminshall     int returnValue = 0;
142332385Sminshall     char *tbp;
142432385Sminshall 
142532385Sminshall     tcc = 0;
142632385Sminshall     count = 0;
142732385Sminshall     while (NETROOM() > 2) {
142832385Sminshall 	register int sc;
142932385Sminshall 	register int c;
143032385Sminshall 
143132385Sminshall 	if (tcc == 0) {
143232385Sminshall 	    if (count) {
143332528Sminshall 		ring_consumed(&ttyiring, count);
143432385Sminshall 		returnValue = 1;
143532385Sminshall 		count = 0;
143632385Sminshall 	    }
143732528Sminshall 	    tbp = ttyiring.consume;
143832528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
143932385Sminshall 	    if (tcc == 0) {
144032385Sminshall 		break;
144132385Sminshall 	    }
144232385Sminshall 	}
144332385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
144432385Sminshall 	if (sc == escape) {
144538689Sborman 	    /*
144638689Sborman 	     * Double escape is a pass through of a single escape character.
144738689Sborman 	     */
144838689Sborman 	    if (tcc && strip(*tbp) == escape) {
144938689Sborman 		tbp++;
145038689Sborman 		tcc--;
145138689Sborman 		count++;
145238689Sborman 	    } else {
145338689Sborman 		command(0, tbp, tcc);
145438689Sborman 		count += tcc;
145538689Sborman 		tcc = 0;
145638689Sborman 		flushline = 1;
145738689Sborman 		break;
145838689Sborman 	    }
145938689Sborman 	}
146038689Sborman #ifdef	KLUDGELINEMODE
146138689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
146232385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
146332385Sminshall 		tcc--; tbp++; count++;
146432385Sminshall 	    } else {
146532385Sminshall 		dontlecho = !dontlecho;
146632385Sminshall 		settimer(echotoggle);
146738689Sborman 		setconnmode(0);
146832385Sminshall 		flushline = 1;
146932385Sminshall 		break;
147032385Sminshall 	    }
147132385Sminshall 	}
147238689Sborman #endif
147338689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
147432385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
147532385Sminshall 		break;
147632385Sminshall 	    }
147732385Sminshall 	}
147838689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
147932385Sminshall 	    switch (c) {
148032385Sminshall 	    case '\n':
148132385Sminshall 		    /*
148232385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
148332385Sminshall 		     * on our local machine, then probably
148432385Sminshall 		     * a newline (unix) is CRLF (TELNET).
148532385Sminshall 		     */
148632385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
148732385Sminshall 		    NETADD('\r');
148832385Sminshall 		}
148932385Sminshall 		NETADD('\n');
149032385Sminshall 		flushline = 1;
149132385Sminshall 		break;
149232385Sminshall 	    case '\r':
149332385Sminshall 		if (!crlf) {
149432385Sminshall 		    NET2ADD('\r', '\0');
149532385Sminshall 		} else {
149632385Sminshall 		    NET2ADD('\r', '\n');
149732385Sminshall 		}
149832385Sminshall 		flushline = 1;
149932385Sminshall 		break;
150032385Sminshall 	    case IAC:
150132385Sminshall 		NET2ADD(IAC, IAC);
150232385Sminshall 		break;
150332385Sminshall 	    default:
150432385Sminshall 		NETADD(c);
150532385Sminshall 		break;
150632385Sminshall 	    }
150732385Sminshall 	} else if (c == IAC) {
150832385Sminshall 	    NET2ADD(IAC, IAC);
150932385Sminshall 	} else {
151032385Sminshall 	    NETADD(c);
151132385Sminshall 	}
151232385Sminshall     }
151332667Sminshall     if (count)
151432667Sminshall 	ring_consumed(&ttyiring, count);
151532385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
151632385Sminshall }
151732377Sminshall 
151827088Sminshall /*
151932377Sminshall  * Scheduler()
152032377Sminshall  *
152132377Sminshall  * Try to do something.
152232377Sminshall  *
152332377Sminshall  * If we do something useful, return 1; else return 0.
152432377Sminshall  *
152527110Sminshall  */
152627110Sminshall 
152727110Sminshall 
152832377Sminshall int
152932377Sminshall Scheduler(block)
153032377Sminshall int	block;			/* should we block in the select ? */
153127110Sminshall {
153232377Sminshall 		/* One wants to be a bit careful about setting returnValue
153332377Sminshall 		 * to one, since a one implies we did some useful work,
153432377Sminshall 		 * and therefore probably won't be called to block next
153532377Sminshall 		 * time (TN3270 mode only).
153632377Sminshall 		 */
153732531Sminshall     int returnValue;
153832531Sminshall     int netin, netout, netex, ttyin, ttyout;
153927110Sminshall 
154032531Sminshall     /* Decide which rings should be processed */
154132531Sminshall 
154232531Sminshall     netout = ring_full_count(&netoring) &&
154338689Sborman 	    (flushline ||
154438689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
154538689Sborman #ifdef	KLUDGELINEMODE
154638689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
154738689Sborman #endif
154838689Sborman 		) ||
154938689Sborman 			my_want_state_is_will(TELOPT_BINARY));
155032531Sminshall     ttyout = ring_full_count(&ttyoring);
155132531Sminshall 
155232377Sminshall #if	defined(TN3270)
155332531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
155432377Sminshall #else	/* defined(TN3270) */
155532531Sminshall     ttyin = ring_empty_count(&ttyiring);
155632377Sminshall #endif	/* defined(TN3270) */
155732531Sminshall 
155832531Sminshall #if	defined(TN3270)
155932531Sminshall     netin = ring_empty_count(&netiring);
156032377Sminshall #   else /* !defined(TN3270) */
156132531Sminshall     netin = !ISend && ring_empty_count(&netiring);
156232377Sminshall #   endif /* !defined(TN3270) */
156332531Sminshall 
156432531Sminshall     netex = !SYNCHing;
156532531Sminshall 
156632531Sminshall     /* If we have seen a signal recently, reset things */
156732377Sminshall #   if defined(TN3270) && defined(unix)
156832377Sminshall     if (HaveInput) {
156932377Sminshall 	HaveInput = 0;
157032377Sminshall 	signal(SIGIO, inputAvailable);
157132377Sminshall     }
157232377Sminshall #endif	/* defined(TN3270) && defined(unix) */
157332377Sminshall 
157432531Sminshall     /* Call to system code to process rings */
157527178Sminshall 
157632531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
157727178Sminshall 
157832531Sminshall     /* Now, look at the input rings, looking for work to do. */
157932377Sminshall 
158032531Sminshall     if (ring_full_count(&ttyiring)) {
158132377Sminshall #   if defined(TN3270)
158232377Sminshall 	if (In3270) {
158334848Sminshall 	    int c;
158434848Sminshall 
158533804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
158632528Sminshall 					ring_full_consecutive(&ttyiring));
158732377Sminshall 	    if (c) {
158832377Sminshall 		returnValue = 1;
158932667Sminshall 	        ring_consumed(&ttyiring, c);
159032377Sminshall 	    }
159132377Sminshall 	} else {
159232377Sminshall #   endif /* defined(TN3270) */
159332554Sminshall 	    returnValue |= telsnd();
159432377Sminshall #   if defined(TN3270)
159527178Sminshall 	}
159632531Sminshall #   endif /* defined(TN3270) */
159727178Sminshall     }
159832377Sminshall 
159932528Sminshall     if (ring_full_count(&netiring)) {
160032377Sminshall #	if !defined(TN3270)
160132385Sminshall 	returnValue |= telrcv();
160232377Sminshall #	else /* !defined(TN3270) */
160332377Sminshall 	returnValue = Push3270();
160432377Sminshall #	endif /* !defined(TN3270) */
160532377Sminshall     }
160632377Sminshall     return returnValue;
160727178Sminshall }
160827178Sminshall 
160927178Sminshall /*
161032377Sminshall  * Select from tty and network...
161127088Sminshall  */
161232377Sminshall void
161332377Sminshall telnet()
161427088Sminshall {
161532531Sminshall     sys_telnet_init();
161627088Sminshall 
161732377Sminshall #   if !defined(TN3270)
161832377Sminshall     if (telnetport) {
161938689Sborman 	send_do(TELOPT_SGA, 1);
162038689Sborman 	send_will(TELOPT_TTYPE, 1);
162138689Sborman 	send_will(TELOPT_NAWS, 1);
162238689Sborman 	send_will(TELOPT_TSPEED, 1);
162338689Sborman 	send_will(TELOPT_LFLOW, 1);
162438689Sborman 	send_will(TELOPT_LINEMODE, 1);
162538908Sborman 	send_do(TELOPT_STATUS, 1);
162627178Sminshall     }
162732377Sminshall #   endif /* !defined(TN3270) */
162827088Sminshall 
162932377Sminshall #   if !defined(TN3270)
163032377Sminshall     for (;;) {
163132385Sminshall 	int schedValue;
163232385Sminshall 
163332385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
163432385Sminshall 	    if (schedValue == -1) {
163532385Sminshall 		setcommandmode();
163632385Sminshall 		return;
163732385Sminshall 	    }
163832385Sminshall 	}
163932385Sminshall 
164032531Sminshall 	if (Scheduler(1) == -1) {
164132377Sminshall 	    setcommandmode();
164232377Sminshall 	    return;
164332377Sminshall 	}
164432377Sminshall     }
164532377Sminshall #   else /* !defined(TN3270) */
164632377Sminshall     for (;;) {
164732377Sminshall 	int schedValue;
164827088Sminshall 
164932377Sminshall 	while (!In3270 && !shell_active) {
165032531Sminshall 	    if (Scheduler(1) == -1) {
165132377Sminshall 		setcommandmode();
165232377Sminshall 		return;
165332377Sminshall 	    }
165427088Sminshall 	}
165532377Sminshall 
165632377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
165732377Sminshall 	    if (schedValue == -1) {
165832377Sminshall 		setcommandmode();
165932377Sminshall 		return;
166032377Sminshall 	    }
166127088Sminshall 	}
166232377Sminshall 		/* If there is data waiting to go out to terminal, don't
166332377Sminshall 		 * schedule any more data for the terminal.
166432377Sminshall 		 */
166534304Sminshall 	if (ring_full_count(&ttyoring)) {
166632377Sminshall 	    schedValue = 1;
166727088Sminshall 	} else {
166832377Sminshall 	    if (shell_active) {
166932377Sminshall 		if (shell_continue() == 0) {
167032377Sminshall 		    ConnectScreen();
167127088Sminshall 		}
167232377Sminshall 	    } else if (In3270) {
167332377Sminshall 		schedValue = DoTerminalOutput();
167432377Sminshall 	    }
167527088Sminshall 	}
167632377Sminshall 	if (schedValue && (shell_active == 0)) {
167732531Sminshall 	    if (Scheduler(1) == -1) {
167832377Sminshall 		setcommandmode();
167932377Sminshall 		return;
168032377Sminshall 	    }
168127088Sminshall 	}
168232377Sminshall     }
168332377Sminshall #   endif /* !defined(TN3270) */
168427088Sminshall }
168532377Sminshall 
168634848Sminshall #if	0	/* XXX - this not being in is a bug */
168727088Sminshall /*
168832554Sminshall  * nextitem()
168932554Sminshall  *
169032554Sminshall  *	Return the address of the next "item" in the TELNET data
169132554Sminshall  * stream.  This will be the address of the next character if
169232554Sminshall  * the current address is a user data character, or it will
169332554Sminshall  * be the address of the character following the TELNET command
169432554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
169532554Sminshall  * character.
169632554Sminshall  */
169732554Sminshall 
169832554Sminshall static char *
169932554Sminshall nextitem(current)
170032554Sminshall char	*current;
170132554Sminshall {
170232554Sminshall     if ((*current&0xff) != IAC) {
170332554Sminshall 	return current+1;
170432554Sminshall     }
170532554Sminshall     switch (*(current+1)&0xff) {
170632554Sminshall     case DO:
170732554Sminshall     case DONT:
170832554Sminshall     case WILL:
170932554Sminshall     case WONT:
171032554Sminshall 	return current+3;
171132554Sminshall     case SB:		/* loop forever looking for the SE */
171232554Sminshall 	{
171332554Sminshall 	    register char *look = current+2;
171432554Sminshall 
171532554Sminshall 	    for (;;) {
171632554Sminshall 		if ((*look++&0xff) == IAC) {
171732554Sminshall 		    if ((*look++&0xff) == SE) {
171832554Sminshall 			return look;
171932554Sminshall 		    }
172032554Sminshall 		}
172132554Sminshall 	    }
172232554Sminshall 	}
172332554Sminshall     default:
172432554Sminshall 	return current+2;
172532554Sminshall     }
172632554Sminshall }
172734848Sminshall #endif	/* 0 */
172832554Sminshall 
172932554Sminshall /*
173032554Sminshall  * netclear()
173132554Sminshall  *
173232554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
173332554Sminshall  * the path to the network.
173432554Sminshall  *
173532554Sminshall  *	Things are a bit tricky since we may have sent the first
173632554Sminshall  * byte or so of a previous TELNET command into the network.
173732554Sminshall  * So, we have to scan the network buffer from the beginning
173832554Sminshall  * until we are up to where we want to be.
173932554Sminshall  *
174032554Sminshall  *	A side effect of what we do, just to keep things
174132554Sminshall  * simple, is to clear the urgent data pointer.  The principal
174232554Sminshall  * caller should be setting the urgent data pointer AFTER calling
174332554Sminshall  * us in any case.
174432554Sminshall  */
174532554Sminshall 
174632554Sminshall static void
174732554Sminshall netclear()
174832554Sminshall {
174932554Sminshall #if	0	/* XXX */
175032554Sminshall     register char *thisitem, *next;
175132554Sminshall     char *good;
175232554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
175332554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
175432554Sminshall 
175532554Sminshall     thisitem = netobuf;
175632554Sminshall 
175732554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
175832554Sminshall 	thisitem = next;
175932554Sminshall     }
176032554Sminshall 
176132554Sminshall     /* Now, thisitem is first before/at boundary. */
176232554Sminshall 
176332554Sminshall     good = netobuf;	/* where the good bytes go */
176432554Sminshall 
176532554Sminshall     while (netoring.add > thisitem) {
176632554Sminshall 	if (wewant(thisitem)) {
176732554Sminshall 	    int length;
176832554Sminshall 
176932554Sminshall 	    next = thisitem;
177032554Sminshall 	    do {
177132554Sminshall 		next = nextitem(next);
177232554Sminshall 	    } while (wewant(next) && (nfrontp > next));
177332554Sminshall 	    length = next-thisitem;
177432554Sminshall 	    memcpy(good, thisitem, length);
177532554Sminshall 	    good += length;
177632554Sminshall 	    thisitem = next;
177732554Sminshall 	} else {
177832554Sminshall 	    thisitem = nextitem(thisitem);
177932554Sminshall 	}
178032554Sminshall     }
178132554Sminshall 
178232554Sminshall #endif	/* 0 */
178332554Sminshall }
178432554Sminshall 
178532554Sminshall /*
178632377Sminshall  * These routines add various telnet commands to the data stream.
178727088Sminshall  */
178832377Sminshall 
178932554Sminshall static void
179032554Sminshall doflush()
179132554Sminshall {
179232554Sminshall     NET2ADD(IAC, DO);
179332554Sminshall     NETADD(TELOPT_TM);
179432554Sminshall     flushline = 1;
179532554Sminshall     flushout = 1;
179632554Sminshall     ttyflush(1);			/* Flush/drop output */
179732554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
179837226Sminshall     printoption("SENT", "do", TELOPT_TM);
179932554Sminshall }
180032554Sminshall 
180132377Sminshall void
180232377Sminshall xmitAO()
180327088Sminshall {
180432377Sminshall     NET2ADD(IAC, AO);
180538908Sborman     printoption("SENT", "IAC", AO);
180632377Sminshall     if (autoflush) {
180732377Sminshall 	doflush();
180832377Sminshall     }
180932377Sminshall }
181027088Sminshall 
181132377Sminshall 
181232377Sminshall void
181332377Sminshall xmitEL()
181427088Sminshall {
181532377Sminshall     NET2ADD(IAC, EL);
181638908Sborman     printoption("SENT", "IAC", EL);
181727088Sminshall }
181827088Sminshall 
181932377Sminshall void
182032377Sminshall xmitEC()
182127088Sminshall {
182232377Sminshall     NET2ADD(IAC, EC);
182338908Sborman     printoption("SENT", "IAC", EC);
182427088Sminshall }
182527088Sminshall 
182632377Sminshall 
182732377Sminshall #if	defined(NOT43)
182832377Sminshall int
182932377Sminshall #else	/* defined(NOT43) */
183032377Sminshall void
183132377Sminshall #endif	/* defined(NOT43) */
183232377Sminshall dosynch()
183327088Sminshall {
183432377Sminshall     netclear();			/* clear the path to the network */
183533294Sminshall     NETADD(IAC);
183633294Sminshall     setneturg();
183733294Sminshall     NETADD(DM);
183838908Sborman     printoption("SENT", "IAC", DM);
183927088Sminshall 
184032377Sminshall #if	defined(NOT43)
184132377Sminshall     return 0;
184232377Sminshall #endif	/* defined(NOT43) */
184327088Sminshall }
184427088Sminshall 
184532377Sminshall void
184638908Sborman get_status()
184738908Sborman {
184838908Sborman     char tmp[16];
184938908Sborman     register char *cp;
185038908Sborman 
185138908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
185238908Sborman 	printf("Remote side does not support STATUS option\n");
185338908Sborman 	return;
185438908Sborman     }
185538908Sborman     if (!showoptions)
185638908Sborman 	printf("You will not see the response unless you set \"options\"\n");
185738908Sborman 
185838908Sborman     cp = tmp;
185938908Sborman 
186038908Sborman     *cp++ = IAC;
186138908Sborman     *cp++ = SB;
186238908Sborman     *cp++ = TELOPT_STATUS;
186338908Sborman     *cp++ = TELQUAL_SEND;
186438908Sborman     *cp++ = IAC;
186538908Sborman     *cp++ = SE;
186638908Sborman     if (NETROOM() >= cp - tmp) {
186738908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
186838908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
186938908Sborman     }
187038908Sborman }
187138908Sborman 
187238908Sborman void
187332377Sminshall intp()
187427088Sminshall {
187532377Sminshall     NET2ADD(IAC, IP);
187638908Sborman     printoption("SENT", "IAC", IP);
187732377Sminshall     flushline = 1;
187832377Sminshall     if (autoflush) {
187932377Sminshall 	doflush();
188032377Sminshall     }
188132377Sminshall     if (autosynch) {
188232377Sminshall 	dosynch();
188332377Sminshall     }
188427088Sminshall }
188527186Sminshall 
188632377Sminshall void
188732377Sminshall sendbrk()
188827186Sminshall {
188932377Sminshall     NET2ADD(IAC, BREAK);
189038908Sborman     printoption("SENT", "IAC", BREAK);
189132377Sminshall     flushline = 1;
189232377Sminshall     if (autoflush) {
189332377Sminshall 	doflush();
189432377Sminshall     }
189532377Sminshall     if (autosynch) {
189632377Sminshall 	dosynch();
189732377Sminshall     }
189827186Sminshall }
189938689Sborman 
190038689Sborman void
190138689Sborman sendabort()
190238689Sborman {
190338689Sborman     NET2ADD(IAC, ABORT);
190438908Sborman     printoption("SENT", "IAC", ABORT);
190538689Sborman     flushline = 1;
190638689Sborman     if (autoflush) {
190738689Sborman 	doflush();
190838689Sborman     }
190938689Sborman     if (autosynch) {
191038689Sborman 	dosynch();
191138689Sborman     }
191238689Sborman }
191338689Sborman 
191438689Sborman void
191538689Sborman sendsusp()
191638689Sborman {
191738689Sborman     NET2ADD(IAC, SUSP);
191838908Sborman     printoption("SENT", "IAC", SUSP);
191938689Sborman     flushline = 1;
192038689Sborman     if (autoflush) {
192138689Sborman 	doflush();
192238689Sborman     }
192338689Sborman     if (autosynch) {
192438689Sborman 	dosynch();
192538689Sborman     }
192638689Sborman }
192738689Sborman 
192838689Sborman void
192938689Sborman sendeof()
193038689Sborman {
193138908Sborman     NET2ADD(IAC, xEOF);
193238908Sborman     printoption("SENT", "IAC", xEOF);
193338689Sborman }
193438689Sborman 
193537219Sminshall /*
193637219Sminshall  * Send a window size update to the remote system.
193737219Sminshall  */
193837219Sminshall 
193937219Sminshall void
194037219Sminshall sendnaws()
194137219Sminshall {
194237219Sminshall     long rows, cols;
194338689Sborman     unsigned char tmp[16];
194438689Sborman     register unsigned char *cp;
194537219Sminshall 
194638689Sborman     if (my_state_is_wont(TELOPT_NAWS))
194738689Sborman 	return;
194837219Sminshall 
194938689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
195038689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
195138689Sborman 
195237219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
195337219Sminshall 	return;
195437219Sminshall     }
195537219Sminshall 
195638689Sborman     cp = tmp;
195738689Sborman 
195838689Sborman     *cp++ = IAC;
195938689Sborman     *cp++ = SB;
196038689Sborman     *cp++ = TELOPT_NAWS;
196138689Sborman     PUTSHORT(cp, cols);
196238689Sborman     PUTSHORT(cp, rows);
196338689Sborman     *cp++ = IAC;
196438689Sborman     *cp++ = SE;
196538689Sborman     if (NETROOM() >= cp - tmp) {
196638689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
196738689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
196837219Sminshall     }
196937219Sminshall }
197037226Sminshall 
197138908Sborman tel_enter_binary(rw)
197238908Sborman int rw;
197337226Sminshall {
197438908Sborman     if (rw&1)
197538908Sborman 	send_do(TELOPT_BINARY, 1);
197638908Sborman     if (rw&2)
197738908Sborman 	send_will(TELOPT_BINARY, 1);
197837226Sminshall }
197937226Sminshall 
198038908Sborman tel_leave_binary(rw)
198138908Sborman int rw;
198237226Sminshall {
198338908Sborman     if (rw&1)
198438908Sborman 	send_dont(TELOPT_BINARY, 1);
198538908Sborman     if (rw&2)
198638908Sborman 	send_wont(TELOPT_BINARY, 1);
198737226Sminshall }
1988