xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 32531)
111758Ssam #ifndef lint
232377Sminshall static char copyright[] =
332377Sminshall "@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
421580Sdist  All rights reserved.\n";
532377Sminshall #endif	/* not lint */
611758Ssam 
721580Sdist #ifndef lint
832377Sminshall static char sccsid[] = "@(#)telnet.c	1.2 (Berkeley) 9/25/87";
932377Sminshall #endif	/* not lint */
1021580Sdist 
119217Ssam #include <sys/types.h>
129217Ssam 
1332377Sminshall #if	defined(unix)
1432377Sminshall /* By the way, we need to include curses.h before telnet.h since,
1532377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
1632377Sminshall  * declared in curses.h.
1732377Sminshall  */
1832377Sminshall #include <curses.h>
1932377Sminshall #endif	/* defined(unix) */
2032377Sminshall 
2112212Ssam #include <arpa/telnet.h>
2232377Sminshall 
2332377Sminshall #if	defined(unix)
2427186Sminshall #include <strings.h>
2532377Sminshall #else	/* defined(unix) */
2632377Sminshall #include <string.h>
2732377Sminshall #endif	/* defined(unix) */
289217Ssam 
2932381Sminshall #include "ring.h"
3032381Sminshall 
3132377Sminshall #include "defines.h"
3232377Sminshall #include "externs.h"
3332377Sminshall #include "types.h"
3432377Sminshall #include "general.h"
3527178Sminshall 
3627178Sminshall 
3727228Sminshall #define	strip(x)	((x)&0x7f)
386000Sroot 
3927088Sminshall 
4032377Sminshall static char	subbuffer[SUBBUFSIZE],
4132377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
4227676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
4327676Sminshall #define	SB_TERM()	subend = subpointer;
4427676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
4527676Sminshall 				*subpointer++ = (c); \
4627676Sminshall 			}
4727676Sminshall 
486000Sroot char	hisopts[256];
496000Sroot char	myopts[256];
506000Sroot 
516000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
526000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
536000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
546000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
556000Sroot 
5632377Sminshall int
5732377Sminshall 	connected,
5832377Sminshall 	showoptions,
5932377Sminshall 	In3270,		/* Are we in 3270 mode? */
6032377Sminshall 	ISend,		/* trying to send network data in */
6132377Sminshall 	debug = 0,
6232377Sminshall 	crmod,
6332377Sminshall 	netdata,	/* Print out network data flow */
6432377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
6532377Sminshall 	noasynch = 0,	/* User specified "-noasynch" on command line */
6632377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
67*32531Sminshall 	telnetport = 1,
68*32531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
69*32531Sminshall 	flushout,	/* flush output */
70*32531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
71*32531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
72*32531Sminshall 	localchars,	/* we recognize interrupt/quit */
73*32531Sminshall 	donelclchars,	/* the user has set "localchars" */
74*32531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
75*32531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
76*32531Sminshall 	globalmode;
7727088Sminshall 
7832377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
796000Sroot 
8032377Sminshall char
8132377Sminshall 	*prompt = 0,
8232377Sminshall 	escape,
8332377Sminshall 	echoc;
8427186Sminshall 
8527186Sminshall /*
866000Sroot  * Telnet receiver states for fsm
876000Sroot  */
886000Sroot #define	TS_DATA		0
896000Sroot #define	TS_IAC		1
906000Sroot #define	TS_WILL		2
916000Sroot #define	TS_WONT		3
926000Sroot #define	TS_DO		4
936000Sroot #define	TS_DONT		5
9427021Sminshall #define	TS_CR		6
9527676Sminshall #define	TS_SB		7		/* sub-option collection */
9627676Sminshall #define	TS_SE		8		/* looking for sub-option end */
976000Sroot 
9832377Sminshall static int	telrcv_state;
996000Sroot 
10032377Sminshall jmp_buf	toplevel = { 0 };
10132377Sminshall jmp_buf	peerdied;
1026000Sroot 
10332377Sminshall int	flushline;
10427021Sminshall 
10532377Sminshall /*
10632377Sminshall  * The following are some clocks used to decide how to interpret
10732377Sminshall  * the relationship between various variables.
10832377Sminshall  */
1096000Sroot 
11032377Sminshall Clocks clocks;
11132377Sminshall 
11232377Sminshall Modelist modelist[] = {
11332377Sminshall 	{ "telnet command mode", COMMAND_LINE },
11432377Sminshall 	{ "character-at-a-time mode", 0 },
11532377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
11632377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
11732377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
11832377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
11932377Sminshall 	{ "3270 mode", 0 },
12032377Sminshall };
1216000Sroot 
12232377Sminshall 
12332377Sminshall /*
12432377Sminshall  * Initialize telnet environment.
12532377Sminshall  */
1266000Sroot 
12732377Sminshall init_telnet()
12832377Sminshall {
12932377Sminshall     /* Don't change telnetport */
13032377Sminshall     SB_CLEAR();
13132377Sminshall     ClearArray(hisopts);
13232377Sminshall     ClearArray(myopts);
1336000Sroot 
13432385Sminshall     connected = net = In3270 = ISend = donebinarytoggle = 0;
13532377Sminshall     telnetport = 0;
1366000Sroot 
13732377Sminshall #if	defined(unix) && defined(TN3270)
13832377Sminshall     HaveInput = 0;
13932377Sminshall #endif	/* defined(unix) && defined(TN3270) */
1406000Sroot 
14132377Sminshall     SYNCHing = 0;
1426000Sroot 
14332377Sminshall     /* Don't change NetTrace */
1446000Sroot 
14532377Sminshall     escape = CONTROL(']');
14632377Sminshall     echoc = CONTROL('E');
1476000Sroot 
14832377Sminshall     flushline = 1;
14932377Sminshall     telrcv_state = TS_DATA;
15032377Sminshall }
1516000Sroot 
1526000Sroot 
15332377Sminshall void
15427676Sminshall willoption(option, reply)
15527676Sminshall 	int option, reply;
1566000Sroot {
1576000Sroot 	char *fmt;
1586000Sroot 
1596000Sroot 	switch (option) {
1606000Sroot 
1616000Sroot 	case TELOPT_ECHO:
16232377Sminshall #	if defined(TN3270)
16332377Sminshall 	    /*
16432377Sminshall 	     * The following is a pain in the rear-end.
16532377Sminshall 	     * Various IBM servers (some versions of Wiscnet,
16632377Sminshall 	     * possibly Fibronics/Spartacus, and who knows who
16732377Sminshall 	     * else) will NOT allow us to send "DO SGA" too early
16832377Sminshall 	     * in the setup proceedings.  On the other hand,
16932377Sminshall 	     * 4.2 servers (telnetd) won't set SGA correctly.
17032377Sminshall 	     * So, we are stuck.  Empirically (but, based on
17132377Sminshall 	     * a VERY small sample), the IBM servers don't send
17232377Sminshall 	     * out anything about ECHO, so we postpone our sending
17332377Sminshall 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
17432377Sminshall 	     * DO send).
17532377Sminshall 	     */
17632377Sminshall 	    {
17732377Sminshall 		if (askedSGA == 0) {
17832377Sminshall 		    askedSGA = 1;
17932377Sminshall 		    if (!hisopts[TELOPT_SGA]) {
18032377Sminshall 			willoption(TELOPT_SGA, 0);
18132377Sminshall 		    }
18232377Sminshall 		}
18332377Sminshall 	    }
18432377Sminshall 		/* Fall through */
18532377Sminshall 	case TELOPT_EOR:
18632377Sminshall 	case TELOPT_BINARY:
18732377Sminshall #endif	/* defined(TN3270) */
1886000Sroot 	case TELOPT_SGA:
18927110Sminshall 		settimer(modenegotiated);
1906000Sroot 		hisopts[option] = 1;
1916000Sroot 		fmt = doopt;
19227110Sminshall 		setconnmode();		/* possibly set new tty mode */
1936000Sroot 		break;
1946000Sroot 
1956000Sroot 	case TELOPT_TM:
19627110Sminshall 		return;			/* Never reply to TM will's/wont's */
1976000Sroot 
1986000Sroot 	default:
1996000Sroot 		fmt = dont;
2006000Sroot 		break;
2016000Sroot 	}
20232381Sminshall 	netoprint(fmt, option);
20327676Sminshall 	if (reply)
20432377Sminshall 		printoption(">SENT", fmt, option, reply);
20527676Sminshall 	else
20632377Sminshall 		printoption("<SENT", fmt, option, reply);
2076000Sroot }
2086000Sroot 
20932377Sminshall void
21027676Sminshall wontoption(option, reply)
21127676Sminshall 	int option, reply;
2126000Sroot {
2136000Sroot 	char *fmt;
2146000Sroot 
2156000Sroot 	switch (option) {
2166000Sroot 
2176000Sroot 	case TELOPT_ECHO:
2186000Sroot 	case TELOPT_SGA:
21927110Sminshall 		settimer(modenegotiated);
2206000Sroot 		hisopts[option] = 0;
2216000Sroot 		fmt = dont;
22227110Sminshall 		setconnmode();			/* Set new tty mode */
2236000Sroot 		break;
2246000Sroot 
22527110Sminshall 	case TELOPT_TM:
22627110Sminshall 		return;		/* Never reply to TM will's/wont's */
22727110Sminshall 
2286000Sroot 	default:
2296000Sroot 		fmt = dont;
2306000Sroot 	}
23132381Sminshall 	netoprint(fmt, option);
23227676Sminshall 	if (reply)
23332377Sminshall 		printoption(">SENT", fmt, option, reply);
23427676Sminshall 	else
23532377Sminshall 		printoption("<SENT", fmt, option, reply);
2366000Sroot }
2376000Sroot 
23832377Sminshall static void
2396000Sroot dooption(option)
2406000Sroot 	int option;
2416000Sroot {
2426000Sroot 	char *fmt;
2436000Sroot 
2446000Sroot 	switch (option) {
2456000Sroot 
2466000Sroot 	case TELOPT_TM:
24713231Ssam 		fmt = will;
24813231Ssam 		break;
24913231Ssam 
25032377Sminshall #	if defined(TN3270)
25132377Sminshall 	case TELOPT_EOR:
25232377Sminshall 	case TELOPT_BINARY:
25332377Sminshall #	endif	/* defined(TN3270) */
25427676Sminshall 	case TELOPT_TTYPE:		/* terminal type option */
25527110Sminshall 	case TELOPT_SGA:		/* no big deal */
2566000Sroot 		fmt = will;
25727110Sminshall 		myopts[option] = 1;
2586000Sroot 		break;
2596000Sroot 
26027110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
2616000Sroot 	default:
2626000Sroot 		fmt = wont;
2636000Sroot 		break;
2646000Sroot 	}
26532381Sminshall 	netoprint(fmt, option);
26632377Sminshall 	printoption(">SENT", fmt, option, 0);
2676000Sroot }
26827676Sminshall 
26927676Sminshall /*
27027676Sminshall  * suboption()
27127676Sminshall  *
27227676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
27327676Sminshall  * side.
27427676Sminshall  *
27527676Sminshall  *	Currently we recognize:
27627676Sminshall  *
27727676Sminshall  *		Terminal type, send request.
27827676Sminshall  */
27927676Sminshall 
28032377Sminshall static void
28127676Sminshall suboption()
28227676Sminshall {
28332377Sminshall     printsub("<", subbuffer, subend-subbuffer+1);
28427676Sminshall     switch (subbuffer[0]&0xff) {
28527676Sminshall     case TELOPT_TTYPE:
28627676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
28727676Sminshall 	    ;
28827676Sminshall 	} else {
28927676Sminshall 	    char *name;
29027676Sminshall 	    char namebuf[41];
29132377Sminshall 	    extern char *getenv();
29227676Sminshall 	    int len;
29327676Sminshall 
29432377Sminshall #if	defined(TN3270)
295*32531Sminshall 	    if (tn3270_ttype()) {
29632377Sminshall 		return;
29732377Sminshall 	    }
29832377Sminshall #endif	/* defined(TN3270) */
29927676Sminshall 	    name = getenv("TERM");
30027676Sminshall 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
30127676Sminshall 		name = "UNKNOWN";
30227676Sminshall 	    }
30327676Sminshall 	    if ((len + 4+2) < NETROOM()) {
30427676Sminshall 		strcpy(namebuf, name);
30527676Sminshall 		upcase(namebuf);
30632381Sminshall 		netoprint("%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
30727676Sminshall 				    TELQUAL_IS, namebuf, IAC, SE);
30832381Sminshall 		/* XXX */
30932381Sminshall 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
31032377Sminshall 	    } else {
31132381Sminshall 		ExitString("No room in buffer for terminal type.\n",
31232377Sminshall 							1);
31332377Sminshall 		/*NOTREACHED*/
31427676Sminshall 	    }
31527676Sminshall 	}
31627676Sminshall 
31727676Sminshall     default:
31827676Sminshall 	break;
31927676Sminshall     }
32027676Sminshall }
32132377Sminshall 
32227088Sminshall 
32332385Sminshall static int
32432377Sminshall telrcv()
32527110Sminshall {
32632377Sminshall     register int c;
32732385Sminshall     register int scc;
32832385Sminshall     register char *sbp;
32932385Sminshall     int count;
33032385Sminshall     int returnValue = 0;
33127088Sminshall 
33232385Sminshall     scc = 0;
33332385Sminshall     count = 0;
33432385Sminshall     while (TTYROOM() > 2) {
33532385Sminshall 	if (scc == 0) {
33632385Sminshall 	    if (count) {
33732528Sminshall 		ring_consumed(&netiring, count);
33832385Sminshall 		returnValue = 1;
33932385Sminshall 		count = 0;
34032385Sminshall 	    }
34132528Sminshall 	    sbp = netiring.consume;
34232528Sminshall 	    scc = ring_full_consecutive(&netiring);
34332385Sminshall 	    if (scc == 0) {
34432385Sminshall 		/* No more data coming in */
34532385Sminshall 		break;
34632385Sminshall 	    }
34732385Sminshall 	}
34832385Sminshall 
34932385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
35032385Sminshall 
35132377Sminshall 	switch (telrcv_state) {
35227110Sminshall 
35332377Sminshall 	case TS_CR:
35432377Sminshall 	    telrcv_state = TS_DATA;
35532377Sminshall 	    if (c == '\0') {
35632377Sminshall 		break;	/* Ignore \0 after CR */
35732377Sminshall 	    } else if (c == '\n') {
35832377Sminshall 		if (hisopts[TELOPT_ECHO] && !crmod) {
35932377Sminshall 		    TTYADD(c);
36032377Sminshall 		}
36132377Sminshall 		break;
36232377Sminshall 	    }
36332377Sminshall 	    /* Else, fall through */
36427088Sminshall 
36532377Sminshall 	case TS_DATA:
36632377Sminshall 	    if (c == IAC) {
36732377Sminshall 		telrcv_state = TS_IAC;
36832377Sminshall 		continue;
36932377Sminshall 	    }
37032377Sminshall #	    if defined(TN3270)
37132377Sminshall 	    if (In3270) {
37232377Sminshall 		*Ifrontp++ = c;
37332385Sminshall 		while (scc > 0) {
37432385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
37532377Sminshall 		    if (c == IAC) {
37632377Sminshall 			telrcv_state = TS_IAC;
37732377Sminshall 			break;
37832377Sminshall 		    }
37932377Sminshall 		    *Ifrontp++ = c;
38032377Sminshall 		}
38132377Sminshall 	    } else
38232377Sminshall #	    endif /* defined(TN3270) */
38332377Sminshall 		    /*
38432377Sminshall 		     * The 'crmod' hack (see following) is needed
38532377Sminshall 		     * since we can't * set CRMOD on output only.
38632377Sminshall 		     * Machines like MULTICS like to send \r without
38732377Sminshall 		     * \n; since we must turn off CRMOD to get proper
38832377Sminshall 		     * input, the mapping is done here (sigh).
38932377Sminshall 		     */
39032377Sminshall 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
39132377Sminshall 		if (scc > 0) {
39232377Sminshall 		    c = *sbp&0xff;
39332377Sminshall 		    if (c == 0) {
39432385Sminshall 			sbp++, scc--; count++;
39532377Sminshall 			/* a "true" CR */
39632377Sminshall 			TTYADD('\r');
39732377Sminshall 		    } else if (!hisopts[TELOPT_ECHO] &&
39832377Sminshall 					(c == '\n')) {
39932385Sminshall 			sbp++, scc--; count++;
40032377Sminshall 			TTYADD('\n');
40132377Sminshall 		    } else {
40232377Sminshall 			TTYADD('\r');
40332377Sminshall 			if (crmod) {
40432377Sminshall 				TTYADD('\n');
40532377Sminshall 			}
40632377Sminshall 		    }
40732377Sminshall 		} else {
40832377Sminshall 		    telrcv_state = TS_CR;
40932377Sminshall 		    TTYADD('\r');
41032377Sminshall 		    if (crmod) {
41132377Sminshall 			    TTYADD('\n');
41232377Sminshall 		    }
41332377Sminshall 		}
41432377Sminshall 	    } else {
41532377Sminshall 		TTYADD(c);
41632377Sminshall 	    }
41732377Sminshall 	    continue;
41827088Sminshall 
41932377Sminshall 	case TS_IAC:
42032377Sminshall 	    switch (c) {
42132377Sminshall 
42232377Sminshall 	    case WILL:
42332377Sminshall 		telrcv_state = TS_WILL;
42432377Sminshall 		continue;
42527261Sminshall 
42632377Sminshall 	    case WONT:
42732377Sminshall 		telrcv_state = TS_WONT;
42832377Sminshall 		continue;
42927261Sminshall 
43032377Sminshall 	    case DO:
43132377Sminshall 		telrcv_state = TS_DO;
43232377Sminshall 		continue;
43327261Sminshall 
43432377Sminshall 	    case DONT:
43532377Sminshall 		telrcv_state = TS_DONT;
43632377Sminshall 		continue;
43727261Sminshall 
43832377Sminshall 	    case DM:
43932377Sminshall 		    /*
44032377Sminshall 		     * We may have missed an urgent notification,
44132377Sminshall 		     * so make sure we flush whatever is in the
44232377Sminshall 		     * buffer currently.
44332377Sminshall 		     */
44432377Sminshall 		SYNCHing = 1;
44532377Sminshall 		ttyflush(1);
44632377Sminshall 		SYNCHing = stilloob(net);
44732377Sminshall 		settimer(gotDM);
44832377Sminshall 		break;
44927088Sminshall 
45032377Sminshall 	    case NOP:
45132377Sminshall 	    case GA:
45232377Sminshall 		break;
45327088Sminshall 
45432377Sminshall 	    case SB:
45532377Sminshall 		SB_CLEAR();
45632377Sminshall 		telrcv_state = TS_SB;
45732377Sminshall 		continue;
45827261Sminshall 
45932377Sminshall #	    if defined(TN3270)
46032377Sminshall 	    case EOR:
46132377Sminshall 		if (In3270) {
46232377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
46332377Sminshall 		    if (Ibackp == Ifrontp) {
46432377Sminshall 			Ibackp = Ifrontp = Ibuf;
46532377Sminshall 			ISend = 0;	/* should have been! */
46632377Sminshall 		    } else {
46732377Sminshall 			ISend = 1;
46827088Sminshall 		    }
46927088Sminshall 		}
47027088Sminshall 		break;
47132377Sminshall #	    endif /* defined(TN3270) */
47232377Sminshall 
47332377Sminshall 	    case IAC:
47432377Sminshall #	    if !defined(TN3270)
47532377Sminshall 		TTYADD(IAC);
47632377Sminshall #	    else /* !defined(TN3270) */
47732377Sminshall 		if (In3270) {
47832377Sminshall 		    *Ifrontp++ = IAC;
47932377Sminshall 		} else {
48032377Sminshall 		    TTYADD(IAC);
48132377Sminshall 		}
48232377Sminshall #	    endif /* !defined(TN3270) */
48327088Sminshall 		break;
48432377Sminshall 
48527088Sminshall 	    default:
48627088Sminshall 		break;
48727088Sminshall 	    }
48832377Sminshall 	    telrcv_state = TS_DATA;
48932377Sminshall 	    continue;
49027088Sminshall 
49132377Sminshall 	case TS_WILL:
49232377Sminshall 	    printoption(">RCVD", will, c, !hisopts[c]);
49332377Sminshall 	    if (c == TELOPT_TM) {
49432377Sminshall 		if (flushout) {
49532377Sminshall 		    flushout = 0;
49632377Sminshall 		}
49732377Sminshall 	    } else if (!hisopts[c]) {
49832377Sminshall 		willoption(c, 1);
49932377Sminshall 	    }
50032377Sminshall 	    SetIn3270();
50132377Sminshall 	    telrcv_state = TS_DATA;
50232377Sminshall 	    continue;
50327110Sminshall 
50432377Sminshall 	case TS_WONT:
50532377Sminshall 	    printoption(">RCVD", wont, c, hisopts[c]);
50632377Sminshall 	    if (c == TELOPT_TM) {
50732377Sminshall 		if (flushout) {
50832377Sminshall 		    flushout = 0;
50932377Sminshall 		}
51032377Sminshall 	    } else if (hisopts[c]) {
51132377Sminshall 		wontoption(c, 1);
51232377Sminshall 	    }
51332377Sminshall 	    SetIn3270();
51432377Sminshall 	    telrcv_state = TS_DATA;
51532377Sminshall 	    continue;
51627088Sminshall 
51732377Sminshall 	case TS_DO:
51832377Sminshall 	    printoption(">RCVD", doopt, c, !myopts[c]);
51932377Sminshall 	    if (!myopts[c])
52032377Sminshall 		dooption(c);
52132377Sminshall 	    SetIn3270();
52232377Sminshall 	    telrcv_state = TS_DATA;
52332377Sminshall 	    continue;
52427088Sminshall 
52532377Sminshall 	case TS_DONT:
52632377Sminshall 	    printoption(">RCVD", dont, c, myopts[c]);
52732377Sminshall 	    if (myopts[c]) {
52832377Sminshall 		myopts[c] = 0;
52932381Sminshall 		netoprint(wont, c);
53032377Sminshall 		flushline = 1;
53132377Sminshall 		setconnmode();	/* set new tty mode (maybe) */
53232377Sminshall 		printoption(">SENT", wont, c, 0);
53332377Sminshall 	    }
53432377Sminshall 	    SetIn3270();
53532377Sminshall 	    telrcv_state = TS_DATA;
53632377Sminshall 	    continue;
53727088Sminshall 
53832377Sminshall 	case TS_SB:
53932377Sminshall 	    if (c == IAC) {
54032377Sminshall 		telrcv_state = TS_SE;
54132377Sminshall 	    } else {
54232377Sminshall 		SB_ACCUM(c);
54332377Sminshall 	    }
54432377Sminshall 	    continue;
54527088Sminshall 
54632377Sminshall 	case TS_SE:
54732377Sminshall 	    if (c != SE) {
54832377Sminshall 		if (c != IAC) {
54932377Sminshall 		    SB_ACCUM(IAC);
55032377Sminshall 		}
55132377Sminshall 		SB_ACCUM(c);
55232377Sminshall 		telrcv_state = TS_SB;
55332377Sminshall 	    } else {
55432377Sminshall 		SB_TERM();
55532377Sminshall 		suboption();	/* handle sub-option */
55632377Sminshall 		SetIn3270();
55732377Sminshall 		telrcv_state = TS_DATA;
55832377Sminshall 	    }
55927088Sminshall 	}
56027088Sminshall     }
56132528Sminshall     ring_consumed(&netiring, count);
56232385Sminshall     return returnValue||count;
56327088Sminshall }
56432385Sminshall 
56532385Sminshall static int
56632385Sminshall telsnd(ring)
56732385Sminshall Ring	*ring;			/* Input ring */
56832385Sminshall {
56932385Sminshall     int tcc;
57032385Sminshall     int count;
57132385Sminshall     int returnValue = 0;
57232385Sminshall     char *tbp;
57332385Sminshall 
57432385Sminshall     tcc = 0;
57532385Sminshall     count = 0;
57632385Sminshall     while (NETROOM() > 2) {
57732385Sminshall 	register int sc;
57832385Sminshall 	register int c;
57932385Sminshall 
58032385Sminshall 	if (tcc == 0) {
58132385Sminshall 	    if (count) {
58232528Sminshall 		ring_consumed(&ttyiring, count);
58332385Sminshall 		returnValue = 1;
58432385Sminshall 		count = 0;
58532385Sminshall 	    }
58632528Sminshall 	    tbp = ttyiring.consume;
58732528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
58832385Sminshall 	    if (tcc == 0) {
58932385Sminshall 		break;
59032385Sminshall 	    }
59132385Sminshall 	}
59232385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
59332385Sminshall 	if (sc == escape) {
59432385Sminshall 	    command(0);
59532385Sminshall 	    tcc = 0;
59632385Sminshall 	    flushline = 1;
59732385Sminshall 	    break;
59832385Sminshall 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
59932385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
60032385Sminshall 		tcc--; tbp++; count++;
60132385Sminshall 	    } else {
60232385Sminshall 		dontlecho = !dontlecho;
60332385Sminshall 		settimer(echotoggle);
60432385Sminshall 		setconnmode();
60532385Sminshall 		flushline = 1;
60632385Sminshall 		break;
60732385Sminshall 	    }
60832385Sminshall 	}
60932385Sminshall 	if (localchars) {
61032385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
61132385Sminshall 		break;
61232385Sminshall 	    }
61332385Sminshall 	}
61432385Sminshall 	if (!myopts[TELOPT_BINARY]) {
61532385Sminshall 	    switch (c) {
61632385Sminshall 	    case '\n':
61732385Sminshall 		    /*
61832385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
61932385Sminshall 		     * on our local machine, then probably
62032385Sminshall 		     * a newline (unix) is CRLF (TELNET).
62132385Sminshall 		     */
62232385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
62332385Sminshall 		    NETADD('\r');
62432385Sminshall 		}
62532385Sminshall 		NETADD('\n');
62632385Sminshall 		flushline = 1;
62732385Sminshall 		break;
62832385Sminshall 	    case '\r':
62932385Sminshall 		if (!crlf) {
63032385Sminshall 		    NET2ADD('\r', '\0');
63132385Sminshall 		} else {
63232385Sminshall 		    NET2ADD('\r', '\n');
63332385Sminshall 		}
63432385Sminshall 		flushline = 1;
63532385Sminshall 		break;
63632385Sminshall 	    case IAC:
63732385Sminshall 		NET2ADD(IAC, IAC);
63832385Sminshall 		break;
63932385Sminshall 	    default:
64032385Sminshall 		NETADD(c);
64132385Sminshall 		break;
64232385Sminshall 	    }
64332385Sminshall 	} else if (c == IAC) {
64432385Sminshall 	    NET2ADD(IAC, IAC);
64532385Sminshall 	} else {
64632385Sminshall 	    NETADD(c);
64732385Sminshall 	}
64832385Sminshall     }
64932528Sminshall     ring_consumed(&ttyiring, count);
65032385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
65132385Sminshall }
65232377Sminshall 
65327088Sminshall /*
65432377Sminshall  * Scheduler()
65532377Sminshall  *
65632377Sminshall  * Try to do something.
65732377Sminshall  *
65832377Sminshall  * If we do something useful, return 1; else return 0.
65932377Sminshall  *
66027110Sminshall  */
66127110Sminshall 
66227110Sminshall 
66332377Sminshall int
66432377Sminshall Scheduler(block)
66532377Sminshall int	block;			/* should we block in the select ? */
66627110Sminshall {
66732377Sminshall     register int c;
66832377Sminshall 		/* One wants to be a bit careful about setting returnValue
66932377Sminshall 		 * to one, since a one implies we did some useful work,
67032377Sminshall 		 * and therefore probably won't be called to block next
67132377Sminshall 		 * time (TN3270 mode only).
67232377Sminshall 		 */
673*32531Sminshall     int returnValue;
674*32531Sminshall     int netin, netout, netex, ttyin, ttyout;
67527110Sminshall 
676*32531Sminshall     /* Decide which rings should be processed */
677*32531Sminshall 
678*32531Sminshall     netout = ring_full_count(&netoring) &&
679*32531Sminshall 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
680*32531Sminshall     ttyout = ring_full_count(&ttyoring);
681*32531Sminshall 
68232377Sminshall #if	defined(TN3270)
683*32531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
68432377Sminshall #else	/* defined(TN3270) */
685*32531Sminshall     ttyin = ring_empty_count(&ttyiring);
68632377Sminshall #endif	/* defined(TN3270) */
687*32531Sminshall 
688*32531Sminshall #if	defined(TN3270)
689*32531Sminshall     netin = ring_empty_count(&netiring);
69032377Sminshall #   else /* !defined(TN3270) */
691*32531Sminshall     netin = !ISend && ring_empty_count(&netiring);
69232377Sminshall #   endif /* !defined(TN3270) */
693*32531Sminshall 
694*32531Sminshall     netex = !SYNCHing;
695*32531Sminshall 
696*32531Sminshall     /* If we have seen a signal recently, reset things */
69732377Sminshall #   if defined(TN3270) && defined(unix)
69832377Sminshall     if (HaveInput) {
69932377Sminshall 	HaveInput = 0;
70032377Sminshall 	signal(SIGIO, inputAvailable);
70132377Sminshall     }
70232377Sminshall #endif	/* defined(TN3270) && defined(unix) */
70332377Sminshall 
704*32531Sminshall     /* Call to system code to process rings */
70527178Sminshall 
706*32531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
70727178Sminshall 
708*32531Sminshall     /* Now, look at the input rings, looking for work to do. */
70932377Sminshall 
710*32531Sminshall     if (ring_full_count(&ttyiring)) {
71132377Sminshall #   if defined(TN3270)
71232377Sminshall 	if (In3270) {
71332385Sminshall 	    c = DataFromTerminal(ttyiring.send,
71432528Sminshall 					ring_full_consecutive(&ttyiring));
71532377Sminshall 	    if (c) {
71632377Sminshall 		returnValue = 1;
71732377Sminshall 	    }
71832528Sminshall 	    ring_consumed(&ttyiring, c);
71932377Sminshall 	} else {
72032377Sminshall #   endif /* defined(TN3270) */
72132385Sminshall 	    returnValue |= telsnd(&ttyiring);
72232377Sminshall #   if defined(TN3270)
72327178Sminshall 	}
724*32531Sminshall #   endif /* defined(TN3270) */
72527178Sminshall     }
72632377Sminshall 
72732528Sminshall     if (ring_full_count(&netiring)) {
72832377Sminshall #	if !defined(TN3270)
72932385Sminshall 	returnValue |= telrcv();
73032377Sminshall #	else /* !defined(TN3270) */
73132377Sminshall 	returnValue = Push3270();
73232377Sminshall #	endif /* !defined(TN3270) */
73332377Sminshall     }
73432377Sminshall     return returnValue;
73527178Sminshall }
73627178Sminshall 
73727178Sminshall /*
73832377Sminshall  * Select from tty and network...
73927088Sminshall  */
74032377Sminshall void
74132377Sminshall telnet()
74227088Sminshall {
743*32531Sminshall     sys_telnet_init();
74427088Sminshall 
74532377Sminshall #   if !defined(TN3270)
74632377Sminshall     if (telnetport) {
74732377Sminshall 	if (!hisopts[TELOPT_SGA]) {
74832377Sminshall 	    willoption(TELOPT_SGA, 0);
74927110Sminshall 	}
75032377Sminshall 	if (!myopts[TELOPT_TTYPE]) {
75132377Sminshall 	    dooption(TELOPT_TTYPE, 0);
75232377Sminshall 	}
75327178Sminshall     }
75432377Sminshall #   endif /* !defined(TN3270) */
75527088Sminshall 
75632377Sminshall #   if !defined(TN3270)
75732377Sminshall     for (;;) {
75832385Sminshall 	int schedValue;
75932385Sminshall 
76032385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
76132385Sminshall 	    if (schedValue == -1) {
76232385Sminshall 		setcommandmode();
76332385Sminshall 		return;
76432385Sminshall 	    }
76532385Sminshall 	}
76632385Sminshall 
767*32531Sminshall 	if (Scheduler(1) == -1) {
76832377Sminshall 	    setcommandmode();
76932377Sminshall 	    return;
77032377Sminshall 	}
77132377Sminshall     }
77232377Sminshall #   else /* !defined(TN3270) */
77332377Sminshall     for (;;) {
77432377Sminshall 	int schedValue;
77527088Sminshall 
77632377Sminshall 	while (!In3270 && !shell_active) {
777*32531Sminshall 	    if (Scheduler(1) == -1) {
77832377Sminshall 		setcommandmode();
77932377Sminshall 		return;
78032377Sminshall 	    }
78127088Sminshall 	}
78232377Sminshall 
78332377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
78432377Sminshall 	    if (schedValue == -1) {
78532377Sminshall 		setcommandmode();
78632377Sminshall 		return;
78732377Sminshall 	    }
78827088Sminshall 	}
78932377Sminshall 		/* If there is data waiting to go out to terminal, don't
79032377Sminshall 		 * schedule any more data for the terminal.
79132377Sminshall 		 */
79232377Sminshall 	if (tfrontp-tbackp) {
79332377Sminshall 	    schedValue = 1;
79427088Sminshall 	} else {
79532377Sminshall 	    if (shell_active) {
79632377Sminshall 		if (shell_continue() == 0) {
79732377Sminshall 		    ConnectScreen();
79827088Sminshall 		}
79932377Sminshall 	    } else if (In3270) {
80032377Sminshall 		schedValue = DoTerminalOutput();
80132377Sminshall 	    }
80227088Sminshall 	}
80332377Sminshall 	if (schedValue && (shell_active == 0)) {
804*32531Sminshall 	    if (Scheduler(1) == -1) {
80532377Sminshall 		setcommandmode();
80632377Sminshall 		return;
80732377Sminshall 	    }
80827088Sminshall 	}
80932377Sminshall     }
81032377Sminshall #   endif /* !defined(TN3270) */
81127088Sminshall }
81232377Sminshall 
81327088Sminshall /*
81432377Sminshall  * These routines add various telnet commands to the data stream.
81527088Sminshall  */
81632377Sminshall 
81732377Sminshall void
81832377Sminshall xmitAO()
81927088Sminshall {
82032377Sminshall     NET2ADD(IAC, AO);
82132377Sminshall     if (autoflush) {
82232377Sminshall 	doflush();
82332377Sminshall     }
82432377Sminshall }
82527088Sminshall 
82632377Sminshall 
82732377Sminshall void
82832377Sminshall xmitEL()
82927088Sminshall {
83032377Sminshall     NET2ADD(IAC, EL);
83127088Sminshall }
83227088Sminshall 
83332377Sminshall void
83432377Sminshall xmitEC()
83527088Sminshall {
83632377Sminshall     NET2ADD(IAC, EC);
83727088Sminshall }
83827088Sminshall 
83932377Sminshall 
84032377Sminshall #if	defined(NOT43)
84132377Sminshall int
84232377Sminshall #else	/* defined(NOT43) */
84332377Sminshall void
84432377Sminshall #endif	/* defined(NOT43) */
84532377Sminshall dosynch()
84627088Sminshall {
84732377Sminshall     netclear();			/* clear the path to the network */
84832377Sminshall     NET2ADD(IAC, DM);
84927088Sminshall 
85032377Sminshall #if	defined(NOT43)
85132377Sminshall     return 0;
85232377Sminshall #endif	/* defined(NOT43) */
85327088Sminshall }
85427088Sminshall 
85532377Sminshall void
85632377Sminshall doflush()
85727088Sminshall {
85832377Sminshall     NET2ADD(IAC, DO);
85932377Sminshall     NETADD(TELOPT_TM);
86032377Sminshall     flushline = 1;
86132377Sminshall     flushout = 1;
86232377Sminshall     ttyflush(1);			/* Flush/drop output */
86332377Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
86432377Sminshall     printoption("<SENT", doopt, TELOPT_TM, 0);
86527088Sminshall }
86627088Sminshall 
86732377Sminshall void
86832377Sminshall intp()
86927088Sminshall {
87032377Sminshall     NET2ADD(IAC, IP);
87132377Sminshall     flushline = 1;
87232377Sminshall     if (autoflush) {
87332377Sminshall 	doflush();
87432377Sminshall     }
87532377Sminshall     if (autosynch) {
87632377Sminshall 	dosynch();
87732377Sminshall     }
87827088Sminshall }
87927186Sminshall 
88032377Sminshall void
88132377Sminshall sendbrk()
88227186Sminshall {
88332377Sminshall     NET2ADD(IAC, BREAK);
88432377Sminshall     flushline = 1;
88532377Sminshall     if (autoflush) {
88632377Sminshall 	doflush();
88732377Sminshall     }
88832377Sminshall     if (autosynch) {
88932377Sminshall 	dosynch();
89032377Sminshall     }
89127186Sminshall }
892