xref: /original-bsd/usr.bin/telnet/sys_bsd.c (revision 27393bdf)
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.3 (Berkeley) 02/16/95";
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
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 	SIG_FUNC_RET susp();
589 #endif	/* SIGTSTP */
590 #ifdef	SIGINFO
591 	SIG_FUNC_RET ayt();
592 #endif
593 
594 #ifdef	SIGTSTP
595 	(void) signal(SIGTSTP, susp);
596 #endif	/* SIGTSTP */
597 #ifdef	SIGINFO
598 	(void) signal(SIGINFO, ayt);
599 #endif
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
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 /*
682  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
683  */
684 #if B4800 != 4800
685 #define	DECODE_BAUD
686 #endif
687 
688 #ifdef	DECODE_BAUD
689 #ifndef	B7200
690 #define B7200   B4800
691 #endif
692 
693 #ifndef	B14400
694 #define B14400  B9600
695 #endif
696 
697 #ifndef	B19200
698 # define B19200 B14400
699 #endif
700 
701 #ifndef	B28800
702 #define B28800  B19200
703 #endif
704 
705 #ifndef	B38400
706 # define B38400 B28800
707 #endif
708 
709 #ifndef B57600
710 #define B57600  B38400
711 #endif
712 
713 #ifndef B76800
714 #define B76800  B57600
715 #endif
716 
717 #ifndef B115200
718 #define B115200 B76800
719 #endif
720 
721 #ifndef B230400
722 #define B230400 B115200
723 #endif
724 
725 
726 /*
727  * This code assumes that the values B0, B50, B75...
728  * are in ascending order.  They do not have to be
729  * contiguous.
730  */
731 struct termspeeds {
732 	long speed;
733 	long value;
734 } termspeeds[] = {
735 	{ 0,      B0 },      { 50,    B50 },    { 75,     B75 },
736 	{ 110,    B110 },    { 134,   B134 },   { 150,    B150 },
737 	{ 200,    B200 },    { 300,   B300 },   { 600,    B600 },
738 	{ 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
739 	{ 4800,   B4800 },   { 7200,  B7200 },  { 9600,   B9600 },
740 	{ 14400,  B14400 },  { 19200, B19200 }, { 28800,  B28800 },
741 	{ 38400,  B38400 },  { 57600, B57600 }, { 115200, B115200 },
742 	{ 230400, B230400 }, { -1,    B230400 }
743 };
744 #endif	/* DECODE_BAUD */
745 
746     void
747 TerminalSpeeds(ispeed, ospeed)
748     long *ispeed;
749     long *ospeed;
750 {
751 #ifdef	DECODE_BAUD
752     register struct termspeeds *tp;
753 #endif	/* DECODE_BAUD */
754     register long in, out;
755 
756     out = cfgetospeed(&old_tc);
757     in = cfgetispeed(&old_tc);
758     if (in == 0)
759 	in = out;
760 
761 #ifdef	DECODE_BAUD
762     tp = termspeeds;
763     while ((tp->speed != -1) && (tp->value < in))
764 	tp++;
765     *ispeed = tp->speed;
766 
767     tp = termspeeds;
768     while ((tp->speed != -1) && (tp->value < out))
769 	tp++;
770     *ospeed = tp->speed;
771 #else	/* DECODE_BAUD */
772 	*ispeed = in;
773 	*ospeed = out;
774 #endif	/* DECODE_BAUD */
775 }
776 
777     int
778 TerminalWindowSize(rows, cols)
779     long *rows, *cols;
780 {
781 #ifdef	TIOCGWINSZ
782     struct winsize ws;
783 
784     if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
785 	*rows = ws.ws_row;
786 	*cols = ws.ws_col;
787 	return 1;
788     }
789 #endif	/* TIOCGWINSZ */
790     return 0;
791 }
792 
793     int
794 NetClose(fd)
795     int	fd;
796 {
797     return close(fd);
798 }
799 
800 
801     void
802 NetNonblockingIO(fd, onoff)
803     int fd;
804     int onoff;
805 {
806     ioctl(fd, FIONBIO, (char *)&onoff);
807 }
808 
809 #if	defined(TN3270)
810     void
811 NetSigIO(fd, onoff)
812     int fd;
813     int onoff;
814 {
815     ioctl(fd, FIOASYNC, (char *)&onoff);	/* hear about input */
816 }
817 
818     void
819 NetSetPgrp(fd)
820     int fd;
821 {
822     int myPid;
823 
824     myPid = getpid();
825     fcntl(fd, F_SETOWN, myPid);
826 }
827 #endif	/*defined(TN3270)*/
828 
829 /*
830  * Various signal handling routines.
831  */
832 
833     /* ARGSUSED */
834     SIG_FUNC_RET
835 deadpeer(sig)
836     int sig;
837 {
838 	setcommandmode();
839 	longjmp(peerdied, -1);
840 }
841 
842     /* ARGSUSED */
843     SIG_FUNC_RET
844 intr(sig)
845     int sig;
846 {
847     if (localchars) {
848 	intp();
849 	return;
850     }
851     setcommandmode();
852     longjmp(toplevel, -1);
853 }
854 
855     /* ARGSUSED */
856     SIG_FUNC_RET
857 intr2(sig)
858     int sig;
859 {
860     if (localchars) {
861 #ifdef	KLUDGELINEMODE
862 	if (kludgelinemode)
863 	    sendbrk();
864 	else
865 #endif
866 	    sendabort();
867 	return;
868     }
869 }
870 
871 #ifdef	SIGTSTP
872     /* ARGSUSED */
873     SIG_FUNC_RET
874 susp(sig)
875     int sig;
876 {
877     if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
878 	return;
879     if (localchars)
880 	sendsusp();
881 }
882 #endif
883 
884 #ifdef	SIGWINCH
885     /* ARGSUSED */
886     SIG_FUNC_RET
887 sendwin(sig)
888     int sig;
889 {
890     if (connected) {
891 	sendnaws();
892     }
893 }
894 #endif
895 
896 #ifdef	SIGINFO
897     /* ARGSUSED */
898     SIG_FUNC_RET
899 ayt(sig)
900     int sig;
901 {
902     if (connected)
903 	sendayt();
904     else
905 	ayt_status();
906 }
907 #endif
908 
909 
910     void
911 sys_telnet_init()
912 {
913     (void) signal(SIGINT, intr);
914     (void) signal(SIGQUIT, intr2);
915     (void) signal(SIGPIPE, deadpeer);
916 #ifdef	SIGWINCH
917     (void) signal(SIGWINCH, sendwin);
918 #endif
919 #ifdef	SIGTSTP
920     (void) signal(SIGTSTP, susp);
921 #endif
922 #ifdef	SIGINFO
923     (void) signal(SIGINFO, ayt);
924 #endif
925 
926     setconnmode(0);
927 
928     NetNonblockingIO(net, 1);
929 
930 #if	defined(TN3270)
931     if (noasynchnet == 0) {			/* DBX can't handle! */
932 	NetSigIO(net, 1);
933 	NetSetPgrp(net);
934     }
935 #endif	/* defined(TN3270) */
936 
937 #if	defined(SO_OOBINLINE)
938     if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
939 	perror("SetSockOpt");
940     }
941 #endif	/* defined(SO_OOBINLINE) */
942 }
943 
944 /*
945  * Process rings -
946  *
947  *	This routine tries to fill up/empty our various rings.
948  *
949  *	The parameter specifies whether this is a poll operation,
950  *	or a block-until-something-happens operation.
951  *
952  *	The return value is 1 if something happened, 0 if not.
953  */
954 
955     int
956 process_rings(netin, netout, netex, ttyin, ttyout, poll)
957     int poll;		/* If 0, then block until something to do */
958 {
959     register int c;
960 		/* One wants to be a bit careful about setting returnValue
961 		 * to one, since a one implies we did some useful work,
962 		 * and therefore probably won't be called to block next
963 		 * time (TN3270 mode only).
964 		 */
965     int returnValue = 0;
966     static struct timeval TimeValue = { 0 };
967 
968     if (netout) {
969 	FD_SET(net, &obits);
970     }
971     if (ttyout) {
972 	FD_SET(tout, &obits);
973     }
974 #if	defined(TN3270)
975     if (ttyin) {
976 	FD_SET(tin, &ibits);
977     }
978 #else	/* defined(TN3270) */
979     if (ttyin) {
980 	FD_SET(tin, &ibits);
981     }
982 #endif	/* defined(TN3270) */
983 #if	defined(TN3270)
984     if (netin) {
985 	FD_SET(net, &ibits);
986     }
987 #   else /* !defined(TN3270) */
988     if (netin) {
989 	FD_SET(net, &ibits);
990     }
991 #   endif /* !defined(TN3270) */
992     if (netex) {
993 	FD_SET(net, &xbits);
994     }
995     if ((c = select(16, &ibits, &obits, &xbits,
996 			(poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
997 	if (c == -1) {
998 		    /*
999 		     * we can get EINTR if we are in line mode,
1000 		     * and the user does an escape (TSTP), or
1001 		     * some other signal generator.
1002 		     */
1003 	    if (errno == EINTR) {
1004 		return 0;
1005 	    }
1006 #	    if defined(TN3270)
1007 		    /*
1008 		     * we can get EBADF if we were in transparent
1009 		     * mode, and the transcom process died.
1010 		    */
1011 	    if (errno == EBADF) {
1012 			/*
1013 			 * zero the bits (even though kernel does it)
1014 			 * to make sure we are selecting on the right
1015 			 * ones.
1016 			*/
1017 		FD_ZERO(&ibits);
1018 		FD_ZERO(&obits);
1019 		FD_ZERO(&xbits);
1020 		return 0;
1021 	    }
1022 #	    endif /* defined(TN3270) */
1023 		    /* I don't like this, does it ever happen? */
1024 	    printf("sleep(5) from telnet, after select\r\n");
1025 	    sleep(5);
1026 	}
1027 	return 0;
1028     }
1029 
1030     /*
1031      * Any urgent data?
1032      */
1033     if (FD_ISSET(net, &xbits)) {
1034 	FD_CLR(net, &xbits);
1035 	SYNCHing = 1;
1036 	(void) ttyflush(1);	/* flush already enqueued data */
1037     }
1038 
1039     /*
1040      * Something to read from the network...
1041      */
1042     if (FD_ISSET(net, &ibits)) {
1043 	int canread;
1044 
1045 	FD_CLR(net, &ibits);
1046 	canread = ring_empty_consecutive(&netiring);
1047 #if	!defined(SO_OOBINLINE)
1048 	    /*
1049 	     * In 4.2 (and some early 4.3) systems, the
1050 	     * OOB indication and data handling in the kernel
1051 	     * is such that if two separate TCP Urgent requests
1052 	     * come in, one byte of TCP data will be overlaid.
1053 	     * This is fatal for Telnet, but we try to live
1054 	     * with it.
1055 	     *
1056 	     * In addition, in 4.2 (and...), a special protocol
1057 	     * is needed to pick up the TCP Urgent data in
1058 	     * the correct sequence.
1059 	     *
1060 	     * What we do is:  if we think we are in urgent
1061 	     * mode, we look to see if we are "at the mark".
1062 	     * If we are, we do an OOB receive.  If we run
1063 	     * this twice, we will do the OOB receive twice,
1064 	     * but the second will fail, since the second
1065 	     * time we were "at the mark", but there wasn't
1066 	     * any data there (the kernel doesn't reset
1067 	     * "at the mark" until we do a normal read).
1068 	     * Once we've read the OOB data, we go ahead
1069 	     * and do normal reads.
1070 	     *
1071 	     * There is also another problem, which is that
1072 	     * since the OOB byte we read doesn't put us
1073 	     * out of OOB state, and since that byte is most
1074 	     * likely the TELNET DM (data mark), we would
1075 	     * stay in the TELNET SYNCH (SYNCHing) state.
1076 	     * So, clocks to the rescue.  If we've "just"
1077 	     * received a DM, then we test for the
1078 	     * presence of OOB data when the receive OOB
1079 	     * fails (and AFTER we did the normal mode read
1080 	     * to clear "at the mark").
1081 	     */
1082 	if (SYNCHing) {
1083 	    int atmark;
1084 	    static int bogus_oob = 0, first = 1;
1085 
1086 	    ioctl(net, SIOCATMARK, (char *)&atmark);
1087 	    if (atmark) {
1088 		c = recv(net, netiring.supply, canread, MSG_OOB);
1089 		if ((c == -1) && (errno == EINVAL)) {
1090 		    c = recv(net, netiring.supply, canread, 0);
1091 		    if (clocks.didnetreceive < clocks.gotDM) {
1092 			SYNCHing = stilloob(net);
1093 		    }
1094 		} else if (first && c > 0) {
1095 		    /*
1096 		     * Bogosity check.  Systems based on 4.2BSD
1097 		     * do not return an error if you do a second
1098 		     * recv(MSG_OOB).  So, we do one.  If it
1099 		     * succeeds and returns exactly the same
1100 		     * data, then assume that we are running
1101 		     * on a broken system and set the bogus_oob
1102 		     * flag.  (If the data was different, then
1103 		     * we probably got some valid new data, so
1104 		     * increment the count...)
1105 		     */
1106 		    int i;
1107 		    i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
1108 		    if (i == c &&
1109 			  bcmp(netiring.supply, netiring.supply + c, i) == 0) {
1110 			bogus_oob = 1;
1111 			first = 0;
1112 		    } else if (i < 0) {
1113 			bogus_oob = 0;
1114 			first = 0;
1115 		    } else
1116 			c += i;
1117 		}
1118 		if (bogus_oob && c > 0) {
1119 		    int i;
1120 		    /*
1121 		     * Bogosity.  We have to do the read
1122 		     * to clear the atmark to get out of
1123 		     * an infinate loop.
1124 		     */
1125 		    i = read(net, netiring.supply + c, canread - c);
1126 		    if (i > 0)
1127 			c += i;
1128 		}
1129 	    } else {
1130 		c = recv(net, netiring.supply, canread, 0);
1131 	    }
1132 	} else {
1133 	    c = recv(net, netiring.supply, canread, 0);
1134 	}
1135 	settimer(didnetreceive);
1136 #else	/* !defined(SO_OOBINLINE) */
1137 	c = recv(net, (char *)netiring.supply, canread, 0);
1138 #endif	/* !defined(SO_OOBINLINE) */
1139 	if (c < 0 && errno == EWOULDBLOCK) {
1140 	    c = 0;
1141 	} else if (c <= 0) {
1142 	    return -1;
1143 	}
1144 	if (netdata) {
1145 	    Dump('<', netiring.supply, c);
1146 	}
1147 	if (c)
1148 	    ring_supplied(&netiring, c);
1149 	returnValue = 1;
1150     }
1151 
1152     /*
1153      * Something to read from the tty...
1154      */
1155     if (FD_ISSET(tin, &ibits)) {
1156 	FD_CLR(tin, &ibits);
1157 	c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
1158 	if (c < 0 && errno == EIO)
1159 	    c = 0;
1160 	if (c < 0 && errno == EWOULDBLOCK) {
1161 	    c = 0;
1162 	} else {
1163 	    /* EOF detection for line mode!!!! */
1164 	    if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
1165 			/* must be an EOF... */
1166 		*ttyiring.supply = termEofChar;
1167 		c = 1;
1168 	    }
1169 	    if (c <= 0) {
1170 		return -1;
1171 	    }
1172 	    if (termdata) {
1173 		Dump('<', ttyiring.supply, c);
1174 	    }
1175 	    ring_supplied(&ttyiring, c);
1176 	}
1177 	returnValue = 1;		/* did something useful */
1178     }
1179 
1180     if (FD_ISSET(net, &obits)) {
1181 	FD_CLR(net, &obits);
1182 	returnValue |= netflush();
1183     }
1184     if (FD_ISSET(tout, &obits)) {
1185 	FD_CLR(tout, &obits);
1186 	returnValue |= (ttyflush(SYNCHing|flushout) > 0);
1187     }
1188 
1189     return returnValue;
1190 }
1191