1 /* 2 From: Jeff Solomon <jsolomon@stanford.edu> 3 Date: Fri, 9 Apr 1999 10:13:27 -0700 (PDT) 4 To: chet@po.cwru.edu 5 Subject: new readline example 6 Message-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU> 7 8 Chet, 9 10 I've been using readline 4.0. Specifically, I've been using the perl 11 version Term::ReadLine::Gnu. It works great. 12 13 Anyway, I've been playing around the alternate interface and I wanted 14 to contribute a little C program, callback.c, to you that you could 15 use as an example of the alternate interface in the /examples 16 directory of the readline distribution. 17 18 My example shows how, using the alternate interface, you can 19 interactively change the prompt (which is very nice imo). Also, I 20 point out that you must roll your own terminal setting when using the 21 alternate interface because readline depreps (using your parlance) the 22 terminal while in the user callback. I try to demostrate what I mean 23 with an example. I've included the program below. 24 25 To compile, I just put the program in the examples directory and made 26 the appropriate changes to the EXECUTABLES and OBJECTS line and added 27 an additional target 'callback'. 28 29 I compiled on my Sun Solaris2.6 box using Sun's cc. 30 31 Let me know what you think. 32 33 Jeff 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 84 void process_line(char *line); 85 int change_prompt(void); 86 char *get_prompt(void); 87 88 int prompt = 1; 89 char prompt_buf[40], line_buf[256]; 90 tcflag_t old_lflag; 91 cc_t old_vtime; 92 struct termios term; 93 94 int 95 main() 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(fileno(stdin) + 1, &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 135 void 136 process_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 160 int 161 change_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 181 char * 182 get_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