Commit | Line | Data |
---|---|---|
b585a9fa EZ |
1 | /* |
2 | * | |
3 | * Another test harness for the readline callback interface. | |
4 | * | |
5 | * Author: Bob Rossi <bob@brasko.net> | |
6 | */ | |
7 | ||
8 | #if defined (HAVE_CONFIG_H) | |
9 | #include <config.h> | |
10 | #endif | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <sys/types.h> | |
14 | #include <errno.h> | |
15 | #include <curses.h> | |
16 | ||
17 | #include <stdlib.h> | |
18 | #include <unistd.h> | |
19 | ||
20 | #include <signal.h> | |
21 | ||
22 | #if 0 /* LINUX */ | |
23 | #include <pty.h> | |
24 | #else | |
25 | #include <util.h> | |
26 | #endif | |
27 | ||
28 | #ifdef READLINE_LIBRARY | |
29 | # include "readline.h" | |
30 | #else | |
31 | # include <readline/readline.h> | |
32 | #endif | |
33 | ||
34 | /** | |
35 | * Master/Slave PTY used to keep readline off of stdin/stdout. | |
36 | */ | |
37 | static int masterfd = -1; | |
38 | static int slavefd; | |
39 | ||
40 | void | |
41 | sigint (s) | |
42 | int s; | |
43 | { | |
44 | tty_reset (STDIN_FILENO); | |
45 | close (masterfd); | |
46 | close (slavefd); | |
47 | printf ("\n"); | |
48 | exit (0); | |
49 | } | |
50 | ||
51 | static int | |
52 | user_input() | |
53 | { | |
54 | int size; | |
55 | const int MAX = 1024; | |
56 | char *buf = (char *)malloc(MAX+1); | |
57 | ||
58 | size = read (STDIN_FILENO, buf, MAX); | |
59 | if (size == -1) | |
60 | return -1; | |
61 | ||
62 | size = write (masterfd, buf, size); | |
63 | if (size == -1) | |
64 | return -1; | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static int | |
70 | readline_input() | |
71 | { | |
72 | const int MAX = 1024; | |
73 | char *buf = (char *)malloc(MAX+1); | |
74 | int size; | |
75 | ||
76 | size = read (masterfd, buf, MAX); | |
77 | if (size == -1) | |
78 | { | |
79 | free( buf ); | |
80 | buf = NULL; | |
81 | return -1; | |
82 | } | |
83 | ||
84 | buf[size] = 0; | |
85 | ||
86 | /* Display output from readline */ | |
87 | if ( size > 0 ) | |
88 | fprintf(stderr, "%s", buf); | |
89 | ||
90 | free( buf ); | |
91 | buf = NULL; | |
92 | return 0; | |
93 | } | |
94 | ||
95 | static void | |
96 | rlctx_send_user_command(char *line) | |
97 | { | |
98 | /* This happens when rl_callback_read_char gets EOF */ | |
99 | if ( line == NULL ) | |
100 | return; | |
101 | ||
102 | if (strcmp (line, "exit") == 0) { | |
103 | tty_reset (STDIN_FILENO); | |
104 | close (masterfd); | |
105 | close (slavefd); | |
106 | printf ("\n"); | |
107 | exit (0); | |
108 | } | |
109 | ||
110 | /* Don't add the enter command */ | |
111 | if ( line && *line != '\0' ) | |
112 | add_history(line); | |
113 | } | |
114 | ||
115 | static void | |
116 | custom_deprep_term_function () | |
117 | { | |
118 | } | |
119 | ||
120 | static int | |
121 | init_readline (int inputfd, int outputfd) | |
122 | { | |
123 | FILE *inputFILE, *outputFILE; | |
124 | ||
125 | inputFILE = fdopen (inputfd, "r"); | |
126 | if (!inputFILE) | |
127 | return -1; | |
128 | ||
129 | outputFILE = fdopen (outputfd, "w"); | |
130 | if (!outputFILE) | |
131 | return -1; | |
132 | ||
133 | rl_instream = inputFILE; | |
134 | rl_outstream = outputFILE; | |
135 | ||
136 | /* Tell readline what the prompt is if it needs to put it back */ | |
137 | rl_callback_handler_install("(rltest): ", rlctx_send_user_command); | |
138 | ||
139 | /* Set the terminal type to dumb so the output of readline can be | |
140 | * understood by tgdb */ | |
141 | if ( rl_reset_terminal("dumb") == -1 ) | |
142 | return -1; | |
143 | ||
144 | /* For some reason, readline can not deprep the terminal. | |
145 | * However, it doesn't matter because no other application is working on | |
146 | * the terminal besides readline */ | |
147 | rl_deprep_term_function = custom_deprep_term_function; | |
148 | ||
149 | using_history(); | |
150 | read_history(".history"); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int | |
156 | main_loop(void) | |
157 | { | |
158 | fd_set rset; | |
159 | int max; | |
160 | ||
161 | max = (masterfd > STDIN_FILENO) ? masterfd : STDIN_FILENO; | |
162 | max = (max > slavefd) ? max : slavefd; | |
163 | ||
164 | for (;;) | |
165 | { | |
166 | /* Reset the fd_set, and watch for input from GDB or stdin */ | |
167 | FD_ZERO(&rset); | |
168 | ||
169 | FD_SET(STDIN_FILENO, &rset); | |
170 | FD_SET(slavefd, &rset); | |
171 | FD_SET(masterfd, &rset); | |
172 | ||
173 | /* Wait for input */ | |
174 | if (select(max + 1, &rset, NULL, NULL, NULL) == -1) | |
175 | { | |
176 | if (errno == EINTR) | |
177 | continue; | |
178 | else | |
179 | return -1; | |
180 | } | |
181 | ||
182 | /* Input received through the pty: Handle it | |
183 | * Wrote to masterfd, slave fd has that input, alert readline to read it. | |
184 | */ | |
185 | if (FD_ISSET(slavefd, &rset)) | |
186 | rl_callback_read_char(); | |
187 | ||
188 | /* Input received through the pty. | |
189 | * Readline read from slavefd, and it wrote to the masterfd. | |
190 | */ | |
191 | if (FD_ISSET(masterfd, &rset)) | |
192 | if ( readline_input() == -1 ) | |
193 | return -1; | |
194 | ||
195 | /* Input received: Handle it, write to masterfd (input to readline) */ | |
196 | if (FD_ISSET(STDIN_FILENO, &rset)) | |
197 | if ( user_input() == -1 ) | |
198 | return -1; | |
199 | } | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | /* The terminal attributes before calling tty_cbreak */ | |
205 | static struct termios save_termios; | |
206 | static struct winsize size; | |
207 | static enum { RESET, TCBREAK } ttystate = RESET; | |
208 | ||
209 | /* tty_cbreak: Sets terminal to cbreak mode. Also known as noncanonical mode. | |
210 | * 1. Signal handling is still turned on, so the user can still type those. | |
211 | * 2. echo is off | |
212 | * 3. Read in one char at a time. | |
213 | * | |
214 | * fd - The file descriptor of the terminal | |
215 | * | |
216 | * Returns: 0 on sucess, -1 on error | |
217 | */ | |
218 | int tty_cbreak(int fd){ | |
219 | struct termios buf; | |
220 | int ttysavefd = -1; | |
221 | ||
222 | if(tcgetattr(fd, &save_termios) < 0) | |
223 | return -1; | |
224 | ||
225 | buf = save_termios; | |
226 | buf.c_lflag &= ~(ECHO | ICANON); | |
227 | buf.c_iflag &= ~(ICRNL | INLCR); | |
228 | buf.c_cc[VMIN] = 1; | |
229 | buf.c_cc[VTIME] = 0; | |
230 | ||
231 | #if defined (VLNEXT) && defined (_POSIX_VDISABLE) | |
232 | buf.c_cc[VLNEXT] = _POSIX_VDISABLE; | |
233 | #endif | |
234 | ||
235 | #if defined (VDSUSP) && defined (_POSIX_VDISABLE) | |
236 | buf.c_cc[VDSUSP] = _POSIX_VDISABLE; | |
237 | #endif | |
238 | ||
239 | /* enable flow control; only stty start char can restart output */ | |
240 | #if 0 | |
241 | buf.c_iflag |= (IXON|IXOFF); | |
242 | #ifdef IXANY | |
243 | buf.c_iflag &= ~IXANY; | |
244 | #endif | |
245 | #endif | |
246 | ||
247 | /* disable flow control; let ^S and ^Q through to pty */ | |
248 | buf.c_iflag &= ~(IXON|IXOFF); | |
249 | #ifdef IXANY | |
250 | buf.c_iflag &= ~IXANY; | |
251 | #endif | |
252 | ||
253 | if(tcsetattr(fd, TCSAFLUSH, &buf) < 0) | |
254 | return -1; | |
255 | ||
256 | ttystate = TCBREAK; | |
257 | ttysavefd = fd; | |
258 | ||
259 | /* set size */ | |
260 | if(ioctl(fd, TIOCGWINSZ, (char *)&size) < 0) | |
261 | return -1; | |
262 | ||
263 | #ifdef DEBUG | |
264 | err_msg("%d rows and %d cols\n", size.ws_row, size.ws_col); | |
265 | #endif | |
266 | ||
267 | return (0); | |
268 | } | |
269 | ||
270 | int | |
271 | tty_off_xon_xoff (int fd) | |
272 | { | |
273 | struct termios buf; | |
274 | int ttysavefd = -1; | |
275 | ||
276 | if(tcgetattr(fd, &buf) < 0) | |
277 | return -1; | |
278 | ||
279 | buf.c_iflag &= ~(IXON|IXOFF); | |
280 | ||
281 | if(tcsetattr(fd, TCSAFLUSH, &buf) < 0) | |
282 | return -1; | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
287 | /* tty_reset: Sets the terminal attributes back to their previous state. | |
288 | * PRE: tty_cbreak must have already been called. | |
289 | * | |
290 | * fd - The file descrioptor of the terminal to reset. | |
291 | * | |
292 | * Returns: 0 on success, -1 on error | |
293 | */ | |
294 | int tty_reset(int fd) | |
295 | { | |
296 | if(ttystate != TCBREAK) | |
297 | return (0); | |
298 | ||
299 | if(tcsetattr(fd, TCSAFLUSH, &save_termios) < 0) | |
300 | return (-1); | |
301 | ||
302 | ttystate = RESET; | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | int | |
308 | main() | |
309 | { | |
310 | int val; | |
311 | val = openpty (&masterfd, &slavefd, NULL, NULL, NULL); | |
312 | if (val == -1) | |
313 | return -1; | |
314 | ||
315 | val = tty_off_xon_xoff (masterfd); | |
316 | if (val == -1) | |
317 | return -1; | |
318 | ||
319 | val = init_readline (slavefd, slavefd); | |
320 | if (val == -1) | |
321 | return -1; | |
322 | ||
323 | val = tty_cbreak (STDIN_FILENO); | |
324 | if (val == -1) | |
325 | return -1; | |
326 | ||
327 | signal (SIGINT, sigint); | |
328 | ||
329 | val = main_loop (); | |
330 | ||
331 | tty_reset (STDIN_FILENO); | |
332 | ||
333 | if (val == -1) | |
334 | return -1; | |
335 | ||
336 | return 0; | |
337 | } |