xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 32528)
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>
1232377Sminshall #include <sys/time.h>
139217Ssam #include <sys/socket.h>
149217Ssam 
159217Ssam #include <netinet/in.h>
169217Ssam 
1732377Sminshall #if	defined(unix)
1832377Sminshall /* By the way, we need to include curses.h before telnet.h since,
1932377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2032377Sminshall  * declared in curses.h.
2132377Sminshall  */
2232377Sminshall #include <curses.h>
2332377Sminshall #endif	/* defined(unix) */
2432377Sminshall 
2512212Ssam #include <arpa/telnet.h>
2632377Sminshall 
2732377Sminshall #if	!defined(NOT43)
2827186Sminshall #include <arpa/inet.h>
2932377Sminshall #else	/* !defined(NOT43) */
3032377Sminshall extern unsigned long inet_addr();
3132377Sminshall extern char	*inet_ntoa();
3232377Sminshall #endif	/* !defined(NOT43) */
3312212Ssam 
346000Sroot #include <ctype.h>
356000Sroot #include <errno.h>
368345Ssam #include <netdb.h>
3732377Sminshall 
3832377Sminshall #if	defined(unix)
3927186Sminshall #include <strings.h>
4032377Sminshall #else	/* defined(unix) */
4132377Sminshall #include <string.h>
4232377Sminshall #endif	/* defined(unix) */
439217Ssam 
4432381Sminshall #include "ring.h"
4532381Sminshall 
4632377Sminshall #include "defines.h"
4732377Sminshall #include "externs.h"
4832377Sminshall #include "types.h"
4932377Sminshall #include "general.h"
5027178Sminshall 
5127178Sminshall 
5232377Sminshall void	setcommandmode(), command();	/* forward declarations */
5332377Sminshall 
5427676Sminshall #ifndef	FD_SETSIZE
5527178Sminshall /*
5627178Sminshall  * The following is defined just in case someone should want to run
5727178Sminshall  * this telnet on a 4.2 system.
5827178Sminshall  *
5927178Sminshall  */
6027178Sminshall 
6127676Sminshall #define	FD_SET(n, p)	((p)->fds_bits[0] |= (1<<(n)))
6227676Sminshall #define	FD_CLR(n, p)	((p)->fds_bits[0] &= ~(1<<(n)))
6327676Sminshall #define	FD_ISSET(n, p)	((p)->fds_bits[0] & (1<<(n)))
6427676Sminshall #define FD_ZERO(p)	((p)->fds_bits[0] = 0)
6527178Sminshall 
6627178Sminshall #endif
6727178Sminshall 
6827228Sminshall #define	strip(x)	((x)&0x7f)
6932377Sminshall #define min(x,y)	((x<y)? x:y)
706000Sroot 
7132377Sminshall #if	defined(TN3270)
7232377Sminshall static char	Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
7332377Sminshall #endif	/* defined(TN3270) */
7427088Sminshall 
756000Sroot 
7632377Sminshall static char	subbuffer[SUBBUFSIZE],
7732377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
7827676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
7927676Sminshall #define	SB_TERM()	subend = subpointer;
8027676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
8127676Sminshall 				*subpointer++ = (c); \
8227676Sminshall 			}
8327676Sminshall 
8432377Sminshall static char	sb_terminal[] = { IAC, SB,
8532377Sminshall 			TELOPT_TTYPE, TELQUAL_IS,
8632377Sminshall 			'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
8732377Sminshall 			IAC, SE };
8832377Sminshall #define	SBTERMMODEL	13
8932377Sminshall 
9032377Sminshall 
916000Sroot char	hisopts[256];
926000Sroot char	myopts[256];
936000Sroot 
946000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
956000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
966000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
976000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
986000Sroot 
9932385Sminshall static Ring	netiring, ttyiring;
10032385Sminshall static char	netibuf[BUFSIZ];
10132385Sminshall static char	ttyibuf[BUFSIZ];
10232377Sminshall static fd_set ibits, obits, xbits;
10327088Sminshall 
10427088Sminshall 
10532377Sminshall int
10632377Sminshall 	connected,
10732377Sminshall 	net,
10832377Sminshall 	showoptions,
10932377Sminshall 	In3270,		/* Are we in 3270 mode? */
11032377Sminshall 	ISend,		/* trying to send network data in */
11132377Sminshall 	debug = 0,
11232377Sminshall 	crmod,
11332377Sminshall 	netdata,	/* Print out network data flow */
11432377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
11532377Sminshall 	noasynch = 0,	/* User specified "-noasynch" on command line */
11632377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
11732377Sminshall 	telnetport = 1;
11827088Sminshall 
11932377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
1206000Sroot 
12132377Sminshall char
12232377Sminshall 	*prompt = 0,
12332377Sminshall 	escape,
12432377Sminshall 	echoc;
12527186Sminshall 
12632377Sminshall int
12732377Sminshall 	SYNCHing,		/* we are in TELNET SYNCH mode */
12832377Sminshall 	flushout,		/* flush output */
12932377Sminshall 	autoflush = 0,		/* flush output when interrupting? */
13032377Sminshall 	autosynch,		/* send interrupt characters with SYNCH? */
13132377Sminshall 	localchars,		/* we recognize interrupt/quit */
13232377Sminshall 	donelclchars,		/* the user has set "localchars" */
13332377Sminshall 	donebinarytoggle,	/* the user has put us in binary */
13432377Sminshall 	dontlecho,		/* do we suppress local echoing right now? */
13532377Sminshall 	globalmode;
1366000Sroot 
13732377Sminshall /*	The following are some tn3270 specific flags */
13832377Sminshall #if	defined(TN3270)
1396000Sroot 
14032377Sminshall static int
14132377Sminshall 	Sent3270TerminalType;	/* Have we said we are a 3270? */
1426000Sroot 
14332377Sminshall #endif	/* defined(TN3270) */
14427186Sminshall int
14527186Sminshall 
14632377Sminshall 	tout,			/* Output file descriptor */
14732377Sminshall 	tin;			/* Input file descriptor */
14827186Sminshall 
14927186Sminshall 
15027186Sminshall 
15127186Sminshall /*
1526000Sroot  * Telnet receiver states for fsm
1536000Sroot  */
1546000Sroot #define	TS_DATA		0
1556000Sroot #define	TS_IAC		1
1566000Sroot #define	TS_WILL		2
1576000Sroot #define	TS_WONT		3
1586000Sroot #define	TS_DO		4
1596000Sroot #define	TS_DONT		5
16027021Sminshall #define	TS_CR		6
16127676Sminshall #define	TS_SB		7		/* sub-option collection */
16227676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1636000Sroot 
16432377Sminshall static int	telrcv_state;
1656000Sroot 
16632377Sminshall jmp_buf	toplevel = { 0 };
16732377Sminshall jmp_buf	peerdied;
1686000Sroot 
16932377Sminshall int	flushline;
17027021Sminshall 
17132377Sminshall /*
17232377Sminshall  * The following are some clocks used to decide how to interpret
17332377Sminshall  * the relationship between various variables.
17432377Sminshall  */
1756000Sroot 
17632377Sminshall Clocks clocks;
17732377Sminshall 
17832377Sminshall Modelist modelist[] = {
17932377Sminshall 	{ "telnet command mode", COMMAND_LINE },
18032377Sminshall 	{ "character-at-a-time mode", 0 },
18132377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
18232377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
18332377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
18432377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
18532377Sminshall 	{ "3270 mode", 0 },
18632377Sminshall };
1876000Sroot 
18832377Sminshall 
18932377Sminshall /*
19032377Sminshall  * Initialize telnet environment.
19132377Sminshall  */
1926000Sroot 
19332377Sminshall init_telnet()
19432377Sminshall {
19532377Sminshall     /* Don't change telnetport */
19632377Sminshall     SB_CLEAR();
19732377Sminshall     ClearArray(hisopts);
19832377Sminshall     ClearArray(myopts);
19932385Sminshall     ring_init(&netiring, netibuf, sizeof netibuf);
20032385Sminshall     ring_init(&ttyiring, ttyibuf, sizeof ttyibuf);
2016000Sroot 
20232385Sminshall     connected = net = In3270 = ISend = donebinarytoggle = 0;
20332377Sminshall     telnetport = 0;
2046000Sroot 
20532377Sminshall #if	defined(unix) && defined(TN3270)
20632377Sminshall     HaveInput = 0;
20732377Sminshall #endif	/* defined(unix) && defined(TN3270) */
2086000Sroot 
20932377Sminshall     SYNCHing = 0;
2106000Sroot 
21132377Sminshall     errno = 0;
21227676Sminshall 
21332377Sminshall     /* Don't change NetTrace */
2146000Sroot 
21532377Sminshall     escape = CONTROL(']');
21632377Sminshall     echoc = CONTROL('E');
2176000Sroot 
21832377Sminshall     flushline = 1;
21932377Sminshall     telrcv_state = TS_DATA;
22032377Sminshall }
2216000Sroot 
2226000Sroot 
22332377Sminshall void
22427676Sminshall willoption(option, reply)
22527676Sminshall 	int option, reply;
2266000Sroot {
2276000Sroot 	char *fmt;
2286000Sroot 
2296000Sroot 	switch (option) {
2306000Sroot 
2316000Sroot 	case TELOPT_ECHO:
23232377Sminshall #	if defined(TN3270)
23332377Sminshall 	    /*
23432377Sminshall 	     * The following is a pain in the rear-end.
23532377Sminshall 	     * Various IBM servers (some versions of Wiscnet,
23632377Sminshall 	     * possibly Fibronics/Spartacus, and who knows who
23732377Sminshall 	     * else) will NOT allow us to send "DO SGA" too early
23832377Sminshall 	     * in the setup proceedings.  On the other hand,
23932377Sminshall 	     * 4.2 servers (telnetd) won't set SGA correctly.
24032377Sminshall 	     * So, we are stuck.  Empirically (but, based on
24132377Sminshall 	     * a VERY small sample), the IBM servers don't send
24232377Sminshall 	     * out anything about ECHO, so we postpone our sending
24332377Sminshall 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
24432377Sminshall 	     * DO send).
24532377Sminshall 	     */
24632377Sminshall 	    {
24732377Sminshall 		if (askedSGA == 0) {
24832377Sminshall 		    askedSGA = 1;
24932377Sminshall 		    if (!hisopts[TELOPT_SGA]) {
25032377Sminshall 			willoption(TELOPT_SGA, 0);
25132377Sminshall 		    }
25232377Sminshall 		}
25332377Sminshall 	    }
25432377Sminshall 		/* Fall through */
25532377Sminshall 	case TELOPT_EOR:
25632377Sminshall 	case TELOPT_BINARY:
25732377Sminshall #endif	/* defined(TN3270) */
2586000Sroot 	case TELOPT_SGA:
25927110Sminshall 		settimer(modenegotiated);
2606000Sroot 		hisopts[option] = 1;
2616000Sroot 		fmt = doopt;
26227110Sminshall 		setconnmode();		/* possibly set new tty mode */
2636000Sroot 		break;
2646000Sroot 
2656000Sroot 	case TELOPT_TM:
26627110Sminshall 		return;			/* Never reply to TM will's/wont's */
2676000Sroot 
2686000Sroot 	default:
2696000Sroot 		fmt = dont;
2706000Sroot 		break;
2716000Sroot 	}
27232381Sminshall 	netoprint(fmt, option);
27327676Sminshall 	if (reply)
27432377Sminshall 		printoption(">SENT", fmt, option, reply);
27527676Sminshall 	else
27632377Sminshall 		printoption("<SENT", fmt, option, reply);
2776000Sroot }
2786000Sroot 
27932377Sminshall void
28027676Sminshall wontoption(option, reply)
28127676Sminshall 	int option, reply;
2826000Sroot {
2836000Sroot 	char *fmt;
2846000Sroot 
2856000Sroot 	switch (option) {
2866000Sroot 
2876000Sroot 	case TELOPT_ECHO:
2886000Sroot 	case TELOPT_SGA:
28927110Sminshall 		settimer(modenegotiated);
2906000Sroot 		hisopts[option] = 0;
2916000Sroot 		fmt = dont;
29227110Sminshall 		setconnmode();			/* Set new tty mode */
2936000Sroot 		break;
2946000Sroot 
29527110Sminshall 	case TELOPT_TM:
29627110Sminshall 		return;		/* Never reply to TM will's/wont's */
29727110Sminshall 
2986000Sroot 	default:
2996000Sroot 		fmt = dont;
3006000Sroot 	}
30132381Sminshall 	netoprint(fmt, option);
30227676Sminshall 	if (reply)
30332377Sminshall 		printoption(">SENT", fmt, option, reply);
30427676Sminshall 	else
30532377Sminshall 		printoption("<SENT", fmt, option, reply);
3066000Sroot }
3076000Sroot 
30832377Sminshall static void
3096000Sroot dooption(option)
3106000Sroot 	int option;
3116000Sroot {
3126000Sroot 	char *fmt;
3136000Sroot 
3146000Sroot 	switch (option) {
3156000Sroot 
3166000Sroot 	case TELOPT_TM:
31713231Ssam 		fmt = will;
31813231Ssam 		break;
31913231Ssam 
32032377Sminshall #	if defined(TN3270)
32132377Sminshall 	case TELOPT_EOR:
32232377Sminshall 	case TELOPT_BINARY:
32332377Sminshall #	endif	/* defined(TN3270) */
32427676Sminshall 	case TELOPT_TTYPE:		/* terminal type option */
32527110Sminshall 	case TELOPT_SGA:		/* no big deal */
3266000Sroot 		fmt = will;
32727110Sminshall 		myopts[option] = 1;
3286000Sroot 		break;
3296000Sroot 
33027110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
3316000Sroot 	default:
3326000Sroot 		fmt = wont;
3336000Sroot 		break;
3346000Sroot 	}
33532381Sminshall 	netoprint(fmt, option);
33632377Sminshall 	printoption(">SENT", fmt, option, 0);
3376000Sroot }
33827676Sminshall 
33927676Sminshall /*
34027676Sminshall  * suboption()
34127676Sminshall  *
34227676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
34327676Sminshall  * side.
34427676Sminshall  *
34527676Sminshall  *	Currently we recognize:
34627676Sminshall  *
34727676Sminshall  *		Terminal type, send request.
34827676Sminshall  */
34927676Sminshall 
35032377Sminshall static void
35127676Sminshall suboption()
35227676Sminshall {
35332377Sminshall     printsub("<", subbuffer, subend-subbuffer+1);
35427676Sminshall     switch (subbuffer[0]&0xff) {
35527676Sminshall     case TELOPT_TTYPE:
35627676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
35727676Sminshall 	    ;
35827676Sminshall 	} else {
35927676Sminshall 	    char *name;
36027676Sminshall 	    char namebuf[41];
36132377Sminshall 	    extern char *getenv();
36227676Sminshall 	    int len;
36327676Sminshall 
36432377Sminshall #if	defined(TN3270)
36532377Sminshall 	    /*
36632377Sminshall 	     * Try to send a 3270 type terminal name.  Decide which one based
36732377Sminshall 	     * on the format of our screen, and (in the future) color
36832377Sminshall 	     * capaiblities.
36932377Sminshall 	     */
37032377Sminshall #if	defined(unix)
37132377Sminshall 	    if (initscr() != ERR) {	/* Initialize curses to get line size */
37232377Sminshall 		MaxNumberLines = LINES;
37332377Sminshall 		MaxNumberColumns = COLS;
37432377Sminshall 	    }
37532377Sminshall #else	/* defined(unix) */
37632377Sminshall 	    InitTerminal();
37732377Sminshall #endif	/* defined(unix) */
37832377Sminshall 	    if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
37932377Sminshall 		Sent3270TerminalType = 1;
38032377Sminshall 		if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
38132377Sminshall 		    MaxNumberLines = 27;
38232377Sminshall 		    MaxNumberColumns = 132;
38332377Sminshall 		    sb_terminal[SBTERMMODEL] = '5';
38432377Sminshall 		} else if (MaxNumberLines >= 43) {
38532377Sminshall 		    MaxNumberLines = 43;
38632377Sminshall 		    MaxNumberColumns = 80;
38732377Sminshall 		    sb_terminal[SBTERMMODEL] = '4';
38832377Sminshall 		} else if (MaxNumberLines >= 32) {
38932377Sminshall 		    MaxNumberLines = 32;
39032377Sminshall 		    MaxNumberColumns = 80;
39132377Sminshall 		    sb_terminal[SBTERMMODEL] = '3';
39232377Sminshall 		} else {
39332377Sminshall 		    MaxNumberLines = 24;
39432377Sminshall 		    MaxNumberColumns = 80;
39532377Sminshall 		    sb_terminal[SBTERMMODEL] = '2';
39632377Sminshall 		}
39732377Sminshall 		NumberLines = 24;		/* before we start out... */
39832377Sminshall 		NumberColumns = 80;
39932377Sminshall 		ScreenSize = NumberLines*NumberColumns;
40032377Sminshall 		if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
40132381Sminshall 		    ExitString("Programming error:  MAXSCREENSIZE too small.\n",
40232381Sminshall 									1);
40332377Sminshall 		    /*NOTREACHED*/
40432377Sminshall 		}
40532381Sminshall 		printsub(">", sb_terminal+2, sizeof sb_terminal-2);
406*32528Sminshall 		ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal);
40732377Sminshall 		return;
40832377Sminshall 	    }
40932377Sminshall #endif	/* defined(TN3270) */
41032377Sminshall 
41127676Sminshall 	    name = getenv("TERM");
41227676Sminshall 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
41327676Sminshall 		name = "UNKNOWN";
41427676Sminshall 	    }
41527676Sminshall 	    if ((len + 4+2) < NETROOM()) {
41627676Sminshall 		strcpy(namebuf, name);
41727676Sminshall 		upcase(namebuf);
41832381Sminshall 		netoprint("%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
41927676Sminshall 				    TELQUAL_IS, namebuf, IAC, SE);
42032381Sminshall 		/* XXX */
42132381Sminshall 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
42232377Sminshall 	    } else {
42332381Sminshall 		ExitString("No room in buffer for terminal type.\n",
42432377Sminshall 							1);
42532377Sminshall 		/*NOTREACHED*/
42627676Sminshall 	    }
42727676Sminshall 	}
42827676Sminshall 
42927676Sminshall     default:
43027676Sminshall 	break;
43127676Sminshall     }
43227676Sminshall }
43327088Sminshall 
43432377Sminshall #if	defined(TN3270)
43532377Sminshall static void
43632377Sminshall SetIn3270()
43727088Sminshall {
43832377Sminshall     if (Sent3270TerminalType && myopts[TELOPT_BINARY]
43932377Sminshall 			    && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
44032377Sminshall 	if (!In3270) {
44132377Sminshall 	    In3270 = 1;
44232377Sminshall 	    Init3270();		/* Initialize 3270 functions */
44332377Sminshall 	    /* initialize terminal key mapping */
44432377Sminshall 	    InitTerminal();	/* Start terminal going */
44532377Sminshall 	    setconnmode();
44632377Sminshall 	}
44732377Sminshall     } else {
44832377Sminshall 	if (In3270) {
44932377Sminshall 	    StopScreen(1);
45032377Sminshall 	    In3270 = 0;
45132377Sminshall 	    Stop3270();		/* Tell 3270 we aren't here anymore */
45232377Sminshall 	    setconnmode();
45332377Sminshall 	}
45427228Sminshall     }
45527088Sminshall }
45632377Sminshall #endif	/* defined(TN3270) */
45732377Sminshall 
45827088Sminshall 
45932385Sminshall static int
46032377Sminshall telrcv()
46127110Sminshall {
46232377Sminshall     register int c;
46332385Sminshall     register int scc;
46432385Sminshall     register char *sbp;
46532385Sminshall     int count;
46632385Sminshall     int returnValue = 0;
46727088Sminshall 
46832385Sminshall     scc = 0;
46932385Sminshall     count = 0;
47032385Sminshall     while (TTYROOM() > 2) {
47132385Sminshall 	if (scc == 0) {
47232385Sminshall 	    if (count) {
473*32528Sminshall 		ring_consumed(&netiring, count);
47432385Sminshall 		returnValue = 1;
47532385Sminshall 		count = 0;
47632385Sminshall 	    }
477*32528Sminshall 	    sbp = netiring.consume;
478*32528Sminshall 	    scc = ring_full_consecutive(&netiring);
47932385Sminshall 	    if (scc == 0) {
48032385Sminshall 		/* No more data coming in */
48132385Sminshall 		break;
48232385Sminshall 	    }
48332385Sminshall 	}
48432385Sminshall 
48532385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
48632385Sminshall 
48732377Sminshall 	switch (telrcv_state) {
48827110Sminshall 
48932377Sminshall 	case TS_CR:
49032377Sminshall 	    telrcv_state = TS_DATA;
49132377Sminshall 	    if (c == '\0') {
49232377Sminshall 		break;	/* Ignore \0 after CR */
49332377Sminshall 	    } else if (c == '\n') {
49432377Sminshall 		if (hisopts[TELOPT_ECHO] && !crmod) {
49532377Sminshall 		    TTYADD(c);
49632377Sminshall 		}
49732377Sminshall 		break;
49832377Sminshall 	    }
49932377Sminshall 	    /* Else, fall through */
50027088Sminshall 
50132377Sminshall 	case TS_DATA:
50232377Sminshall 	    if (c == IAC) {
50332377Sminshall 		telrcv_state = TS_IAC;
50432377Sminshall 		continue;
50532377Sminshall 	    }
50632377Sminshall #	    if defined(TN3270)
50732377Sminshall 	    if (In3270) {
50832377Sminshall 		*Ifrontp++ = c;
50932385Sminshall 		while (scc > 0) {
51032385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
51132377Sminshall 		    if (c == IAC) {
51232377Sminshall 			telrcv_state = TS_IAC;
51332377Sminshall 			break;
51432377Sminshall 		    }
51532377Sminshall 		    *Ifrontp++ = c;
51632377Sminshall 		}
51732377Sminshall 	    } else
51832377Sminshall #	    endif /* defined(TN3270) */
51932377Sminshall 		    /*
52032377Sminshall 		     * The 'crmod' hack (see following) is needed
52132377Sminshall 		     * since we can't * set CRMOD on output only.
52232377Sminshall 		     * Machines like MULTICS like to send \r without
52332377Sminshall 		     * \n; since we must turn off CRMOD to get proper
52432377Sminshall 		     * input, the mapping is done here (sigh).
52532377Sminshall 		     */
52632377Sminshall 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
52732377Sminshall 		if (scc > 0) {
52832377Sminshall 		    c = *sbp&0xff;
52932377Sminshall 		    if (c == 0) {
53032385Sminshall 			sbp++, scc--; count++;
53132377Sminshall 			/* a "true" CR */
53232377Sminshall 			TTYADD('\r');
53332377Sminshall 		    } else if (!hisopts[TELOPT_ECHO] &&
53432377Sminshall 					(c == '\n')) {
53532385Sminshall 			sbp++, scc--; count++;
53632377Sminshall 			TTYADD('\n');
53732377Sminshall 		    } else {
53832377Sminshall 			TTYADD('\r');
53932377Sminshall 			if (crmod) {
54032377Sminshall 				TTYADD('\n');
54132377Sminshall 			}
54232377Sminshall 		    }
54332377Sminshall 		} else {
54432377Sminshall 		    telrcv_state = TS_CR;
54532377Sminshall 		    TTYADD('\r');
54632377Sminshall 		    if (crmod) {
54732377Sminshall 			    TTYADD('\n');
54832377Sminshall 		    }
54932377Sminshall 		}
55032377Sminshall 	    } else {
55132377Sminshall 		TTYADD(c);
55232377Sminshall 	    }
55332377Sminshall 	    continue;
55427088Sminshall 
55532377Sminshall 	case TS_IAC:
55632377Sminshall 	    switch (c) {
55732377Sminshall 
55832377Sminshall 	    case WILL:
55932377Sminshall 		telrcv_state = TS_WILL;
56032377Sminshall 		continue;
56127261Sminshall 
56232377Sminshall 	    case WONT:
56332377Sminshall 		telrcv_state = TS_WONT;
56432377Sminshall 		continue;
56527261Sminshall 
56632377Sminshall 	    case DO:
56732377Sminshall 		telrcv_state = TS_DO;
56832377Sminshall 		continue;
56927261Sminshall 
57032377Sminshall 	    case DONT:
57132377Sminshall 		telrcv_state = TS_DONT;
57232377Sminshall 		continue;
57327261Sminshall 
57432377Sminshall 	    case DM:
57532377Sminshall 		    /*
57632377Sminshall 		     * We may have missed an urgent notification,
57732377Sminshall 		     * so make sure we flush whatever is in the
57832377Sminshall 		     * buffer currently.
57932377Sminshall 		     */
58032377Sminshall 		SYNCHing = 1;
58132377Sminshall 		ttyflush(1);
58232377Sminshall 		SYNCHing = stilloob(net);
58332377Sminshall 		settimer(gotDM);
58432377Sminshall 		break;
58527088Sminshall 
58632377Sminshall 	    case NOP:
58732377Sminshall 	    case GA:
58832377Sminshall 		break;
58927088Sminshall 
59032377Sminshall 	    case SB:
59132377Sminshall 		SB_CLEAR();
59232377Sminshall 		telrcv_state = TS_SB;
59332377Sminshall 		continue;
59427261Sminshall 
59532377Sminshall #	    if defined(TN3270)
59632377Sminshall 	    case EOR:
59732377Sminshall 		if (In3270) {
59832377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
59932377Sminshall 		    if (Ibackp == Ifrontp) {
60032377Sminshall 			Ibackp = Ifrontp = Ibuf;
60132377Sminshall 			ISend = 0;	/* should have been! */
60232377Sminshall 		    } else {
60332377Sminshall 			ISend = 1;
60427088Sminshall 		    }
60527088Sminshall 		}
60627088Sminshall 		break;
60732377Sminshall #	    endif /* defined(TN3270) */
60832377Sminshall 
60932377Sminshall 	    case IAC:
61032377Sminshall #	    if !defined(TN3270)
61132377Sminshall 		TTYADD(IAC);
61232377Sminshall #	    else /* !defined(TN3270) */
61332377Sminshall 		if (In3270) {
61432377Sminshall 		    *Ifrontp++ = IAC;
61532377Sminshall 		} else {
61632377Sminshall 		    TTYADD(IAC);
61732377Sminshall 		}
61832377Sminshall #	    endif /* !defined(TN3270) */
61927088Sminshall 		break;
62032377Sminshall 
62127088Sminshall 	    default:
62227088Sminshall 		break;
62327088Sminshall 	    }
62432377Sminshall 	    telrcv_state = TS_DATA;
62532377Sminshall 	    continue;
62627088Sminshall 
62732377Sminshall 	case TS_WILL:
62832377Sminshall 	    printoption(">RCVD", will, c, !hisopts[c]);
62932377Sminshall 	    if (c == TELOPT_TM) {
63032377Sminshall 		if (flushout) {
63132377Sminshall 		    flushout = 0;
63232377Sminshall 		}
63332377Sminshall 	    } else if (!hisopts[c]) {
63432377Sminshall 		willoption(c, 1);
63532377Sminshall 	    }
63632377Sminshall 	    SetIn3270();
63732377Sminshall 	    telrcv_state = TS_DATA;
63832377Sminshall 	    continue;
63927110Sminshall 
64032377Sminshall 	case TS_WONT:
64132377Sminshall 	    printoption(">RCVD", wont, c, hisopts[c]);
64232377Sminshall 	    if (c == TELOPT_TM) {
64332377Sminshall 		if (flushout) {
64432377Sminshall 		    flushout = 0;
64532377Sminshall 		}
64632377Sminshall 	    } else if (hisopts[c]) {
64732377Sminshall 		wontoption(c, 1);
64832377Sminshall 	    }
64932377Sminshall 	    SetIn3270();
65032377Sminshall 	    telrcv_state = TS_DATA;
65132377Sminshall 	    continue;
65227088Sminshall 
65332377Sminshall 	case TS_DO:
65432377Sminshall 	    printoption(">RCVD", doopt, c, !myopts[c]);
65532377Sminshall 	    if (!myopts[c])
65632377Sminshall 		dooption(c);
65732377Sminshall 	    SetIn3270();
65832377Sminshall 	    telrcv_state = TS_DATA;
65932377Sminshall 	    continue;
66027088Sminshall 
66132377Sminshall 	case TS_DONT:
66232377Sminshall 	    printoption(">RCVD", dont, c, myopts[c]);
66332377Sminshall 	    if (myopts[c]) {
66432377Sminshall 		myopts[c] = 0;
66532381Sminshall 		netoprint(wont, c);
66632377Sminshall 		flushline = 1;
66732377Sminshall 		setconnmode();	/* set new tty mode (maybe) */
66832377Sminshall 		printoption(">SENT", wont, c, 0);
66932377Sminshall 	    }
67032377Sminshall 	    SetIn3270();
67132377Sminshall 	    telrcv_state = TS_DATA;
67232377Sminshall 	    continue;
67327088Sminshall 
67432377Sminshall 	case TS_SB:
67532377Sminshall 	    if (c == IAC) {
67632377Sminshall 		telrcv_state = TS_SE;
67732377Sminshall 	    } else {
67832377Sminshall 		SB_ACCUM(c);
67932377Sminshall 	    }
68032377Sminshall 	    continue;
68127088Sminshall 
68232377Sminshall 	case TS_SE:
68332377Sminshall 	    if (c != SE) {
68432377Sminshall 		if (c != IAC) {
68532377Sminshall 		    SB_ACCUM(IAC);
68632377Sminshall 		}
68732377Sminshall 		SB_ACCUM(c);
68832377Sminshall 		telrcv_state = TS_SB;
68932377Sminshall 	    } else {
69032377Sminshall 		SB_TERM();
69132377Sminshall 		suboption();	/* handle sub-option */
69232377Sminshall 		SetIn3270();
69332377Sminshall 		telrcv_state = TS_DATA;
69432377Sminshall 	    }
69527088Sminshall 	}
69627088Sminshall     }
697*32528Sminshall     ring_consumed(&netiring, count);
69832385Sminshall     return returnValue||count;
69927088Sminshall }
70032385Sminshall 
70132385Sminshall static int
70232385Sminshall telsnd(ring)
70332385Sminshall Ring	*ring;			/* Input ring */
70432385Sminshall {
70532385Sminshall     int tcc;
70632385Sminshall     int count;
70732385Sminshall     int returnValue = 0;
70832385Sminshall     char *tbp;
70932385Sminshall 
71032385Sminshall     tcc = 0;
71132385Sminshall     count = 0;
71232385Sminshall     while (NETROOM() > 2) {
71332385Sminshall 	register int sc;
71432385Sminshall 	register int c;
71532385Sminshall 
71632385Sminshall 	if (tcc == 0) {
71732385Sminshall 	    if (count) {
718*32528Sminshall 		ring_consumed(&ttyiring, count);
71932385Sminshall 		returnValue = 1;
72032385Sminshall 		count = 0;
72132385Sminshall 	    }
722*32528Sminshall 	    tbp = ttyiring.consume;
723*32528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
72432385Sminshall 	    if (tcc == 0) {
72532385Sminshall 		break;
72632385Sminshall 	    }
72732385Sminshall 	}
72832385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
72932385Sminshall 	if (sc == escape) {
73032385Sminshall 	    command(0);
73132385Sminshall 	    tcc = 0;
73232385Sminshall 	    flushline = 1;
73332385Sminshall 	    break;
73432385Sminshall 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
73532385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
73632385Sminshall 		tcc--; tbp++; count++;
73732385Sminshall 	    } else {
73832385Sminshall 		dontlecho = !dontlecho;
73932385Sminshall 		settimer(echotoggle);
74032385Sminshall 		setconnmode();
74132385Sminshall 		flushline = 1;
74232385Sminshall 		break;
74332385Sminshall 	    }
74432385Sminshall 	}
74532385Sminshall 	if (localchars) {
74632385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
74732385Sminshall 		break;
74832385Sminshall 	    }
74932385Sminshall 	}
75032385Sminshall 	if (!myopts[TELOPT_BINARY]) {
75132385Sminshall 	    switch (c) {
75232385Sminshall 	    case '\n':
75332385Sminshall 		    /*
75432385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
75532385Sminshall 		     * on our local machine, then probably
75632385Sminshall 		     * a newline (unix) is CRLF (TELNET).
75732385Sminshall 		     */
75832385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
75932385Sminshall 		    NETADD('\r');
76032385Sminshall 		}
76132385Sminshall 		NETADD('\n');
76232385Sminshall 		flushline = 1;
76332385Sminshall 		break;
76432385Sminshall 	    case '\r':
76532385Sminshall 		if (!crlf) {
76632385Sminshall 		    NET2ADD('\r', '\0');
76732385Sminshall 		} else {
76832385Sminshall 		    NET2ADD('\r', '\n');
76932385Sminshall 		}
77032385Sminshall 		flushline = 1;
77132385Sminshall 		break;
77232385Sminshall 	    case IAC:
77332385Sminshall 		NET2ADD(IAC, IAC);
77432385Sminshall 		break;
77532385Sminshall 	    default:
77632385Sminshall 		NETADD(c);
77732385Sminshall 		break;
77832385Sminshall 	    }
77932385Sminshall 	} else if (c == IAC) {
78032385Sminshall 	    NET2ADD(IAC, IAC);
78132385Sminshall 	} else {
78232385Sminshall 	    NETADD(c);
78332385Sminshall 	}
78432385Sminshall     }
785*32528Sminshall     ring_consumed(&ttyiring, count);
78632385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
78732385Sminshall }
78832377Sminshall 
78932377Sminshall #if	defined(TN3270)
79032377Sminshall static void
79132377Sminshall SetForExit()
79232377Sminshall {
79332377Sminshall     setconnmode();
79432377Sminshall     if (In3270) {
79532377Sminshall 	Finish3270();
79632377Sminshall     }
79732377Sminshall     setcommandmode();
79832377Sminshall     fflush(stdout);
79932377Sminshall     fflush(stderr);
80032377Sminshall     if (In3270) {
80132377Sminshall 	StopScreen(1);
80232377Sminshall     }
80332377Sminshall     setconnmode();
80432377Sminshall     setcommandmode();
80532377Sminshall }
80627088Sminshall 
80732377Sminshall static void
80832377Sminshall Exit(returnCode)
80932377Sminshall int returnCode;
81027088Sminshall {
81132377Sminshall     SetForExit();
81232377Sminshall     exit(returnCode);
81327088Sminshall }
81427088Sminshall 
81532377Sminshall void
81632381Sminshall ExitString(string, returnCode)
81732377Sminshall char *string;
81832377Sminshall int returnCode;
81927088Sminshall {
82032377Sminshall     SetForExit();
82132381Sminshall     fwrite(string, 1, strlen(string), stderr);
82232377Sminshall     exit(returnCode);
82327088Sminshall }
82427088Sminshall 
82532377Sminshall void
82632377Sminshall ExitPerror(string, returnCode)
82732377Sminshall char *string;
82832377Sminshall int returnCode;
82927088Sminshall {
83032377Sminshall     SetForExit();
83132377Sminshall     perror(string);
83232377Sminshall     exit(returnCode);
83332377Sminshall }
83432377Sminshall #endif	/* defined(TN3270) */
83527088Sminshall 
83627088Sminshall 
83727088Sminshall /*
83832377Sminshall  * Scheduler()
83932377Sminshall  *
84032377Sminshall  * Try to do something.
84132377Sminshall  *
84232377Sminshall  * If we do something useful, return 1; else return 0.
84332377Sminshall  *
84427110Sminshall  */
84527110Sminshall 
84627110Sminshall 
84732377Sminshall int
84832377Sminshall Scheduler(block)
84932377Sminshall int	block;			/* should we block in the select ? */
85027110Sminshall {
85132377Sminshall     register int c;
85232377Sminshall 		/* One wants to be a bit careful about setting returnValue
85332377Sminshall 		 * to one, since a one implies we did some useful work,
85432377Sminshall 		 * and therefore probably won't be called to block next
85532377Sminshall 		 * time (TN3270 mode only).
85632377Sminshall 		 */
85732377Sminshall     int returnValue = 0;
85832377Sminshall     static struct timeval TimeValue = { 0 };
85927110Sminshall 
86032377Sminshall     if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
86132377Sminshall 	FD_SET(net, &obits);
86232377Sminshall     }
86332377Sminshall #if	!defined(MSDOS)
86432377Sminshall     if (TTYBYTES()) {
86532377Sminshall 	FD_SET(tout, &obits);
86627110Sminshall     }
86732377Sminshall #if	defined(TN3270)
86832377Sminshall     if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
86932377Sminshall 	FD_SET(tin, &ibits);
87027110Sminshall     }
87132377Sminshall #else	/* defined(TN3270) */
87232385Sminshall     if (ring_empty_count(&netiring) && NETROOM()) {
87332377Sminshall 	FD_SET(tin, &ibits);
87427110Sminshall     }
87532377Sminshall #endif	/* defined(TN3270) */
87632377Sminshall #endif	/* !defined(MSDOS) */
87732377Sminshall #   if !defined(TN3270)
87832377Sminshall     if (TTYROOM()) {
87932377Sminshall 	FD_SET(net, &ibits);
88027110Sminshall     }
88132377Sminshall #   else /* !defined(TN3270) */
88232377Sminshall     if (!ISend && TTYROOM()) {
88332377Sminshall 	FD_SET(net, &ibits);
88427110Sminshall     }
88532377Sminshall #   endif /* !defined(TN3270) */
88632377Sminshall     if (!SYNCHing) {
88732377Sminshall 	FD_SET(net, &xbits);
88832377Sminshall     }
88932377Sminshall #   if defined(TN3270) && defined(unix)
89032377Sminshall     if (HaveInput) {
89132377Sminshall 	HaveInput = 0;
89232377Sminshall 	signal(SIGIO, inputAvailable);
89332377Sminshall     }
89432377Sminshall #endif	/* defined(TN3270) && defined(unix) */
89532377Sminshall     if ((c = select(16, &ibits, &obits, &xbits,
89632377Sminshall 			block? (struct timeval *)0 : &TimeValue)) < 0) {
89732377Sminshall 	if (c == -1) {
89832377Sminshall 		    /*
89932377Sminshall 		     * we can get EINTR if we are in line mode,
90032377Sminshall 		     * and the user does an escape (TSTP), or
90132377Sminshall 		     * some other signal generator.
90232377Sminshall 		     */
90332377Sminshall 	    if (errno == EINTR) {
90432377Sminshall 		return 0;
90532377Sminshall 	    }
90632377Sminshall #	    if defined(TN3270)
90732377Sminshall 		    /*
90832377Sminshall 		     * we can get EBADF if we were in transparent
90932377Sminshall 		     * mode, and the transcom process died.
91032377Sminshall 		    */
91132377Sminshall 	    if (errno == EBADF) {
91232377Sminshall 			/*
91332377Sminshall 			 * zero the bits (even though kernel does it)
91432377Sminshall 			 * to make sure we are selecting on the right
91532377Sminshall 			 * ones.
91632377Sminshall 			*/
91732377Sminshall 		FD_ZERO(&ibits);
91832377Sminshall 		FD_ZERO(&obits);
91932377Sminshall 		FD_ZERO(&xbits);
92032377Sminshall 		return 0;
92132377Sminshall 	    }
92232377Sminshall #	    endif /* defined(TN3270) */
92332377Sminshall 		    /* I don't like this, does it ever happen? */
92432377Sminshall 	    printf("sleep(5) from telnet, after select\r\n");
92532377Sminshall #if	defined(unix)
92632377Sminshall 	    sleep(5);
92732377Sminshall #endif	/* defined(unix) */
92827110Sminshall 	}
92927261Sminshall 	return 0;
93027110Sminshall     }
93132377Sminshall 
93232377Sminshall     /*
93332377Sminshall      * Any urgent data?
93432377Sminshall      */
93532377Sminshall     if (FD_ISSET(net, &xbits)) {
93632377Sminshall 	FD_CLR(net, &xbits);
93732377Sminshall 	SYNCHing = 1;
93832377Sminshall 	ttyflush(1);	/* flush already enqueued data */
93927110Sminshall     }
94027178Sminshall 
94132377Sminshall     /*
94232377Sminshall      * Something to read from the network...
94332377Sminshall      */
94432377Sminshall     if (FD_ISSET(net, &ibits)) {
94532377Sminshall 	int canread;
94627178Sminshall 
94732377Sminshall 	FD_CLR(net, &ibits);
94832385Sminshall 	canread = ring_empty_consecutive(&netiring);
94932377Sminshall #if	!defined(SO_OOBINLINE)
95032377Sminshall 	    /*
95132377Sminshall 	     * In 4.2 (and some early 4.3) systems, the
95232377Sminshall 	     * OOB indication and data handling in the kernel
95332377Sminshall 	     * is such that if two separate TCP Urgent requests
95432377Sminshall 	     * come in, one byte of TCP data will be overlaid.
95532377Sminshall 	     * This is fatal for Telnet, but we try to live
95632377Sminshall 	     * with it.
95732377Sminshall 	     *
95832377Sminshall 	     * In addition, in 4.2 (and...), a special protocol
95932377Sminshall 	     * is needed to pick up the TCP Urgent data in
96032377Sminshall 	     * the correct sequence.
96132377Sminshall 	     *
96232377Sminshall 	     * What we do is:  if we think we are in urgent
96332377Sminshall 	     * mode, we look to see if we are "at the mark".
96432377Sminshall 	     * If we are, we do an OOB receive.  If we run
96532377Sminshall 	     * this twice, we will do the OOB receive twice,
96632377Sminshall 	     * but the second will fail, since the second
96732377Sminshall 	     * time we were "at the mark", but there wasn't
96832377Sminshall 	     * any data there (the kernel doesn't reset
96932377Sminshall 	     * "at the mark" until we do a normal read).
97032377Sminshall 	     * Once we've read the OOB data, we go ahead
97132377Sminshall 	     * and do normal reads.
97232377Sminshall 	     *
97332377Sminshall 	     * There is also another problem, which is that
97432377Sminshall 	     * since the OOB byte we read doesn't put us
97532377Sminshall 	     * out of OOB state, and since that byte is most
97632377Sminshall 	     * likely the TELNET DM (data mark), we would
97732377Sminshall 	     * stay in the TELNET SYNCH (SYNCHing) state.
97832377Sminshall 	     * So, clocks to the rescue.  If we've "just"
97932377Sminshall 	     * received a DM, then we test for the
98032377Sminshall 	     * presence of OOB data when the receive OOB
98132377Sminshall 	     * fails (and AFTER we did the normal mode read
98232377Sminshall 	     * to clear "at the mark").
98332377Sminshall 	     */
98432377Sminshall 	if (SYNCHing) {
98532377Sminshall 	    int atmark;
98632377Sminshall 
98732377Sminshall 	    ioctl(net, SIOCATMARK, (char *)&atmark);
98832377Sminshall 	    if (atmark) {
98932377Sminshall 		c = recv(net, sbp+scc, canread, MSG_OOB);
99032377Sminshall 		if ((c == -1) && (errno == EINVAL)) {
99132377Sminshall 		    c = recv(net, sbp+scc, canread, 0);
99232377Sminshall 		    if (clocks.didnetreceive < clocks.gotDM) {
99332377Sminshall 			SYNCHing = stilloob(net);
99427261Sminshall 		    }
99532377Sminshall 		}
99632377Sminshall 	    } else {
99732377Sminshall 		c = recv(net, sbp+scc, canread, 0);
99832377Sminshall 	    }
99932377Sminshall 	} else {
100032377Sminshall 	    c = recv(net, sbp+scc, canread, 0);
100132377Sminshall 	}
100232377Sminshall 	settimer(didnetreceive);
100332377Sminshall #else	/* !defined(SO_OOBINLINE) */
1004*32528Sminshall 	c = recv(net, netiring.supply, canread, 0);
100532377Sminshall #endif	/* !defined(SO_OOBINLINE) */
100632377Sminshall 	if (c < 0 && errno == EWOULDBLOCK) {
100732377Sminshall 	    c = 0;
100832377Sminshall 	} else if (c <= 0) {
100932377Sminshall 	    return -1;
101032377Sminshall 	}
101132377Sminshall 	if (netdata) {
1012*32528Sminshall 	    Dump('<', netiring.supply, c);
101332377Sminshall 	}
1014*32528Sminshall 	ring_supplied(&netiring, c);
101532377Sminshall 	returnValue = 1;
101632377Sminshall     }
101727178Sminshall 
101832377Sminshall     /*
101932377Sminshall      * Something to read from the tty...
102032377Sminshall      */
102132377Sminshall #if	defined(MSDOS)
102232377Sminshall     if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
102332377Sminshall #else	/* defined(MSDOS) */
102432377Sminshall     if (FD_ISSET(tin, &ibits))
102532377Sminshall #endif	/* defined(MSDOS) */
102632377Sminshall 				    {
102732377Sminshall 	FD_CLR(tin, &ibits);
1028*32528Sminshall 	c = TerminalRead(tin, ttyiring.supply,
1029*32528Sminshall 			ring_empty_consecutive(&ttyiring));
103032377Sminshall 	if (c < 0 && errno == EWOULDBLOCK) {
103132377Sminshall 	    c = 0;
103232377Sminshall 	} else {
103332377Sminshall #if	defined(unix)
103432377Sminshall 	    /* EOF detection for line mode!!!! */
103532377Sminshall 	    if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
103632377Sminshall 			/* must be an EOF... */
1037*32528Sminshall 		*ttyiring.supply = termEofChar;
103832377Sminshall 		c = 1;
103932377Sminshall 	    }
104032377Sminshall #endif	/* defined(unix) */
104132377Sminshall 	    if (c <= 0) {
104232377Sminshall 		return -1;
104332377Sminshall 	    }
104427178Sminshall 	}
1045*32528Sminshall 	ring_supplied(&ttyiring, c);
104632377Sminshall 	returnValue = 1;		/* did something useful */
104732377Sminshall     }
104827178Sminshall 
104932377Sminshall #   if defined(TN3270)
1050*32528Sminshall     if (ring_full_count(&ttyiring)) {
105132377Sminshall 	if (In3270) {
105232385Sminshall 	    c = DataFromTerminal(ttyiring.send,
1053*32528Sminshall 					ring_full_consecutive(&ttyiring));
105432377Sminshall 	    if (c) {
105532377Sminshall 		returnValue = 1;
105632377Sminshall 	    }
1057*32528Sminshall 	    ring_consumed(&ttyiring, c);
105832377Sminshall 	} else {
105932377Sminshall #   endif /* defined(TN3270) */
106032385Sminshall 	    returnValue |= telsnd(&ttyiring);
106132377Sminshall #   if defined(TN3270)
106227178Sminshall 	}
106327178Sminshall     }
106432377Sminshall #   endif /* defined(TN3270) */
106532377Sminshall 
106632377Sminshall     if ((!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]) &&
106732377Sminshall 	FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
106832377Sminshall 	FD_CLR(net, &obits);
106932377Sminshall 	returnValue = netflush();
107032377Sminshall     }
1071*32528Sminshall     if (ring_full_count(&netiring)) {
107232377Sminshall #	if !defined(TN3270)
107332385Sminshall 	returnValue |= telrcv();
107432377Sminshall #	else /* !defined(TN3270) */
107532377Sminshall 	returnValue = Push3270();
107632377Sminshall #	endif /* !defined(TN3270) */
107732377Sminshall     }
107832377Sminshall #if	defined(MSDOS)
107932377Sminshall     if (TTYBYTES())
108032377Sminshall #else	/* defined(MSDOS) */
108132377Sminshall     if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
108232377Sminshall #endif	/* defined(MSDOS) */
108332377Sminshall 						    {
108432377Sminshall 	FD_CLR(tout, &obits);
108532377Sminshall 	returnValue = ttyflush(SYNCHing|flushout);
108632377Sminshall     }
108732377Sminshall     return returnValue;
108827178Sminshall }
108927178Sminshall 
109027178Sminshall /*
109132377Sminshall  * Select from tty and network...
109227088Sminshall  */
109332377Sminshall void
109432377Sminshall telnet()
109527088Sminshall {
109632377Sminshall #if	defined(MSDOS)
109732377Sminshall #define	SCHED_BLOCK	0		/* Don't block in MSDOS */
109832377Sminshall #else	/* defined(MSDOS) */
109932377Sminshall #define	SCHED_BLOCK	1
110032377Sminshall #endif	/* defined(MSDOS) */
110127088Sminshall 
110232377Sminshall #if	defined(TN3270) && defined(unix)
110332377Sminshall     int myPid;
110432377Sminshall #endif	/* defined(TN3270) */
110527088Sminshall 
110632377Sminshall     tout = fileno(stdout);
110732377Sminshall     tin = fileno(stdin);
110832377Sminshall     setconnmode();
110932377Sminshall     FD_ZERO(&ibits);
111032377Sminshall     FD_ZERO(&obits);
111132377Sminshall     FD_ZERO(&xbits);
111227261Sminshall 
111332377Sminshall     NetNonblockingIO(net, 1);
111427088Sminshall 
111532377Sminshall #if	defined(TN3270)
111632377Sminshall     if (noasynch == 0) {			/* DBX can't handle! */
111732377Sminshall 	NetSigIO(net, 1);
111832377Sminshall     }
111932377Sminshall     NetSetPgrp(net);
112032377Sminshall #endif	/* defined(TN3270) */
112127088Sminshall 
112227088Sminshall 
112332377Sminshall #if	defined(SO_OOBINLINE) && !defined(MSDOS)
112432377Sminshall     SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
112532377Sminshall #endif	/* defined(SO_OOBINLINE) && !defined(MSDOS) */
112627088Sminshall 
112732377Sminshall #   if !defined(TN3270)
112832377Sminshall     if (telnetport) {
112932377Sminshall 	if (!hisopts[TELOPT_SGA]) {
113032377Sminshall 	    willoption(TELOPT_SGA, 0);
113127110Sminshall 	}
113232377Sminshall 	if (!myopts[TELOPT_TTYPE]) {
113332377Sminshall 	    dooption(TELOPT_TTYPE, 0);
113432377Sminshall 	}
113527178Sminshall     }
113632377Sminshall #   endif /* !defined(TN3270) */
113727088Sminshall 
113832377Sminshall #   if !defined(TN3270)
113932377Sminshall     for (;;) {
114032385Sminshall 	int schedValue;
114132385Sminshall 
114232385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
114332385Sminshall 	    if (schedValue == -1) {
114432385Sminshall 		setcommandmode();
114532385Sminshall 		return;
114632385Sminshall 	    }
114732385Sminshall 	}
114832385Sminshall 
114932377Sminshall 	if (Scheduler(SCHED_BLOCK) == -1) {
115032377Sminshall 	    setcommandmode();
115132377Sminshall 	    return;
115232377Sminshall 	}
115332377Sminshall     }
115432377Sminshall #   else /* !defined(TN3270) */
115532377Sminshall     for (;;) {
115632377Sminshall 	int schedValue;
115727088Sminshall 
115832377Sminshall 	while (!In3270 && !shell_active) {
115932377Sminshall 	    if (Scheduler(SCHED_BLOCK) == -1) {
116032377Sminshall 		setcommandmode();
116132377Sminshall 		return;
116232377Sminshall 	    }
116327088Sminshall 	}
116432377Sminshall 
116532377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
116632377Sminshall 	    if (schedValue == -1) {
116732377Sminshall 		setcommandmode();
116832377Sminshall 		return;
116932377Sminshall 	    }
117027088Sminshall 	}
117132377Sminshall 		/* If there is data waiting to go out to terminal, don't
117232377Sminshall 		 * schedule any more data for the terminal.
117332377Sminshall 		 */
117432377Sminshall 	if (tfrontp-tbackp) {
117532377Sminshall 	    schedValue = 1;
117627088Sminshall 	} else {
117732377Sminshall 	    if (shell_active) {
117832377Sminshall 		if (shell_continue() == 0) {
117932377Sminshall 		    ConnectScreen();
118027088Sminshall 		}
118132377Sminshall 	    } else if (In3270) {
118232377Sminshall 		schedValue = DoTerminalOutput();
118332377Sminshall 	    }
118427088Sminshall 	}
118532377Sminshall 	if (schedValue && (shell_active == 0)) {
118632377Sminshall 	    if (Scheduler(SCHED_BLOCK) == -1) {
118732377Sminshall 		setcommandmode();
118832377Sminshall 		return;
118932377Sminshall 	    }
119027088Sminshall 	}
119132377Sminshall     }
119232377Sminshall #   endif /* !defined(TN3270) */
119327088Sminshall }
119432377Sminshall 
119527088Sminshall /*
119632377Sminshall  * These routines add various telnet commands to the data stream.
119727088Sminshall  */
119832377Sminshall 
119932377Sminshall void
120032377Sminshall xmitAO()
120127088Sminshall {
120232377Sminshall     NET2ADD(IAC, AO);
120332377Sminshall     if (autoflush) {
120432377Sminshall 	doflush();
120532377Sminshall     }
120632377Sminshall }
120727088Sminshall 
120832377Sminshall 
120932377Sminshall void
121032377Sminshall xmitEL()
121127088Sminshall {
121232377Sminshall     NET2ADD(IAC, EL);
121327088Sminshall }
121427088Sminshall 
121532377Sminshall void
121632377Sminshall xmitEC()
121727088Sminshall {
121832377Sminshall     NET2ADD(IAC, EC);
121927088Sminshall }
122027088Sminshall 
122132377Sminshall 
122232377Sminshall #if	defined(NOT43)
122332377Sminshall int
122432377Sminshall #else	/* defined(NOT43) */
122532377Sminshall void
122632377Sminshall #endif	/* defined(NOT43) */
122732377Sminshall dosynch()
122827088Sminshall {
122932377Sminshall     netclear();			/* clear the path to the network */
123032377Sminshall     NET2ADD(IAC, DM);
123127088Sminshall 
123232377Sminshall #if	defined(NOT43)
123332377Sminshall     return 0;
123432377Sminshall #endif	/* defined(NOT43) */
123527088Sminshall }
123627088Sminshall 
123732377Sminshall void
123832377Sminshall doflush()
123927088Sminshall {
124032377Sminshall     NET2ADD(IAC, DO);
124132377Sminshall     NETADD(TELOPT_TM);
124232377Sminshall     flushline = 1;
124332377Sminshall     flushout = 1;
124432377Sminshall     ttyflush(1);			/* Flush/drop output */
124532377Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
124632377Sminshall     printoption("<SENT", doopt, TELOPT_TM, 0);
124727088Sminshall }
124827088Sminshall 
124932377Sminshall void
125032377Sminshall intp()
125127088Sminshall {
125232377Sminshall     NET2ADD(IAC, IP);
125332377Sminshall     flushline = 1;
125432377Sminshall     if (autoflush) {
125532377Sminshall 	doflush();
125632377Sminshall     }
125732377Sminshall     if (autosynch) {
125832377Sminshall 	dosynch();
125932377Sminshall     }
126027088Sminshall }
126127186Sminshall 
126232377Sminshall void
126332377Sminshall sendbrk()
126427186Sminshall {
126532377Sminshall     NET2ADD(IAC, BREAK);
126632377Sminshall     flushline = 1;
126732377Sminshall     if (autoflush) {
126832377Sminshall 	doflush();
126932377Sminshall     }
127032377Sminshall     if (autosynch) {
127132377Sminshall 	dosynch();
127232377Sminshall     }
127327186Sminshall }
1274