slutty.c 11 KB

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