slutty.c 11 KB

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