GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/telnet/sys_bsd.c Lines: 0 230 0.0 %
Date: 2016-12-06 Branches: 0 170 0.0 %

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