ncurses 5.6 - patch 20061223
[ncurses.git] / tack / sysdep.c
1 /*
2 ** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3 ** 
4 ** This file is part of TACK.
5 ** 
6 ** TACK is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2, or (at your option)
9 ** any later version.
10 ** 
11 ** TACK is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ** GNU General Public License for more details.
15 ** 
16 ** You should have received a copy of the GNU General Public License
17 ** along with TACK; see the file COPYING.  If not, write to
18 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 ** Boston, MA 02110-1301, USA
20 */
21 /*
22  * Operating system dependent functions.  We assume the POSIX API.
23  * Note: on strict-POSIX systems (including BSD/OS) the select_delay_type
24  * global has no effect.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include <ncurses_cfg.h>
29 #endif
30 #include <signal.h>     /* include before curses.h to work around glibc bug */
31
32 #include <tack.h>
33
34 #include <term.h>
35 #include <errno.h>
36
37 #if defined(__BEOS__)
38 #undef false
39 #undef true
40 #include <OS.h>
41 #endif
42
43 #if HAVE_SELECT
44 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
45 #include <sys/time.h>
46 #endif
47 #if HAVE_SYS_SELECT_H
48 #include <sys/select.h>
49 #endif
50 #endif
51
52 MODULE_ID("$Id: sysdep.c,v 1.15 2005/09/17 19:49:16 tom Exp $")
53
54 #if DECL_ERRNO
55 extern int errno;
56 #endif
57
58 #ifdef TERMIOS
59 #define PUT_TTY(fd, buf) tcsetattr(fd, TCSAFLUSH, buf)
60 #else
61 #define PUT_TTY(fd, buf) stty(fd, buf)
62 #endif
63
64 /* globals */
65 int tty_frame_size;             /* asynch frame size times 2 */
66 unsigned tty_baud_rate;         /* baud rate - bits per second */
67 int not_a_tty;                  /* TRUE if output is not a tty (i.e. pipe) */
68 int nodelay_read;               /* TRUE if NDELAY is set */
69
70 #ifdef TERMIOS
71 #define TTY_IS_NOECHO   !(new_modes.c_lflag & ECHO)
72 #define TTY_IS_OUT_TRANS (new_modes.c_oflag & OPOST)
73 #define TTY_IS_CHAR_MODE !(new_modes.c_lflag & ICANON)
74 #define TTY_WAS_CS8 ((old_modes.c_cflag & CSIZE) == CS8)
75 #define TTY_WAS_XON_XOFF (old_modes.c_iflag & (IXON|IXOFF))
76 #else
77 #define TTY_IS_NOECHO   !(new_modes.sg_flags & (ECHO))
78 #define TTY_IS_OUT_TRANS (new_modes.sg_flags & (CRMOD))
79 #define TTY_IS_CHAR_MODE (new_modes.sg_flags & (RAW|CBREAK))
80 #define TTY_WAS_CS8      (old_modes.sg_flags & (PASS8))
81 #define TTY_WAS_XON_XOFF (old_modes.sg_flags & (TANDEM|MDMBUF|DECCTQ))
82 #endif
83
84 static TTY old_modes, new_modes;
85
86 void catchsig(void);
87
88 /*
89  * These are a sneaky way of conditionalizing bit unsets so strict-POSIX
90  * systems won't see them.
91  */
92 #ifndef XCASE
93 #define XCASE   0
94 #endif
95 #ifndef OLCUC
96 #define OLCUC   0
97 #endif
98 #ifndef IUCLC
99 #define IUCLC   0
100 #endif
101 #ifndef TABDLY
102 #define TABDLY  0
103 #endif
104 #ifndef IXANY
105 #define IXANY   0
106 #endif
107
108 void
109 tty_raw(int minch GCC_UNUSED, int mask)
110 {                               /* set tty to raw noecho */
111         new_modes = old_modes;
112 #ifdef TERMIOS
113 #if HAVE_SELECT
114         new_modes.c_cc[VMIN] = 1;
115 #else
116         new_modes.c_cc[VMIN] = minch;
117 #endif
118         new_modes.c_cc[VTIME] = 2;
119         new_modes.c_lflag &=
120                 ~(ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK | ECHONL);
121 #ifdef LOBLK
122         new_modes.c_lflag &= ~LOBLK;
123 #endif
124         new_modes.c_oflag &= ~(OPOST | OLCUC | TABDLY);
125         if (mask == ALLOW_PARITY) {
126                 new_modes.c_cflag &= ~(CSIZE | PARENB | HUPCL);
127                 new_modes.c_cflag |= CS8;
128         }
129         new_modes.c_iflag &=
130                 ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL |
131                 IUCLC | IXON | IXANY | IXOFF);
132 #else
133         new_modes.sg_flags |= RAW;
134 #endif
135         if (not_a_tty)
136                 return;
137         PUT_TTY(fileno(stdin), &new_modes);
138 }
139
140 void 
141 tty_set(void)
142 {                               /* set tty to special modes */
143         new_modes = old_modes;
144 #ifdef TERMIOS
145         new_modes.c_cc[VMIN] = 1;
146         new_modes.c_cc[VTIME] = 1;
147         new_modes.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
148 #if defined(ONLCR) && defined(OCRNL) && defined(ONLRET) && defined(OFILL)
149         new_modes.c_oflag &= ~(ONLCR | OCRNL | ONLRET | OFILL);
150 #else
151         new_modes.c_oflag &= ~(OPOST);
152 #endif
153         if (char_mask == ALLOW_PARITY)
154                 new_modes.c_iflag &= ~ISTRIP;
155         switch (select_xon_xoff) {
156         case 0:
157                 new_modes.c_iflag &= ~(IXON | IXOFF);
158                 break;
159         case 1:
160 #if defined(sequent) && sequent
161                 /* the sequent System V emulation is broken */
162                 new_modes = old_modes;
163                 new_modes.c_cc[VEOL] = 6;       /* control F  (ACK) */
164 #endif
165                 new_modes.c_iflag |= IXON | IXOFF;
166                 break;
167         }
168         switch (select_delay_type) {
169         case 0:
170 #ifdef NLDLY
171                 new_modes.c_oflag &=
172                         ~(NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY);
173 #endif  /* NLDLY */
174                 break;
175         case 1:
176 #ifdef NLDLY
177                 new_modes.c_oflag &=
178                         ~(NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY);
179 #endif  /* NLDLY */
180 #ifdef NL1
181                 new_modes.c_oflag |= NL1 | CR2;
182 #endif  /* NL1 */
183                 break;
184         }
185         if (!(new_modes.c_oflag & (unsigned long) ~OPOST))
186                 new_modes.c_oflag &= (unsigned long) ~OPOST;
187 #else
188         new_modes.sg_flags |= RAW;
189         if (not_a_tty)
190                 return;
191 #endif
192         PUT_TTY(fileno(stdin), &new_modes);
193 }
194
195
196 void 
197 tty_reset(void)
198 {                               /* reset the tty to the original modes */
199         fflush(stdout);
200         if (not_a_tty)
201                 return;
202         PUT_TTY(fileno(stdin), &old_modes);
203 }
204
205
206 void 
207 tty_init(void)
208 {                               /* ATT terminal init */
209 #if defined(F_GETFL) && defined(O_NDELAY)
210         int flags;
211
212         flags = fcntl(fileno(stdin), F_GETFL, 0);
213         nodelay_read = flags & O_NDELAY;
214 #else
215         nodelay_read = FALSE;
216 #endif
217         not_a_tty = FALSE;
218         if (GET_TTY(fileno(stdin), &old_modes) == -1) {
219                 if (errno == ENOTTY) {
220                         tty_frame_size = 20;
221                         not_a_tty = TRUE;
222                         return;
223                 }
224                 printf("tcgetattr error: %d\n", errno);
225                 exit(1);
226         }
227         /* if TAB3 is set then setterm() wipes out tabs (ht) */
228         new_modes = old_modes;
229 #ifdef TERMIOS
230 #ifdef TABDLY
231         new_modes.c_oflag &= ~TABDLY;
232 #endif  /* TABDLY */
233 #endif
234         if (PUT_TTY(fileno(stdin), &new_modes) == -1) {
235                 printf("tcsetattr error: %d\n", errno);
236                 exit(1);
237         }
238 #ifdef sequent
239         /* the sequent ATT emulation is broken soooo. */
240         old_modes.c_cflag &= ~(CSIZE | CSTOPB);
241         old_modes.c_cflag |= CS7 | PARENB;
242 #endif
243         catchsig();
244 #ifdef TERMIOS
245         switch (old_modes.c_cflag & CSIZE) {
246 #if defined(CS5) && (CS5 != 0)
247         case CS5:
248                 tty_frame_size = 10;
249                 break;
250 #endif
251 #if defined(CS6) && (CS6 != 0)
252         case CS6:
253                 tty_frame_size = 12;
254                 break;
255 #endif
256 #if defined(CS7) && (CS7 != 0)
257         case CS7:
258                 tty_frame_size = 14;
259                 break;
260 #endif
261 #if defined(CS8) && (CS8 != 0)
262         case CS8:
263                 tty_frame_size = 16;
264                 break;
265 #endif
266         }
267         tty_frame_size += 2 +
268                 ((old_modes.c_cflag & PARENB) ? 2 : 0) +
269                 ((old_modes.c_cflag & CSTOPB) ? 4 : 2);
270 #else
271         tty_frame_size = 6 +
272                 (old_modes.sg_flags & PASS8) ? 16 : 14;
273 #endif
274 }
275
276 /*
277 **      stty_query(question)
278 **
279 **      Does the current driver settings have this property?
280 */
281 int
282 stty_query(int q)
283 {
284         switch (q) {
285                 case TTY_NOECHO:
286                 return TTY_IS_NOECHO;
287         case TTY_OUT_TRANS:
288                 return TTY_IS_OUT_TRANS;
289         case TTY_CHAR_MODE:
290                 return TTY_IS_CHAR_MODE;
291         }
292         return (-1);
293 }
294
295 /*
296 **      initial_stty_query(question)
297 **
298 **      Did the initial driver settings have this property?
299 */
300 int
301 initial_stty_query(int q)
302 {
303         switch (q) {
304         case TTY_8_BIT:
305                 return TTY_WAS_CS8;
306         case TTY_XON_XOFF:
307                 return TTY_WAS_XON_XOFF;
308         }
309         return (-1);
310 }
311
312 #if HAVE_SELECT && defined(FD_ZERO)
313 static int
314 char_ready(void)
315 {
316         int n;
317         fd_set ifds;
318         struct timeval tv;
319
320         FD_ZERO(&ifds);
321         FD_SET(fileno(stdin), &ifds);
322         tv.tv_sec = 0;
323         tv.tv_usec = 200000;
324         n = select(fileno(stdin)+1, &ifds, NULL, NULL, &tv);
325         return (n != 0);
326 }
327
328 #else
329 #ifdef FIONREAD
330 int
331 char_ready(void)
332 {
333         int i, j;
334
335         /* the following loop has to be tuned for each computer */
336         for (j = 0; j < 1000; j++) {
337                 ioctl(fileno(stdin), FIONREAD, &i);
338                 if (i)
339                         return i;
340         }
341         return i;
342 }
343
344 #else
345 #if defined(__BEOS__)
346 int
347 char_ready(void)
348 {
349         int n = 0;
350         int howmany = ioctl(0, 'ichr', &n);
351         return (howmany >= 0 && n > 0);
352 }
353 #else
354 #define char_ready() 1
355 #endif
356 #endif
357 #endif
358
359 /*
360 **      spin_flush()
361 **
362 **      Wait for the input stream to stop.
363 **      Throw away all input characters.
364 */
365 void
366 spin_flush(void)
367 {
368         unsigned char buf[64];
369
370         fflush(stdout);
371         event_start(TIME_FLUSH);        /* start the timer */
372         do {
373                 if (char_ready()) {
374                         (void) read(fileno(stdin), &buf, sizeof(buf));
375                 }
376         } while (event_time(TIME_FLUSH) < 400000);
377 }
378
379 /*
380 **      read_key(input-buffer, length-of-buffer)
381 **
382 **      read one function key from the input stream.
383 **      A null character is converted to 0x80.
384 */
385 void 
386 read_key(char *buf, int max)
387 {
388         int got, ask, i, l;
389         char *s;
390
391         *buf = '\0';
392         s = buf;
393         fflush(stdout);
394         /* ATT unix may return 0 or 1, Berkeley Unix should be 1 */
395         while (read(fileno(stdin), s, 1) == 0);
396         ++s;
397         --max;
398         while (max > 0 && (ask = char_ready())) {
399                 if (ask > max) {
400                         ask = max;
401                 }
402                 if ((got = read(fileno(stdin), s, (unsigned) ask))) {
403                         s += got;
404                 } else {
405                         break;
406                 }
407                 max -= got;
408         }
409         *s = '\0';
410         l = s - buf;
411         for (s = buf, i = 0; i < l; i++) {
412                 if ((*s & 0x7f) == 0) {
413                         /* convert nulls to 0x80 */
414                         *(unsigned char *)s = 128;
415                 } else {
416                         /* strip high order bits (if any) */
417                         *s &= char_mask;
418                 }
419         }
420 }
421
422
423 void 
424 ignoresig(void)
425 {
426         /* ignore signals */
427         signal(SIGINT, SIG_IGN);
428         signal(SIGHUP, SIG_IGN);
429         signal(SIGQUIT, SIG_IGN);
430         signal(SIGTERM, SIG_IGN);
431         signal(SIGALRM, SIG_IGN);
432 }
433
434  /*
435     onintr( )
436  
437  is the interrupt handling routine onintr turns off interrupts while doing
438     clean-up
439  
440  onintr always exits fatally
441  */
442
443
444 static RETSIGTYPE 
445 onintr(int sig GCC_UNUSED)
446 {
447         ignoresig();
448         tty_reset();
449         exit(1);
450 }
451
452
453  /*
454     catchsig( )
455  
456  set up to field interrupts (via function onintr( )) so that if interrupted
457     we can restore the correct terminal modes
458  
459  catchsig simply returns
460  */
461
462
463 void 
464 catchsig(void)
465 {
466         if ((signal(SIGINT, SIG_IGN)) == SIG_DFL)
467                 signal(SIGINT, onintr);
468
469         if ((signal(SIGHUP, SIG_IGN)) == SIG_DFL)
470                 signal(SIGHUP, onintr);
471
472         if ((signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
473                 signal(SIGQUIT, onintr);
474
475         if ((signal(SIGTERM, SIG_IGN)) == SIG_DFL)
476                 signal(SIGTERM, onintr);
477
478 }
479
480 /*
481 **      alarm_event(sig)
482 **
483 **      Come here for an alarm event
484 */
485 static void
486 alarm_event(
487         int sig GCC_UNUSED)
488 {
489         no_alarm_event = 0;
490 }
491
492 /*
493 **      set_alarm_clock(seconds)
494 **
495 **      Set the alarm clock to fire in <seconds>
496 */
497 void
498 set_alarm_clock(
499         int seconds)
500 {
501         signal(SIGALRM, alarm_event);
502         no_alarm_event = 1;
503         (void) alarm((unsigned) seconds);
504 }