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