1157184Sache/*
2157184Sache *
3157184Sache * Another test harness for the readline callback interface.
4157184Sache *
5157184Sache * Author: Bob Rossi <bob@brasko.net>
6157184Sache */
7157184Sache
8157184Sache#if defined (HAVE_CONFIG_H)
9157184Sache#include <config.h>
10157184Sache#endif
11157184Sache
12157184Sache#include <stdio.h>
13157184Sache#include <sys/types.h>
14157184Sache#include <errno.h>
15157184Sache#include <curses.h>
16157184Sache
17157184Sache#include <stdlib.h>
18157184Sache#include <unistd.h>
19157184Sache
20157184Sache#include <signal.h>
21157184Sache
22157184Sache#if 0	/* LINUX */
23157184Sache#include <pty.h>
24157184Sache#else
25157184Sache#include <util.h>
26157184Sache#endif
27157184Sache
28157184Sache#ifdef READLINE_LIBRARY
29157184Sache#  include "readline.h"
30157184Sache#else
31157184Sache#  include <readline/readline.h>
32157184Sache#endif
33157184Sache
34157184Sache/**
35157184Sache * Master/Slave PTY used to keep readline off of stdin/stdout.
36157184Sache */
37157184Sachestatic int masterfd = -1;
38157184Sachestatic int slavefd;
39157184Sache
40157184Sachevoid
41157184Sachesigint (s)
42157184Sache     int s;
43157184Sache{
44157184Sache  tty_reset (STDIN_FILENO);
45157184Sache  close (masterfd);
46157184Sache  close (slavefd);
47157184Sache  printf ("\n");
48157184Sache  exit (0);
49157184Sache}
50157184Sache
51157184Sachestatic int
52157184Sacheuser_input()
53157184Sache{
54157184Sache  int size;
55157184Sache  const int MAX = 1024;
56157184Sache  char *buf = (char *)malloc(MAX+1);
57157184Sache
58157184Sache  size = read (STDIN_FILENO, buf, MAX);
59157184Sache  if (size == -1)
60157184Sache    return -1;
61157184Sache
62157184Sache  size = write (masterfd, buf, size);
63157184Sache  if (size == -1)
64157184Sache    return -1;
65157184Sache
66157184Sache  return 0;
67157184Sache}
68157184Sache
69157184Sachestatic int
70157184Sachereadline_input()
71157184Sache{
72157184Sache  const int MAX = 1024;
73157184Sache  char *buf = (char *)malloc(MAX+1);
74157184Sache  int size;
75157184Sache
76157184Sache  size = read (masterfd, buf, MAX);
77157184Sache  if (size == -1)
78157184Sache    {
79157184Sache      free( buf );
80157184Sache      buf = NULL;
81157184Sache      return -1;
82157184Sache    }
83157184Sache
84157184Sache  buf[size] = 0;
85157184Sache
86157184Sache  /* Display output from readline */
87157184Sache  if ( size > 0 )
88157184Sache    fprintf(stderr, "%s", buf);
89157184Sache
90157184Sache  free( buf );
91157184Sache  buf = NULL;
92157184Sache  return 0;
93157184Sache}
94157184Sache
95157184Sachestatic void
96157184Sacherlctx_send_user_command(char *line)
97157184Sache{
98157184Sache  /* This happens when rl_callback_read_char gets EOF */
99157184Sache  if ( line == NULL )
100157184Sache    return;
101157184Sache
102157184Sache  if (strcmp (line, "exit") == 0) {
103157184Sache  	tty_reset (STDIN_FILENO);
104157184Sache  	close (masterfd);
105157184Sache  	close (slavefd);
106157184Sache  	printf ("\n");
107157184Sache	exit (0);
108157184Sache  }
109157184Sache
110157184Sache  /* Don't add the enter command */
111157184Sache  if ( line && *line != '\0' )
112157184Sache    add_history(line);
113157184Sache}
114157184Sache
115157184Sachestatic void
116157184Sachecustom_deprep_term_function ()
117157184Sache{
118157184Sache}
119157184Sache
120157184Sachestatic int
121157184Sacheinit_readline (int inputfd, int outputfd)
122157184Sache{
123157184Sache  FILE *inputFILE, *outputFILE;
124157184Sache
125157184Sache  inputFILE = fdopen (inputfd, "r");
126157184Sache  if (!inputFILE)
127157184Sache    return -1;
128157184Sache
129157184Sache  outputFILE = fdopen (outputfd, "w");
130157184Sache  if (!outputFILE)
131157184Sache    return -1;
132157184Sache
133157184Sache  rl_instream = inputFILE;
134157184Sache  rl_outstream = outputFILE;
135157184Sache
136157184Sache  /* Tell readline what the prompt is if it needs to put it back */
137157184Sache  rl_callback_handler_install("(rltest):  ", rlctx_send_user_command);
138157184Sache
139157184Sache  /* Set the terminal type to dumb so the output of readline can be
140157184Sache   * understood by tgdb */
141157184Sache  if ( rl_reset_terminal("dumb") == -1 )
142157184Sache    return -1;
143157184Sache
144157184Sache  /* For some reason, readline can not deprep the terminal.
145157184Sache   * However, it doesn't matter because no other application is working on
146157184Sache   * the terminal besides readline */
147157184Sache  rl_deprep_term_function = custom_deprep_term_function;
148157184Sache
149157184Sache  using_history();
150157184Sache  read_history(".history");
151157184Sache
152157184Sache  return 0;
153157184Sache}
154157184Sache
155157184Sachestatic int
156157184Sachemain_loop(void)
157157184Sache{
158157184Sache  fd_set rset;
159157184Sache  int max;
160157184Sache
161157184Sache  max = (masterfd > STDIN_FILENO) ? masterfd : STDIN_FILENO;
162157184Sache  max = (max > slavefd) ? max : slavefd;
163157184Sache
164157184Sache  for (;;)
165157184Sache    {
166157184Sache      /* Reset the fd_set, and watch for input from GDB or stdin */
167157184Sache      FD_ZERO(&rset);
168157184Sache
169157184Sache      FD_SET(STDIN_FILENO, &rset);
170157184Sache      FD_SET(slavefd, &rset);
171157184Sache      FD_SET(masterfd, &rset);
172157184Sache
173157184Sache      /* Wait for input */
174157184Sache      if (select(max + 1, &rset, NULL, NULL, NULL) == -1)
175157184Sache        {
176157184Sache          if (errno == EINTR)
177157184Sache             continue;
178157184Sache          else
179157184Sache            return -1;
180157184Sache        }
181157184Sache
182157184Sache      /* Input received through the pty:  Handle it
183157184Sache       * Wrote to masterfd, slave fd has that input, alert readline to read it.
184157184Sache       */
185157184Sache      if (FD_ISSET(slavefd, &rset))
186157184Sache        rl_callback_read_char();
187157184Sache
188157184Sache      /* Input received through the pty.
189157184Sache       * Readline read from slavefd, and it wrote to the masterfd.
190157184Sache       */
191157184Sache      if (FD_ISSET(masterfd, &rset))
192157184Sache        if ( readline_input() == -1 )
193157184Sache          return -1;
194157184Sache
195157184Sache      /* Input received:  Handle it, write to masterfd (input to readline) */
196157184Sache      if (FD_ISSET(STDIN_FILENO, &rset))
197157184Sache        if ( user_input() == -1 )
198157184Sache          return -1;
199157184Sache  }
200157184Sache
201157184Sache  return 0;
202157184Sache}
203157184Sache
204157184Sache/* The terminal attributes before calling tty_cbreak */
205157184Sachestatic struct termios save_termios;
206157184Sachestatic struct winsize size;
207157184Sachestatic enum { RESET, TCBREAK } ttystate = RESET;
208157184Sache
209157184Sache/* tty_cbreak: Sets terminal to cbreak mode. Also known as noncanonical mode.
210157184Sache *    1. Signal handling is still turned on, so the user can still type those.
211157184Sache *    2. echo is off
212157184Sache *    3. Read in one char at a time.
213157184Sache *
214157184Sache * fd    - The file descriptor of the terminal
215157184Sache *
216157184Sache * Returns: 0 on sucess, -1 on error
217157184Sache */
218157184Sacheint tty_cbreak(int fd){
219157184Sache   struct termios buf;
220157184Sache    int ttysavefd = -1;
221157184Sache
222157184Sache   if(tcgetattr(fd, &save_termios) < 0)
223157184Sache      return -1;
224157184Sache
225157184Sache   buf = save_termios;
226157184Sache   buf.c_lflag &= ~(ECHO | ICANON);
227157184Sache   buf.c_iflag &= ~(ICRNL | INLCR);
228157184Sache   buf.c_cc[VMIN] = 1;
229157184Sache   buf.c_cc[VTIME] = 0;
230157184Sache
231157184Sache#if defined (VLNEXT) && defined (_POSIX_VDISABLE)
232157184Sache   buf.c_cc[VLNEXT] = _POSIX_VDISABLE;
233157184Sache#endif
234157184Sache
235157184Sache#if defined (VDSUSP) && defined (_POSIX_VDISABLE)
236157184Sache   buf.c_cc[VDSUSP] = _POSIX_VDISABLE;
237157184Sache#endif
238157184Sache
239157184Sache  /* enable flow control; only stty start char can restart output */
240157184Sache#if 0
241157184Sache  buf.c_iflag |= (IXON|IXOFF);
242157184Sache#ifdef IXANY
243157184Sache  buf.c_iflag &= ~IXANY;
244157184Sache#endif
245157184Sache#endif
246157184Sache
247157184Sache  /* disable flow control; let ^S and ^Q through to pty */
248157184Sache  buf.c_iflag &= ~(IXON|IXOFF);
249157184Sache#ifdef IXANY
250157184Sache  buf.c_iflag &= ~IXANY;
251157184Sache#endif
252157184Sache
253157184Sache  if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)
254157184Sache      return -1;
255157184Sache
256157184Sache   ttystate = TCBREAK;
257157184Sache   ttysavefd = fd;
258157184Sache
259157184Sache   /* set size */
260157184Sache   if(ioctl(fd, TIOCGWINSZ, (char *)&size) < 0)
261157184Sache      return -1;
262157184Sache
263157184Sache#ifdef DEBUG
264157184Sache   err_msg("%d rows and %d cols\n", size.ws_row, size.ws_col);
265157184Sache#endif
266157184Sache
267157184Sache   return (0);
268157184Sache}
269157184Sache
270157184Sacheint
271157184Sachetty_off_xon_xoff (int fd)
272157184Sache{
273157184Sache  struct termios buf;
274157184Sache  int ttysavefd = -1;
275157184Sache
276157184Sache  if(tcgetattr(fd, &buf) < 0)
277157184Sache    return -1;
278157184Sache
279157184Sache  buf.c_iflag &= ~(IXON|IXOFF);
280157184Sache
281157184Sache  if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)
282157184Sache    return -1;
283157184Sache
284157184Sache  return 0;
285157184Sache}
286157184Sache
287157184Sache/* tty_reset: Sets the terminal attributes back to their previous state.
288157184Sache * PRE: tty_cbreak must have already been called.
289157184Sache *
290157184Sache * fd    - The file descrioptor of the terminal to reset.
291157184Sache *
292157184Sache * Returns: 0 on success, -1 on error
293157184Sache */
294157184Sacheint tty_reset(int fd)
295157184Sache{
296157184Sache   if(ttystate != TCBREAK)
297157184Sache      return (0);
298157184Sache
299157184Sache   if(tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
300157184Sache      return (-1);
301157184Sache
302157184Sache   ttystate = RESET;
303157184Sache
304157184Sache   return 0;
305157184Sache}
306157184Sache
307157184Sacheint
308157184Sachemain()
309157184Sache{
310157184Sache  int val;
311157184Sache  val = openpty (&masterfd, &slavefd, NULL, NULL, NULL);
312157184Sache  if (val == -1)
313157184Sache    return -1;
314157184Sache
315157184Sache  val = tty_off_xon_xoff (masterfd);
316157184Sache  if (val == -1)
317157184Sache    return -1;
318157184Sache
319157184Sache  val = init_readline (slavefd, slavefd);
320157184Sache  if (val == -1)
321157184Sache    return -1;
322157184Sache
323157184Sache  val = tty_cbreak (STDIN_FILENO);
324157184Sache  if (val == -1)
325157184Sache    return -1;
326157184Sache
327157184Sache  signal (SIGINT, sigint);
328157184Sache
329157184Sache  val = main_loop ();
330157184Sache
331157184Sache  tty_reset (STDIN_FILENO);
332157184Sache
333157184Sache  if (val == -1)
334157184Sache    return -1;
335157184Sache
336157184Sache  return 0;
337157184Sache}
338