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/* 36Copyright (C) 1999 Jeff Solomon 37*/ 38 39#if defined (HAVE_CONFIG_H) 40#include <config.h> 41#endif 42 43#include <stdio.h> 44#include <sys/types.h> 45 46#ifdef HAVE_UNISTD_H 47#include <unistd.h> 48#endif 49 50#include <termios.h> /* xxx - should make this more general */ 51 52#ifdef READLINE_LIBRARY 53# include "readline.h" 54#else 55# include <readline/readline.h> 56#endif 57 58/* This little examples demonstrates the alternate interface to using readline. 59 * In the alternate interface, the user maintains control over program flow and 60 * only calls readline when STDIN is readable. Using the alternate interface, 61 * you can do anything else while still using readline (like talking to a 62 * network or another program) without blocking. 63 * 64 * Specifically, this program highlights two importants features of the 65 * alternate interface. The first is the ability to interactively change the 66 * prompt, which can't be done using the regular interface since rl_prompt is 67 * read-only. 68 * 69 * The second feature really highlights a subtle point when using the alternate 70 * interface. That is, readline will not alter the terminal when inside your 71 * callback handler. So let's so, your callback executes a user command that 72 * takes a non-trivial amount of time to complete (seconds). While your 73 * executing the command, the user continues to type keystrokes and expects them 74 * to be re-echoed on the new prompt when it returns. Unfortunately, the default 75 * terminal configuration doesn't do this. After the prompt returns, the user 76 * must hit one additional keystroke and then will see all of his previous 77 * keystrokes. To illustrate this, compile and run this program. Type "sleep" at 78 * the prompt and then type "bar" before the prompt returns (you have 3 79 * seconds). Notice how "bar" is re-echoed on the prompt after the prompt 80 * returns? This is what you expect to happen. Now comment out the 4 lines below 81 * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do 82 * the same thing. When the prompt returns, you should not see "bar". Now type 83 * "f", see how "barf" magically appears? This behavior is un-expected and not 84 * desired. 85 */ 86 87void process_line(char *line); 88int change_prompt(void); 89char *get_prompt(void); 90 91int prompt = 1; 92char prompt_buf[40], line_buf[256]; 93tcflag_t old_lflag; 94cc_t old_vtime; 95struct termios term; 96 97int 98main() 99{ 100 fd_set fds; 101 102 /* Adjust the terminal slightly before the handler is installed. Disable 103 * canonical mode processing and set the input character time flag to be 104 * non-blocking. 105 */ 106 if( tcgetattr(STDIN_FILENO, &term) < 0 ) { 107 perror("tcgetattr"); 108 exit(1); 109 } 110 old_lflag = term.c_lflag; 111 old_vtime = term.c_cc[VTIME]; 112 term.c_lflag &= ~ICANON; 113 term.c_cc[VTIME] = 1; 114 /* COMMENT LINE BELOW - see above */ 115 if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 116 perror("tcsetattr"); 117 exit(1); 118 } 119 120 rl_add_defun("change-prompt", change_prompt, CTRL('t')); 121 rl_callback_handler_install(get_prompt(), process_line); 122 123 while(1) { 124 FD_ZERO(&fds); 125 FD_SET(fileno(stdin), &fds); 126 127 if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) { 128 perror("select"); 129 exit(1); 130 } 131 132 if( FD_ISSET(fileno(stdin), &fds) ) { 133 rl_callback_read_char(); 134 } 135 } 136} 137 138void 139process_line(char *line) 140{ 141 if( line == NULL ) { 142 fprintf(stderr, "\n", line); 143 144 /* reset the old terminal setting before exiting */ 145 term.c_lflag = old_lflag; 146 term.c_cc[VTIME] = old_vtime; 147 if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 148 perror("tcsetattr"); 149 exit(1); 150 } 151 exit(0); 152 } 153 154 if( strcmp(line, "sleep") == 0 ) { 155 sleep(3); 156 } else { 157 fprintf(stderr, "|%s|\n", line); 158 } 159 160 free (line); 161} 162 163int 164change_prompt(void) 165{ 166 /* toggle the prompt variable */ 167 prompt = !prompt; 168 169 /* save away the current contents of the line */ 170 strcpy(line_buf, rl_line_buffer); 171 172 /* install a new handler which will change the prompt and erase the current line */ 173 rl_callback_handler_install(get_prompt(), process_line); 174 175 /* insert the old text on the new line */ 176 rl_insert_text(line_buf); 177 178 /* redraw the current line - this is an undocumented function. It invokes the 179 * redraw-current-line command. 180 */ 181 rl_refresh_line(0, 0); 182} 183 184char * 185get_prompt(void) 186{ 187 /* The prompts can even be different lengths! */ 188 sprintf(prompt_buf, "%s", 189 prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> "); 190 return prompt_buf; 191} 192