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