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