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