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