1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (c) 1988, 1990, 1993
8 *	The Regents of the University of California.  All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 */
39
40#ifndef lint
41static char sccsid[] = "@(#)sys_bsd.c	8.1 (Berkeley) 6/6/93";
42#endif /* not lint */
43
44/*
45 * The following routines try to encapsulate what is system dependent
46 * (at least between 4.x and dos) which is used in telnet.c.
47 */
48
49
50#include <fcntl.h>
51#include <sys/types.h>
52#include <sys/time.h>
53#include <sys/socket.h>
54#include <sys/uio.h>
55#include <signal.h>
56#include <errno.h>
57#include <arpa/telnet.h>
58
59#include "ring.h"
60
61#include "defines.h"
62#include "externs.h"
63#include "types.h"
64
65#define	SIG_FUNC_RET	void
66
67int tout;			/* Output file descriptor */
68static int tin;			/* Input file descriptor */
69int net = -1;
70
71
72#ifndef	USE_TERMIO
73struct	tchars otc = { 0 }, ntc = { 0 };
74struct	ltchars oltc = { 0 }, nltc = { 0 };
75struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
76int	olmode = 0;
77#define	cfgetispeed(ptr)	(ptr)->sg_ispeed
78#define	cfgetospeed(ptr)	(ptr)->sg_ospeed
79#define	old_tc ottyb
80
81#else	/* USE_TERMIO */
82static struct	termio old_tc = { 0 };
83extern struct termio new_tc;
84#endif	/* USE_TERMIO */
85
86static fd_set ibits, obits, xbits;
87
88static SIG_FUNC_RET susp(int);
89void fatal_tty_error(char *doing_what);
90
91
92void
93init_sys()
94{
95	tout = fileno(stdout);
96	tin = fileno(stdin);
97	FD_ZERO(&ibits);
98	FD_ZERO(&obits);
99	FD_ZERO(&xbits);
100
101	errno = 0;
102}
103
104
105int
106TerminalWrite(buf, n)
107	char *buf;
108	int  n;
109{
110	return (write(tout, buf, n));
111}
112
113static int
114TerminalRead(buf, n)
115	char *buf;
116	int  n;
117{
118	return (read(tin, buf, n));
119}
120
121#ifdef	KLUDGELINEMODE
122extern int kludgelinemode;
123#endif
124/*
125 * TerminalSpecialChars()
126 *
127 * Look at an input character to see if it is a special character
128 * and decide what to do.
129 *
130 * Output:
131 *
132 *	0	Don't add this character.
133 *	1	Do add this character
134 */
135int
136TerminalSpecialChars(c)
137	int	c;
138{
139	/*
140	 * Don't check for signal characters here.  If MODE_TRAPSIG is on,
141	 * then the various signal handlers will catch the characters.  If
142	 * the character in question gets here, then it must have been LNEXTed
143	 */
144	if (c == termQuitChar) {
145#ifdef	KLUDGELINEMODE
146		if (kludgelinemode) {
147			if (sendbrk() == -1) {
148				/* This won't return. */
149				fatal_tty_error("write");
150			}
151			return (0);
152		}
153#endif
154	} else if (c == termFlushChar) {
155		/* Transmit Abort Output */
156		if (xmitAO() == -1) {
157			/* This won't return. */
158			fatal_tty_error("write");
159		}
160		return (0);
161	} else if (!MODE_LOCAL_CHARS(globalmode)) {
162		if (c == termKillChar) {
163			xmitEL();
164			return (0);
165		} else if (c == termEraseChar) {
166			xmitEC();	/* Transmit Erase Character */
167			return (0);
168		}
169	}
170	return (1);
171}
172
173
174/*
175 * Flush output to the terminal
176 */
177
178void
179TerminalFlushOutput()
180{
181	if (isatty(fileno(stdout))) {
182		(void) ioctl(fileno(stdout), TIOCFLUSH, NULL);
183	}
184}
185
186void
187TerminalSaveState()
188{
189#ifndef	USE_TERMIO
190	(void) ioctl(0, TIOCGETP, &ottyb);
191	(void) ioctl(0, TIOCGETC, &otc);
192	(void) ioctl(0, TIOCGLTC, &oltc);
193	(void) ioctl(0, TIOCLGET, &olmode);
194
195	ntc = otc;
196	nltc = oltc;
197	nttyb = ottyb;
198
199#else	/* USE_TERMIO */
200	(void) tcgetattr(0, &old_tc);
201
202	new_tc = old_tc;
203	termAytChar = CONTROL('T');
204#endif	/* USE_TERMIO */
205}
206
207cc_t *
208tcval(func)
209	register int func;
210{
211	switch (func) {
212	case SLC_IP:	return (&termIntChar);
213	case SLC_ABORT:	return (&termQuitChar);
214	case SLC_EOF:	return (&termEofChar);
215	case SLC_EC:	return (&termEraseChar);
216	case SLC_EL:	return (&termKillChar);
217	case SLC_XON:	return (&termStartChar);
218	case SLC_XOFF:	return (&termStopChar);
219	case SLC_FORW1:	return (&termForw1Char);
220#ifdef	USE_TERMIO
221	case SLC_FORW2:	return (&termForw2Char);
222	case SLC_AO:	return (&termFlushChar);
223	case SLC_SUSP:	return (&termSuspChar);
224	case SLC_EW:	return (&termWerasChar);
225	case SLC_RP:	return (&termRprntChar);
226	case SLC_LNEXT:	return (&termLiteralNextChar);
227#endif
228
229	case SLC_SYNCH:
230	case SLC_BRK:
231	case SLC_EOR:
232	default:
233		return ((cc_t *)0);
234	}
235}
236
237void
238TerminalDefaultChars()
239{
240#ifndef	USE_TERMIO
241	ntc = otc;
242	nltc = oltc;
243	nttyb.sg_kill = ottyb.sg_kill;
244	nttyb.sg_erase = ottyb.sg_erase;
245#else	/* USE_TERMIO */
246	(void) memcpy(new_tc.c_cc, old_tc.c_cc, sizeof (old_tc.c_cc));
247	termAytChar = CONTROL('T');
248#endif	/* USE_TERMIO */
249}
250
251/*
252 * TerminalNewMode - set up terminal to a specific mode.
253 *	MODE_ECHO: do local terminal echo
254 *	MODE_FLOW: do local flow control
255 *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
256 *	MODE_EDIT: do local line editing
257 *
258 *	Command mode:
259 *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
260 *		local echo
261 *		local editing
262 *		local xon/xoff
263 *		local signal mapping
264 *
265 *	Linemode:
266 *		local/no editing
267 *	Both Linemode and Single Character mode:
268 *		local/remote echo
269 *		local/no xon/xoff
270 *		local/no signal mapping
271 */
272
273
274void
275TerminalNewMode(f)
276	register int f;
277{
278	static int prevmode = -2;	/* guaranteed unique */
279#ifndef	USE_TERMIO
280	struct tchars tc;
281	struct ltchars ltc;
282	struct sgttyb sb;
283	int lmode;
284#else	/* USE_TERMIO */
285	struct termio tmp_tc;
286#endif	/* USE_TERMIO */
287	int onoff;
288	int old;
289	cc_t esc;
290	sigset_t nset;
291
292	globalmode = f&~MODE_FORCE;
293	if (prevmode == f)
294		return;
295
296	/*
297	 * Write any outstanding data before switching modes
298	 * ttyflush() returns 0 only when there was no data
299	 * to write out; it returns -1 if it couldn't do
300	 * anything at all, returns -2 if there was a write
301	 * error (other than EWOULDBLOCK), and otherwise it
302	 * returns 1 + the number of characters left to write.
303	 */
304#ifndef	USE_TERMIO
305	/*
306	 * We would really like ask the kernel to wait for the output
307	 * to drain, like we can do with the TCSADRAIN, but we don't have
308	 * that option.  The only ioctl that waits for the output to
309	 * drain, TIOCSETP, also flushes the input queue, which is NOT
310	 * what we want(TIOCSETP is like TCSADFLUSH).
311	 */
312#endif
313	old = ttyflush(SYNCHing|flushout);
314	if (old == -1 || old > 1) {
315#ifdef	USE_TERMIO
316		(void) tcgetattr(tin, &tmp_tc);
317#endif	/* USE_TERMIO */
318		do {
319			/*
320			 * Wait for data to drain, then flush again.
321			 */
322#ifdef	USE_TERMIO
323			(void) tcsetattr(tin, TCSADRAIN, &tmp_tc);
324#endif	/* USE_TERMIO */
325			old = ttyflush(SYNCHing|flushout);
326		} while (old == -1 || old > 1);
327	}
328
329	old = prevmode;
330	prevmode = f&~MODE_FORCE;
331#ifndef	USE_TERMIO
332	sb = nttyb;
333	tc = ntc;
334	ltc = nltc;
335	lmode = olmode;
336#else
337	tmp_tc = new_tc;
338#endif
339
340	if (f&MODE_ECHO) {
341#ifndef	USE_TERMIO
342		sb.sg_flags |= ECHO;
343#else
344		tmp_tc.c_lflag |= ECHO;
345		tmp_tc.c_oflag |= ONLCR;
346		if (crlf)
347			tmp_tc.c_iflag |= ICRNL;
348#endif
349	} else {
350#ifndef	USE_TERMIO
351		sb.sg_flags &= ~ECHO;
352#else
353		tmp_tc.c_lflag &= ~ECHO;
354		tmp_tc.c_oflag &= ~ONLCR;
355#ifdef notdef
356		if (crlf)
357			tmp_tc.c_iflag &= ~ICRNL;
358#endif
359#endif
360	}
361
362	if ((f&MODE_FLOW) == 0) {
363#ifndef	USE_TERMIO
364		tc.t_startc = _POSIX_VDISABLE;
365		tc.t_stopc = _POSIX_VDISABLE;
366#else
367		tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */
368	} else {
369		if (restartany < 0) {
370			/* Leave the IXANY bit alone */
371			tmp_tc.c_iflag |= IXOFF|IXON;
372		} else if (restartany > 0) {
373			tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
374		} else {
375			tmp_tc.c_iflag |= IXOFF|IXON;
376			tmp_tc.c_iflag &= ~IXANY;
377		}
378#endif
379	}
380
381	if ((f&MODE_TRAPSIG) == 0) {
382#ifndef	USE_TERMIO
383		tc.t_intrc = _POSIX_VDISABLE;
384		tc.t_quitc = _POSIX_VDISABLE;
385		tc.t_eofc = _POSIX_VDISABLE;
386		ltc.t_suspc = _POSIX_VDISABLE;
387		ltc.t_dsuspc = _POSIX_VDISABLE;
388#else
389		tmp_tc.c_lflag &= ~ISIG;
390#endif
391		localchars = 0;
392	} else {
393#ifdef	USE_TERMIO
394		tmp_tc.c_lflag |= ISIG;
395#endif
396		localchars = 1;
397	}
398
399	if (f&MODE_EDIT) {
400#ifndef	USE_TERMIO
401		sb.sg_flags &= ~CBREAK;
402		sb.sg_flags |= CRMOD;
403#else
404		tmp_tc.c_lflag |= ICANON;
405#endif
406	} else {
407#ifndef	USE_TERMIO
408		sb.sg_flags |= CBREAK;
409		if (f&MODE_ECHO)
410			sb.sg_flags |= CRMOD;
411		else
412			sb.sg_flags &= ~CRMOD;
413#else
414		tmp_tc.c_lflag &= ~ICANON;
415		tmp_tc.c_iflag &= ~ICRNL;
416		tmp_tc.c_cc[VMIN] = 1;
417		tmp_tc.c_cc[VTIME] = 0;
418#endif
419	}
420
421	if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
422#ifndef	USE_TERMIO
423		ltc.t_lnextc = _POSIX_VDISABLE;
424#else
425		tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
426#endif
427	}
428
429	if (f&MODE_SOFT_TAB) {
430#ifndef USE_TERMIO
431		sb.sg_flags |= XTABS;
432#else
433		tmp_tc.c_oflag &= ~TABDLY;
434		tmp_tc.c_oflag |= TAB3;
435#endif
436	} else {
437#ifndef USE_TERMIO
438		sb.sg_flags &= ~XTABS;
439#else
440		tmp_tc.c_oflag &= ~TABDLY;
441#endif
442	}
443
444	if (f&MODE_LIT_ECHO) {
445#ifndef USE_TERMIO
446		lmode &= ~LCTLECH;
447#else
448		tmp_tc.c_lflag &= ~ECHOCTL;
449#endif
450	} else {
451#ifndef USE_TERMIO
452		lmode |= LCTLECH;
453#else
454		tmp_tc.c_lflag |= ECHOCTL;
455#endif
456	}
457
458	if (f == -1) {
459		onoff = 0;
460	} else {
461#ifndef	USE_TERMIO
462		if (f & MODE_OUTBIN)
463			lmode |= LLITOUT;
464		else
465			lmode &= ~LLITOUT;
466#else
467		if (f & MODE_OUTBIN) {
468			tmp_tc.c_cflag &= ~(CSIZE|PARENB);
469			tmp_tc.c_cflag |= CS8;
470			tmp_tc.c_oflag &= ~OPOST;
471		} else {
472			tmp_tc.c_cflag &= ~(CSIZE|PARENB);
473			tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
474			tmp_tc.c_oflag |= OPOST;
475		}
476#endif
477		onoff = 1;
478	}
479
480	if (f != -1) {
481
482		(void) signal(SIGTSTP, susp);
483
484#if	defined(USE_TERMIO) && defined(NOKERNINFO)
485		tmp_tc.c_lflag |= NOKERNINFO;
486#endif
487		/*
488		 * We don't want to process ^Y here.  It's just another
489		 * character that we'll pass on to the back end.  It has
490		 * to process it because it will be processed when the
491		 * user attempts to read it, not when we send it.
492		 */
493#ifndef	USE_TERMIO
494		ltc.t_dsuspc = _POSIX_VDISABLE;
495#else
496		tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
497#endif
498#ifdef	USE_TERMIO
499		/*
500		 * If the VEOL character is already set, then use VEOL2,
501		 * otherwise use VEOL.
502		 */
503		esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
504		if ((tmp_tc.c_cc[VEOL] != esc)
505		    /* XXX */ &&
506		    (tmp_tc.c_cc[VEOL2] != esc)
507		    /* XXX */) {
508			if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
509				tmp_tc.c_cc[VEOL] = esc;
510			else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
511				tmp_tc.c_cc[VEOL2] = esc;
512		}
513#else
514		if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE))
515			tc.t_brkc = esc;
516#endif
517	} else {
518		(void) signal(SIGTSTP, SIG_DFL);
519		(void) sigemptyset(&nset);
520		(void) sigaddset(&nset, SIGTSTP);
521		(void) sigprocmask(SIG_UNBLOCK, &nset, 0);
522#ifndef USE_TERMIO
523		ltc = oltc;
524		tc = otc;
525		sb = ottyb;
526		lmode = olmode;
527#else
528		tmp_tc = old_tc;
529#endif
530	}
531	if (isatty(tin)) {
532#ifndef USE_TERMIO
533		(void) ioctl(tin, TIOCLSET, &lmode);
534		(void) ioctl(tin, TIOCSLTC, &ltc);
535		(void) ioctl(tin, TIOCSETC, &tc);
536		(void) ioctl(tin, TIOCSETN, &sb);
537#else
538		if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
539			(void) tcsetattr(tin, TCSANOW, &tmp_tc);
540#endif
541		(void) ioctl(tin, FIONBIO, &onoff);
542		(void) ioctl(tout, FIONBIO, &onoff);
543	}
544
545}
546
547/*
548 * This code assumes that the values B0, B50, B75...
549 * are in ascending order.  They do not have to be
550 * contiguous.
551 */
552static struct termspeeds {
553	int speed;
554	int value;
555} termspeeds[] = {
556	{ 0,	B0 },		{ 50,	B50 },		{ 75, B75 },
557	{ 110,	B110 },		{ 134,	B134 },		{ 150, B150 },
558	{ 200,	B200 },		{ 300,   B300 },	{ 600, B600 },
559	{ 1200,	B1200 },	{ 1800,  B1800 },	{ 2400, B2400 },
560	{ 4800,	B4800 },	{ 9600,  B9600 },	{ 19200, B19200 },
561	{ 38400, B38400 },	{ 57600, B57600 },	{ 76800, B76800 },
562	{ 115200, B115200 },	{ 153600, B153600 },	{ 230400, B230400 },
563	{ 307200, B307200 },	{ 460800, B460800 },	{ 921600, B921600 },
564	{ -1, B0 }
565};
566
567void
568TerminalSpeeds(ispeed, ospeed)
569	int *ispeed;
570	int *ospeed;
571{
572	register struct termspeeds *tp;
573	register int in, out;
574
575	out = cfgetospeed(&old_tc);
576	in = cfgetispeed(&old_tc);
577	if (in == 0)
578		in = out;
579
580	tp = termspeeds;
581	while ((tp->speed != -1) && (tp->value < in)) {
582		tp++;
583	}
584	if (tp->speed == -1)
585		tp--;			/* back up to fastest defined speed */
586	*ispeed = tp->speed;
587
588	tp = termspeeds;
589	while ((tp->speed != -1) && (tp->value < out)) {
590		tp++;
591	}
592	if (tp->speed == -1)
593		tp--;
594	*ospeed = tp->speed;
595}
596
597int
598TerminalWindowSize(rows, cols)
599	unsigned short *rows, *cols;
600{
601	struct winsize ws;
602
603	if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) {
604		*rows = ws.ws_row;
605		*cols = ws.ws_col;
606		return (1);
607	}
608	return (0);
609}
610
611static void
612NetNonblockingIO(fd, onoff)
613	int fd;
614	int onoff;
615{
616	(void) ioctl(fd, FIONBIO, &onoff);
617}
618
619/*
620 * Various signal handling routines.
621 */
622
623/* ARGSUSED */
624static SIG_FUNC_RET
625deadpeer(sig)
626	int sig;
627{
628	/*
629	 * Once is all we should catch SIGPIPE.  If we get it again,
630	 * it means we tried to put still more data out to a pipe
631	 * which has disappeared.  In that case, telnet will exit.
632	 */
633	(void) signal(SIGPIPE, SIG_IGN);
634	flushout = 1;
635	setcommandmode();
636	longjmp(peerdied, -1);
637}
638
639boolean_t intr_happened	= B_FALSE;
640boolean_t intr_waiting	= B_FALSE;
641
642/* ARGSUSED */
643static SIG_FUNC_RET
644intr(sig)
645	int sig;
646{
647	if (intr_waiting) {
648		intr_happened = 1;
649		return;
650	}
651	(void) signal(SIGINT, intr);
652	if (localchars) {
653		intp();
654		return;
655	}
656	setcommandmode();
657	longjmp(toplevel, -1);
658}
659
660/* ARGSUSED */
661static SIG_FUNC_RET
662intr2(sig)
663	int sig;
664{
665	(void) signal(SIGQUIT, intr2);
666	if (localchars) {
667		/*
668		 * Ignore return to the next two function calls
669		 * since we're doing SIGQUIT
670		 */
671#ifdef	KLUDGELINEMODE
672		if (kludgelinemode) {
673			(void) sendbrk();
674		}
675		else
676#endif
677			sendabort();
678		return;
679	}
680}
681
682/* ARGSUSED */
683static SIG_FUNC_RET
684susp(sig)
685	int sig;
686{
687	(void) signal(SIGTSTP, susp);
688	if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
689		return;
690	if (localchars)
691		sendsusp();
692}
693
694/* ARGSUSED */
695static SIG_FUNC_RET
696sendwin(sig)
697	int sig;
698{
699	(void) signal(SIGWINCH, sendwin);
700	if (connected) {
701		sendnaws();
702	}
703}
704
705void
706sys_telnet_init()
707{
708	(void) signal(SIGINT, intr);
709	(void) signal(SIGQUIT, intr2);
710	(void) signal(SIGPIPE, deadpeer);
711	(void) signal(SIGWINCH, sendwin);
712	(void) signal(SIGTSTP, susp);
713
714	setconnmode(0);
715
716	NetNonblockingIO(net, 1);
717
718	if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
719		perror("SetSockOpt");
720	}
721}
722
723
724/*
725 * fatal_tty_error -
726 *	Handle case where there is an unrecoverable error on the tty
727 *      connections.  Print an error, reset the terminal settings
728 *	and get out as painlessly as possible.
729 */
730void
731fatal_tty_error(char *doing_what)
732{
733	TerminalNewMode(-1);
734	(void) fprintf(stderr, "Error processing %s:  %s\n", doing_what,
735	    strerror(errno));
736	exit(1);
737}
738
739
740/*
741 * Process rings -
742 *
743 *	This routine tries to fill up/empty our various rings.
744 *
745 *	The parameter specifies whether this is a poll operation,
746 *	or a block-until-something-happens operation.
747 *
748 *	The return value is 1 if something happened, 0 if not.
749 */
750
751int
752process_rings(netin, netout, netex, ttyin, ttyout, poll)
753	int poll;		/* If 0, then block until something to do */
754{
755	register int c;
756	/*
757	 * One wants to be a bit careful about setting returnValue
758	 * to one, since a one implies we did some useful work,
759	 * and therefore probably won't be called to block next
760	 * time (TN3270 mode only).
761	 */
762	int returnValue = 0;
763	static struct timeval TimeValue = { 0 };
764	int i;
765
766	if (netout) {
767		FD_SET(net, &obits);
768	}
769	if (ttyout) {
770		FD_SET(tout, &obits);
771	}
772	if (ttyin) {
773		FD_SET(tin, &ibits);
774	}
775	if (netin) {
776		FD_SET(net, &ibits);
777	}
778	if (netex) {
779		FD_SET(net, &xbits);
780	}
781	if ((c = select(16, &ibits, &obits, &xbits,
782			(poll == 0) ? NULL : &TimeValue)) < 0) {
783		if (c == -1) {
784			/*
785			 * we can get EINTR if we are in line mode,
786			 * and the user does an escape (TSTP), or
787			 * some other signal generator.
788			 */
789			if (errno == EINTR) {
790				return (0);
791			}
792			/* I don't like this, does it ever happen? */
793			(void) printf("sleep(5) from telnet, after select\r\n");
794			(void) sleep(5);
795		}
796		return (0);
797	}
798
799	/*
800	 * Any urgent data?
801	 */
802	if (FD_ISSET(net, &xbits)) {
803		FD_CLR(net, &xbits);
804		SYNCHing = 1;
805
806		/* flush any data that is already enqueued */
807		i = ttyflush(1);
808		if (i == -2) {
809			/* This will not return. */
810			fatal_tty_error("write");
811		}
812	}
813
814	/*
815	 * Something to read from the network...
816	 */
817	if (FD_ISSET(net, &ibits)) {
818		int canread;
819
820		FD_CLR(net, &ibits);
821		canread = ring_empty_consecutive(&netiring);
822		c = recv(net, netiring.supply, canread, 0);
823		if (c < 0 && errno == EWOULDBLOCK) {
824			c = 0;
825		} else if (c <= 0) {
826			return (-1);
827		}
828		if (netdata) {
829			Dump('<', netiring.supply, c);
830		}
831		if (c)
832			ring_supplied(&netiring, c);
833		returnValue = 1;
834	}
835
836	/*
837	 * Something to read from the tty...
838	 */
839	if (FD_ISSET(tin, &ibits)) {
840		FD_CLR(tin, &ibits);
841		c = TerminalRead((char *)ttyiring.supply,
842		    ring_empty_consecutive(&ttyiring));
843		if (c < 0) {
844			if (errno != EWOULDBLOCK) {
845				/* This will not return. */
846				fatal_tty_error("read");
847			}
848			c = 0;
849		} else {
850			/* EOF detection for line mode!!!! */
851			if ((c == 0) && MODE_LOCAL_CHARS(globalmode) &&
852			    isatty(tin)) {
853				/* must be an EOF... */
854				eof_pending = 1;
855				return (1);
856			}
857			if (c <= 0) {
858				returnValue = -1;
859				goto next;
860			}
861			if (termdata) {
862				Dump('<', ttyiring.supply, c);
863			}
864			ring_supplied(&ttyiring, c);
865		}
866		returnValue = 1;		/* did something useful */
867	}
868
869next:
870	if (FD_ISSET(net, &obits)) {
871		FD_CLR(net, &obits);
872		returnValue |= netflush();
873	}
874	if (FD_ISSET(tout, &obits)) {
875		FD_CLR(tout, &obits);
876		i = ttyflush(SYNCHing|flushout);
877		if (i == -2) {
878			/* This will not return. */
879			fatal_tty_error("write");
880		}
881		returnValue |= (i > 0);
882	}
883
884	return (returnValue);
885}
886