1 /* term.c -- Routines for managing terminal I/O settings by Alan Cox.
2  * From LJ 17 */
3 
4 /* Thanks to Dave Cook for rescuing it */
5 
6 /* CHANGE LOG
7  * --------------------------------------------------------------------
8  * 28Apr03  dm  include ioctl.h and declare void ctcinit();
9  */
10 
11 
12 #include <termios.h>
13 #ifndef __APPLE__
14 #include <asm/ioctls.h>
15 #endif
16 #include <sys/ioctl.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 
22 void ctcinit(void);
23 
24 /* This will be used for new terminal settings. */
25 static struct termios current;
26 
27 /* This will hold the initial state so that we can restor it later. */
28 static struct termios initial;
29 
30 /* Restor the termianl settings to those saved when term_init was called. */
term_restore(void)31 void term_restore(void)
32 {
33     tcsetattr(0, TCSANOW, &initial);
34 }
35 
36 /* Clean up termianl; called on exit. */
term_exit(void)37 void term_exit(void)
38 {
39     term_restore();
40 }
41 
42 /* Will be called when contrl-Z is pressed;
43  * this correctly handles the terminal. */
term_ctrlz(int i)44 void term_ctrlz(int i)
45 {
46     signal(SIGTSTP, term_ctrlz);
47     term_restore();
48     kill(getpid(), SIGSTOP);
49 }
50 
51 /* Will be called when the application is continued
52  * after having been stopped. */
term_cont(int i)53 void term_cont(int i)
54 {
55     signal(SIGCONT, term_cont);
56     tcsetattr(0, TCSANOW, &current);
57 }
58 
59 /* callback for SIGQUIT is different type from atexit callback */
term_quit(int i)60 void term_quit(int i)
61 {
62     term_exit();
63 }
64 
65 /* Needs to be called to initialize the terminal. */
term_init(void)66 void term_init(void)
67 {
68     /* If stdin isn't a terminal this fails.
69        But then so does tcsetattr(), so it doesn't matter. */
70     tcgetattr(0, &initial);
71     /* Save a copy to work with later. */
72     current = initial;
73     /* We _must_ clean up when we exit. */
74     /* signal(SIGINT, term_exit); */
75     ctcinit(); /* XLisp wants to catch ctrl C */
76     signal(SIGQUIT, term_quit);
77     /* Control-Z must also be handled. */
78     signal(SIGTSTP, term_ctrlz);
79     signal(SIGCONT, term_cont);
80     atexit(term_exit);
81 }
82 
83 /* Set character-by-character input mode. */
term_character(void)84 void term_character(void)
85 {
86     /* One or more characters are sufficient to cause a read return. */
87     current.c_cc[VMIN] = 1;
88     /* No timeout; read waits forever until ready. */
89     current.c_cc[VTIME] = 0;
90     /* Line-by-line mode off */
91     current.c_lflag &= ~ICANON;
92 #ifndef READ_LINE
93     current.c_lflag &= ~ECHO;
94 #endif
95     tcsetattr(0, TCSANOW, &current);
96 }
97 
98 /* Return to line-by-line input mode. */
term_line(void)99 void term_line(void)
100 {
101     current.c_lflag |= ICANON;
102     tcsetattr(0, TCSANOW, &current);
103 }
104 
105 
106 #define ERROR(s)        return (perror(s), -1)
107 
108 /* term_testchar -- tell whether character is ready or not,
109  *
110  * if ready, return it, otherwise return -2
111  */
term_testchar(void)112 int term_testchar(void)
113 {
114     int n;
115     char c;
116 
117     if (ioctl(0, FIONREAD, &n) < 0)
118         ERROR("IOgetchar");
119     if (n == 0) return -2;
120     switch(read(0, &c, 1)) {
121       case 1:
122         return c;
123       case 0:
124         return EOF;
125       default:
126         ERROR("IOgetchar-read");
127     }
128 }
129 
130 
131 /* term_getchar -- get a character (block if necessary) */
132 /**/
term_getchar(void)133 int term_getchar(void)
134 {
135     char c;
136     int rslt = read(0, &c, 1);
137     return (rslt == 1 ? c : EOF);
138 }
139 
140