1 /* -*-C-*-
2
3 Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
4 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Massachusetts
6 Institute of Technology
7
8 This file is part of MIT/GNU Scheme.
9
10 MIT/GNU Scheme is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or (at
13 your option) any later version.
14
15 MIT/GNU Scheme is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with MIT/GNU Scheme; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
23 USA.
24
25 */
26
27 #include "ux.h"
28 #include "osctty.h"
29 #include "ossig.h"
30
31 /* If `ctty_fildes' is nonnegative, it is an open file descriptor for
32 the controlling terminal of the process.
33
34 If `ctty_fildes' is negative, Scheme should not alter the control
35 terminal's settings. */
36 static int ctty_fildes;
37
38 /* This flag says whether Scheme was in the foreground when it was
39 last entered. Provided that no other process forces Scheme out of
40 the foreground, it will remain in the foreground until it exits or
41 is stopped.
42
43 If `scheme_in_foreground' is zero, Scheme should not alter the
44 control terminal's settings, nor should it alter the settings of
45 stdin, stdout, or stderr if they are terminals. */
46 int scheme_in_foreground;
47
48 /* This flag, set during initialization, says whether we are
49 permitted to change the settings of the control terminal. */
50 static int permit_ctty_control;
51
52 /* Original states of the control terminal, stdin, and stdout when
53 Scheme was last continued or stopped, respectively. If the
54 corresponding `_recorded' flag is zero, then no information is
55 saved. */
56
57 struct terminal_state_recording
58 {
59 int fd;
60 int recorded_p;
61 Ttty_state state;
62 int flags;
63 };
64
65 static struct terminal_state_recording outside_ctty_state;
66 static struct terminal_state_recording outside_stdin_state;
67 static struct terminal_state_recording outside_stdout_state;
68 static struct terminal_state_recording inside_ctty_state;
69 static struct terminal_state_recording inside_stdin_state;
70 static struct terminal_state_recording inside_stdout_state;
71
72 static void ctty_update_interrupt_chars (void);
73
74 static int
get_terminal_state(int fd,Ttty_state * s)75 get_terminal_state (int fd, Ttty_state * s)
76 {
77 while (1)
78 {
79 int scr = (UX_terminal_get_state (fd, s));
80 if ((scr >= 0) || (errno != EINTR))
81 return (scr);
82 }
83 }
84
85 static int
set_terminal_state(int fd,Ttty_state * s)86 set_terminal_state (int fd, Ttty_state * s)
87 {
88 while (1)
89 {
90 int scr = (UX_terminal_set_state (fd, s));
91 if ((scr >= 0) || (errno != EINTR))
92 return (scr);
93 }
94 }
95
96
97 static int
get_flags(int fd,int * flags)98 get_flags (int fd, int * flags)
99 {
100 #ifdef FCNTL_NONBLOCK
101 while (1)
102 {
103 int scr = (UX_fcntl (fd, F_GETFL, 0));
104 if (scr >= 0)
105 {
106 (*flags) = scr;
107 return (0);
108 }
109 if (errno != EINTR)
110 return (-1);
111 }
112 #else
113 return (0);
114 #endif
115 }
116
117 static int
set_flags(int fd,int * flags)118 set_flags (int fd, int * flags)
119 {
120 #ifdef FCNTL_NONBLOCK
121 while (1)
122 {
123 int scr = (UX_fcntl (fd, F_SETFL, (*flags)));
124 if ((scr >= 0) || (errno != EINTR))
125 return (scr);
126 }
127 #else
128 return (0);
129 #endif
130 }
131
132 static void
save_external_state(struct terminal_state_recording * s)133 save_external_state (struct terminal_state_recording * s)
134 {
135 (s -> recorded_p) =
136 (scheme_in_foreground
137 && (isatty (s -> fd))
138 && ((get_terminal_state ((s -> fd), (& (s -> state)))) >= 0)
139 && ((get_flags ((s -> fd), (& (s -> flags)))) >= 0));
140 }
141
142 static void
restore_external_state(struct terminal_state_recording * s)143 restore_external_state (struct terminal_state_recording * s)
144 {
145 if (s -> recorded_p)
146 {
147 set_terminal_state ((s -> fd), (& (s -> state)));
148 set_flags ((s -> fd), (& (s -> flags)));
149 (s -> recorded_p) = 0;
150 }
151 }
152
153 void
save_internal_state(struct terminal_state_recording * s,struct terminal_state_recording * es)154 save_internal_state (struct terminal_state_recording * s,
155 struct terminal_state_recording * es)
156 {
157 /* Don't do anything unless we have a recording of the external
158 state. Otherwise, we should preserve the previous recording of
159 the internal state, if any. */
160 if (es -> recorded_p)
161 (s -> recorded_p) =
162 (((get_terminal_state ((s -> fd), (& (s -> state)))) >= 0)
163 && ((get_flags ((s -> fd), (& (s -> flags)))) >= 0));
164 }
165
166 static void
restore_internal_state(struct terminal_state_recording * s,struct terminal_state_recording * es)167 restore_internal_state (struct terminal_state_recording * s,
168 struct terminal_state_recording * es)
169 {
170 /* When we recorded the internal state, we had a recording of the
171 external state. But since we've stopped Scheme and restarted it,
172 we may no longer have a current recording of the external state.
173 If we don't, then we can't restore the internal state.
174
175 The usual reason that we don't have a recording is that Scheme is
176 in the background. In that case it would be nice to preserve the
177 previous internal state until we go back to the foreground. But
178 doing that transparently would also require tracking all
179 attempted state changes in the recording, which is a pain. So if
180 we can't restore the internal state, we just thrown it away. */
181 if (s -> recorded_p)
182 {
183 if (es -> recorded_p)
184 {
185 set_terminal_state ((s -> fd), (& (s -> state)));
186 set_flags ((s -> fd), (& (s -> flags)));
187 }
188 (s -> recorded_p) = 0;
189 }
190 }
191
192 void
UX_ctty_save_external_state(void)193 UX_ctty_save_external_state (void)
194 {
195 if (permit_ctty_control && (ctty_fildes >= 0))
196 {
197 pid_t pgrp_id = (UX_tcgetpgrp (ctty_fildes));
198 scheme_in_foreground =
199 ((pgrp_id < 0)
200 /* If no job control, assume we're in foreground. */
201 ? (errno == ENOSYS)
202 : ((UX_getpgrp ()) == pgrp_id));
203 }
204 else
205 scheme_in_foreground = 0;
206 save_external_state (&outside_ctty_state);
207 save_external_state (&outside_stdin_state);
208 save_external_state (&outside_stdout_state);
209 }
210
211 void
UX_ctty_restore_external_state(void)212 UX_ctty_restore_external_state (void)
213 {
214 restore_external_state (&outside_ctty_state);
215 restore_external_state (&outside_stdin_state);
216 restore_external_state (&outside_stdout_state);
217 }
218
219 void
UX_ctty_save_internal_state(void)220 UX_ctty_save_internal_state (void)
221 {
222 save_internal_state ((&inside_ctty_state), (&outside_ctty_state));
223 save_internal_state ((&inside_stdin_state), (&outside_stdin_state));
224 save_internal_state ((&inside_stdout_state), (&outside_stdout_state));
225 }
226
227 void
UX_ctty_restore_internal_state(void)228 UX_ctty_restore_internal_state (void)
229 {
230 int do_update =
231 ((inside_ctty_state . recorded_p)
232 && (outside_ctty_state . recorded_p));
233 restore_internal_state ((&inside_ctty_state), (&outside_ctty_state));
234 restore_internal_state ((&inside_stdin_state), (&outside_stdin_state));
235 restore_internal_state ((&inside_stdout_state), (&outside_stdout_state));
236 if (do_update)
237 ctty_update_interrupt_chars ();
238 }
239
240 int
OS_ctty_interrupt_control(void)241 OS_ctty_interrupt_control (void)
242 {
243 return (outside_ctty_state . recorded_p);
244 }
245
246 int
UX_terminal_control_ok(int fd)247 UX_terminal_control_ok (int fd)
248 {
249 return
250 ((fd == STDIN_FILENO)
251 ? (outside_stdin_state . recorded_p)
252 : (fd == STDOUT_FILENO)
253 ? (outside_stdout_state . recorded_p)
254 : 1);
255 }
256
257 /* Keyboard Interrupt Characters */
258
259 typedef struct
260 {
261 cc_t quit;
262 cc_t intrpt;
263 cc_t tstp;
264 cc_t dtstp;
265 } Tinterrupt_chars;
266
267 static Tinterrupt_enables current_interrupt_enables;
268 static Tinterrupt_chars current_interrupt_chars;
269
270 #define DEFAULT_SIGQUIT_CHAR ((cc_t) '\003') /* ^C */
271 #define DEFAULT_SIGINT_CHAR ((cc_t) '\007') /* ^G */
272 #define DEFAULT_SIGTSTP_CHAR ((cc_t) '\032') /* ^Z */
273
274 #define KEYBOARD_QUIT_INTERRUPT 0x1
275 #define KEYBOARD_INTRPT_INTERRUPT 0x2
276 #define KEYBOARD_TSTP_INTERRUPT 0x4
277 #define KEYBOARD_ALL_INTERRUPTS 0x7
278
279 cc_t
OS_ctty_quit_char(void)280 OS_ctty_quit_char (void)
281 {
282 return (current_interrupt_chars . quit);
283 }
284
285 cc_t
OS_ctty_int_char(void)286 OS_ctty_int_char (void)
287 {
288 return (current_interrupt_chars . intrpt);
289 }
290
291 cc_t
OS_ctty_tstp_char(void)292 OS_ctty_tstp_char (void)
293 {
294 return (current_interrupt_chars . tstp);
295 }
296
297 cc_t
OS_ctty_disabled_char(void)298 OS_ctty_disabled_char (void)
299 {
300 return ((ctty_fildes >= 0) ? (UX_PC_VDISABLE (ctty_fildes)) : '\377');
301 }
302
303 int
OS_ctty_fd(void)304 OS_ctty_fd (void)
305 {
306 return (ctty_fildes);
307 }
308
309 #if 0
310
311 /* not currently used */
312 static void
313 ctty_get_interrupt_chars (Tinterrupt_chars * ic)
314 {
315 Ttty_state s;
316 if ((get_terminal_state (ctty_fildes, (&s))) == 0)
317 {
318 #ifdef HAVE_TERMIOS_H
319 (ic -> quit) = ((s . tio . c_cc) [VQUIT]);
320 (ic -> intrpt) = ((s . tio . c_cc) [VINTR]);
321 (ic -> tstp) = ((s . tio . c_cc) [VSUSP]);
322
323 #ifdef VDSUSP
324 (ic -> dtstp) = ((s . tio . c_cc) [VDSUSP]);
325 #else /* not VDSUSP */
326 #ifdef __HPUX__
327 (ic -> dtstp) = (s . ltc . t_dsuspc);
328 #endif /* __HPUX__ */
329 #endif /* not VDSUSP */
330
331 #else /* not HAVE_TERMIOS_H */
332 #ifdef HAVE_TERMIO_H
333
334 (ic -> quit) = ((s . tio . c_cc) [VQUIT]);
335 (ic -> intrpt) = ((s . tio . c_cc) [VINTR]);
336 #ifdef HAVE_STRUCT_LTCHARS
337 (ic -> tstp) = (s . ltc . t_suspc);
338 (ic -> dtstp) = (s . ltc . t_dsuspc);
339 #else /* not HAVE_STRUCT_LTCHARS */
340 {
341 cc_t disabled_char = (UX_PC_VDISABLE (ctty_fildes));
342 (ic -> tstp) = disabled_char;
343 (ic -> dtstp) = disabled_char;
344 }
345 #endif /* not HAVE_STRUCT_LTCHARS */
346
347 #else /* not HAVE_TERMIO_H */
348 #ifdef HAVE_SGTTY_H
349
350 (ic -> quit) = (s . tc . t_quitc);
351 (ic -> intrpt) = (s . tc . t_intrc);
352 #ifdef HAVE_STRUCT_LTCHARS
353 (ic -> tstp) = (s . ltc . t_suspc);
354 (ic -> dtstp) = (s . ltc . t_dsuspc);
355 #else /* not HAVE_STRUCT_LTCHARS */
356 {
357 cc_t disabled_char = (UX_PC_VDISABLE (ctty_fildes));
358 (ic -> tstp) = disabled_char;
359 (ic -> dtstp) = disabled_char;
360 }
361 #endif /* not HAVE_STRUCT_LTCHARS */
362
363 #endif /* HAVE_SGTTY_H */
364 #endif /* HAVE_TERMIO_H */
365 #endif /* HAVE_TERMIOS_H */
366 }
367 else
368 {
369 cc_t disabled_char = (UX_PC_VDISABLE (ctty_fildes));
370 (ic -> quit) = disabled_char;
371 (ic -> intrpt) = disabled_char;
372 (ic -> tstp) = disabled_char;
373 (ic -> dtstp) = disabled_char;
374 }
375 }
376 #endif /* 0 */
377
378 static void
ctty_set_interrupt_chars(Tinterrupt_chars * ic)379 ctty_set_interrupt_chars (Tinterrupt_chars * ic)
380 {
381 Ttty_state s;
382 if ((get_terminal_state (ctty_fildes, (&s))) == 0)
383 {
384 #ifdef HAVE_TERMIOS_H
385 ((s . tio . c_cc) [VQUIT]) = (ic -> quit);
386 ((s . tio . c_cc) [VINTR]) = (ic -> intrpt);
387 ((s . tio . c_cc) [VSUSP]) = (ic -> tstp);
388 #ifdef VDSUSP
389 ((s . tio . c_cc) [VDSUSP]) = (ic -> dtstp);
390 #else /* not VDSUSP */
391 #ifdef __HPUX__
392 (s . ltc . t_suspc) = (ic -> tstp);
393 (s . ltc . t_dsuspc) = (ic -> dtstp);
394 #endif /* __HPUX__ */
395 #endif /* not VDSUSP */
396
397 #else /* not HAVE_TERMIOS_H */
398 #ifdef HAVE_TERMIO_H
399
400 ((s . tio . c_cc) [VQUIT]) = (ic -> quit);
401 ((s . tio . c_cc) [VINTR]) = (ic -> intrpt);
402 #ifdef HAVE_STRUCT_LTCHARS
403 (s . ltc . t_suspc) = (ic -> tstp);
404 (s . ltc . t_dsuspc) = (ic -> dtstp);
405 #endif
406
407 #else /* not HAVE_TERMIO_H */
408 #ifdef HAVE_SGTTY_H
409
410 (s . tc . t_quitc) = (ic -> quit);
411 (s . tc . t_intrc) = (ic -> intrpt);
412 #ifdef HAVE_STRUCT_LTCHARS
413 (s . ltc . t_suspc) = (ic -> tstp);
414 (s . ltc . t_dsuspc) = (ic -> dtstp);
415 #endif
416
417 #endif /* HAVE_SGTTY_H */
418 #endif /* HAVE_TERMIO_H */
419 #endif /* HAVE_TERMIOS_H */
420 set_terminal_state (ctty_fildes, (&s));
421 }
422 }
423
424 static void
ctty_update_interrupt_chars(void)425 ctty_update_interrupt_chars (void)
426 {
427 if (outside_ctty_state . recorded_p)
428 {
429 cc_t disabled_char = (UX_PC_VDISABLE (ctty_fildes));
430 /* Must split declaration and assignment because some compilers
431 do not permit aggregate initializers. */
432 Tinterrupt_chars active_interrupt_chars;
433 active_interrupt_chars = current_interrupt_chars;
434 if ((current_interrupt_enables & KEYBOARD_QUIT_INTERRUPT) == 0)
435 (active_interrupt_chars . quit) = disabled_char;
436 if ((current_interrupt_enables & KEYBOARD_INTRPT_INTERRUPT) == 0)
437 (active_interrupt_chars . intrpt) = disabled_char;
438 if ((current_interrupt_enables & KEYBOARD_TSTP_INTERRUPT) == 0)
439 (active_interrupt_chars . tstp) = disabled_char;
440 (active_interrupt_chars . dtstp) = disabled_char;
441 ctty_set_interrupt_chars (&active_interrupt_chars);
442 }
443 }
444
445 void
OS_ctty_get_interrupt_enables(Tinterrupt_enables * mask)446 OS_ctty_get_interrupt_enables (Tinterrupt_enables * mask)
447 {
448 (*mask) = current_interrupt_enables;
449 }
450
451 void
OS_ctty_set_interrupt_enables(Tinterrupt_enables * mask)452 OS_ctty_set_interrupt_enables (Tinterrupt_enables * mask)
453 {
454 current_interrupt_enables = (*mask);
455 ctty_update_interrupt_chars ();
456 }
457
458 #if 0
459
460 void
461 OS_ctty_set_interrupt_chars (cc_t quit_char,
462 cc_t int_char,
463 cc_t tstp_char)
464 {
465 (current_interrupt_chars . quit) = quit_char;
466 (current_interrupt_chars . intrpt) = int_char;
467 (current_interrupt_chars . tstp) = tstp_char;
468 ctty_update_interrupt_chars ();
469 }
470 #endif
471
472 unsigned int
OS_ctty_num_int_chars(void)473 OS_ctty_num_int_chars (void)
474 {
475 return (3);
476 }
477
478 cc_t *
OS_ctty_get_int_chars(void)479 OS_ctty_get_int_chars (void)
480 {
481 static cc_t int_chars [3];
482
483 int_chars[0] = current_interrupt_chars.quit;
484 int_chars[1] = current_interrupt_chars.intrpt;
485 int_chars[2] = current_interrupt_chars.tstp;
486 return (& int_chars [0]);
487 }
488
489 void
OS_ctty_set_int_chars(cc_t * int_chars)490 OS_ctty_set_int_chars (cc_t * int_chars)
491 {
492 current_interrupt_chars.quit = int_chars[0];
493 current_interrupt_chars.intrpt = int_chars[1];
494 current_interrupt_chars.tstp = int_chars[2];
495 ctty_update_interrupt_chars ();
496 return;
497 }
498
499 extern enum interrupt_handler OS_signal_quit_handler (void);
500 extern enum interrupt_handler OS_signal_int_handler (void);
501 extern enum interrupt_handler OS_signal_tstp_handler (void);
502 extern void OS_signal_set_interrupt_handlers
503 (enum interrupt_handler quit_handler,
504 enum interrupt_handler int_handler,
505 enum interrupt_handler tstp_handler);
506
507 cc_t *
OS_ctty_get_int_char_handlers(void)508 OS_ctty_get_int_char_handlers (void)
509 {
510 static cc_t int_handlers [3];
511
512 int_handlers[0] = ((cc_t) (OS_signal_quit_handler ()));
513 int_handlers[1] = ((cc_t) (OS_signal_int_handler ()));
514 int_handlers[2] = ((cc_t) (OS_signal_tstp_handler ()));
515 return (& int_handlers [0]);
516 }
517
518 void
OS_ctty_set_int_char_handlers(cc_t * int_handlers)519 OS_ctty_set_int_char_handlers (cc_t * int_handlers)
520 {
521 OS_signal_set_interrupt_handlers
522 (((enum interrupt_handler) (int_handlers [0])),
523 ((enum interrupt_handler) (int_handlers [1])),
524 ((enum interrupt_handler) (int_handlers [2])));
525 return;
526 }
527
528 void
UX_initialize_ctty(int interactive)529 UX_initialize_ctty (int interactive)
530 {
531 {
532 char buffer [L_ctermid];
533 char * tty = (UX_ctermid (buffer));
534 ctty_fildes =
535 (((tty == 0) || ((tty[0]) == 0))
536 ? (-1)
537 : (UX_open (tty, O_RDWR, 0)));
538 }
539 permit_ctty_control = interactive;
540 (inside_ctty_state . fd) = (outside_ctty_state . fd) = ctty_fildes;
541 (inside_stdin_state . fd) = (outside_stdin_state . fd) = STDIN_FILENO;
542 (inside_stdout_state . fd) = (outside_stdout_state . fd) = STDOUT_FILENO;
543 UX_ctty_save_external_state ();
544 (inside_ctty_state . recorded_p) = 0;
545 (inside_stdin_state . recorded_p) = 0;
546 (inside_stdout_state . recorded_p) = 0;
547 (current_interrupt_chars . quit) = DEFAULT_SIGQUIT_CHAR;
548 (current_interrupt_chars . intrpt) = DEFAULT_SIGINT_CHAR;
549 (current_interrupt_chars . tstp) = DEFAULT_SIGTSTP_CHAR;
550 (current_interrupt_chars . dtstp) = (UX_PC_VDISABLE (ctty_fildes));
551 current_interrupt_enables = KEYBOARD_ALL_INTERRUPTS;
552 if (outside_ctty_state . recorded_p)
553 ctty_set_interrupt_chars (¤t_interrupt_chars);
554 }
555