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