Home | History | Annotate | Line # | Download | only in telnet
sys_bsd.c revision 1.33
      1 /*	$NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1988, 1990, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 #if 0
     35 from: static char sccsid[] = "@(#)sys_bsd.c	8.4 (Berkeley) 5/30/95";
     36 #else
     37 __RCSID("$NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $");
     38 #endif
     39 #endif /* not lint */
     40 
     41 /*
     42  * The following routines try to encapsulate what is system dependent
     43  * (at least between 4.x and dos) which is used in telnet.c.
     44  */
     45 
     46 
     47 #include <fcntl.h>
     48 #include <sys/types.h>
     49 #include <sys/time.h>
     50 #include <sys/socket.h>
     51 #include <signal.h>
     52 #include <stdlib.h>
     53 #include <unistd.h>
     54 #include <errno.h>
     55 #include <poll.h>
     56 #include <arpa/telnet.h>
     57 
     58 #include "ring.h"
     59 #include "defines.h"
     60 #include "externs.h"
     61 #include "types.h"
     62 
     63 #define	SIG_FUNC_RET	void
     64 
     65 SIG_FUNC_RET susp(int);
     66 SIG_FUNC_RET ayt(int);
     67 
     68 SIG_FUNC_RET intr(int);
     69 SIG_FUNC_RET intr2(int);
     70 SIG_FUNC_RET sendwin(int);
     71 
     72 
     73 int
     74 	tout,			/* Output file descriptor */
     75 	tin,			/* Input file descriptor */
     76 	net;
     77 
     78 struct	termios old_tc = { .c_iflag = 0 };
     79 extern struct termios new_tc;
     80 
     81 # ifndef	TCSANOW
     82 #  ifdef TCSETS
     83 #   define	TCSANOW		TCSETS
     84 #   define	TCSADRAIN	TCSETSW
     85 #   define	tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
     86 #  else
     87 #   ifdef TCSETA
     88 #    define	TCSANOW		TCSETA
     89 #    define	TCSADRAIN	TCSETAW
     90 #    define	tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
     91 #   else
     92 #    define	TCSANOW		TIOCSETA
     93 #    define	TCSADRAIN	TIOCSETAW
     94 #    define	tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
     95 #   endif
     96 #  endif
     97 #  define	tcsetattr(f, a, t) ioctl(f, a, (char *)t)
     98 #  define	cfgetospeed(ptr)	((ptr)->c_cflag&CBAUD)
     99 #  ifdef CIBAUD
    100 #   define	cfgetispeed(ptr)	(((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
    101 #  else
    102 #   define	cfgetispeed(ptr)	cfgetospeed(ptr)
    103 #  endif
    104 # endif /* TCSANOW */
    105 
    106 
    107 void
    108 init_sys(void)
    109 {
    110     tout = fileno(stdout);
    111     tin = fileno(stdin);
    112 
    113     errno = 0;
    114 }
    115 
    116 
    117 int
    118 TerminalWrite(char *buf, int  n)
    119 {
    120     return write(tout, buf, n);
    121 }
    122 
    123 int
    124 TerminalRead(unsigned char *buf, int  n)
    125 {
    126     return read(tin, buf, n);
    127 }
    128 
    129 /*
    130  *
    131  */
    132 
    133 int
    134 TerminalAutoFlush(void)
    135 {
    136     return 1;
    137 }
    138 
    139 #ifdef	KLUDGELINEMODE
    140 extern int kludgelinemode;
    141 #endif
    142 /*
    143  * TerminalSpecialChars()
    144  *
    145  * Look at an input character to see if it is a special character
    146  * and decide what to do.
    147  *
    148  * Output:
    149  *
    150  *	0	Don't add this character.
    151  *	1	Do add this character
    152  */
    153 
    154 int
    155 TerminalSpecialChars(int c)
    156 {
    157     if (c == termIntChar) {
    158 	intp();
    159 	return 0;
    160     } else if (c == termQuitChar) {
    161 #ifdef	KLUDGELINEMODE
    162 	if (kludgelinemode)
    163 	    sendbrk();
    164 	else
    165 #endif
    166 	    sendabort();
    167 	return 0;
    168     } else if (c == termEofChar) {
    169 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
    170 	    sendeof();
    171 	    return 0;
    172 	}
    173 	return 1;
    174     } else if (c == termSuspChar) {
    175 	sendsusp();
    176 	return(0);
    177     } else if (c == termFlushChar) {
    178 	xmitAO();		/* Transmit Abort Output */
    179 	return 0;
    180     } else if (!MODE_LOCAL_CHARS(globalmode)) {
    181 	if (c == termKillChar) {
    182 	    xmitEL();
    183 	    return 0;
    184 	} else if (c == termEraseChar) {
    185 	    xmitEC();		/* Transmit Erase Character */
    186 	    return 0;
    187 	}
    188     }
    189     return 1;
    190 }
    191 
    192 
    193 /*
    194  * Flush output to the terminal
    195  */
    196 
    197 void
    198 TerminalFlushOutput(void)
    199 {
    200     int com = 0;
    201     (void) ioctl(fileno(stdout), TIOCFLUSH, &com);
    202 }
    203 
    204 void
    205 TerminalSaveState(void)
    206 {
    207     tcgetattr(0, &old_tc);
    208 
    209     new_tc = old_tc;
    210 }
    211 
    212 cc_t *
    213 tcval(int func)
    214 {
    215     switch(func) {
    216     case SLC_IP:	return(&termIntChar);
    217     case SLC_ABORT:	return(&termQuitChar);
    218     case SLC_EOF:	return(&termEofChar);
    219     case SLC_EC:	return(&termEraseChar);
    220     case SLC_EL:	return(&termKillChar);
    221     case SLC_XON:	return(&termStartChar);
    222     case SLC_XOFF:	return(&termStopChar);
    223     case SLC_FORW1:	return(&termForw1Char);
    224     case SLC_FORW2:	return(&termForw2Char);
    225     case SLC_AO:	return(&termFlushChar);
    226     case SLC_SUSP:	return(&termSuspChar);
    227     case SLC_EW:	return(&termWerasChar);
    228     case SLC_RP:	return(&termRprntChar);
    229     case SLC_LNEXT:	return(&termLiteralNextChar);
    230     case SLC_AYT:	return(&termAytChar);
    231 
    232     case SLC_SYNCH:
    233     case SLC_BRK:
    234     case SLC_EOR:
    235     default:
    236 	return((cc_t *)0);
    237     }
    238 }
    239 
    240 void
    241 TerminalDefaultChars(void)
    242 {
    243     memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
    244 }
    245 
    246 #ifdef notdef
    247 void
    248 TerminalRestoreState(void)
    249 {
    250 }
    251 #endif
    252 
    253 /*
    254  * TerminalNewMode - set up terminal to a specific mode.
    255  *	MODE_ECHO: do local terminal echo
    256  *	MODE_FLOW: do local flow control
    257  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
    258  *	MODE_EDIT: do local line editing
    259  *
    260  *	Command mode:
    261  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
    262  *		local echo
    263  *		local editing
    264  *		local xon/xoff
    265  *		local signal mapping
    266  *
    267  *	Linemode:
    268  *		local/no editing
    269  *	Both Linemode and Single Character mode:
    270  *		local/remote echo
    271  *		local/no xon/xoff
    272  *		local/no signal mapping
    273  */
    274 
    275 
    276 void
    277 TerminalNewMode(int f)
    278 {
    279     static int prevmode = 0;
    280     struct termios tmp_tc;
    281     int onoff;
    282     int old;
    283     cc_t esc;
    284 
    285     globalmode = f&~MODE_FORCE;
    286     if (prevmode == f)
    287 	return;
    288 
    289     /*
    290      * Write any outstanding data before switching modes
    291      * ttyflush() returns 0 only when there is no more data
    292      * left to write out, it returns -1 if it couldn't do
    293      * anything at all, otherwise it returns 1 + the number
    294      * of characters left to write.
    295 #ifndef	USE_TERMIO
    296      * We would really like to ask the kernel to wait for the output
    297      * to drain, like we can do with the TCSADRAIN, but we don't have
    298      * that option.  The only ioctl that waits for the output to
    299      * drain, TIOCSETP, also flushes the input queue, which is NOT
    300      * what we want (TIOCSETP is like TCSADFLUSH).
    301 #endif
    302      */
    303     old = ttyflush(SYNCHing|flushout);
    304     if (old < 0 || old > 1) {
    305 	tcgetattr(tin, &tmp_tc);
    306 	do {
    307 	    /*
    308 	     * Wait for data to drain, then flush again.
    309 	     */
    310 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
    311 	    old = ttyflush(SYNCHing|flushout);
    312 	} while (old < 0 || old > 1);
    313     }
    314 
    315     old = prevmode;
    316     prevmode = f&~MODE_FORCE;
    317     tmp_tc = new_tc;
    318 
    319     if (f&MODE_ECHO) {
    320 	tmp_tc.c_lflag |= ECHO;
    321 	tmp_tc.c_oflag |= ONLCR;
    322 	if (crlf)
    323 		tmp_tc.c_iflag |= ICRNL;
    324     } else {
    325 	tmp_tc.c_lflag &= ~ECHO;
    326 	tmp_tc.c_oflag &= ~ONLCR;
    327 # ifdef notdef
    328 	if (crlf)
    329 		tmp_tc.c_iflag &= ~ICRNL;
    330 # endif
    331     }
    332 
    333     if ((f&MODE_FLOW) == 0) {
    334 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
    335     } else {
    336 	if (restartany < 0) {
    337 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
    338 	} else if (restartany > 0) {
    339 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
    340 	} else {
    341 		tmp_tc.c_iflag |= IXOFF|IXON;
    342 		tmp_tc.c_iflag &= ~IXANY;
    343 	}
    344     }
    345 
    346     if ((f&MODE_TRAPSIG) == 0) {
    347 	tmp_tc.c_lflag &= ~ISIG;
    348 	localchars = 0;
    349     } else {
    350 	tmp_tc.c_lflag |= ISIG;
    351 	localchars = 1;
    352     }
    353 
    354     if (f&MODE_EDIT) {
    355 	tmp_tc.c_lflag |= ICANON;
    356     } else {
    357 	tmp_tc.c_lflag &= ~ICANON;
    358 	tmp_tc.c_iflag &= ~ICRNL;
    359 	tmp_tc.c_cc[VMIN] = 1;
    360 	tmp_tc.c_cc[VTIME] = 0;
    361     }
    362 
    363     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
    364 	tmp_tc.c_lflag &= ~IEXTEN;
    365     }
    366 
    367     if (f&MODE_SOFT_TAB) {
    368 # ifdef	OXTABS
    369 	tmp_tc.c_oflag |= OXTABS;
    370 # endif
    371 # ifdef	TABDLY
    372 	tmp_tc.c_oflag &= ~TABDLY;
    373 	tmp_tc.c_oflag |= TAB3;
    374 # endif
    375     } else {
    376 # ifdef	OXTABS
    377 	tmp_tc.c_oflag &= ~OXTABS;
    378 # endif
    379 # ifdef	TABDLY
    380 	tmp_tc.c_oflag &= ~TABDLY;
    381 # endif
    382     }
    383 
    384     if (f&MODE_LIT_ECHO) {
    385 # ifdef	ECHOCTL
    386 	tmp_tc.c_lflag &= ~ECHOCTL;
    387 # endif
    388     } else {
    389 # ifdef	ECHOCTL
    390 	tmp_tc.c_lflag |= ECHOCTL;
    391 # endif
    392     }
    393 
    394     if (f == -1) {
    395 	onoff = 0;
    396     } else {
    397 	if (f & MODE_INBIN)
    398 		tmp_tc.c_iflag &= ~ISTRIP;
    399 	else
    400 		tmp_tc.c_iflag |= ISTRIP;
    401 	if (f & MODE_OUTBIN) {
    402 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
    403 		tmp_tc.c_cflag |= CS8;
    404 		tmp_tc.c_oflag &= ~OPOST;
    405 	} else {
    406 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
    407 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
    408 		tmp_tc.c_oflag |= OPOST;
    409 	}
    410 	onoff = 1;
    411     }
    412 
    413     if (f != -1) {
    414 	(void) signal(SIGTSTP, susp);
    415 	(void) signal(SIGINFO, ayt);
    416 #if	defined(USE_TERMIO) && defined(NOKERNINFO)
    417 	tmp_tc.c_lflag |= NOKERNINFO;
    418 #endif
    419 	/*
    420 	 * We don't want to process ^Y here.  It's just another
    421 	 * character that we'll pass on to the back end.  It has
    422 	 * to process it because it will be processed when the
    423 	 * user attempts to read it, not when we send it.
    424 	 */
    425 # ifdef	VDSUSP
    426 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
    427 # endif
    428 	/*
    429 	 * If the VEOL character is already set, then use VEOL2,
    430 	 * otherwise use VEOL.
    431 	 */
    432 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
    433 	if ((tmp_tc.c_cc[VEOL] != esc)
    434 # ifdef	VEOL2
    435 	    && (tmp_tc.c_cc[VEOL2] != esc)
    436 # endif
    437 	    ) {
    438 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
    439 		    tmp_tc.c_cc[VEOL] = esc;
    440 # ifdef	VEOL2
    441 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
    442 		    tmp_tc.c_cc[VEOL2] = esc;
    443 # endif
    444 	}
    445     } else {
    446 	(void) signal(SIGINFO, (void (*)(int)) ayt_status);
    447 	(void) signal(SIGTSTP, SIG_DFL);
    448 	(void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
    449 	tmp_tc = old_tc;
    450     }
    451     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
    452 	tcsetattr(tin, TCSANOW, &tmp_tc);
    453 
    454     ioctl(tin, FIONBIO, (char *)&onoff);
    455     ioctl(tout, FIONBIO, (char *)&onoff);
    456 #if	defined(TN3270)
    457     if (noasynchtty == 0) {
    458 	ioctl(tin, FIOASYNC, (char *)&onoff);
    459     }
    460 #endif	/* defined(TN3270) */
    461 
    462 }
    463 
    464 void
    465 TerminalSpeeds(long *ispeed, long *ospeed)
    466 {
    467     long in, out;
    468 
    469     out = cfgetospeed(&old_tc);
    470     in = cfgetispeed(&old_tc);
    471     if (in == 0)
    472 	in = out;
    473 
    474 	*ispeed = in;
    475 	*ospeed = out;
    476 }
    477 
    478 int
    479 TerminalWindowSize(long *rows, long *cols)
    480 {
    481     struct winsize ws;
    482 
    483     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
    484 	*rows = ws.ws_row;
    485 	*cols = ws.ws_col;
    486 	return 1;
    487     }
    488     return 0;
    489 }
    490 
    491 int
    492 NetClose(int fd)
    493 {
    494     return close(fd);
    495 }
    496 
    497 
    498 void
    499 NetNonblockingIO(int fd, int onoff)
    500 {
    501     ioctl(fd, FIONBIO, (char *)&onoff);
    502 }
    503 
    504 #ifdef TN3270
    505 void
    506 NetSigIO(int fd, int onoff)
    507 {
    508     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
    509 }
    510 
    511 void
    512 NetSetPgrp(int fd)
    513 {
    514     int myPid;
    515 
    516     myPid = getpid();
    517     fcntl(fd, F_SETOWN, myPid);
    518 }
    519 #endif	/*defined(TN3270)*/
    520 
    521 /*
    523  * Various signal handling routines.
    524  */
    525 
    526 /* ARGSUSED */
    527 SIG_FUNC_RET
    528 intr(int sig)
    529 {
    530     if (localchars) {
    531 	intp();
    532 	return;
    533     }
    534     setcommandmode();
    535     longjmp(toplevel, -1);
    536 }
    537 
    538 /* ARGSUSED */
    539 SIG_FUNC_RET
    540 intr2(int sig)
    541 {
    542     if (localchars) {
    543 #ifdef	KLUDGELINEMODE
    544 	if (kludgelinemode)
    545 	    sendbrk();
    546 	else
    547 #endif
    548 	    sendabort();
    549 	return;
    550     }
    551 }
    552 
    553 /* ARGSUSED */
    554 SIG_FUNC_RET
    555 susp(int sig)
    556 {
    557     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
    558 	return;
    559     if (localchars)
    560 	sendsusp();
    561 }
    562 
    563 /* ARGSUSED */
    564 SIG_FUNC_RET
    565 sendwin(int sig)
    566 {
    567     if (connected) {
    568 	sendnaws();
    569     }
    570 }
    571 
    572 /* ARGSUSED */
    573 SIG_FUNC_RET
    574 ayt(int sig)
    575 {
    576     if (connected)
    577 	sendayt();
    578     else
    579 	ayt_status();
    580 }
    581 
    582 
    583 void
    585 sys_telnet_init(void)
    586 {
    587     (void) signal(SIGINT, intr);
    588     (void) signal(SIGQUIT, intr2);
    589     (void) signal(SIGPIPE, SIG_IGN);
    590     (void) signal(SIGWINCH, sendwin);
    591     (void) signal(SIGTSTP, susp);
    592     (void) signal(SIGINFO, ayt);
    593 
    594     setconnmode(0);
    595 
    596     NetNonblockingIO(net, 1);
    597 
    598 #ifdef TN3270
    599     if (noasynchnet == 0) {			/* DBX can't handle! */
    600 	NetSigIO(net, 1);
    601 	NetSetPgrp(net);
    602     }
    603 #endif	/* defined(TN3270) */
    604 
    605     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
    606 	perror("SetSockOpt");
    607     }
    608 }
    609 
    610 /*
    611  * Process rings -
    612  *
    613  *	This routine tries to fill up/empty our various rings.
    614  *
    615  *	The parameter specifies whether this is a poll operation,
    616  *	or a block-until-something-happens operation.
    617  *
    618  *	The return value is 1 if something happened, 0 if not, < 0 if an
    619  *	error occurred.
    620  */
    621 
    622 int
    623 process_rings(int netin, int netout, int netex, int ttyin, int ttyout,
    624     int dopoll)		/* If 0, then block until something to do */
    625 {
    626     struct pollfd set[3];
    627     int c;
    628 		/* One wants to be a bit careful about setting returnValue
    629 		 * to one, since a one implies we did some useful work,
    630 		 * and therefore probably won't be called to block next
    631 		 * time (TN3270 mode only).
    632 		 */
    633     int returnValue = 0;
    634 
    635     set[0].fd = net;
    636     set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) |
    637 	(netex ? POLLPRI : 0);
    638     set[1].fd = tout;
    639     set[1].events = ttyout ? POLLOUT : 0;
    640     set[2].fd = tin;
    641     set[2].events = ttyin ? POLLIN : 0;
    642 
    643     if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) {
    644 	if (c == -1) {
    645 		    /*
    646 		     * we can get EINTR if we are in line mode,
    647 		     * and the user does an escape (TSTP), or
    648 		     * some other signal generator.
    649 		     */
    650 	    if (errno == EINTR) {
    651 		return 0;
    652 	    }
    653 #ifdef TN3270
    654 		    /*
    655 		     * we can get EBADF if we were in transparent
    656 		     * mode, and the transcom process died.
    657 		    */
    658 	    if (errno == EBADF)
    659 		return 0;
    660 #endif /* defined(TN3270) */
    661 		    /* I don't like this, does it ever happen? */
    662 	    printf("sleep(5) from telnet, after poll\r\n");
    663 	    sleep(5);
    664 	}
    665 	return 0;
    666     }
    667 
    668     /*
    669      * Any urgent data?
    670      */
    671     if (set[0].revents & POLLPRI) {
    672 	SYNCHing = 1;
    673 	(void) ttyflush(1);	/* flush already enqueued data */
    674     }
    675 
    676     /*
    677      * Something to read from the network...
    678      */
    679     if (set[0].revents & POLLIN) {
    680 	int canread;
    681 
    682 	canread = ring_empty_consecutive(&netiring);
    683 	c = recv(net, (char *)netiring.supply, canread, 0);
    684 	if (c < 0 && errno == EWOULDBLOCK) {
    685 	    c = 0;
    686 	} else if (c <= 0) {
    687 	    return -1;
    688 	}
    689 	if (netdata) {
    690 	    Dump('<', netiring.supply, c);
    691 	}
    692 	if (c)
    693 	    ring_supplied(&netiring, c);
    694 	returnValue = 1;
    695     }
    696 
    697     /*
    698      * Something to read from the tty...
    699      */
    700     if (set[2].revents & POLLIN) {
    701 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
    702 	if (c < 0 && errno == EIO)
    703 	    c = 0;
    704 	if (c < 0 && errno == EWOULDBLOCK) {
    705 	    c = 0;
    706 	} else {
    707 	    if (c < 0) {
    708 		return -1;
    709 	    }
    710 	    if (c == 0) {
    711 		/* must be an EOF... */
    712 		if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
    713 		    *ttyiring.supply = termEofChar;
    714 		    c = 1;
    715 		} else {
    716 		    clienteof = 1;
    717 		    shutdown(net, 1);
    718 		    return 0;
    719 		}
    720 	    }
    721 	    if (termdata) {
    722 		Dump('<', ttyiring.supply, c);
    723 	    }
    724 	    ring_supplied(&ttyiring, c);
    725 	}
    726 	returnValue = 1;		/* did something useful */
    727     }
    728 
    729     if (set[0].revents & POLLOUT) {
    730 	returnValue |= netflush();
    731     }
    732 
    733     if (set[1].revents & (POLLHUP|POLLNVAL))
    734 	return(-1);
    735 
    736     if (set[1].revents & POLLOUT) {
    737 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
    738     }
    739 
    740     return returnValue;
    741 }
    742