slutty.c 12 KB

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