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