1: /* direct key io driver
2:
3: The following is stolen from the readline library for bash
4: */
5:
6: #include <stdio.h>
7: #include <signal.h>
8: #include <sys/types.h>
9: #include <sys/ioctl.h>
10: #include <fcntl.h>
11: #include <sys/file.h>
12:
13: #if defined (__GNUC__)
14: # define alloca __builtin_alloca
15: #else
16: # if defined (sparc) || defined (HAVE_ALLOCA_H)
17: # include <alloca.h>
18: # endif
19: #endif
20:
21: #if defined (HAVE_UNISTD_H)
22: # include <unistd.h>
23: #endif
24:
25: #define NEW_TTY_DRIVER
26: #define HAVE_BSD_SIGNALS
27: #define USE_XON_XOFF
28:
29: /* Some USG machines have BSD signal handling (sigblock, sigsetmask, etc.) */
30: #if defined (USG) && !defined (hpux)
31: #undef HAVE_BSD_SIGNALS
32: #endif
33:
34: /* System V machines use termio. */
35: #if !defined (_POSIX_VERSION)
36: # if defined (USG) || defined (hpux) || defined (Xenix) || defined (sgi) || defined (DGUX) || defined (ultrix)
37: # undef NEW_TTY_DRIVER
38: # define TERMIO_TTY_DRIVER
39: # include <termio.h>
40: # if !defined (TCOON)
41: # define TCOON 1
42: # endif
43: # endif /* USG || hpux || Xenix || sgi || DUGX || ultrix*/
44: #endif /* !_POSIX_VERSION */
45:
46: /* Posix systems use termios and the Posix signal functions. */
47: #if defined (_POSIX_VERSION)
48: # if !defined (TERMIOS_MISSING)
49: # undef NEW_TTY_DRIVER
50: # define TERMIOS_TTY_DRIVER
51: # include <termios.h>
52: # endif /* !TERMIOS_MISSING */
53: # define HAVE_POSIX_SIGNALS
54: # if !defined (O_NDELAY)
55: # define O_NDELAY O_NONBLOCK /* Posix-style non-blocking i/o */
56: # endif /* O_NDELAY */
57: #endif /* _POSIX_VERSION */
58:
59: /* Other (BSD) machines use sgtty. */
60: #if defined (NEW_TTY_DRIVER)
61: #include <sgtty.h>
62: #endif
63:
64: /* Define _POSIX_VDISABLE if we are not using the `new' tty driver and
65: it is not already defined. It is used both to determine if a
66: special character is disabled and to disable certain special
67: characters. Posix systems should set to 0, USG systems to -1. */
68: #if !defined (NEW_TTY_DRIVER) && !defined (_POSIX_VDISABLE)
69: # if defined (_POSIX_VERSION)
70: # define _POSIX_VDISABLE 0
71: # else /* !_POSIX_VERSION */
72: # define _POSIX_VDISABLE -1
73: # endif /* !_POSIX_VERSION */
74: #endif /* !NEW_TTY_DRIVER && !_POSIX_VDISABLE */
75:
76: #include <errno.h>
77: extern int errno;
78:
79: #if defined (SHELL)
80: # include <posixstat.h>
81: #else
82: # include <sys/stat.h>
83: #endif /* !SHELL */
84: /* #define HACK_TERMCAP_MOTION */
85:
86: #if defined (USG) && defined (hpux)
87: # if !defined (USGr3)
88: # define USGr3
89: # endif /* USGr3 */
90: #endif /* USG && hpux */
91:
92: #if defined (_POSIX_VERSION) || defined (USGr3)
93: # include <dirent.h>
94: # define direct dirent
95: # if defined (_POSIX_VERSION)
96: # define D_NAMLEN(d) (strlen ((d)->d_name))
97: # else /* !_POSIX_VERSION */
98: # define D_NAMLEN(d) ((d)->d_reclen)
99: # endif /* !_POSIX_VERSION */
100: #else /* !_POSIX_VERSION && !USGr3 */
101: # define D_NAMLEN(d) ((d)->d_namlen)
102: # if !defined (USG)
103: # include <sys/dir.h>
104: # else /* USG */
105: # if defined (Xenix)
106: # include <sys/ndir.h>
107: # else /* !Xenix */
108: # include <ndir.h>
109: # endif /* !Xenix */
110: # endif /* USG */
111: #endif /* !POSIX_VERSION && !USGr3 */
112:
113: #if defined (USG) && defined (TIOCGWINSZ)
114: # include <sys/stream.h>
115: # if defined (USGr4) || defined (USGr3)
116: # if defined (Symmetry) || defined (_SEQUENT_)
117: # include <sys/pte.h>
118: # else
119: # include <sys/ptem.h>
120: # endif /* !Symmetry || _SEQUENT_ */
121: # endif /* USGr4 */
122: #endif /* USG && TIOCGWINSZ */
123:
124: #if defined (TERMIOS_TTY_DRIVER)
125: static struct termios otio;
126: #else
127: static struct termio otio;
128: #endif /* !TERMIOS_TTY_DRIVER */
129:
130: /* Non-zero means echo characters as they are read. */
131: int readline_echoing_p = 1;
132:
133: /* The character that can generate an EOF. Really read from
134: the terminal driver... just defaulted here. */
135:
136: #ifndef CTRL
137: #define CTRL(key) ((key)-'@')
138: #endif
139:
140: static int eof_char = CTRL ('D');
141:
142: /* **************************************************************** */
143: /* */
144: /* Saving and Restoring the TTY */
145: /* */
146: /* **************************************************************** */
147:
148: /* Non-zero means that the terminal is in a prepped state. */
149: static int terminal_prepped = 0;
150:
151: #if defined (NEW_TTY_DRIVER)
152:
153: /* Standard flags, including ECHO. */
154: static int original_tty_flags = 0;
155:
156: /* Local mode flags, like LPASS8. */
157: static int local_mode_flags = 0;
158:
159: /* Terminal characters. This has C-s and C-q in it. */
160: static struct tchars original_tchars;
161:
162: /* Local special characters. This has the interrupt characters in it. */
163: #if defined (TIOCGLTC)
164: static struct ltchars original_ltchars;
165: #endif
166:
167: /* Bind KEY to FUNCTION. Returns non-zero if KEY is out of range. */
168:
169: #if defined (TIOCGETC)
170: #if defined (USE_XON_XOFF)
171:
172: int
173: bind_key (key, function)
174: int key;
175: Function *function;
176: {
177: if (key < 0)
178: return (key);
179:
180: if (key > 127 && key < 256)
181: {
182: if (keymap[ESC].type == ISKMAP)
183: {
184: Keymap escmap = (Keymap)keymap[ESC].function;
185:
186: key -= 128;
187: escmap[key].type = ISFUNC;
188: escmap[key].function = function;
189: return (0);
190: }
191: return (key);
192: }
193:
194: keymap[key].type = ISFUNC;
195: keymap[key].function = function;
196: return (0);
197: }
198: #endif
199: #endif
200:
201: /* We use this to get and set the tty_flags. */
202: static struct sgttyb the_ttybuff;
203:
204: #if defined (USE_XON_XOFF)
205: /* If the terminal was in xoff state when we got to it, then xon_char
206: contains the character that is supposed to start it again. */
207: static int xon_char, xoff_state;
208: #endif /* USE_XON_XOFF */
209:
210: /* **************************************************************** */
211: /* */
212: /* Bogus Flow Control */
213: /* */
214: /* **************************************************************** */
215:
216: restart_output (count, key)
217: int count, key;
218: {
219: int fildes = fileno (stdin);
220: #if defined (TIOCSTART)
221: #if defined (apollo)
222: ioctl (&fildes, TIOCSTART, 0);
223: #else
224: ioctl (fildes, TIOCSTART, 0);
225: #endif /* apollo */
226:
227: #else
228: # if defined (TERMIOS_TTY_DRIVER)
229: tcflow (fildes, TCOON);
230: # else
231: # if defined (TCXONC)
232: ioctl (fildes, TCXONC, TCOON);
233: # endif /* TCXONC */
234: # endif /* !TERMIOS_TTY_DRIVER */
235: #endif /* TIOCSTART */
236: }
237:
238: /* Put the terminal in CBREAK mode so that we can detect key presses. */
239: void prep_terminal ()
240: {
241: int tty = fileno (stdin);
242: #if defined (HAVE_BSD_SIGNALS)
243: int oldmask;
244: #endif /* HAVE_BSD_SIGNALS */
245:
246: if (terminal_prepped)
247: return;
248:
249: oldmask = sigblock (sigmask (SIGINT));
250:
251: /* We always get the latest tty values. Maybe stty changed them. */
252: ioctl (tty, TIOCGETP, &the_ttybuff);
253: original_tty_flags = the_ttybuff.sg_flags;
254:
255: readline_echoing_p = (original_tty_flags & ECHO);
256:
257: #if defined (TIOCLGET)
258: ioctl (tty, TIOCLGET, &local_mode_flags);
259: #endif
260:
261: #if !defined (ANYP)
262: # define ANYP (EVENP | ODDP)
263: #endif
264:
265: /* If this terminal doesn't care how the 8th bit is used,
266: then we can use it for the meta-key. We check by seeing
267: if BOTH odd and even parity are allowed. */
268: if (the_ttybuff.sg_flags & ANYP)
269: {
270: #if defined (PASS8)
271: the_ttybuff.sg_flags |= PASS8;
272: #endif
273:
274: /* Hack on local mode flags if we can. */
275: #if defined (TIOCLGET) && defined (LPASS8)
276: {
277: int flags;
278: flags = local_mode_flags | LPASS8;
279: ioctl (tty, TIOCLSET, &flags);
280: }
281: #endif /* TIOCLGET && LPASS8 */
282: }
283:
284: #if defined (TIOCGETC)
285: {
286: struct tchars temp;
287:
288: ioctl (tty, TIOCGETC, &original_tchars);
289: temp = original_tchars;
290:
291: #if defined (USE_XON_XOFF)
292: /* Get rid of C-s and C-q.
293: We remember the value of startc (C-q) so that if the terminal is in
294: xoff state, the user can xon it by pressing that character. */
295: xon_char = temp.t_startc;
296: temp.t_stopc = -1;
297: temp.t_startc = -1;
298:
299: /* If there is an XON character, bind it to restart the output. */
300: if (xon_char != -1)
301: bind_key (xon_char, restart_output);
302: #endif /* USE_XON_XOFF */
303:
304: /* If there is an EOF char, bind eof_char to it. */
305: if (temp.t_eofc != -1)
306: eof_char = temp.t_eofc;
307:
308: #if defined (NO_KILL_INTR)
309: /* Get rid of C-\ and C-c. */
310: temp.t_intrc = temp.t_quitc = -1;
311: #endif /* NO_KILL_INTR */
312:
313: ioctl (tty, TIOCSETC, &temp);
314: }
315: #endif /* TIOCGETC */
316:
317: #if defined (TIOCGLTC)
318: {
319: struct ltchars temp;
320:
321: ioctl (tty, TIOCGLTC, &original_ltchars);
322: temp = original_ltchars;
323:
324: /* Make the interrupt keys go away. Just enough to make people
325: happy. */
326: temp.t_dsuspc = -1; /* C-y */
327: temp.t_lnextc = -1; /* C-v */
328:
329: ioctl (tty, TIOCSLTC, &temp);
330: }
331: #endif /* TIOCGLTC */
332:
333: the_ttybuff.sg_flags &= ~(ECHO | CRMOD);
334: the_ttybuff.sg_flags |= CBREAK;
335: ioctl (tty, TIOCSETN, &the_ttybuff);
336:
337: terminal_prepped = 1;
338:
339: #if defined (HAVE_BSD_SIGNALS)
340: sigsetmask (oldmask);
341: #endif
342: }
343:
344: /* Restore the terminal to its original state. */
345: void deprep_terminal ()
346: {
347: int tty = fileno (stdin);
348: #if defined (HAVE_BSD_SIGNALS)
349: int oldmask;
350: #endif
351:
352: if (!terminal_prepped)
353: return;
354:
355: oldmask = sigblock (sigmask (SIGINT));
356:
357: the_ttybuff.sg_flags = original_tty_flags;
358: ioctl (tty, TIOCSETN, &the_ttybuff);
359: readline_echoing_p = 1;
360:
361: #if defined (TIOCLGET)
362: ioctl (tty, TIOCLSET, &local_mode_flags);
363: #endif
364:
365: #if defined (TIOCSLTC)
366: ioctl (tty, TIOCSLTC, &original_ltchars);
367: #endif
368:
369: #if defined (TIOCSETC)
370: ioctl (tty, TIOCSETC, &original_tchars);
371: #endif
372: terminal_prepped = 0;
373:
374: #if defined (HAVE_BSD_SIGNALS)
375: sigsetmask (oldmask);
376: #endif
377: }
378:
379: #else /* !defined (NEW_TTY_DRIVER) */
380:
381: #if !defined (VMIN)
382: #define VMIN VEOF
383: #endif
384:
385: #if !defined (VTIME)
386: #define VTIME VEOL
387: #endif
388:
389: void prep_terminal ()
390: {
391: int tty = fileno (stdin);
392: #if defined (TERMIOS_TTY_DRIVER)
393: struct termios tio;
394: #else
395: struct termio tio;
396: #endif /* !TERMIOS_TTY_DRIVER */
397:
398: #if defined (HAVE_POSIX_SIGNALS)
399: sigset_t set, oset;
400: #else
401: # if defined (HAVE_BSD_SIGNALS)
402: int oldmask;
403: # endif /* HAVE_BSD_SIGNALS */
404: #endif /* !HAVE_POSIX_SIGNALS */
405:
406: if (terminal_prepped)
407: return;
408:
409: /* Try to keep this function from being INTerrupted. We can do it
410: on POSIX and systems with BSD-like signal handling. */
411: #if defined (HAVE_POSIX_SIGNALS)
412: sigemptyset (&set);
413: sigemptyset (&oset);
414: sigaddset (&set, SIGINT);
415: sigprocmask (SIG_BLOCK, &set, &oset);
416: #else /* !HAVE_POSIX_SIGNALS */
417: # if defined (HAVE_BSD_SIGNALS)
418: oldmask = sigblock (sigmask (SIGINT));
419: # endif /* HAVE_BSD_SIGNALS */
420: #endif /* !HAVE_POSIX_SIGNALS */
421:
422: #if defined (TERMIOS_TTY_DRIVER)
423: tcgetattr (tty, &tio);
424: #else
425: ioctl (tty, TCGETA, &tio);
426: #endif /* !TERMIOS_TTY_DRIVER */
427:
428: otio = tio;
429:
430: readline_echoing_p = (tio.c_lflag & ECHO);
431:
432: tio.c_lflag &= ~(ICANON | ECHO);
433:
434: if (otio.c_cc[VEOF] != _POSIX_VDISABLE)
435: eof_char = otio.c_cc[VEOF];
436:
437: #if defined (USE_XON_XOFF)
438: #if defined (IXANY)
439: tio.c_iflag &= ~(IXON | IXOFF | IXANY);
440: #else
441: /* `strict' Posix systems do not define IXANY. */
442: tio.c_iflag &= ~(IXON | IXOFF);
443: #endif /* IXANY */
444: #endif /* USE_XON_XOFF */
445:
446: /* Only turn this off if we are using all 8 bits. */
447: if ((tio.c_cflag & CSIZE) == CS8)
448: tio.c_iflag &= ~(ISTRIP | INPCK);
449:
450: /* Make sure we differentiate between CR and NL on input. */
451: tio.c_iflag &= ~(ICRNL | INLCR);
452:
453: #if !defined (HANDLE_SIGNALS)
454: tio.c_lflag &= ~ISIG;
455: #else
456: tio.c_lflag |= ISIG;
457: #endif
458:
459: tio.c_cc[VMIN] = 1;
460: tio.c_cc[VTIME] = 0;
461:
462: /* Turn off characters that we need on Posix systems with job control,
463: just to be sure. This includes ^Y and ^V. This should not really
464: be necessary. */
465: #if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_JOB_CONTROL)
466:
467: #if defined (VLNEXT)
468: tio.c_cc[VLNEXT] = _POSIX_VDISABLE;
469: #endif
470:
471: #if defined (VDSUSP)
472: tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
473: #endif
474:
475: #endif /* POSIX && JOB_CONTROL */
476:
477: #if defined (TERMIOS_TTY_DRIVER)
478: tcsetattr (tty, TCSADRAIN, &tio);
479: tcflow (tty, TCOON); /* Simulate a ^Q. */
480: #else
481: ioctl (tty, TCSETAW, &tio);
482: ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */
483: #endif /* !TERMIOS_TTY_DRIVER */
484:
485: terminal_prepped = 1;
486:
487: #if defined (HAVE_POSIX_SIGNALS)
488: sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
489: #else
490: # if defined (HAVE_BSD_SIGNALS)
491: sigsetmask (oldmask);
492: # endif /* HAVE_BSD_SIGNALS */
493: #endif /* !HAVE_POSIX_SIGNALS */
494: }
495:
496: void deprep_terminal ()
497: {
498: int tty = fileno (stdin);
499:
500: /* Try to keep this function from being INTerrupted. We can do it
501: on POSIX and systems with BSD-like signal handling. */
502: #if defined (HAVE_POSIX_SIGNALS)
503: sigset_t set, oset;
504: #else /* !HAVE_POSIX_SIGNALS */
505: # if defined (HAVE_BSD_SIGNALS)
506: int oldmask;
507: # endif /* HAVE_BSD_SIGNALS */
508: #endif /* !HAVE_POSIX_SIGNALS */
509:
510: if (!terminal_prepped)
511: return;
512:
513: #if defined (HAVE_POSIX_SIGNALS)
514: sigemptyset (&set);
515: sigemptyset (&oset);
516: sigaddset (&set, SIGINT);
517: sigprocmask (SIG_BLOCK, &set, &oset);
518: #else /* !HAVE_POSIX_SIGNALS */
519: # if defined (HAVE_BSD_SIGNALS)
520: oldmask = sigblock (sigmask (SIGINT));
521: # endif /* HAVE_BSD_SIGNALS */
522: #endif /* !HAVE_POSIX_SIGNALS */
523:
524: #if defined (TERMIOS_TTY_DRIVER)
525: tcsetattr (tty, TCSADRAIN, &otio);
526: tcflow (tty, TCOON); /* Simulate a ^Q. */
527: #else /* TERMIOS_TTY_DRIVER */
528: ioctl (tty, TCSETAW, &otio);
529: ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */
530: #endif /* !TERMIOS_TTY_DRIVER */
531:
532: terminal_prepped = 0;
533:
534: #if defined (HAVE_POSIX_SIGNALS)
535: sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
536: #else /* !HAVE_POSIX_SIGNALS */
537: # if defined (HAVE_BSD_SIGNALS)
538: sigsetmask (oldmask);
539: # endif /* HAVE_BSD_SIGNALS */
540: #endif /* !HAVE_POSIX_SIGNALS */
541: }
542: #endif /* NEW_TTY_DRIVER */
543:
544: /* If a character is available to be read, then read it
545: and stuff it into IBUFFER. Otherwise, just return. */
546:
547: long pending = -1L;
548:
549: long key_avail (stream)
550: FILE *stream;
551: {
552: int tty = fileno (stream);
553: long chars_avail = pending;
554: int result;
555:
556: #if defined (FIONREAD)
557: result = ioctl (tty, FIONREAD, &chars_avail);
558: #endif
559:
560: if(chars_avail == -1L)
561: { unsigned char inchar;
562:
563: fcntl(tty, F_SETFL, O_NDELAY);
564: result = read(tty, &inchar, sizeof(char));
565: if(result == sizeof(char))
566: {
567: chars_avail = 1;
568: pending = (long)inchar;
569: }
570: else
571: chars_avail = 0;
572: fcntl(tty, F_SETFL, 0);
573: }
574:
575: return chars_avail;
576: }
577:
578: /* Get a key from the buffer of characters to be read.
579: Return the key in KEY.
580: Result is KEY if there was a key, or 0 if there wasn't. */
581:
582: /* When compiling and running in the `Posix' environment, Ultrix does
583: not restart system calls, so this needs to do it. */
584:
585: unsigned char getkey(stream)
586: FILE *stream;
587: {
588: int result;
589: unsigned char c;
590:
591: while (pending < 0)
592: {
593: result = read (fileno (stream), &c, sizeof (char));
594:
595: if (result == sizeof (char))
596: return /* (c == 0x7F ? 0x08 :*/ c /*)*/;
597:
598: /* If zero characters are returned, then the file that we are
599: reading from is empty! Return EOF in that case. */
600: if (result == 0)
601: return (0);
602:
603: /* If the error that we received was SIGINT, then try again,
604: this is simply an interrupted system call to read ().
605: Otherwise, some error ocurred, also signifying EOF. */
606: if (errno != EINTR)
607: return (EOF);
608: }
609:
610: result = (int) pending;
611: pending = -1L;
612:
613: return result;
614: }
615:
616: #ifdef TEST
617:
618: #include <time.h>
619:
620: int timewait=100000;
621:
622: int main()
623: {
624: unsigned char c;
625:
626: prep_terminal();
627:
628: do
629: {
630: int i=0;
631:
632: while(!key_avail(stdin))
633: {
634: printf("%04d",i);
635: fflush(stdout);
636: {
637: struct timeval timeout;
638: timeout.tv_sec=timewait/1000000;
639: timeout.tv_usec=timewait%1000000;
640: (void)select(0,0,0,0,&timeout);
641: }
642: i++;
643: printf("\b\b\b\b");
644: fflush(stdout);
645: }
646: c = getkey(stdin);
647: printf("%02x,",(int)c);
648: fflush(stdout);
649: } while(c != 0x1B);
650:
651: deprep_terminal();
652: puts("");
653: }
654: #endif
655:
656: /* signal handling taken from pfe by Dirk Zoller (Copylefted) - anton */
657: /* !! needs cleanup */
658: char *
659: sigmsg (int sig)
660: {
661: static char buf [25];
662: static char *msg [] =
663: {
664: "Hangup", /* These strings are cited from */
665: "Interrupt", /* Rochkind: Advanced UNIX programming */
666: "Quit",
667: "Illegal Instruction",
668: "Trace Trap",
669: "IOT instruction",
670: "EMT instruction",
671: "Floating point exception",
672: "Kill",
673: "Bus error",
674: "Segmentation Violation",
675: "Bad arg to system call",
676: "Broken pipe",
677: "Alarm clock",
678: "Terminate signal",
679: "User signal 1",
680: "User signal 2",
681: };
682:
683: if ((unsigned)sig <= 17)
684: return msg [sig - 1];
685: sprintf (buf, "signal %d received", sig);
686: return buf;
687: }
688:
689: static void
690: graceful_exit (int sig)
691: {
692: deprep_terminal();
693: if ((unsigned)sig <= 17)
694: fprintf (stderr, "\n\n%s.\n", sigmsg (sig));
695: else
696: fprintf (stderr, "\n\nSignal %d received, terminated.\n", sig);
697: exit (sig);
698: }
699:
700: void
701: install_signal_handlers (void)
702: {
703: /* !! These definitions seem to be system dependent
704: We could have them in the machine.h file,
705: but I would like something more automatic - anton */
706: #define SIGS_TO_IGNORE SIGCHLD
707: #define SIGS_TO_ABORT SIGINT, SIGILL, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2, \
708: SIGALRM, SIGEMT, SIGBUS, SIGSYS
709: #define SIGS_TO_QUIT SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, \
710: SIGTERM
711:
712: static short sigs_to_ignore [] = { SIGS_TO_IGNORE };
713: static short sigs_to_abort [] = { SIGS_TO_ABORT };
714: static short sigs_to_quit [] = { SIGS_TO_QUIT };
715: int i;
716:
717: #define DIM(X) (sizeof (X) / sizeof *(X))
718: for (i = 0; i < DIM (sigs_to_ignore); i++)
719: if (sigs_to_ignore [i])
720: signal (sigs_to_ignore [i], SIG_IGN);
721: for (i = 0; i < DIM (sigs_to_abort); i++)
722: signal (sigs_to_abort [i], graceful_exit); /* !! change to throw */
723: for (i = 0; i < DIM (sigs_to_quit); i++)
724: signal (sigs_to_quit [i], graceful_exit);
725: }
726: /* end signal handling */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>