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