1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include <errno.h>
9 
10 #include "jove.h"
11 
12 #ifdef UNIX /* the body is the rest of the file */
13 
14 #include "fp.h"
15 
16 #ifdef BIFF
17 # include <sys/stat.h>
18 #endif
19 
20 #include "chars.h"
21 #include "term.h"	/* ospeed */
22 #include "ttystate.h"
23 #include "util.h"
24 
25 
26 #ifdef SGTTY
27 struct sgttyb	sg[2];
28 #endif
29 
30 #ifdef TERMIO
31 struct termio	sg[2];
32 #endif
33 
34 #ifdef TERMIOS
35 struct termios	sg[2];
36 #endif
37 
38 #ifdef USE_TIOCSLTC
39 struct ltchars	ls[2];
40 #endif /* USE_TIOCSLTC */
41 
42 #ifdef SGTTY
43 
44 # ifdef TIOCGETC
45 struct tchars	tc[2];
46 # endif
47 
48 # ifdef LPASS8	/* use 4.3BSD's LPASS8 instead of raw for meta-key */
49 int	lmword[2];		/* local mode word */
50 # endif
51 
52 #endif /* SGTTY */
53 
54 
55 /* Set tty to original (if !n) or JOVE (if n) modes.
56  * This is designed to be idempotent: it can be called
57  * several times with the same argument without damage.
58  */
59 
60 bool	OKXonXoff = NO;	/* VAR: XON/XOFF can be used as ordinary chars */
61 ZXchar	IntChar = CTL(']');	/* VAR: ttysetattr sets this to generate SIGINT */
62 
63 #ifdef BIFF
64 bool	DisBiff = NO;		/* VAR: turn off/on biff with entering/exiting jove */
65 #endif /* BIFF */
66 
67 void
ttysetattr(n)68 ttysetattr(n)
69 bool	n;	/* also used as subscript! */
70 {
71 	static bool	prev_n = NO;
72 
73 	if (!prev_n) {
74 		/* Previously, the tty was not in JOVE mode.
75 		 * Find out the current settings:
76 		 * do the ioctls or whatever to fill in NO half
77 		 * of each appropriate tty state pair.
78 		 * NOTE: the nested tangle of ifdefs is intended to follow
79 		 * the structure of the definitions in ttystate.c.
80 		 */
81 #ifdef SGTTY
82 		(void) gtty(0, &sg[NO]);
83 #endif
84 
85 #ifdef TERMIO
86 		(void) ioctl(0, TCGETA, (UnivPtr) &sg[NO]);
87 #endif
88 
89 #ifdef TERMIOS
90 		(void) tcgetattr(0, &sg[NO]);
91 #endif
92 
93 #ifdef USE_TIOCSLTC
94 		(void) ioctl(0, TIOCGLTC, (UnivPtr) &ls[NO]);
95 #endif /* USE_TIOCSLTC */
96 
97 #ifdef SGTTY
98 
99 # ifdef TIOCGETC
100 		(void) ioctl(0, TIOCGETC, (UnivPtr) &tc[NO]);
101 # endif
102 
103 # ifdef LPASS8	/* use 4.3BSD's LPASS8 instead of raw for meta-key */
104 		(void) ioctl(0, TIOCLGET, (UnivPtr) &lmword[NO]);
105 # endif
106 
107 #endif /* SGTTY */
108 
109 /* extract some info from results */
110 
111 #if defined(TERMIO) || defined(TERMIOS)
112 # ifdef TAB3
113 		TABS = (sg[NO].c_oflag & TABDLY) != TAB3;
114 # endif
115 # ifdef TERMIOS
116 		ospeed = cfgetospeed(&sg[NO]);
117 # else /* ! TERMIOS */
118 #  ifdef CBAUD
119 		ospeed = sg[NO].c_cflag & CBAUD;
120 #  else /* ! CBAUD */
121 		ospeed = B9600;	/* XXX */
122 #  endif /* CBAUD */
123 # endif /* TERMIOS */
124 #endif /* defined(TERMIO) || defined(TERMIOS) */
125 
126 #ifdef SGTTY
127 		TABS = !(sg[NO].sg_flags & XTABS);
128 		ospeed = sg[NO].sg_ospeed;
129 #endif /* SGTTY */
130 	}
131 
132 	/* Fill in YES half of each appropriate tty state pair.
133 	 * They are filled in as late as possible so that each will
134 	 * reflect the latest settings of controling variables.
135 	 * NOTE: the nested tangle of ifdefs is intended to follow
136 	 * the structure of the definitions in ttystate.c.
137 	 */
138 
139 	sg[YES] = sg[NO];
140 
141 #ifdef SGTTY
142 	sg[YES].sg_flags &= ~(XTABS|ECHO|CRMOD);
143 # ifdef LPASS8
144 	sg[YES].sg_flags |= CBREAK;
145 # else
146 	sg[YES].sg_flags |= (MetaKey ? RAW : CBREAK);
147 # endif
148 #endif
149 
150 #if defined(TERMIO) || defined(TERMIOS)
151 	if (OKXonXoff)
152 		sg[YES].c_iflag &= ~(IXON | IXOFF);
153 	sg[YES].c_iflag &= ~(INLCR|ICRNL|IGNCR | (MetaKey? ISTRIP : 0));
154 	sg[YES].c_lflag &= ~(ICANON|ECHO);
155 	sg[YES].c_oflag &= ~(OPOST);
156 
157 	/* Set all those c_cc elements that we must.
158 	 * For peculiar systems, one might wish to predefine JVDISABLE
159 	 * in the configuration.  For example, on some unnamed
160 	 * versions of the Convex OS, it would be good to
161 	 * define it as (sg[YES].c_cc[VDISABLE]), saving a system call.
162 	 * Note that the only uses of JDISABLE are in this block,
163 	 * so the macro may safely refer to things in this context.
164 	 */
165 	{
166 # ifndef JVDISABLE
167 #  ifdef _POSIX_VDISABLE
168 #   define JVDISABLE	_POSIX_VDISABLE
169 #  else /* !_POSIX_VDISABLE */
170 #   ifdef _PC_VDISABLE
171 		/* Cache the result of fpathconf to reduce the number of syscalls.
172 		 * We don't handle the error return (-1) because there isn't
173 		 * anything better to do with it.
174 		 */
175 		cc_t	jvd = fpathconf(0, _PC_VDISABLE);
176 #    define JVDISABLE	jvd
177 #   else /* !_PC_VDISABLE */
178 #    define JVDISABLE	0
179 #   endif /* !_PC_VDISABLE */
180 #  endif /* !_POSIX_VDISABLE */
181 # endif /* JVDISABLE */
182 
183 		sg[YES].c_cc[VINTR] = IntChar;
184 
185 # ifdef VQUIT
186 		sg[YES].c_cc[VQUIT] = JVDISABLE;
187 # endif
188 		/* VERASE, VKILL, VEOL2 irrelevant */
189 		/* Beware aliasing! VMIN is VEOF and VTIME is VEOL */
190 # ifdef VSWTCH
191 		sg[YES].c_cc[VSWTCH] = JVDISABLE;
192 # endif
193 
194 		/* Under at least one system (SunOS 4.0), <termio.h>
195 		 * mistakenly defines the extra V symbols of <termios.h>
196 		 * without extending the c_cc array in struct termio
197 		 * to hold them!  This is why the following goo is doubly
198 		 * ifdefed.  It turns out that we don't use <termio.h>
199 		 * on SunOS 4.0, so the problem may be moot.
200 		 */
201 
202 # ifdef TERMIOS
203 #  ifdef VSUSP
204 		sg[YES].c_cc[VSUSP] = JVDISABLE;
205 #  endif
206 #  ifdef VDSUSP
207 		sg[YES].c_cc[VDSUSP] = JVDISABLE;
208 #  endif
209 #  ifdef VDISCARD
210 		/* ??? Under Solaris 2.1 needs VDISCARD disabled, or it will
211 		 * be processed by the tty driver, but not under SysVR4!
212 		 */
213 		sg[YES].c_cc[VDISCARD] = JVDISABLE;	/* flush output */
214 #  endif
215 #  ifdef VLNEXT
216 		sg[YES].c_cc[VLNEXT] = JVDISABLE;	/* literal next char */
217 #  endif
218 # endif /* TERMIOS */
219 
220 		sg[YES].c_cc[VMIN] = 1;
221 		sg[YES].c_cc[VTIME] = 1;
222 	}
223 #endif /* defined(TERMIO) || defined(TERMIOS) */
224 
225 #ifdef USE_TIOCSLTC
226 	ls[YES] = ls[NO];
227 	ls[YES].t_suspc = (char) -1;
228 	ls[YES].t_dsuspc = (char) -1;
229 	ls[YES].t_flushc = (char) -1;
230 	ls[YES].t_lnextc = (char) -1;
231 #endif /* USE_TIOCSLTC */
232 
233 #ifdef SGTTY
234 
235 # ifdef TIOCGETC
236 	tc[YES] = tc[NO];
237 	tc[YES].t_intrc = IntChar;
238 	tc[YES].t_quitc = (char) -1;
239 	if (OKXonXoff) {
240 		tc[YES].t_stopc = (char) -1;
241 		tc[YES].t_startc = (char) -1;
242 	}
243 # endif
244 
245 # ifdef LPASS8	/* use 4.3BSD's LPASS8 instead of raw for meta-key */
246 	lmword[YES] = lmword[NO];
247 
248 	if (MetaKey)
249 		lmword[YES] |= LPASS8;
250 
251 #  ifdef LLITOUT
252 	/* ??? under what conditions should we turn on LLITOUT flag? */
253 #  endif /* LLITOUT */
254 
255 #  ifdef LTILDE
256 	if (Hazeltine)
257 		lmword[YES] &= ~LTILDE;
258 #  endif /* LTILDE */
259 
260 # endif /* LPASS8 */
261 
262 #endif /* SGTTY */
263 
264 	/* Set tty state according to appropriate entry of each state pair.
265 	 * NOTE: the nested tangle of ifdefs is intended to follow
266 	 * the structure of the definitions in ttystate.c.
267 	 */
268 
269 #ifdef SGTTY
270 #  ifdef TIOCSETN
271 	(void) ioctl(0, TIOCSETN, (UnivPtr) &sg[n]);
272 #  else
273 	(void) stty(0, &sg[n]);
274 #  endif
275 #endif
276 
277 #ifdef TERMIO
278 	do ; while (ioctl(0, TCSETAW, (UnivPtr) &sg[n]) < 0 && errno == EINTR);
279 #endif
280 
281 #ifdef TERMIOS
282 	do ; while (tcsetattr(0, TCSADRAIN, &sg[n]) < 0 && errno == EINTR);
283 #endif
284 
285 #ifdef USE_TIOCSLTC
286 	(void) ioctl(0, TIOCSLTC, (UnivPtr) &ls[n]);
287 #endif /* USE_TIOCSLTC */
288 
289 #ifdef SGTTY
290 
291 # ifdef TIOCGETC
292 	(void) ioctl(0, TIOCSETC, (UnivPtr) &tc[n]);
293 # endif
294 
295 # ifdef LPASS8	/* use 4.3BSD's LPASS8 instead of raw for meta-key */
296 	(void) ioctl(0, TIOCLSET, (UnivPtr) &lmword[n]);	/* local mode word */
297 # endif
298 
299 #endif /* SGTTY */
300 
301 #ifdef BIFF
302 
303 	/* biff state is an honorary part of the tty state.
304 	 * On the other hand, it is different from the rest of the state
305 	 * since we only want to examine the setting if DisBiff
306 	 * has been set by the user.  For this reason, the code is
307 	 * somewhat more intricate.
308 	 */
309 	{
310 #		define BS_UNEXAMINED	0	/* we don't know if biff is enabled */
311 #		define BS_DISABLED	1	/* we have disabled biff */
312 #		define BS_UNCHANGED	2	/* we didn't disable biff */
313 		static int	biff_state = BS_UNEXAMINED;
314 
315 		static struct stat	tt_stat;
316 # if !defined(USE_FSTAT) || !defined(USE_FCHMOD)
317 		static char	*tt_name = NULL;	/* name of the control tty */
318 		extern char	*ttyname proto((int));	/* for systems w/o fstat */
319 # endif
320 
321 		if (n && DisBiff) {
322 			/* biff supression is our business */
323 			if (biff_state == BS_UNEXAMINED) {
324 				/* and we haven't looked after it */
325 				biff_state = BS_UNCHANGED;	/* at least so far */
326 				if (
327 # ifdef USE_FSTAT
328 					fstat(0, &tt_stat) != -1
329 # else
330 					((tt_name != NULL) || (tt_name = ttyname(0)) != NULL)
331 					&& stat(tt_name, &tt_stat) != -1
332 # endif
333 				&& (tt_stat.st_mode & S_IEXEC))
334 				{
335 					/* so let's suppress it */
336 # ifdef USE_FCHMOD
337 					(void) fchmod(0, tt_stat.st_mode & ~S_IEXEC);
338 					biff_state = BS_DISABLED;
339 # else
340 					if ((tt_name != NULL || (tt_name = ttyname(0)) != NULL)
341 					&& chmod(tt_name, tt_stat.st_mode & ~S_IEXEC) != -1)
342 					{
343 						/* Note: only change biff_state if we were able to
344 						 * get the tt_name -- this prevents the other
345 						 * chmod from blowing up.
346 						 */
347 						biff_state = BS_DISABLED;
348 					}
349 # endif
350 				}
351 			}
352 		} else {
353 			/* any biff suppression should be undone */
354 			if (biff_state == BS_DISABLED) {
355 				/* and we did suppress it, so we enable it */
356 # ifdef USE_FCHMOD
357 				(void) fchmod(0, tt_stat.st_mode);
358 # else
359 				(void) chmod(tt_name, tt_stat.st_mode);
360 # endif
361 			}
362 			biff_state = BS_UNEXAMINED;	/* it's out of our hands */
363 		}
364 #		undef BS_UNEXAMINED
365 #		undef BS_DISABLED
366 #		undef BS_UNCHANGED
367 	}
368 
369 #endif /* BIFF */
370 	prev_n = n;
371 }
372 
373 /* Determine the number of characters to buffer at each baud rate.  The
374    lower the number, the quicker the response when new input arrives.  Of
375    course the lower the number, the more prone the program is to stop in
376    output.  Decide what matters most to you. This sets ScrBufSize to the right
377    number or chars, and initializes `jstdout'.  */
378 
379 void
settout()380 settout()
381 {
382 	int	speed_chars;
383 
384 	static const struct {
385 		unsigned int bsize;
386 		unsigned int brate;
387 	} speeds[] = {
388 
389 #ifdef B0
390 		{ 1, B0 },
391 #endif
392 #ifdef B50
393 		{ 1, B50 },
394 #endif
395 #ifdef B75
396 		{ 1, B75 },
397 #endif
398 #ifdef B110
399 		{ 1, B110 },
400 #endif
401 #ifdef B134
402 		{ 1, B134 },
403 #endif
404 #ifdef B150
405 		{ 1, B150 },
406 #endif
407 #ifdef B200
408 		{ 1, B200 },
409 #endif
410 #ifdef B300
411 		{ 2, B300 },
412 #endif
413 #ifdef B600
414 		{ 4, B600 },
415 #endif
416 #ifdef B900
417 		{ 6, B900 },
418 #endif
419 #ifdef B1200
420 		{ 8, B1200 },
421 #endif
422 #ifdef B1800
423 		{ 16, B1800 },
424 #endif
425 #ifdef B2400
426 		{ 32, B2400 },
427 #endif
428 #ifdef B3600
429 		{ 64, B3600 },
430 #endif
431 #ifdef B4800
432 		{ 128, B4800 },
433 #endif
434 #ifdef B7200
435 		{ 256, B7200 },
436 #endif
437 #ifdef B9600
438 		{ 256, B9600 },
439 #endif
440 #ifdef EXTA
441 		{ 512, EXTA },
442 #endif
443 #ifdef B19200
444 		{ 512, B19200 },
445 #endif
446 #ifdef EXTB
447 		{ 1024, EXTB },
448 #endif
449 #ifdef B38400
450 		{ 1024, B38400 },
451 #endif
452 #ifdef EXT
453 		{ 1024, EXT }
454 #endif
455 };
456 	int i;
457 	for (i = 0; ; i++) {
458 		if (i == elemsof(speeds)) {
459 			speed_chars = 512;
460 			ospeed = B9600;	/* XXX */
461 			break;
462 		}
463 		if (speeds[i].brate == (unsigned short) ospeed) {
464 			speed_chars = speeds[i].bsize;
465 			break;
466 		}
467 	}
468 
469 	flushscreen();		/* flush the one character buffer */
470 	ScrBufSize = min(MAXTTYBUF, speed_chars * max(LI / 24, 1));
471 #ifndef NO_JSTDOUT
472 	jstdout = fd_open("/dev/tty", F_WRITE|F_LOCKED, 1, (char *)NULL, ScrBufSize);
473 #endif
474 }
475 
476 void
ttsize()477 ttsize()
478 {
479 	/* ??? We really ought to wait until the screen is big enough:
480 	 * at least three lines high (one line each for buffer, mode,
481 	 * and message) and at least twelve columns wide (eight for
482 	 * line number, one for content, two for overflow indicators,
483 	 * and one blank at end).
484 	 */
485 #ifdef TIOCGWINSZ
486 	struct winsize win;
487 
488 	if (ioctl(0, TIOCGWINSZ, (UnivPtr) &win) == 0
489 	&& win.ws_col >= 12
490 	&& win.ws_row >= 3)
491 	{
492 		CO = min(win.ws_col, MAXCOLS);
493 		LI = win.ws_row;
494 	}
495 #else /* !TIOCGWINSZ */
496 # ifdef BTL_BLIT
497 	struct jwinsize jwin;
498 
499 	if (ioctl(0, JWINSIZE, (UnivPtr) &jwin) == 0
500 	&& jwin.bytesx >= 12
501 	&& jwin.bytesy >= 3)
502 	{
503 		CO = min(jwin.bytesx, MAXCOLS);
504 		LI = jwin.bytesy;
505 	}
506 # endif /* BTL_BLIT */
507 #endif /* !TIOCGWINSZ */
508 	ILI = LI - 1;
509 }
510 
511 #endif /* UNIX */
512