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