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