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