1 /* slutty.c --- Unix Low level terminal (tty) functions for S-Lang */
2 /* Copyright (c) 1992, 1999, 2001, 2002 John E. Davis
3 * This file is part of the S-Lang library.
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Perl Artistic License.
7 */
8
9 #include "slinclud.h"
10
11 #include <signal.h>
12 /* sequent support thanks to Kenneth Lorber <keni@oasys.dt.navy.mil> */
13 /* SYSV (SYSV ISC R3.2 v3.0) provided by iain.lea@erlm.siemens.de */
14
15 #if defined (_AIX) && !defined (_ALL_SOURCE)
16 # define _ALL_SOURCE /* so NBBY is defined in <sys/types.h> */
17 #endif
18
19 #include <sys/time.h>
20 #include <sys/types.h>
21
22 #ifdef SYSV
23 # include <fcntl.h>
24 # ifndef CRAY
25 # include <sys/termio.h>
26 # include <sys/stream.h>
27 # include <sys/ptem.h>
28 # include <sys/tty.h>
29 # endif
30 #endif
31
32 #ifdef __BEOS__
33 /* Prototype for select */
34 # include <net/socket.h>
35 #endif
36
37 #include <sys/file.h>
38
39 #ifndef sun
40 # include <sys/ioctl.h>
41 #endif
42
43 #ifdef __QNX__
44 # include <sys/select.h>
45 #endif
46
47 #include <sys/stat.h>
48 #include <errno.h>
49
50 #if defined (_AIX) && !defined (FD_SET)
51 # include <sys/select.h> /* for FD_ISSET, FD_SET, FD_ZERO */
52 #endif
53
54 #ifndef O_RDWR
55 # include <fcntl.h>
56 #endif
57
58 #include "slang.h"
59 #include "_slang.h"
60
61 int SLang_TT_Read_FD = -1;
62 int SLang_TT_Baud_Rate;
63
64 #ifdef HAVE_TERMIOS_H
65 # if !defined(HAVE_TCGETATTR) || !defined(HAVE_TCSETATTR)
66 # undef HAVE_TERMIOS_H
67 # endif
68 #endif
69
70 #ifndef HAVE_TERMIOS_H
71
72 # if !defined(CBREAK) && defined(sun)
73 # ifndef BSD_COMP
74 # define BSD_COMP 1
75 # endif
76 # include <sys/ioctl.h>
77 # endif
78
79 typedef struct
80 {
81 struct tchars t;
82 struct ltchars lt;
83 struct sgttyb s;
84 }
85 TTY_Termio_Type;
86 #else
87 # include <termios.h>
88 typedef struct termios TTY_Termio_Type;
89 #endif
90
91 static TTY_Termio_Type Old_TTY;
92
93 #ifdef HAVE_TERMIOS_H
94 typedef struct
95 {
96 unsigned int key;
97 unsigned int value;
98 } Baud_Rate_Type;
99
100 static Baud_Rate_Type Baud_Rates [] =
101 {
102 #ifdef B0
103 {B0, 0},
104 #endif
105 #ifdef B50
106 {B50, 50},
107 #endif
108 #ifdef B75
109 {B75, 75},
110 #endif
111 #ifdef B110
112 {B110, 110},
113 #endif
114 #ifdef B134
115 {B134, 134},
116 #endif
117 #ifdef B150
118 {B150, 150},
119 #endif
120 #ifdef B200
121 {B200, 200},
122 #endif
123 #ifdef B300
124 {B300, 300},
125 #endif
126 #ifdef B600
127 {B600, 600},
128 #endif
129 #ifdef B1200
130 {B1200, 1200},
131 #endif
132 #ifdef B1800
133 {B1800, 1800},
134 #endif
135 #ifdef B2400
136 {B2400, 2400},
137 #endif
138 #ifdef B4800
139 {B4800, 4800},
140 #endif
141 #ifdef B9600
142 {B9600, 9600},
143 #endif
144 #ifdef B19200
145 {B19200, 19200},
146 #endif
147 #ifdef B38400
148 {B38400, 38400},
149 #endif
150 #ifdef B57600
151 {B57600, 57600},
152 #endif
153 #ifdef B115200
154 {B115200, 115200},
155 #endif
156 #ifdef B230400
157 {B230400, 230400},
158 #endif
159 {0, 0}
160 };
161
162 static void
set_baud_rate(TTY_Termio_Type * tty)163 set_baud_rate (TTY_Termio_Type *tty)
164 {
165 #ifdef HAVE_CFGETOSPEED
166 unsigned int speed;
167 Baud_Rate_Type *b, *bmax;
168
169 if (SLang_TT_Baud_Rate)
170 return; /* already set */
171
172 speed = (unsigned int) cfgetospeed (tty);
173
174 b = Baud_Rates;
175 bmax = b + (sizeof (Baud_Rates)/sizeof(Baud_Rates[0]));
176 while (b < bmax)
177 {
178 if (b->key == speed)
179 {
180 SLang_TT_Baud_Rate = b->value;
181 return;
182 }
183 b++;
184 }
185 #else
186 (void) tty;
187 #endif
188 }
189
190 #endif /* HAVE_TERMIOS_H */
191
192 #ifdef HAVE_TERMIOS_H
193 # define GET_TERMIOS(fd, x) tcgetattr(fd, x)
194 # define SET_TERMIOS(fd, x) tcsetattr(fd, TCSADRAIN, x)
195 #else
196 # ifdef TCGETS
197 # define GET_TERMIOS(fd, x) ioctl(fd, TCGETS, x)
198 # define SET_TERMIOS(fd, x) ioctl(fd, TCSETS, x)
199 # else
200 # define X(x,m) &(((TTY_Termio_Type *)(x))->m)
201 # define GET_TERMIOS(fd, x) \
202 ((ioctl(fd, TIOCGETC, X(x,t)) || \
203 ioctl(fd, TIOCGLTC, X(x,lt)) || \
204 ioctl(fd, TIOCGETP, X(x,s))) ? -1 : 0)
205 # define SET_TERMIOS(fd, x) \
206 ((ioctl(fd, TIOCSETC, X(x,t)) ||\
207 ioctl(fd, TIOCSLTC, X(x,lt)) || \
208 ioctl(fd, TIOCSETP, X(x,s))) ? -1 : 0)
209 # endif
210 #endif
211
212 static int TTY_Inited = 0;
213 static int TTY_Open = 0;
214
215 #ifdef ultrix /* Ultrix gets _POSIX_VDISABLE wrong! */
216 # define NULL_VALUE -1
217 #else
218 # ifdef _POSIX_VDISABLE
219 # define NULL_VALUE _POSIX_VDISABLE
220 # else
221 # define NULL_VALUE 255
222 # endif
223 #endif
224
SLang_init_tty(int abort_char,int no_flow_control,int opost)225 int SLang_init_tty (int abort_char, int no_flow_control, int opost)
226 {
227 TTY_Termio_Type newtty;
228
229 SLsig_block_signals ();
230
231 if (TTY_Inited)
232 {
233 SLsig_unblock_signals ();
234 return 0;
235 }
236
237 TTY_Open = 0;
238
239 if ((SLang_TT_Read_FD == -1)
240 || (1 != isatty (SLang_TT_Read_FD)))
241 {
242 #ifdef O_RDWR
243 # if !defined(__BEOS__) && !defined(__APPLE__)
244 /* I have been told that BEOS will HANG if passed /dev/tty */
245 if ((SLang_TT_Read_FD = open("/dev/tty", O_RDWR)) >= 0)
246 {
247 TTY_Open = 1;
248 }
249 # endif
250 #endif
251 if (TTY_Open == 0)
252 {
253 SLang_TT_Read_FD = fileno (stderr);
254 if (1 != isatty (SLang_TT_Read_FD))
255 {
256 SLang_TT_Read_FD = fileno (stdin);
257 if (1 != isatty (SLang_TT_Read_FD))
258 {
259 fprintf (stderr, "Failed to open terminal.");
260 return -1;
261 }
262 }
263 }
264 }
265
266 SLang_Abort_Char = abort_char;
267
268 /* Some systems may not permit signals to be blocked. As a result, the
269 * return code must be checked.
270 */
271 while (-1 == GET_TERMIOS(SLang_TT_Read_FD, &Old_TTY))
272 {
273 if (errno != EINTR)
274 {
275 SLsig_unblock_signals ();
276 return -1;
277 }
278 }
279
280 while (-1 == GET_TERMIOS(SLang_TT_Read_FD, &newtty))
281 {
282 if (errno != EINTR)
283 {
284 SLsig_unblock_signals ();
285 return -1;
286 }
287 }
288
289 #ifndef HAVE_TERMIOS_H
290 (void) opost;
291 (void) no_flow_control;
292 newtty.s.sg_flags &= ~(ECHO);
293 newtty.s.sg_flags &= ~(CRMOD);
294 /* if (Flow_Control == 0) newtty.s.sg_flags &= ~IXON; */
295 newtty.t.t_eofc = 1;
296 if (abort_char == -1) SLang_Abort_Char = newtty.t.t_intrc;
297 newtty.t.t_intrc = SLang_Abort_Char; /* ^G */
298 newtty.t.t_quitc = 255;
299 newtty.lt.t_suspc = 255; /* to ignore ^Z */
300 newtty.lt.t_dsuspc = 255; /* to ignore ^Y */
301 newtty.lt.t_lnextc = 255;
302 newtty.s.sg_flags |= CBREAK; /* do I want cbreak or raw????? */
303 #else
304
305 /* get baud rate */
306
307 newtty.c_iflag &= ~(ECHO | INLCR | ICRNL);
308 #ifdef ISTRIP
309 newtty.c_iflag &= ~ISTRIP;
310 #endif
311 if (opost == 0) newtty.c_oflag &= ~OPOST;
312
313 set_baud_rate (&newtty);
314
315 if (no_flow_control) newtty.c_iflag &= ~IXON; else newtty.c_iflag |= IXON;
316
317 newtty.c_cc[VEOF] = 1;
318 newtty.c_cc[VMIN] = 1;
319 newtty.c_cc[VTIME] = 0;
320 newtty.c_lflag = ISIG | NOFLSH;
321 if (abort_char == -1) SLang_Abort_Char = newtty.c_cc[VINTR];
322 newtty.c_cc[VINTR] = SLang_Abort_Char; /* ^G */
323 newtty.c_cc[VQUIT] = NULL_VALUE;
324 newtty.c_cc[VSUSP] = NULL_VALUE; /* to ignore ^Z */
325 #ifdef VDSUSP
326 newtty.c_cc[VDSUSP] = NULL_VALUE; /* to ignore ^Y */
327 #endif
328 #ifdef VLNEXT
329 newtty.c_cc[VLNEXT] = NULL_VALUE; /* to ignore ^V ? */
330 #endif
331 #ifdef VSWTCH
332 newtty.c_cc[VSWTCH] = NULL_VALUE; /* to ignore who knows what */
333 #endif
334 #endif /* NOT HAVE_TERMIOS_H */
335
336 while (-1 == SET_TERMIOS(SLang_TT_Read_FD, &newtty))
337 {
338 if (errno != EINTR)
339 {
340 SLsig_unblock_signals ();
341 return -1;
342 }
343 }
344
345 TTY_Inited = 1;
346 SLsig_unblock_signals ();
347 return 0;
348 }
349
SLtty_set_suspend_state(int mode)350 void SLtty_set_suspend_state (int mode)
351 {
352 TTY_Termio_Type newtty;
353
354 SLsig_block_signals ();
355
356 if (TTY_Inited == 0)
357 {
358 SLsig_unblock_signals ();
359 return;
360 }
361
362 while ((-1 == GET_TERMIOS (SLang_TT_Read_FD, &newtty))
363 && (errno == EINTR))
364 ;
365
366 #ifndef HAVE_TERMIOS_H
367 /* I do not know if all systems define the t_dsuspc field */
368 if (mode == 0)
369 {
370 newtty.lt.t_suspc = 255;
371 newtty.lt.t_dsuspc = 255;
372 }
373 else
374 {
375 newtty.lt.t_suspc = Old_TTY.lt.t_suspc;
376 newtty.lt.t_dsuspc = Old_TTY.lt.t_dsuspc;
377 }
378 #else
379 if (mode == 0)
380 {
381 newtty.c_cc[VSUSP] = NULL_VALUE;
382 #ifdef VDSUSP
383 newtty.c_cc[VDSUSP] = NULL_VALUE;
384 #endif
385 }
386 else
387 {
388 newtty.c_cc[VSUSP] = Old_TTY.c_cc[VSUSP];
389 #ifdef VDSUSP
390 newtty.c_cc[VDSUSP] = Old_TTY.c_cc[VDSUSP];
391 #endif
392 }
393 #endif
394
395 while ((-1 == SET_TERMIOS (SLang_TT_Read_FD, &newtty))
396 && (errno == EINTR))
397 ;
398
399 SLsig_unblock_signals ();
400 }
401
SLang_reset_tty(void)402 void SLang_reset_tty (void)
403 {
404 SLsig_block_signals ();
405
406 if (TTY_Inited == 0)
407 {
408 SLsig_unblock_signals ();
409 return;
410 }
411
412 while ((-1 == SET_TERMIOS(SLang_TT_Read_FD, &Old_TTY))
413 && (errno == EINTR))
414 ;
415
416 if (TTY_Open)
417 {
418 while ((-1 == close (SLang_TT_Read_FD))
419 && (errno == EINTR))
420 ;
421
422 TTY_Open = 0;
423 SLang_TT_Read_FD = -1;
424 }
425
426 TTY_Inited = 0;
427 SLsig_unblock_signals ();
428 }
429
default_sigint(int sig)430 static void default_sigint (int sig)
431 {
432 sig = errno; /* use parameter */
433
434 SLKeyBoard_Quit = 1;
435 if (SLang_Ignore_User_Abort == 0) SLang_Error = SL_USER_BREAK;
436 SLsignal_intr (SIGINT, default_sigint);
437 errno = sig;
438 }
439
SLang_set_abort_signal(void (* hand)(int))440 int SLang_set_abort_signal (void (*hand)(int))
441 {
442 int save_errno = errno;
443 SLSig_Fun_Type *f;
444
445 if (hand == NULL) hand = default_sigint;
446 f = SLsignal_intr (SIGINT, hand);
447
448 errno = save_errno;
449
450 if (f == (SLSig_Fun_Type *) SIG_ERR)
451 return -1;
452
453 return 0;
454 }
455
456 #ifndef FD_SET
457 #define FD_SET(fd, tthis) *(tthis) = 1 << (fd)
458 #define FD_ZERO(tthis) *(tthis) = 0
459 #define FD_ISSET(fd, tthis) (*(tthis) & (1 << fd))
460 typedef int fd_set;
461 #endif
462
463 static fd_set Read_FD_Set;
464
465 /* HACK: If > 0, use 1/10 seconds. If < 0, use 1/1000 seconds */
466
_SLsys_input_pending(int tsecs)467 int _SLsys_input_pending(int tsecs)
468 {
469 struct timeval wait;
470 long usecs, secs;
471
472 if ((TTY_Inited == 0)
473 || (SLang_TT_Read_FD < 0))
474 {
475 errno = EBADF;
476 return -1;
477 }
478
479 if (tsecs >= 0)
480 {
481 secs = tsecs / 10;
482 usecs = (tsecs % 10) * 100000;
483 }
484 else
485 {
486 tsecs = -tsecs;
487 secs = tsecs / 1000;
488 usecs = (tsecs % 1000) * 1000;
489 }
490
491 wait.tv_sec = secs;
492 wait.tv_usec = usecs;
493
494 FD_ZERO(&Read_FD_Set);
495 FD_SET(SLang_TT_Read_FD, &Read_FD_Set);
496
497 return select(SLang_TT_Read_FD + 1, &Read_FD_Set, NULL, NULL, &wait);
498 }
499
500 int (*SLang_getkey_intr_hook) (void);
501
handle_interrupt(void)502 static int handle_interrupt (void)
503 {
504 if (SLang_getkey_intr_hook != NULL)
505 {
506 int save_tty_fd = SLang_TT_Read_FD;
507
508 if (-1 == (*SLang_getkey_intr_hook) ())
509 return -1;
510
511 if (save_tty_fd != SLang_TT_Read_FD)
512 return -1;
513 }
514
515 return 0;
516 }
517
_SLsys_getkey(void)518 unsigned int _SLsys_getkey (void)
519 {
520 unsigned char c;
521
522 if (TTY_Inited == 0)
523 {
524 int ic = fgetc (stdin);
525 if (ic == EOF) return SLANG_GETKEY_ERROR;
526 return (unsigned int) ic;
527 }
528
529 while (1)
530 {
531 int ret;
532
533 if (SLKeyBoard_Quit)
534 return SLang_Abort_Char;
535
536 if (0 == (ret = _SLsys_input_pending (100)))
537 continue;
538
539 if (ret != -1)
540 break;
541
542 if (SLKeyBoard_Quit)
543 return SLang_Abort_Char;
544
545 if (errno == EINTR)
546 {
547 if (-1 == handle_interrupt ())
548 return SLANG_GETKEY_ERROR;
549
550 continue;
551 }
552
553 break; /* let read handle it */
554 }
555
556 while (1)
557 {
558 int status = read(SLang_TT_Read_FD, (char *) &c, 1);
559
560 if (status > 0)
561 break;
562
563 if (status == 0)
564 {
565 /* We are at the end of a file. Let application handle it. */
566 return SLANG_GETKEY_ERROR;
567 }
568
569 if (errno == EINTR)
570 {
571 if (-1 == handle_interrupt ())
572 return SLANG_GETKEY_ERROR;
573
574 if (SLKeyBoard_Quit)
575 return SLang_Abort_Char;
576
577 continue;
578 }
579 #ifdef EAGAIN
580 if (errno == EAGAIN)
581 {
582 sleep (1);
583 continue;
584 }
585 #endif
586 #ifdef EWOULDBLOCK
587 if (errno == EWOULDBLOCK)
588 {
589 sleep (1);
590 continue;
591 }
592 #endif
593 #ifdef EIO
594 if (errno == EIO)
595 {
596 SLang_exit_error ("_SLsys_getkey: EIO error.");
597 }
598 #endif
599 return SLANG_GETKEY_ERROR;
600 }
601
602 return((unsigned int) c);
603 }
604
605