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