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 (&current_interrupt_chars);
554 }
555