xref: /original-bsd/usr.bin/telnet/sys_bsd.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)sys_bsd.c	8.1 (Berkeley) 06/06/93";
10 #endif /* not lint */
11 
12 /*
13  * The following routines try to encapsulate what is system dependent
14  * (at least between 4.x and dos) which is used in telnet.c.
15  */
16 
17 
18 #include <fcntl.h>
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/socket.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <arpa/telnet.h>
25 
26 #include "ring.h"
27 
28 #include "fdset.h"
29 
30 #include "defines.h"
31 #include "externs.h"
32 #include "types.h"
33 
34 #if	defined(CRAY) || (defined(USE_TERMIO) && !defined(SYSV_TERMIO))
35 #define	SIG_FUNC_RET	void
36 #else
37 #define	SIG_FUNC_RET	int
38 #endif
39 
40 #ifdef	SIGINFO
41 extern SIG_FUNC_RET ayt_status();
42 #endif	SIGINFO
43 
44 int
45 	tout,			/* Output file descriptor */
46 	tin,			/* Input file descriptor */
47 	net;
48 
49 #ifndef	USE_TERMIO
50 struct	tchars otc = { 0 }, ntc = { 0 };
51 struct	ltchars oltc = { 0 }, nltc = { 0 };
52 struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
53 int	olmode = 0;
54 # define cfgetispeed(ptr)	(ptr)->sg_ispeed
55 # define cfgetospeed(ptr)	(ptr)->sg_ospeed
56 # define old_tc ottyb
57 
58 #else	/* USE_TERMIO */
59 struct	termio old_tc = { 0 };
60 extern struct termio new_tc;
61 
62 # ifndef	TCSANOW
63 #  ifdef TCSETS
64 #   define	TCSANOW		TCSETS
65 #   define	TCSADRAIN	TCSETSW
66 #   define	tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
67 #  else
68 #   ifdef TCSETA
69 #    define	TCSANOW		TCSETA
70 #    define	TCSADRAIN	TCSETAW
71 #    define	tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
72 #   else
73 #    define	TCSANOW		TIOCSETA
74 #    define	TCSADRAIN	TIOCSETAW
75 #    define	tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
76 #   endif
77 #  endif
78 #  define	tcsetattr(f, a, t) ioctl(f, a, (char *)t)
79 #  define	cfgetospeed(ptr)	((ptr)->c_cflag&CBAUD)
80 #  ifdef CIBAUD
81 #   define	cfgetispeed(ptr)	(((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
82 #  else
83 #   define	cfgetispeed(ptr)	cfgetospeed(ptr)
84 #  endif
85 # endif /* TCSANOW */
86 # ifdef	sysV88
87 # define TIOCFLUSH TC_PX_DRAIN
88 # endif
89 #endif	/* USE_TERMIO */
90 
91 static fd_set ibits, obits, xbits;
92 
93 
94     void
95 init_sys()
96 {
97     tout = fileno(stdout);
98     tin = fileno(stdin);
99     FD_ZERO(&ibits);
100     FD_ZERO(&obits);
101     FD_ZERO(&xbits);
102 
103     errno = 0;
104 }
105 
106 
107     int
108 TerminalWrite(buf, n)
109     char *buf;
110     int  n;
111 {
112     return write(tout, buf, n);
113 }
114 
115     int
116 TerminalRead(buf, n)
117     char *buf;
118     int  n;
119 {
120     return read(tin, buf, n);
121 }
122 
123 /*
124  *
125  */
126 
127     int
128 TerminalAutoFlush()
129 {
130 #if	defined(LNOFLSH)
131     int flush;
132 
133     ioctl(0, TIOCLGET, (char *)&flush);
134     return !(flush&LNOFLSH);	/* if LNOFLSH, no autoflush */
135 #else	/* LNOFLSH */
136     return 1;
137 #endif	/* LNOFLSH */
138 }
139 
140 #ifdef	KLUDGELINEMODE
141 extern int kludgelinemode;
142 #endif
143 /*
144  * TerminalSpecialChars()
145  *
146  * Look at an input character to see if it is a special character
147  * and decide what to do.
148  *
149  * Output:
150  *
151  *	0	Don't add this character.
152  *	1	Do add this character
153  */
154 
155 extern void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
156 
157     int
158 TerminalSpecialChars(c)
159     int	c;
160 {
161     if (c == termIntChar) {
162 	intp();
163 	return 0;
164     } else if (c == termQuitChar) {
165 #ifdef	KLUDGELINEMODE
166 	if (kludgelinemode)
167 	    sendbrk();
168 	else
169 #endif
170 	    sendabort();
171 	return 0;
172     } else if (c == termEofChar) {
173 	if (my_want_state_is_will(TELOPT_LINEMODE)) {
174 	    sendeof();
175 	    return 0;
176 	}
177 	return 1;
178     } else if (c == termSuspChar) {
179 	sendsusp();
180 	return(0);
181     } else if (c == termFlushChar) {
182 	xmitAO();		/* Transmit Abort Output */
183 	return 0;
184     } else if (!MODE_LOCAL_CHARS(globalmode)) {
185 	if (c == termKillChar) {
186 	    xmitEL();
187 	    return 0;
188 	} else if (c == termEraseChar) {
189 	    xmitEC();		/* Transmit Erase Character */
190 	    return 0;
191 	}
192     }
193     return 1;
194 }
195 
196 
197 /*
198  * Flush output to the terminal
199  */
200 
201     void
202 TerminalFlushOutput()
203 {
204 #ifdef	TIOCFLUSH
205     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
206 #else
207     (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);
208 #endif
209 }
210 
211     void
212 TerminalSaveState()
213 {
214 #ifndef	USE_TERMIO
215     ioctl(0, TIOCGETP, (char *)&ottyb);
216     ioctl(0, TIOCGETC, (char *)&otc);
217     ioctl(0, TIOCGLTC, (char *)&oltc);
218     ioctl(0, TIOCLGET, (char *)&olmode);
219 
220     ntc = otc;
221     nltc = oltc;
222     nttyb = ottyb;
223 
224 #else	/* USE_TERMIO */
225     tcgetattr(0, &old_tc);
226 
227     new_tc = old_tc;
228 
229 #ifndef	VDISCARD
230     termFlushChar = CONTROL('O');
231 #endif
232 #ifndef	VWERASE
233     termWerasChar = CONTROL('W');
234 #endif
235 #ifndef	VREPRINT
236     termRprntChar = CONTROL('R');
237 #endif
238 #ifndef	VLNEXT
239     termLiteralNextChar = CONTROL('V');
240 #endif
241 #ifndef	VSTART
242     termStartChar = CONTROL('Q');
243 #endif
244 #ifndef	VSTOP
245     termStopChar = CONTROL('S');
246 #endif
247 #ifndef	VSTATUS
248     termAytChar = CONTROL('T');
249 #endif
250 #endif	/* USE_TERMIO */
251 }
252 
253     cc_t *
254 tcval(func)
255     register int func;
256 {
257     switch(func) {
258     case SLC_IP:	return(&termIntChar);
259     case SLC_ABORT:	return(&termQuitChar);
260     case SLC_EOF:	return(&termEofChar);
261     case SLC_EC:	return(&termEraseChar);
262     case SLC_EL:	return(&termKillChar);
263     case SLC_XON:	return(&termStartChar);
264     case SLC_XOFF:	return(&termStopChar);
265     case SLC_FORW1:	return(&termForw1Char);
266 #ifdef	USE_TERMIO
267     case SLC_FORW2:	return(&termForw2Char);
268 # ifdef	VDISCARD
269     case SLC_AO:	return(&termFlushChar);
270 # endif
271 # ifdef	VSUSP
272     case SLC_SUSP:	return(&termSuspChar);
273 # endif
274 # ifdef	VWERASE
275     case SLC_EW:	return(&termWerasChar);
276 # endif
277 # ifdef	VREPRINT
278     case SLC_RP:	return(&termRprntChar);
279 # endif
280 # ifdef	VLNEXT
281     case SLC_LNEXT:	return(&termLiteralNextChar);
282 # endif
283 # ifdef	VSTATUS
284     case SLC_AYT:	return(&termAytChar);
285 # endif
286 #endif
287 
288     case SLC_SYNCH:
289     case SLC_BRK:
290     case SLC_EOR:
291     default:
292 	return((cc_t *)0);
293     }
294 }
295 
296     void
297 TerminalDefaultChars()
298 {
299 #ifndef	USE_TERMIO
300     ntc = otc;
301     nltc = oltc;
302     nttyb.sg_kill = ottyb.sg_kill;
303     nttyb.sg_erase = ottyb.sg_erase;
304 #else	/* USE_TERMIO */
305     memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
306 # ifndef	VDISCARD
307     termFlushChar = CONTROL('O');
308 # endif
309 # ifndef	VWERASE
310     termWerasChar = CONTROL('W');
311 # endif
312 # ifndef	VREPRINT
313     termRprntChar = CONTROL('R');
314 # endif
315 # ifndef	VLNEXT
316     termLiteralNextChar = CONTROL('V');
317 # endif
318 # ifndef	VSTART
319     termStartChar = CONTROL('Q');
320 # endif
321 # ifndef	VSTOP
322     termStopChar = CONTROL('S');
323 # endif
324 # ifndef	VSTATUS
325     termAytChar = CONTROL('T');
326 # endif
327 #endif	/* USE_TERMIO */
328 }
329 
330 #ifdef notdef
331 void
332 TerminalRestoreState()
333 {
334 }
335 #endif
336 
337 /*
338  * TerminalNewMode - set up terminal to a specific mode.
339  *	MODE_ECHO: do local terminal echo
340  *	MODE_FLOW: do local flow control
341  *	MODE_TRAPSIG: do local mapping to TELNET IAC sequences
342  *	MODE_EDIT: do local line editing
343  *
344  *	Command mode:
345  *		MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
346  *		local echo
347  *		local editing
348  *		local xon/xoff
349  *		local signal mapping
350  *
351  *	Linemode:
352  *		local/no editing
353  *	Both Linemode and Single Character mode:
354  *		local/remote echo
355  *		local/no xon/xoff
356  *		local/no signal mapping
357  */
358 
359 
360     void
361 TerminalNewMode(f)
362     register int f;
363 {
364     static int prevmode = 0;
365 #ifndef	USE_TERMIO
366     struct tchars tc;
367     struct ltchars ltc;
368     struct sgttyb sb;
369     int lmode;
370 #else	/* USE_TERMIO */
371     struct termio tmp_tc;
372 #endif	/* USE_TERMIO */
373     int onoff;
374     int old;
375     cc_t esc;
376 
377     globalmode = f&~MODE_FORCE;
378     if (prevmode == f)
379 	return;
380 
381     /*
382      * Write any outstanding data before switching modes
383      * ttyflush() returns 0 only when there is no more data
384      * left to write out, it returns -1 if it couldn't do
385      * anything at all, otherwise it returns 1 + the number
386      * of characters left to write.
387 #ifndef	USE_TERMIO
388      * We would really like ask the kernel to wait for the output
389      * to drain, like we can do with the TCSADRAIN, but we don't have
390      * that option.  The only ioctl that waits for the output to
391      * drain, TIOCSETP, also flushes the input queue, which is NOT
392      * what we want (TIOCSETP is like TCSADFLUSH).
393 #endif
394      */
395     old = ttyflush(SYNCHing|flushout);
396     if (old < 0 || old > 1) {
397 #ifdef	USE_TERMIO
398 	tcgetattr(tin, &tmp_tc);
399 #endif	/* USE_TERMIO */
400 	do {
401 	    /*
402 	     * Wait for data to drain, then flush again.
403 	     */
404 #ifdef	USE_TERMIO
405 	    tcsetattr(tin, TCSADRAIN, &tmp_tc);
406 #endif	/* USE_TERMIO */
407 	    old = ttyflush(SYNCHing|flushout);
408 	} while (old < 0 || old > 1);
409     }
410 
411     old = prevmode;
412     prevmode = f&~MODE_FORCE;
413 #ifndef	USE_TERMIO
414     sb = nttyb;
415     tc = ntc;
416     ltc = nltc;
417     lmode = olmode;
418 #else
419     tmp_tc = new_tc;
420 #endif
421 
422     if (f&MODE_ECHO) {
423 #ifndef	USE_TERMIO
424 	sb.sg_flags |= ECHO;
425 #else
426 	tmp_tc.c_lflag |= ECHO;
427 	tmp_tc.c_oflag |= ONLCR;
428 	if (crlf)
429 		tmp_tc.c_iflag |= ICRNL;
430 #endif
431     } else {
432 #ifndef	USE_TERMIO
433 	sb.sg_flags &= ~ECHO;
434 #else
435 	tmp_tc.c_lflag &= ~ECHO;
436 	tmp_tc.c_oflag &= ~ONLCR;
437 # ifdef notdef
438 	if (crlf)
439 		tmp_tc.c_iflag &= ~ICRNL;
440 # endif
441 #endif
442     }
443 
444     if ((f&MODE_FLOW) == 0) {
445 #ifndef	USE_TERMIO
446 	tc.t_startc = _POSIX_VDISABLE;
447 	tc.t_stopc = _POSIX_VDISABLE;
448 #else
449 	tmp_tc.c_iflag &= ~(IXOFF|IXON);	/* Leave the IXANY bit alone */
450     } else {
451 	if (restartany < 0) {
452 		tmp_tc.c_iflag |= IXOFF|IXON;	/* Leave the IXANY bit alone */
453 	} else if (restartany > 0) {
454 		tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
455 	} else {
456 		tmp_tc.c_iflag |= IXOFF|IXON;
457 		tmp_tc.c_iflag &= ~IXANY;
458 	}
459 #endif
460     }
461 
462     if ((f&MODE_TRAPSIG) == 0) {
463 #ifndef	USE_TERMIO
464 	tc.t_intrc = _POSIX_VDISABLE;
465 	tc.t_quitc = _POSIX_VDISABLE;
466 	tc.t_eofc = _POSIX_VDISABLE;
467 	ltc.t_suspc = _POSIX_VDISABLE;
468 	ltc.t_dsuspc = _POSIX_VDISABLE;
469 #else
470 	tmp_tc.c_lflag &= ~ISIG;
471 #endif
472 	localchars = 0;
473     } else {
474 #ifdef	USE_TERMIO
475 	tmp_tc.c_lflag |= ISIG;
476 #endif
477 	localchars = 1;
478     }
479 
480     if (f&MODE_EDIT) {
481 #ifndef	USE_TERMIO
482 	sb.sg_flags &= ~CBREAK;
483 	sb.sg_flags |= CRMOD;
484 #else
485 	tmp_tc.c_lflag |= ICANON;
486 #endif
487     } else {
488 #ifndef	USE_TERMIO
489 	sb.sg_flags |= CBREAK;
490 	if (f&MODE_ECHO)
491 	    sb.sg_flags |= CRMOD;
492 	else
493 	    sb.sg_flags &= ~CRMOD;
494 #else
495 	tmp_tc.c_lflag &= ~ICANON;
496 	tmp_tc.c_iflag &= ~ICRNL;
497 	tmp_tc.c_cc[VMIN] = 1;
498 	tmp_tc.c_cc[VTIME] = 0;
499 #endif
500     }
501 
502     if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
503 #ifndef	USE_TERMIO
504 	ltc.t_lnextc = _POSIX_VDISABLE;
505 #else
506 # ifdef VLNEXT
507 	tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
508 # endif
509 #endif
510     }
511 
512     if (f&MODE_SOFT_TAB) {
513 #ifndef USE_TERMIO
514 	sb.sg_flags |= XTABS;
515 #else
516 # ifdef	OXTABS
517 	tmp_tc.c_oflag |= OXTABS;
518 # endif
519 # ifdef	TABDLY
520 	tmp_tc.c_oflag &= ~TABDLY;
521 	tmp_tc.c_oflag |= TAB3;
522 # endif
523 #endif
524     } else {
525 #ifndef USE_TERMIO
526 	sb.sg_flags &= ~XTABS;
527 #else
528 # ifdef	OXTABS
529 	tmp_tc.c_oflag &= ~OXTABS;
530 # endif
531 # ifdef	TABDLY
532 	tmp_tc.c_oflag &= ~TABDLY;
533 # endif
534 #endif
535     }
536 
537     if (f&MODE_LIT_ECHO) {
538 #ifndef USE_TERMIO
539 	lmode &= ~LCTLECH;
540 #else
541 # ifdef	ECHOCTL
542 	tmp_tc.c_lflag &= ~ECHOCTL;
543 # endif
544 #endif
545     } else {
546 #ifndef USE_TERMIO
547 	lmode |= LCTLECH;
548 #else
549 # ifdef	ECHOCTL
550 	tmp_tc.c_lflag |= ECHOCTL;
551 # endif
552 #endif
553     }
554 
555     if (f == -1) {
556 	onoff = 0;
557     } else {
558 #ifndef	USE_TERMIO
559 	if (f & MODE_OUTBIN)
560 		lmode |= LLITOUT;
561 	else
562 		lmode &= ~LLITOUT;
563 
564 	if (f & MODE_INBIN)
565 		lmode |= LPASS8;
566 	else
567 		lmode &= ~LPASS8;
568 #else
569 	if (f & MODE_INBIN)
570 		tmp_tc.c_iflag &= ~ISTRIP;
571 	else
572 		tmp_tc.c_iflag |= ISTRIP;
573 	if (f & MODE_OUTBIN) {
574 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
575 		tmp_tc.c_cflag |= CS8;
576 		tmp_tc.c_oflag &= ~OPOST;
577 	} else {
578 		tmp_tc.c_cflag &= ~(CSIZE|PARENB);
579 		tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
580 		tmp_tc.c_oflag |= OPOST;
581 	}
582 #endif
583 	onoff = 1;
584     }
585 
586     if (f != -1) {
587 #ifdef	SIGTSTP
588 	static SIG_FUNC_RET susp();
589 #endif	/* SIGTSTP */
590 #ifdef	SIGINFO
591 	static SIG_FUNC_RET ayt();
592 #endif	SIGINFO
593 
594 #ifdef	SIGTSTP
595 	(void) signal(SIGTSTP, susp);
596 #endif	/* SIGTSTP */
597 #ifdef	SIGINFO
598 	(void) signal(SIGINFO, ayt);
599 #endif	SIGINFO
600 #if	defined(USE_TERMIO) && defined(NOKERNINFO)
601 	tmp_tc.c_lflag |= NOKERNINFO;
602 #endif
603 	/*
604 	 * We don't want to process ^Y here.  It's just another
605 	 * character that we'll pass on to the back end.  It has
606 	 * to process it because it will be processed when the
607 	 * user attempts to read it, not when we send it.
608 	 */
609 #ifndef	USE_TERMIO
610 	ltc.t_dsuspc = _POSIX_VDISABLE;
611 #else
612 # ifdef	VDSUSP
613 	tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
614 # endif
615 #endif
616 #ifdef	USE_TERMIO
617 	/*
618 	 * If the VEOL character is already set, then use VEOL2,
619 	 * otherwise use VEOL.
620 	 */
621 	esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
622 	if ((tmp_tc.c_cc[VEOL] != esc)
623 # ifdef	VEOL2
624 	    && (tmp_tc.c_cc[VEOL2] != esc)
625 # endif
626 	    ) {
627 		if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
628 		    tmp_tc.c_cc[VEOL] = esc;
629 # ifdef	VEOL2
630 		else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
631 		    tmp_tc.c_cc[VEOL2] = esc;
632 # endif
633 	}
634 #else
635 	if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE))
636 		tc.t_brkc = esc;
637 #endif
638     } else {
639 #ifdef	SIGINFO
640 	SIG_FUNC_RET ayt_status();
641 
642 	(void) signal(SIGINFO, ayt_status);
643 #endif	SIGINFO
644 #ifdef	SIGTSTP
645 	(void) signal(SIGTSTP, SIG_DFL);
646 	(void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
647 #endif	/* SIGTSTP */
648 #ifndef USE_TERMIO
649 	ltc = oltc;
650 	tc = otc;
651 	sb = ottyb;
652 	lmode = olmode;
653 #else
654 	tmp_tc = old_tc;
655 #endif
656     }
657 #ifndef USE_TERMIO
658     ioctl(tin, TIOCLSET, (char *)&lmode);
659     ioctl(tin, TIOCSLTC, (char *)&ltc);
660     ioctl(tin, TIOCSETC, (char *)&tc);
661     ioctl(tin, TIOCSETN, (char *)&sb);
662 #else
663     if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
664 	tcsetattr(tin, TCSANOW, &tmp_tc);
665 #endif
666 
667 #if	(!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
668 # if	!defined(sysV88)
669     ioctl(tin, FIONBIO, (char *)&onoff);
670     ioctl(tout, FIONBIO, (char *)&onoff);
671 # endif
672 #endif	/* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
673 #if	defined(TN3270)
674     if (noasynchtty == 0) {
675 	ioctl(tin, FIOASYNC, (char *)&onoff);
676     }
677 #endif	/* defined(TN3270) */
678 
679 }
680 
681 #ifndef	B19200
682 # define B19200 B9600
683 #endif
684 
685 #ifndef	B38400
686 # define B38400 B19200
687 #endif
688 
689 /*
690  * This code assumes that the values B0, B50, B75...
691  * are in ascending order.  They do not have to be
692  * contiguous.
693  */
694 struct termspeeds {
695 	long speed;
696 	long value;
697 } termspeeds[] = {
698 	{ 0,     B0 },     { 50,    B50 },   { 75,    B75 },
699 	{ 110,   B110 },   { 134,   B134 },  { 150,   B150 },
700 	{ 200,   B200 },   { 300,   B300 },  { 600,   B600 },
701 	{ 1200,  B1200 },  { 1800,  B1800 }, { 2400,  B2400 },
702 	{ 4800,  B4800 },  { 9600,  B9600 }, { 19200, B19200 },
703 	{ 38400, B38400 }, { -1,    B38400 }
704 };
705 
706     void
707 TerminalSpeeds(ispeed, ospeed)
708     long *ispeed;
709     long *ospeed;
710 {
711     register struct termspeeds *tp;
712     register long in, out;
713 
714     out = cfgetospeed(&old_tc);
715     in = cfgetispeed(&old_tc);
716     if (in == 0)
717 	in = out;
718 
719     tp = termspeeds;
720     while ((tp->speed != -1) && (tp->value < in))
721 	tp++;
722     *ispeed = tp->speed;
723 
724     tp = termspeeds;
725     while ((tp->speed != -1) && (tp->value < out))
726 	tp++;
727     *ospeed = tp->speed;
728 }
729 
730     int
731 TerminalWindowSize(rows, cols)
732     long *rows, *cols;
733 {
734 #ifdef	TIOCGWINSZ
735     struct winsize ws;
736 
737     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
738 	*rows = ws.ws_row;
739 	*cols = ws.ws_col;
740 	return 1;
741     }
742 #endif	/* TIOCGWINSZ */
743     return 0;
744 }
745 
746     int
747 NetClose(fd)
748     int	fd;
749 {
750     return close(fd);
751 }
752 
753 
754     void
755 NetNonblockingIO(fd, onoff)
756     int fd;
757     int onoff;
758 {
759     ioctl(fd, FIONBIO, (char *)&onoff);
760 }
761 
762 #if	defined(TN3270)
763     void
764 NetSigIO(fd, onoff)
765     int fd;
766     int onoff;
767 {
768     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
769 }
770 
771     void
772 NetSetPgrp(fd)
773     int fd;
774 {
775     int myPid;
776 
777     myPid = getpid();
778     fcntl(fd, F_SETOWN, myPid);
779 }
780 #endif	/*defined(TN3270)*/
781 
782 /*
783  * Various signal handling routines.
784  */
785 
786     /* ARGSUSED */
787     static SIG_FUNC_RET
788 deadpeer(sig)
789     int sig;
790 {
791 	setcommandmode();
792 	longjmp(peerdied, -1);
793 }
794 
795     /* ARGSUSED */
796     static SIG_FUNC_RET
797 intr(sig)
798     int sig;
799 {
800     if (localchars) {
801 	intp();
802 	return;
803     }
804     setcommandmode();
805     longjmp(toplevel, -1);
806 }
807 
808     /* ARGSUSED */
809     static SIG_FUNC_RET
810 intr2(sig)
811     int sig;
812 {
813     if (localchars) {
814 #ifdef	KLUDGELINEMODE
815 	if (kludgelinemode)
816 	    sendbrk();
817 	else
818 #endif
819 	    sendabort();
820 	return;
821     }
822 }
823 
824 #ifdef	SIGTSTP
825     /* ARGSUSED */
826     static SIG_FUNC_RET
827 susp(sig)
828     int sig;
829 {
830     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
831 	return;
832     if (localchars)
833 	sendsusp();
834 }
835 #endif
836 
837 #ifdef	SIGWINCH
838     /* ARGSUSED */
839     static SIG_FUNC_RET
840 sendwin(sig)
841     int sig;
842 {
843     if (connected) {
844 	sendnaws();
845     }
846 }
847 #endif
848 
849 #ifdef	SIGINFO
850     /* ARGSUSED */
851     static SIG_FUNC_RET
852 ayt(sig)
853     int sig;
854 {
855     if (connected)
856 	sendayt();
857     else
858 	ayt_status();
859 }
860 #endif
861 
862 
863     void
864 sys_telnet_init()
865 {
866     (void) signal(SIGINT, intr);
867     (void) signal(SIGQUIT, intr2);
868     (void) signal(SIGPIPE, deadpeer);
869 #ifdef	SIGWINCH
870     (void) signal(SIGWINCH, sendwin);
871 #endif
872 #ifdef	SIGTSTP
873     (void) signal(SIGTSTP, susp);
874 #endif
875 #ifdef	SIGINFO
876     (void) signal(SIGINFO, ayt);
877 #endif
878 
879     setconnmode(0);
880 
881     NetNonblockingIO(net, 1);
882 
883 #if	defined(TN3270)
884     if (noasynchnet == 0) {			/* DBX can't handle! */
885 	NetSigIO(net, 1);
886 	NetSetPgrp(net);
887     }
888 #endif	/* defined(TN3270) */
889 
890 #if	defined(SO_OOBINLINE)
891     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
892 	perror("SetSockOpt");
893     }
894 #endif	/* defined(SO_OOBINLINE) */
895 }
896 
897 /*
898  * Process rings -
899  *
900  *	This routine tries to fill up/empty our various rings.
901  *
902  *	The parameter specifies whether this is a poll operation,
903  *	or a block-until-something-happens operation.
904  *
905  *	The return value is 1 if something happened, 0 if not.
906  */
907 
908     int
909 process_rings(netin, netout, netex, ttyin, ttyout, poll)
910     int poll;		/* If 0, then block until something to do */
911 {
912     register int c;
913 		/* One wants to be a bit careful about setting returnValue
914 		 * to one, since a one implies we did some useful work,
915 		 * and therefore probably won't be called to block next
916 		 * time (TN3270 mode only).
917 		 */
918     int returnValue = 0;
919     static struct timeval TimeValue = { 0 };
920 
921     if (netout) {
922 	FD_SET(net, &obits);
923     }
924     if (ttyout) {
925 	FD_SET(tout, &obits);
926     }
927 #if	defined(TN3270)
928     if (ttyin) {
929 	FD_SET(tin, &ibits);
930     }
931 #else	/* defined(TN3270) */
932     if (ttyin) {
933 	FD_SET(tin, &ibits);
934     }
935 #endif	/* defined(TN3270) */
936 #if	defined(TN3270)
937     if (netin) {
938 	FD_SET(net, &ibits);
939     }
940 #   else /* !defined(TN3270) */
941     if (netin) {
942 	FD_SET(net, &ibits);
943     }
944 #   endif /* !defined(TN3270) */
945     if (netex) {
946 	FD_SET(net, &xbits);
947     }
948     if ((c = select(16, &ibits, &obits, &xbits,
949 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
950 	if (c == -1) {
951 		    /*
952 		     * we can get EINTR if we are in line mode,
953 		     * and the user does an escape (TSTP), or
954 		     * some other signal generator.
955 		     */
956 	    if (errno == EINTR) {
957 		return 0;
958 	    }
959 #	    if defined(TN3270)
960 		    /*
961 		     * we can get EBADF if we were in transparent
962 		     * mode, and the transcom process died.
963 		    */
964 	    if (errno == EBADF) {
965 			/*
966 			 * zero the bits (even though kernel does it)
967 			 * to make sure we are selecting on the right
968 			 * ones.
969 			*/
970 		FD_ZERO(&ibits);
971 		FD_ZERO(&obits);
972 		FD_ZERO(&xbits);
973 		return 0;
974 	    }
975 #	    endif /* defined(TN3270) */
976 		    /* I don't like this, does it ever happen? */
977 	    printf("sleep(5) from telnet, after select\r\n");
978 	    sleep(5);
979 	}
980 	return 0;
981     }
982 
983     /*
984      * Any urgent data?
985      */
986     if (FD_ISSET(net, &xbits)) {
987 	FD_CLR(net, &xbits);
988 	SYNCHing = 1;
989 	(void) ttyflush(1);	/* flush already enqueued data */
990     }
991 
992     /*
993      * Something to read from the network...
994      */
995     if (FD_ISSET(net, &ibits)) {
996 	int canread;
997 
998 	FD_CLR(net, &ibits);
999 	canread = ring_empty_consecutive(&netiring);
1000 #if	!defined(SO_OOBINLINE)
1001 	    /*
1002 	     * In 4.2 (and some early 4.3) systems, the
1003 	     * OOB indication and data handling in the kernel
1004 	     * is such that if two separate TCP Urgent requests
1005 	     * come in, one byte of TCP data will be overlaid.
1006 	     * This is fatal for Telnet, but we try to live
1007 	     * with it.
1008 	     *
1009 	     * In addition, in 4.2 (and...), a special protocol
1010 	     * is needed to pick up the TCP Urgent data in
1011 	     * the correct sequence.
1012 	     *
1013 	     * What we do is:  if we think we are in urgent
1014 	     * mode, we look to see if we are "at the mark".
1015 	     * If we are, we do an OOB receive.  If we run
1016 	     * this twice, we will do the OOB receive twice,
1017 	     * but the second will fail, since the second
1018 	     * time we were "at the mark", but there wasn't
1019 	     * any data there (the kernel doesn't reset
1020 	     * "at the mark" until we do a normal read).
1021 	     * Once we've read the OOB data, we go ahead
1022 	     * and do normal reads.
1023 	     *
1024 	     * There is also another problem, which is that
1025 	     * since the OOB byte we read doesn't put us
1026 	     * out of OOB state, and since that byte is most
1027 	     * likely the TELNET DM (data mark), we would
1028 	     * stay in the TELNET SYNCH (SYNCHing) state.
1029 	     * So, clocks to the rescue.  If we've "just"
1030 	     * received a DM, then we test for the
1031 	     * presence of OOB data when the receive OOB
1032 	     * fails (and AFTER we did the normal mode read
1033 	     * to clear "at the mark").
1034 	     */
1035 	if (SYNCHing) {
1036 	    int atmark;
1037 	    static int bogus_oob = 0, first = 1;
1038 
1039 	    ioctl(net, SIOCATMARK, (char *)&atmark);
1040 	    if (atmark) {
1041 		c = recv(net, netiring.supply, canread, MSG_OOB);
1042 		if ((c == -1) && (errno == EINVAL)) {
1043 		    c = recv(net, netiring.supply, canread, 0);
1044 		    if (clocks.didnetreceive < clocks.gotDM) {
1045 			SYNCHing = stilloob(net);
1046 		    }
1047 		} else if (first && c > 0) {
1048 		    /*
1049 		     * Bogosity check.  Systems based on 4.2BSD
1050 		     * do not return an error if you do a second
1051 		     * recv(MSG_OOB).  So, we do one.  If it
1052 		     * succeeds and returns exactly the same
1053 		     * data, then assume that we are running
1054 		     * on a broken system and set the bogus_oob
1055 		     * flag.  (If the data was different, then
1056 		     * we probably got some valid new data, so
1057 		     * increment the count...)
1058 		     */
1059 		    int i;
1060 		    i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
1061 		    if (i == c &&
1062 			  bcmp(netiring.supply, netiring.supply + c, i) == 0) {
1063 			bogus_oob = 1;
1064 			first = 0;
1065 		    } else if (i < 0) {
1066 			bogus_oob = 0;
1067 			first = 0;
1068 		    } else
1069 			c += i;
1070 		}
1071 		if (bogus_oob && c > 0) {
1072 		    int i;
1073 		    /*
1074 		     * Bogosity.  We have to do the read
1075 		     * to clear the atmark to get out of
1076 		     * an infinate loop.
1077 		     */
1078 		    i = read(net, netiring.supply + c, canread - c);
1079 		    if (i > 0)
1080 			c += i;
1081 		}
1082 	    } else {
1083 		c = recv(net, netiring.supply, canread, 0);
1084 	    }
1085 	} else {
1086 	    c = recv(net, netiring.supply, canread, 0);
1087 	}
1088 	settimer(didnetreceive);
1089 #else	/* !defined(SO_OOBINLINE) */
1090 	c = recv(net, netiring.supply, canread, 0);
1091 #endif	/* !defined(SO_OOBINLINE) */
1092 	if (c < 0 && errno == EWOULDBLOCK) {
1093 	    c = 0;
1094 	} else if (c <= 0) {
1095 	    return -1;
1096 	}
1097 	if (netdata) {
1098 	    Dump('<', netiring.supply, c);
1099 	}
1100 	if (c)
1101 	    ring_supplied(&netiring, c);
1102 	returnValue = 1;
1103     }
1104 
1105     /*
1106      * Something to read from the tty...
1107      */
1108     if (FD_ISSET(tin, &ibits)) {
1109 	FD_CLR(tin, &ibits);
1110 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
1111 	if (c < 0 && errno == EWOULDBLOCK) {
1112 	    c = 0;
1113 	} else {
1114 	    /* EOF detection for line mode!!!! */
1115 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
1116 			/* must be an EOF... */
1117 		*ttyiring.supply = termEofChar;
1118 		c = 1;
1119 	    }
1120 	    if (c <= 0) {
1121 		return -1;
1122 	    }
1123 	    if (termdata) {
1124 		Dump('<', ttyiring.supply, c);
1125 	    }
1126 	    ring_supplied(&ttyiring, c);
1127 	}
1128 	returnValue = 1;		/* did something useful */
1129     }
1130 
1131     if (FD_ISSET(net, &obits)) {
1132 	FD_CLR(net, &obits);
1133 	returnValue |= netflush();
1134     }
1135     if (FD_ISSET(tout, &obits)) {
1136 	FD_CLR(tout, &obits);
1137 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
1138     }
1139 
1140     return returnValue;
1141 }
1142