1/* 2From: Jeff Solomon <jsolomon@stanford.edu> 3Date: Fri, 9 Apr 1999 10:13:27 -0700 (PDT) 4To: chet@po.cwru.edu 5Subject: new readline example 6Message-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU> 7 8Chet, 9 10I've been using readline 4.0. Specifically, I've been using the perl 11version Term::ReadLine::Gnu. It works great. 12 13Anyway, I've been playing around the alternate interface and I wanted 14to contribute a little C program, callback.c, to you that you could 15use as an example of the alternate interface in the /examples 16directory of the readline distribution. 17 18My example shows how, using the alternate interface, you can 19interactively change the prompt (which is very nice imo). Also, I 20point out that you must roll your own terminal setting when using the 21alternate interface because readline depreps (using your parlance) the 22terminal while in the user callback. I try to demostrate what I mean 23with an example. I've included the program below. 24 25To compile, I just put the program in the examples directory and made 26the appropriate changes to the EXECUTABLES and OBJECTS line and added 27an additional target 'callback'. 28 29I compiled on my Sun Solaris2.6 box using Sun's cc. 30 31Let me know what you think. 32 33Jeff 34*/ 35 36#if defined (HAVE_CONFIG_H) 37#include <config.h> 38#endif 39 40#include <stdio.h> 41#include <sys/types.h> 42 43#ifdef HAVE_UNISTD_H 44#include <unistd.h> 45#endif 46 47#include <termios.h> /* xxx - should make this more general */ 48 49#ifdef READLINE_LIBRARY 50# include "readline.h" 51#else 52# include <readline/readline.h> 53#endif 54 55/* This little examples demonstrates the alternate interface to using readline. 56 * In the alternate interface, the user maintains control over program flow and 57 * only calls readline when STDIN is readable. Using the alternate interface, 58 * you can do anything else while still using readline (like talking to a 59 * network or another program) without blocking. 60 * 61 * Specifically, this program highlights two importants features of the 62 * alternate interface. The first is the ability to interactively change the 63 * prompt, which can't be done using the regular interface since rl_prompt is 64 * read-only. 65 * 66 * The second feature really highlights a subtle point when using the alternate 67 * interface. That is, readline will not alter the terminal when inside your 68 * callback handler. So let's so, your callback executes a user command that 69 * takes a non-trivial amount of time to complete (seconds). While your 70 * executing the command, the user continues to type keystrokes and expects them 71 * to be re-echoed on the new prompt when it returns. Unfortunately, the default 72 * terminal configuration doesn't do this. After the prompt returns, the user 73 * must hit one additional keystroke and then will see all of his previous 74 * keystrokes. To illustrate this, compile and run this program. Type "sleep" at 75 * the prompt and then type "bar" before the prompt returns (you have 3 76 * seconds). Notice how "bar" is re-echoed on the prompt after the prompt 77 * returns? This is what you expect to happen. Now comment out the 4 lines below 78 * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do 79 * the same thing. When the prompt returns, you should not see "bar". Now type 80 * "f", see how "barf" magically appears? This behavior is un-expected and not 81 * desired. 82 */ 83 84void process_line(char *line); 85int change_prompt(void); 86char *get_prompt(void); 87 88int prompt = 1; 89char prompt_buf[40], line_buf[256]; 90tcflag_t old_lflag; 91cc_t old_vtime; 92struct termios term; 93 94int 95main() 96{ 97 fd_set fds; 98 99 /* Adjust the terminal slightly before the handler is installed. Disable 100 * canonical mode processing and set the input character time flag to be 101 * non-blocking. 102 */ 103 if( tcgetattr(STDIN_FILENO, &term) < 0 ) { 104 perror("tcgetattr"); 105 exit(1); 106 } 107 old_lflag = term.c_lflag; 108 old_vtime = term.c_cc[VTIME]; 109 term.c_lflag &= ~ICANON; 110 term.c_cc[VTIME] = 1; 111 /* COMMENT LINE BELOW - see above */ 112 if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 113 perror("tcsetattr"); 114 exit(1); 115 } 116 117 rl_add_defun("change-prompt", change_prompt, CTRL('t')); 118 rl_callback_handler_install(get_prompt(), process_line); 119 120 while(1) { 121 FD_ZERO(&fds); 122 FD_SET(fileno(stdin), &fds); 123 124 if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) { 125 perror("select"); 126 exit(1); 127 } 128 129 if( FD_ISSET(fileno(stdin), &fds) ) { 130 rl_callback_read_char(); 131 } 132 } 133} 134 135void 136process_line(char *line) 137{ 138 if( line == NULL ) { 139 fprintf(stderr, "\n", line); 140 141 /* reset the old terminal setting before exiting */ 142 term.c_lflag = old_lflag; 143 term.c_cc[VTIME] = old_vtime; 144 if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 145 perror("tcsetattr"); 146 exit(1); 147 } 148 exit(0); 149 } 150 151 if( strcmp(line, "sleep") == 0 ) { 152 sleep(3); 153 } else { 154 fprintf(stderr, "|%s|\n", line); 155 } 156 157 free (line); 158} 159 160int 161change_prompt(void) 162{ 163 /* toggle the prompt variable */ 164 prompt = !prompt; 165 166 /* save away the current contents of the line */ 167 strcpy(line_buf, rl_line_buffer); 168 169 /* install a new handler which will change the prompt and erase the current line */ 170 rl_callback_handler_install(get_prompt(), process_line); 171 172 /* insert the old text on the new line */ 173 rl_insert_text(line_buf); 174 175 /* redraw the current line - this is an undocumented function. It invokes the 176 * redraw-current-line command. 177 */ 178 rl_refresh_line(0, 0); 179} 180 181char * 182get_prompt(void) 183{ 184 /* The prompts can even be different lengths! */ 185 sprintf(prompt_buf, "%s", 186 prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> "); 187 return prompt_buf; 188} 189