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