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 */ 37static int masterfd = -1; 38static int slavefd; 39 40void 41sigint (s) 42 int s; 43{ 44 tty_reset (STDIN_FILENO); 45 close (masterfd); 46 close (slavefd); 47 printf ("\n"); 48 exit (0); 49} 50 51static int 52user_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 69static int 70readline_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 95static void 96rlctx_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 115static void 116custom_deprep_term_function () 117{ 118} 119 120static int 121init_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 155static int 156main_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 */ 205static struct termios save_termios; 206static struct winsize size; 207static 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 */ 218int 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 270int 271tty_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 */ 294int 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 307int 308main() 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} 338