158310Sache/*
258310SacheFrom: Jeff Solomon <jsolomon@stanford.edu>
358310SacheDate: Fri,  9 Apr 1999 10:13:27 -0700 (PDT)
458310SacheTo: chet@po.cwru.edu
558310SacheSubject: new readline example
658310SacheMessage-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU>
758310Sache
858310SacheChet,
958310Sache
1058310SacheI've been using readline 4.0. Specifically, I've been using the perl
1158310Sacheversion Term::ReadLine::Gnu. It works great.
1258310Sache
1358310SacheAnyway, I've been playing around the alternate interface and I wanted
1458310Sacheto contribute a little C program, callback.c, to you that you could
1558310Sacheuse as an example of the alternate interface in the /examples
1658310Sachedirectory of the readline distribution.
1758310Sache
1858310SacheMy example shows how, using the alternate interface, you can
1958310Sacheinteractively change the prompt (which is very nice imo). Also, I
2058310Sachepoint out that you must roll your own terminal setting when using the
2158310Sachealternate interface because readline depreps (using your parlance) the
2258310Sacheterminal while in the user callback. I try to demostrate what I mean
2358310Sachewith an example. I've included the program below.
2458310Sache
2558310SacheTo compile, I just put the program in the examples directory and made
2658310Sachethe appropriate changes to the EXECUTABLES and OBJECTS line and added
2758310Sachean additional target 'callback'.
2858310Sache
2958310SacheI compiled on my Sun Solaris2.6 box using Sun's cc.
3058310Sache
3158310SacheLet me know what you think.
3258310Sache
3358310SacheJeff
3458310Sache*/
35165670Sache/*
36165670SacheCopyright (C) 1999 Jeff Solomon
37165670Sache*/
3858310Sache
3958310Sache#if defined (HAVE_CONFIG_H)
4058310Sache#include <config.h>
4158310Sache#endif
4258310Sache
4358310Sache#include <stdio.h>
4458310Sache#include <sys/types.h>
4558310Sache
4658310Sache#ifdef HAVE_UNISTD_H
4758310Sache#include <unistd.h>
4858310Sache#endif
4958310Sache
5058310Sache#include <termios.h>	/* xxx - should make this more general */
5158310Sache
5258310Sache#ifdef READLINE_LIBRARY
5358310Sache#  include "readline.h"
5458310Sache#else
5558310Sache#  include <readline/readline.h>
5658310Sache#endif
5758310Sache
5858310Sache/* This little examples demonstrates the alternate interface to using readline.
5958310Sache * In the alternate interface, the user maintains control over program flow and
6058310Sache * only calls readline when STDIN is readable. Using the alternate interface,
6158310Sache * you can do anything else while still using readline (like talking to a
6258310Sache * network or another program) without blocking.
6358310Sache *
6458310Sache * Specifically, this program highlights two importants features of the
6558310Sache * alternate interface. The first is the ability to interactively change the
6658310Sache * prompt, which can't be done using the regular interface since rl_prompt is
6758310Sache * read-only.
6858310Sache *
6958310Sache * The second feature really highlights a subtle point when using the alternate
7058310Sache * interface. That is, readline will not alter the terminal when inside your
7158310Sache * callback handler. So let's so, your callback executes a user command that
7258310Sache * takes a non-trivial amount of time to complete (seconds). While your
7358310Sache * executing the command, the user continues to type keystrokes and expects them
7458310Sache * to be re-echoed on the new prompt when it returns. Unfortunately, the default
7558310Sache * terminal configuration doesn't do this. After the prompt returns, the user
7658310Sache * must hit one additional keystroke and then will see all of his previous
7758310Sache * keystrokes. To illustrate this, compile and run this program. Type "sleep" at
7858310Sache * the prompt and then type "bar" before the prompt returns (you have 3
7958310Sache * seconds). Notice how "bar" is re-echoed on the prompt after the prompt
8058310Sache * returns? This is what you expect to happen. Now comment out the 4 lines below
8158310Sache * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do
8258310Sache * the same thing. When the prompt returns, you should not see "bar". Now type
8358310Sache * "f", see how "barf" magically appears? This behavior is un-expected and not
8458310Sache * desired.
8558310Sache */
8658310Sache
8758310Sachevoid process_line(char *line);
8858310Sacheint  change_prompt(void);
8958310Sachechar *get_prompt(void);
9058310Sache
9158310Sacheint prompt = 1;
9258310Sachechar prompt_buf[40], line_buf[256];
9358310Sachetcflag_t old_lflag;
9458310Sachecc_t     old_vtime;
9558310Sachestruct termios term;
9658310Sache
9758310Sacheint
9858310Sachemain()
9958310Sache{
10058310Sache    fd_set fds;
10158310Sache
10258310Sache    /* Adjust the terminal slightly before the handler is installed. Disable
10358310Sache     * canonical mode processing and set the input character time flag to be
10458310Sache     * non-blocking.
10558310Sache     */
10658310Sache    if( tcgetattr(STDIN_FILENO, &term) < 0 ) {
10758310Sache        perror("tcgetattr");
10858310Sache        exit(1);
10958310Sache    }
11058310Sache    old_lflag = term.c_lflag;
11158310Sache    old_vtime = term.c_cc[VTIME];
11258310Sache    term.c_lflag &= ~ICANON;
11358310Sache    term.c_cc[VTIME] = 1;
11458310Sache    /* COMMENT LINE BELOW - see above */
11558310Sache    if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) {
11658310Sache        perror("tcsetattr");
11758310Sache        exit(1);
11858310Sache    }
11958310Sache
12058310Sache    rl_add_defun("change-prompt", change_prompt, CTRL('t'));
12158310Sache    rl_callback_handler_install(get_prompt(), process_line);
12258310Sache
12358310Sache    while(1) {
12458310Sache      FD_ZERO(&fds);
12558310Sache      FD_SET(fileno(stdin), &fds);
12658310Sache
12758310Sache      if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
12858310Sache        perror("select");
12958310Sache        exit(1);
13058310Sache      }
13158310Sache
13258310Sache      if( FD_ISSET(fileno(stdin), &fds) ) {
13358310Sache        rl_callback_read_char();
13458310Sache      }
13558310Sache    }
13658310Sache}
13758310Sache
13858310Sachevoid
13958310Sacheprocess_line(char *line)
14058310Sache{
14158310Sache  if( line == NULL ) {
14258310Sache    fprintf(stderr, "\n", line);
14358310Sache
14458310Sache    /* reset the old terminal setting before exiting */
14558310Sache    term.c_lflag     = old_lflag;
14658310Sache    term.c_cc[VTIME] = old_vtime;
14758310Sache    if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) {
14858310Sache        perror("tcsetattr");
14958310Sache        exit(1);
15058310Sache    }
15158310Sache    exit(0);
15258310Sache  }
15358310Sache
15458310Sache  if( strcmp(line, "sleep") == 0 ) {
15558310Sache    sleep(3);
15658310Sache  } else {
15758310Sache    fprintf(stderr, "|%s|\n", line);
15858310Sache  }
15975406Sache
16075406Sache  free (line);
16158310Sache}
16258310Sache
16358310Sacheint
16458310Sachechange_prompt(void)
16558310Sache{
16658310Sache  /* toggle the prompt variable */
16758310Sache  prompt = !prompt;
16858310Sache
16958310Sache  /* save away the current contents of the line */
17058310Sache  strcpy(line_buf, rl_line_buffer);
17158310Sache
17258310Sache  /* install a new handler which will change the prompt and erase the current line */
17358310Sache  rl_callback_handler_install(get_prompt(), process_line);
17458310Sache
17558310Sache  /* insert the old text on the new line */
17658310Sache  rl_insert_text(line_buf);
17758310Sache
17858310Sache  /* redraw the current line - this is an undocumented function. It invokes the
17958310Sache   * redraw-current-line command.
18058310Sache   */
18158310Sache  rl_refresh_line(0, 0);
18258310Sache}
18358310Sache
18458310Sachechar *
18558310Sacheget_prompt(void)
18658310Sache{
18758310Sache  /* The prompts can even be different lengths! */
18858310Sache  sprintf(prompt_buf, "%s",
18958310Sache    prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> ");
19058310Sache  return prompt_buf;
19158310Sache}
192