summaryrefslogtreecommitdiffstats
path: root/examples/rlptytest.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/rlptytest.c')
-rw-r--r--examples/rlptytest.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/examples/rlptytest.c b/examples/rlptytest.c
new file mode 100644
index 0000000..9c42f8a
--- /dev/null
+++ b/examples/rlptytest.c
@@ -0,0 +1,345 @@
+/*
+ *
+ * Another test harness for the readline callback interface.
+ *
+ * Author: Bob Rossi <bob@brasko.net>
+ */
+
+#if defined (HAVE_CONFIG_H)
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <curses.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <signal.h>
+
+#if 1 /* LINUX */
+#include <pty.h>
+#else
+#include <util.h>
+#endif
+
+#ifdef READLINE_LIBRARY
+# include "readline.h"
+#else
+# include <readline/readline.h>
+#endif
+
+/**
+ * Master/Slave PTY used to keep readline off of stdin/stdout.
+ */
+static int masterfd = -1;
+static int slavefd;
+
+void
+sigint (s)
+ int s;
+{
+ tty_reset (STDIN_FILENO);
+ close (masterfd);
+ close (slavefd);
+ printf ("\n");
+ exit (0);
+}
+
+void
+sigwinch (s)
+ int s;
+{
+ rl_resize_terminal ();
+}
+
+static int
+user_input()
+{
+ int size;
+ const int MAX = 1024;
+ char *buf = (char *)malloc(MAX+1);
+
+ size = read (STDIN_FILENO, buf, MAX);
+ if (size == -1)
+ return -1;
+
+ size = write (masterfd, buf, size);
+ if (size == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+readline_input()
+{
+ const int MAX = 1024;
+ char *buf = (char *)malloc(MAX+1);
+ int size;
+
+ size = read (masterfd, buf, MAX);
+ if (size == -1)
+ {
+ free( buf );
+ buf = NULL;
+ return -1;
+ }
+
+ buf[size] = 0;
+
+ /* Display output from readline */
+ if ( size > 0 )
+ fprintf(stderr, "%s", buf);
+
+ free( buf );
+ buf = NULL;
+ return 0;
+}
+
+static void
+rlctx_send_user_command(char *line)
+{
+ /* This happens when rl_callback_read_char gets EOF */
+ if ( line == NULL )
+ return;
+
+ if (strcmp (line, "exit") == 0) {
+ tty_reset (STDIN_FILENO);
+ close (masterfd);
+ close (slavefd);
+ printf ("\n");
+ exit (0);
+ }
+
+ /* Don't add the enter command */
+ if ( line && *line != '\0' )
+ add_history(line);
+}
+
+static void
+custom_deprep_term_function ()
+{
+}
+
+static int
+init_readline (int inputfd, int outputfd)
+{
+ FILE *inputFILE, *outputFILE;
+
+ inputFILE = fdopen (inputfd, "r");
+ if (!inputFILE)
+ return -1;
+
+ outputFILE = fdopen (outputfd, "w");
+ if (!outputFILE)
+ return -1;
+
+ rl_instream = inputFILE;
+ rl_outstream = outputFILE;
+
+ /* Tell readline what the prompt is if it needs to put it back */
+ rl_callback_handler_install("(rltest): ", rlctx_send_user_command);
+
+ /* Set the terminal type to dumb so the output of readline can be
+ * understood by tgdb */
+ if ( rl_reset_terminal("dumb") == -1 )
+ return -1;
+
+ /* For some reason, readline can not deprep the terminal.
+ * However, it doesn't matter because no other application is working on
+ * the terminal besides readline */
+ rl_deprep_term_function = custom_deprep_term_function;
+
+ using_history();
+ read_history(".history");
+
+ return 0;
+}
+
+static int
+main_loop(void)
+{
+ fd_set rset;
+ int max;
+
+ max = (masterfd > STDIN_FILENO) ? masterfd : STDIN_FILENO;
+ max = (max > slavefd) ? max : slavefd;
+
+ for (;;)
+ {
+ /* Reset the fd_set, and watch for input from GDB or stdin */
+ FD_ZERO(&rset);
+
+ FD_SET(STDIN_FILENO, &rset);
+ FD_SET(slavefd, &rset);
+ FD_SET(masterfd, &rset);
+
+ /* Wait for input */
+ if (select(max + 1, &rset, NULL, NULL, NULL) == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+
+ /* Input received through the pty: Handle it
+ * Wrote to masterfd, slave fd has that input, alert readline to read it.
+ */
+ if (FD_ISSET(slavefd, &rset))
+ rl_callback_read_char();
+
+ /* Input received through the pty.
+ * Readline read from slavefd, and it wrote to the masterfd.
+ */
+ if (FD_ISSET(masterfd, &rset))
+ if ( readline_input() == -1 )
+ return -1;
+
+ /* Input received: Handle it, write to masterfd (input to readline) */
+ if (FD_ISSET(STDIN_FILENO, &rset))
+ if ( user_input() == -1 )
+ return -1;
+ }
+
+ return 0;
+}
+
+/* The terminal attributes before calling tty_cbreak */
+static struct termios save_termios;
+static struct winsize size;
+static enum { RESET, TCBREAK } ttystate = RESET;
+
+/* tty_cbreak: Sets terminal to cbreak mode. Also known as noncanonical mode.
+ * 1. Signal handling is still turned on, so the user can still type those.
+ * 2. echo is off
+ * 3. Read in one char at a time.
+ *
+ * fd - The file descriptor of the terminal
+ *
+ * Returns: 0 on sucess, -1 on error
+ */
+int tty_cbreak(int fd){
+ struct termios buf;
+ int ttysavefd = -1;
+
+ if(tcgetattr(fd, &save_termios) < 0)
+ return -1;
+
+ buf = save_termios;
+ buf.c_lflag &= ~(ECHO | ICANON);
+ buf.c_iflag &= ~(ICRNL | INLCR);
+ buf.c_cc[VMIN] = 1;
+ buf.c_cc[VTIME] = 0;
+
+#if defined (VLNEXT) && defined (_POSIX_VDISABLE)
+ buf.c_cc[VLNEXT] = _POSIX_VDISABLE;
+#endif
+
+#if defined (VDSUSP) && defined (_POSIX_VDISABLE)
+ buf.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+
+ /* enable flow control; only stty start char can restart output */
+#if 0
+ buf.c_iflag |= (IXON|IXOFF);
+#ifdef IXANY
+ buf.c_iflag &= ~IXANY;
+#endif
+#endif
+
+ /* disable flow control; let ^S and ^Q through to pty */
+ buf.c_iflag &= ~(IXON|IXOFF);
+#ifdef IXANY
+ buf.c_iflag &= ~IXANY;
+#endif
+
+ if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)
+ return -1;
+
+ ttystate = TCBREAK;
+ ttysavefd = fd;
+
+ /* set size */
+ if(ioctl(fd, TIOCGWINSZ, (char *)&size) < 0)
+ return -1;
+
+#ifdef DEBUG
+ err_msg("%d rows and %d cols\n", size.ws_row, size.ws_col);
+#endif
+
+ return (0);
+}
+
+int
+tty_off_xon_xoff (int fd)
+{
+ struct termios buf;
+ int ttysavefd = -1;
+
+ if(tcgetattr(fd, &buf) < 0)
+ return -1;
+
+ buf.c_iflag &= ~(IXON|IXOFF);
+
+ if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* tty_reset: Sets the terminal attributes back to their previous state.
+ * PRE: tty_cbreak must have already been called.
+ *
+ * fd - The file descrioptor of the terminal to reset.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+int tty_reset(int fd)
+{
+ if(ttystate != TCBREAK)
+ return (0);
+
+ if(tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
+ return (-1);
+
+ ttystate = RESET;
+
+ return 0;
+}
+
+int
+main()
+{
+ int val;
+ val = openpty (&masterfd, &slavefd, NULL, NULL, NULL);
+ if (val == -1)
+ return -1;
+
+ val = tty_off_xon_xoff (masterfd);
+ if (val == -1)
+ return -1;
+
+ signal (SIGWINCH, sigwinch);
+ signal (SIGINT, sigint);
+
+ val = init_readline (slavefd, slavefd);
+ if (val == -1)
+ return -1;
+
+ val = tty_cbreak (STDIN_FILENO);
+ if (val == -1)
+ return -1;
+
+ val = main_loop ();
+
+ tty_reset (STDIN_FILENO);
+
+ if (val == -1)
+ return -1;
+
+ return 0;
+}