1 /* readline.c: interacting with the GNU readline library
2 (C) 2000-2007 Hans Lub
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License , or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 You may contact the author by:
19 e-mail: hanslub42@gmail.com
20 */
21
22
23 #include "rlwrap.h"
24
25 /* global vars */
26 int remember_for_completion = FALSE; /* whether we should put al words from in/output on the list */
27 char *multiline_separator = NULL; /* character sequence to use in lieu of newline when storing multi-line input in a single history line */
28 char *pre_given = NULL; /* pre-given user input when rlwrap starts up */
29 struct rl_state saved_rl_state = { "", "", 0, 0, 0 }; /* saved state of readline */
30 bool bracketed_paste_enabled = FALSE;
31 static char return_key; /* Key pressed to enter line */
32 static int forget_line;
33 static char *colour_start, *colour_end; /* colour codes */
34
35 int multiline_prompts = TRUE;
36
37 /* forward declarations */
38 static void line_handler(char *);
39 static void my_add_history(char *);
40 static int my_accept_line(int, int);
41 static int my_accept_line_and_forget(int, int);
42 static void munge_file_in_editor(const char *filename, int lineno, int colno);
43 static Keymap getmap(const char *name);
44 static void bindkey(int key, rl_command_func_t *function, const char *function_name, const char *maplist);
45 #define RL_COMMAND_FUN(f) f, #f
46
47 /* readline bindable functions: */
48 static int munge_line_in_editor(int, int);
49 static int direct_keypress(int, int);
50 static int direct_prefix(int, int);
51 static int handle_hotkey(int, int);
52 static int handle_hotkey_without_history(int, int);
53 static int please_update_alaf(int,int);
54 static int please_update_ce(int,int);
55
56 /* only useful while debugging: */
57 static int debug_ad_hoc(int,int);
58 static int dump_all_keybindings(int,int);
59
60
61
62 void
init_readline(char * UNUSED (prompt))63 init_readline(char *UNUSED(prompt))
64 {
65 DPRINTF1(DEBUG_READLINE, "Initialising readline version %x", rl_readline_version);
66 rl_add_defun("rlwrap-accept-line", my_accept_line,-1);
67 rl_add_defun("rlwrap-accept-line-and-forget", my_accept_line_and_forget,-1);
68 rl_add_defun("rlwrap-call-editor", munge_line_in_editor, -1);
69 rl_add_defun("rlwrap-direct-keypress", direct_keypress, -1);
70 rl_add_defun("rlwrap-direct-prefix", direct_prefix, -1);
71 rl_add_defun("rlwrap-hotkey", handle_hotkey, -1);
72 rl_add_defun("rlwrap-hotkey-without-history", handle_hotkey_without_history, -1);
73
74 /* only useful while debugging */
75 rl_add_defun("rlwrap-dump-all-keybindings", dump_all_keybindings,-1);
76 rl_add_defun("rlwrap-debug-ad-hoc", debug_ad_hoc, -1);
77
78 /* if someone's .inputrc binds a key to accept-line, make it use our own version in lieu of readline's */
79 rl_add_defun("accept-line", my_accept_line, -1);
80
81
82
83 /* the old rlwrap bindable function names with underscores are deprecated: */
84 rl_add_defun("rlwrap_accept_line_and_forget", please_update_alaf,-1);
85 rl_add_defun("rlwrap_call_editor", please_update_ce,-1);
86
87 /* put the next variable binding(s) *before* rl_initialize(), so they can be overridden */
88 rl_variable_bind("blink-matching-paren","on");
89
90 bindkey('\n', RL_COMMAND_FUN(my_accept_line), "emacs-standard; vi-insert; vi-command");
91 bindkey('\r', RL_COMMAND_FUN(my_accept_line), "emacs-standard; vi-insert; vi-command");
92 bindkey(15, RL_COMMAND_FUN(my_accept_line_and_forget), "emacs-standard; vi-insert; vi-command"); /* ascii #15 (Control-O) is unused in readline's emacs and vi keymaps */
93 if (multiline_separator)
94 bindkey(30, RL_COMMAND_FUN(munge_line_in_editor), "emacs-standard;vi-insert;vi-command"); /* CTRL-^: unused in vi-insert-mode, hardly used in emacs (doubles arrow-up) */
95
96
97
98 /* rl_variable_bind("gnah","gnerp"); It is not possible to create new readline variables (only functions) */
99 rl_catch_signals = 0;
100 rl_initialize(); /* This has to happen *before* we set rl_redisplay_function, otherwise
101 readline will not bother to call tgetent(), will be agnostic about terminal
102 capabilities and hence not be able to honour e.g. a set horizontal-scroll-mode off
103 in .inputrc */
104
105 /* put the next variable binding(s) *after* rl_initialize(), so they cannot be overridden */
106 /* rl_variable_bind("enable-bracketed-paste","off"); */ /* enable-bracketed-paste changes cursor positioning after printing the prompt ...
107 ... causing rlwrap to overwrite it after accepting input */
108
109 using_history();
110 rl_redisplay_function = my_redisplay;
111 rl_completion_entry_function =
112 (rl_compentry_func_t *) & my_completion_function;
113
114 rl_catch_signals = FALSE;
115 rl_catch_sigwinch = FALSE;
116
117 saved_rl_state.input_buffer = mysavestring(pre_given ? pre_given : ""); /* Even though the pre-given input won't be displayed before the first
118 cooking takes place, we still want it to be accepted when the user
119 presses ENTER before that (e.g. because she already knows the
120 pre-given input and wants to accept that) */
121 saved_rl_state.point = strlen(saved_rl_state.input_buffer);
122 saved_rl_state.raw_prompt = mysavestring("");
123 saved_rl_state.cooked_prompt = NULL;
124
125 bracketed_paste_enabled = term_enable_bracketed_paste != NULL && strings_are_equal(rl_variable_value("enable-bracketed-paste"),"on");
126
127 if (bracketed_paste_enabled) {
128 DPRINTF0(DEBUG_READLINE, "bracketed-paste is enabled");
129 my_putstr(term_enable_bracketed_paste);
130 }
131 }
132
133
134 /* save readline internal state in rl_state, redisplay the prompt
135 (so that client output gets printed at the right place) */
136 void
save_rl_state()137 save_rl_state()
138 {
139 free(saved_rl_state.input_buffer); /* free(saved_rl_state.raw_prompt) */;
140 saved_rl_state.input_buffer = mysavestring(rl_line_buffer);
141 /* saved_rl_state.raw_prompt = mysavestring(rl_prompt); */
142
143 saved_rl_state.point = rl_point; /* and point */
144 rl_line_buffer[0] = '\0';
145 if (saved_rl_state.already_saved)
146 return;
147 saved_rl_state.already_saved = 1;
148 rl_delete_text(0, rl_end); /* clear line (after prompt) */
149 rl_point = 0;
150 my_redisplay(); /* and redisplay (this time without user input, cf the comments for the line_handler() function below) */
151 rl_callback_handler_remove(); /* restore original terminal settings */
152 rl_deprep_terminal();
153 }
154
155
156 /* Restore readline internal state from rl_state. */
157
158 void
restore_rl_state()159 restore_rl_state()
160 {
161
162 char *newprompt;
163 move_cursor_to_start_of_prompt(impatient_prompt ? ERASE : DONT_ERASE);
164
165 cook_prompt_if_necessary();
166 newprompt = mark_invisible(saved_rl_state.cooked_prompt); /* bracket (colour) control sequences with \001 and \002 */
167 if (!strings_are_equal(newprompt, saved_rl_state.cooked_prompt))
168 DPRINTF2(DEBUG_READLINE, "newprompt: <%s>, cooked prompt: <%s>", M(newprompt), M(saved_rl_state.cooked_prompt));
169 rl_expand_prompt(newprompt);
170 mirror_slaves_echo_mode(); /* don't show passwords etc */
171
172 DPRINTF1(DEBUG_READLINE,"newprompt now %s", M(newprompt));
173 rl_callback_handler_install(newprompt, &line_handler);
174 DPRINTF0(DEBUG_AD_HOC, "freeing newprompt");
175 free(newprompt); /* readline docs don't say it, but we can free newprompt now (readline apparently
176 uses its own copy) */
177 rl_insert_text(saved_rl_state.input_buffer);
178 rl_point = saved_rl_state.point;
179 saved_rl_state.already_saved = 0;
180 DPRINTF0(DEBUG_AD_HOC, "Starting redisplay");
181 rl_redisplay();
182 rl_prep_terminal(1);
183 prompt_is_still_uncooked = FALSE; /* has been done right now */
184 }
185
186 /* display (or remove, if message == NULL) message in echo area, with the appropriate bookkeeping */
187 void
message_in_echo_area(char * message)188 message_in_echo_area(char *message)
189 {
190 static int message_in_echo_area = FALSE;
191 DPRINTF1(DEBUG_READLINE, "message: %s", M(message));
192 if (message) {
193 rl_save_prompt();
194 message_in_echo_area = TRUE;
195 rl_message(message);
196 } else {
197 if (message_in_echo_area)
198 rl_restore_prompt();
199 rl_clear_message();
200 message_in_echo_area = FALSE;
201 }
202 }
203
204 #ifndef HAVE_RL_VARIABLE_VALUE /* very ancient readline? */
205 # define rl_variable_value(s) "off"
206 #endif
207
208
209 static void
line_handler(char * line)210 line_handler(char *line)
211 {
212 char *rewritten_line, *filtered_line;
213
214 if (line == NULL) { /* EOF on input, forward it */
215 DPRINTF1(DEBUG_READLINE, "EOF detected, writing character %d", term_eof);
216 /* colour_the_prompt = FALSE; don't mess with the cruft that may come out of dying command @@@ but command may not die!*/
217 write_EOF_to_master_pty();
218 } else {
219 /* NB: with bracketed-paste, "line" may actually contain newline characters */
220 if (*line && /* forget empty lines */
221 redisplay && /* forget passwords */
222 !forget_line && /* forget lines entered by CTRL-O */
223 !match_regexp(line, forget_regexp, TRUE)) { /* forget lines (case-inseitively) matching -g option regexp */
224 my_add_history(line); /* if line consists of multiple lines, each line is added to history separately. Is this documented somewhere? */
225 }
226 forget_line = FALSE; /* until CTRL-O is used again */
227
228 /* Time for a design decision: when we send the accepted line to the client command, it will most probably be echoed
229 back. We have two choices: we leave the entered line on screen and suppress just enough of the clients output (I
230 believe this is what rlfe does), or we remove the entered input (but not the prompt!) and let it be replaced by
231 the echo.
232
233 This is what we do; as a bonus, if the program doesn't echo, e.g. at a password prompt, the **** which has been
234 put there by our homegrown_redisplay function will be removed (@@@is this what we want?)
235
236 I think this method is more natural for multi-line input as well, (we will actually see our multi-line input as
237 multiple lines) but not everyone will agree with that.
238
239 O.K, we know for sure that cursor is at start of line. When clients output arrives, it will be printed at
240 just the right place - but first we 'll erase the user input (as it may be about to be changed by the filter) */
241
242 if (bracketed_paste_enabled) /* we're at start of line and the prompt has just been erased */
243 my_putstr(saved_rl_state.cooked_prompt);
244
245 rl_delete_text(0, rl_end); /* clear line (after prompt) */
246 rl_point = 0;
247 my_redisplay(); /* and redisplay (this time without user input, cf the comments for the line_handler() function below) */
248 rewritten_line =
249 (multiline_separator ?
250 search_and_replace(multiline_separator, "\n", line, 0, NULL,
251 NULL) : mysavestring(line));
252
253
254
255
256 if (redisplay)
257 filtered_line = pass_through_filter(TAG_INPUT, rewritten_line);
258 else { /* password, none of filters business */
259 pass_through_filter(TAG_INPUT, "***"); /* filter some input anyway, to keep filter in sync (result is discarded). */
260 filtered_line = mysavestring(rewritten_line);
261 }
262 free(rewritten_line);
263
264
265 /* do we have to adapt clients winzsize now? */
266 if (deferred_adapt_commands_window_size) {
267 adapt_tty_winsize(STDIN_FILENO, master_pty_fd);
268 kill(-command_pid, SIGWINCH);
269 deferred_adapt_commands_window_size = FALSE;
270 }
271
272
273
274 /*OK, now feed line to underlying command and wait for the echo to come back */
275 put_in_output_queue(filtered_line);
276 DPRINTF2(DEBUG_READLINE, "putting %d bytes %s in output queue", (int) strlen(rewritten_line),
277 M(rewritten_line));
278 write_EOL_to_master_pty(return_key ? &return_key : "\n");
279
280 accepted_lines++;
281 free_foreign(line); /* free_foreign because line was malloc'ed by readline, not by rlwrap */
282 free(filtered_line); /* we're done with them */
283
284 return_key = 0;
285 within_line_edit = FALSE;
286 if(!RL_ISSTATE(RL_STATE_MACROINPUT)) /* when called during playback of a multi-line macro, line_handler() will be called more
287 than once whithout re-entering main_loop(). If we'd remove it here, the second call
288 would crash */
289 rl_callback_handler_remove();
290 set_echo(FALSE);
291 free(saved_rl_state.input_buffer);
292 free(saved_rl_state.raw_prompt);
293 free(saved_rl_state.cooked_prompt);
294
295 saved_rl_state.input_buffer = mysavestring("");
296 saved_rl_state.raw_prompt = mysavestring("");
297 saved_rl_state.cooked_prompt = NULL;
298 saved_rl_state.point = 0;
299 saved_rl_state.already_saved = TRUE;
300 redisplay = TRUE;
301
302 if (one_shot_rlwrap)
303 write_EOF_to_master_pty();
304
305 /* readline only outputs term_enable_bracketed_paste when we call rl_prep_terminal(). That's too */
306 /* late for us, as we only call rl_prep_terminal *after* we have received user input */
307 if (bracketed_paste_enabled)
308 my_putstr(term_enable_bracketed_paste);
309 }
310 }
311
312
313 /* this function (drop-in replacement for readline's own accept-line())
314 will be bound to RETURN key: */
315 static int
my_accept_line(int UNUSED (count),int key)316 my_accept_line(int UNUSED(count), int key)
317 {
318 SHOWCURSOR('0');
319 rl_point = 0; /* leave cursor on predictable place */
320 my_redisplay();
321 rl_done = 1;
322 return_key = (char)key;
323 return 0;
324 }
325
326 /* this function will be bound to rl_accept_key_and_forget key (normally CTRL-O) */
327 static int
my_accept_line_and_forget(int count,int UNUSED (key))328 my_accept_line_and_forget(int count, int UNUSED(key))
329 {
330 forget_line = 1;
331 return my_accept_line(count, '\n');
332 }
333
334 static int
dump_all_keybindings(int count,int key)335 dump_all_keybindings(int count, int key)
336 {
337 rl_dump_functions(count,key);
338 rl_variable_dumper(FALSE);
339 rl_macro_dumper(FALSE);
340 return 0;
341 }
342
343
344 /* format line and add it to history list, avoiding duplicates if necessary */
345 static void
my_add_history(char * line)346 my_add_history(char *line)
347 {
348 int lookback, count, here;
349 char *new_entry, *filtered_line, **lineptr, **list;
350
351 /* with bracketed-paste mode, line may actually be multiple lines. We'll treat each of those lines */
352 /* as a separate history item (I believe that is what bash does as well). */
353
354 list = split_with(line,"\n");
355 for (lineptr = list; *lineptr; lineptr++) {
356 filtered_line = pass_through_filter(TAG_HISTORY, *lineptr);
357
358
359 switch (history_duplicate_avoidance_policy) {
360 case KEEP_ALL_DOUBLES:
361 lookback = 0; break;
362 case ELIMINATE_SUCCESIVE_DOUBLES:
363 lookback = 1; break;
364 case ELIMINATE_ALL_DOUBLES:
365 lookback = history_length; break;
366 }
367
368
369 new_entry = filtered_line;
370
371 lookback = min(history_length, lookback);
372 for (count = 0, here = history_length - 1;
373 count < lookback ;
374 count++, here--) {
375 DPRINTF4(DEBUG_READLINE, "comparing <%s> and <%s> (count = %d, here = %d)", line
376 , history_get(history_base + here)->line ,count, here);
377 if (strncmp(new_entry, history_get(history_base + here) -> line, 10000) == 0) { /* history_get uses the logical offset history_base .. */
378 HIST_ENTRY *entry = remove_history (here); /* .. but remove_history doesn't! */
379 DPRINTF2(DEBUG_READLINE, "removing duplicate entry #%d (%s)", here, entry->line);
380 free_foreign(entry->line);
381 free_foreign(entry);
382 }
383 }
384 add_history(new_entry);
385 free(new_entry);
386 }
387 free_splitlist(list);
388 }
389
390 /* Homegrown redisplay function - erases current line and prints the
391 new one. Used for passwords (where we want to show **** instead of
392 user input) and whenever HOMEGROWN_REDISPLAY is defined (for
393 systems where rl_redisplay() misbehaves, like sometimes on
394 Solaris). Otherwise we use the much faster and smoother
395 rl_redisplay() This function cannot display multiple lines: it will
396 only scroll horizontally (even if horizontal-scroll-mode is off in
397 .inputrc)
398 */
399
400
401 static void
my_homegrown_redisplay(int hide_passwords)402 my_homegrown_redisplay(int hide_passwords)
403 {
404 static int line_start = 0; /* at which position of prompt_plus_line does the printed line start? */
405 static int line_extends_right = 0;
406 static int line_extends_left = 0;
407 static char *previous_line = NULL;
408
409
410 int width = winsize.ws_col;
411 int skip = max(1, min(width / 5, 10)); /* jumpscroll this many positions when cursor reaches edge of terminal */
412
413 char *prompt_without_ignore_markers;
414 int colourless_promptlen = colourless_strlen(rl_prompt, &prompt_without_ignore_markers,0);
415 int promptlen = strlen(prompt_without_ignore_markers);
416 int invisible_chars_in_prompt = promptlen - colourless_promptlen;
417 char *prompt_plus_line = add2strings(prompt_without_ignore_markers, rl_line_buffer);
418 char *new_line;
419 int total_length = strlen(prompt_plus_line);
420 int curpos = promptlen + rl_point; /* cursor position within prompt_plus_line */
421 int i, printed_length,
422 new_curpos, /* cursor position on screen */
423 keep_old_line, vlinestart, printwidth, last_column;
424 DPRINTF3(DEBUG_AD_HOC,"rl_prompt: <%s>, prompt_without_ignore_markers: <%s>, prompt_plus_line: <%s>", rl_prompt, prompt_without_ignore_markers, prompt_plus_line);
425
426 /* In order to handle prompt with colour we either print the whole prompt, or start past it:
427 starting in the middle is too difficult (i.e. I am too lazy) to get it right.
428 We use a "virtual line start" vlinestart, which is the number of invisible chars in prompt in the former case, or
429 linestart in the latter (which then must be >= strlen(prompt))
430
431 At all times (before redisplay and after) the following is true:
432 - the cursor is at column (curpos - vlinestart) (may be < 0 or > width)
433 - the character under the cursor is prompt_plus_line[curpos]
434 - the character at column 0 is prompt_plus_line[linestart]
435 - the last column is at <number of printed visible or invisible chars> - vlinestart
436
437 the goal of this function is to display (part of) prompt_plus_line such
438 that the cursor is visible again */
439
440
441 if (hide_passwords)
442 for (i = promptlen; i < total_length; i++)
443 prompt_plus_line[i] = '*'; /* hide a pasword by making user input unreadable */
444
445
446 if (rl_point == 0) /* (re)set at program start and after accept_line (where rl_point is zeroed) */
447 line_start = 0;
448 assert(line_start == 0 || line_start >= promptlen); /* the line *never* starts in the middle of the prompt (too complicated to handle)*/
449 vlinestart = (line_start > promptlen ? line_start : invisible_chars_in_prompt);
450
451
452 if (curpos - vlinestart > width - line_extends_right) /* cursor falls off right edge ? */
453 vlinestart = (curpos - width + line_extends_right) + skip; /* jumpscroll left */
454
455 else if (curpos < vlinestart + line_extends_left) { /* cursor falls off left edge ? */
456 if (curpos == total_length) /* .. but still at end of line? */
457 vlinestart = max(0, total_length - width); /* .. try to display entire line */
458 else /* in not at end of line .. */
459 vlinestart = curpos - line_extends_left - skip; /* ... jumpscroll right .. */
460 }
461 if (vlinestart <= invisible_chars_in_prompt) {
462 line_start = 0; /* ... but not past start of line! */
463 vlinestart = invisible_chars_in_prompt;
464 } else if (vlinestart > invisible_chars_in_prompt && vlinestart <= promptlen) {
465 line_start = vlinestart = promptlen;
466 } else {
467 line_start = vlinestart;
468 }
469
470 printwidth = (line_start > 0 ? width : width + invisible_chars_in_prompt);
471 printed_length = min(printwidth, total_length - line_start); /* never print more than width */
472 last_column = printed_length - vlinestart;
473
474
475 /* some invariants : 0 <= line_start <= curpos <= line_start + printed_length <= total_length */
476 /* these are interesting: ^ ^ */
477
478 assert(0 <= line_start);
479 assert(line_start <= curpos);
480 assert(curpos <= line_start + printed_length); /* <=, rather than <, as cursor may be past eol */
481 assert(line_start + printed_length <= total_length);
482
483
484 new_line = prompt_plus_line + line_start;
485 new_line[printed_length] = '\0';
486 new_curpos = curpos - vlinestart;
487
488 /* indicate whether line extends past right or left edge (i.e. whether the "interesting
489 inequalities marked ^ above are really unequal) */
490
491 line_extends_left = (line_start > 0 ? 1 : 0);
492 line_extends_right = (total_length - vlinestart > width ? 1 : 0);
493 if (line_extends_left)
494 new_line[0] = '<';
495 if (line_extends_right)
496 new_line[printwidth - 1] = '>';
497
498
499
500 keep_old_line = FALSE;
501 if (term_cursor_hpos) {
502 if (previous_line && strcmp(new_line, previous_line) == 0) {
503 keep_old_line = TRUE;
504 } else {
505 if (previous_line)
506 free(previous_line);
507 previous_line = mysavestring(new_line);
508 }
509 }
510 /* DPRINTF2(DEBUG_AD_HOC, "keep_old_line=%d, new_line=<%s>", keep_old_line, new_line); */
511 /* keep_old_line = TRUE; */
512 if (!keep_old_line) {
513 clear_line();
514 cr();
515 write_patiently(STDOUT_FILENO, new_line, printed_length, "to stdout");
516 }
517
518 assert(term_cursor_hpos || !keep_old_line); /* if we cannot position cursor, we must have reprinted ... */
519
520 if (term_cursor_hpos)
521 cursor_hpos(new_curpos);
522 else /* ... so we know we're 1 past last position on line */
523 backspace(last_column - new_curpos);
524 free(prompt_plus_line);
525 free(prompt_without_ignore_markers);
526 }
527
528
529
530
531 void
my_redisplay()532 my_redisplay()
533 {
534 int debug_force_homegrown_redisplay = 0;
535
536 #ifdef DEBUG
537 debug_force_homegrown_redisplay = debug & FORCE_HOMEGROWN_REDISPLAY;
538 #endif
539
540 #ifndef HOMEGROWN_REDISPLAY
541 if (redisplay && !debug_force_homegrown_redisplay) {
542 rl_redisplay();
543 } else
544 #endif
545 my_homegrown_redisplay(!redisplay);
546 }
547
548
549
550
551 static void
munge_file_in_editor(const char * filename,int lineno,int colno)552 munge_file_in_editor(const char *filename, int lineno, int colno)
553 {
554
555 int ret;
556 char *editor_command1, *editor_command2, *editor_command3,
557 *editor_command4, *line_number_as_string, *column_number_as_string, **possible_editor_commands;
558
559 /* find out which editor command we have to use */
560 possible_editor_commands = list4(getenv("RLWRAP_EDITOR"), getenv("EDITOR"), getenv("VISUAL"), "vi +%L");
561 editor_command1 = first_of(possible_editor_commands);
562 line_number_as_string = as_string(lineno);
563 column_number_as_string = as_string(colno);
564 editor_command2 = search_and_replace("%L", line_number_as_string, editor_command1, 0, NULL, NULL);
565 editor_command3 = search_and_replace("%C", column_number_as_string, editor_command2, 0, NULL, NULL);
566 editor_command4 = strstr(editor_command3, "%F") ?
567 search_and_replace("%F", filename, editor_command3, 0, NULL, NULL) :
568 add3strings(editor_command3, " ", filename);
569
570
571 /* call editor, temporarily restoring terminal settings */
572 if (terminal_settings_saved && (tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_terminal_settings) < 0)) /* reset terminal */
573 myerror(FATAL|USE_ERRNO, "tcsetattr error on stdin");
574 DPRINTF1(DEBUG_READLINE, "calling %s", editor_command4);
575 if ((ret = system(editor_command4))) {
576 if (WIFSIGNALED(ret)) {
577 fprintf(stderr, "\n");
578 myerror(FATAL|NOERRNO, "editor killed by signal");
579 } else {
580 myerror(FATAL|USE_ERRNO, "failed to invoke editor with '%s'", editor_command4);
581 }
582 }
583 completely_mirror_slaves_terminal_settings();
584 ignore_queued_input = TRUE;
585
586 free_multiple(possible_editor_commands, editor_command2, editor_command3,
587 editor_command4, line_number_as_string, column_number_as_string, FMEND);
588 }
589
590
591 static int
munge_line_in_editor(int UNUSED (count),int UNUSED (key))592 munge_line_in_editor(int UNUSED(count), int UNUSED(key))
593 {
594 int line_number = 0, column_number = 0, tmpfile_fd, bytes_read;
595 size_t tmpfilesize;
596 char *p, *tmpfilename, *text_to_edit;
597 char *input, *rewritten_input, *rewritten_input2;
598
599
600 if (!multiline_separator)
601 return 0;
602
603 tmpfile_fd = open_unique_tempfile(multi_line_tmpfile_ext, &tmpfilename);
604
605 text_to_edit =
606 search_and_replace(multiline_separator, "\n", rl_line_buffer, rl_point,
607 &line_number, &column_number);
608 write_patiently(tmpfile_fd, text_to_edit, strlen(text_to_edit), "to temporary file");
609
610 if (close(tmpfile_fd) != 0) /* improbable */
611 myerror(FATAL|USE_ERRNO, "couldn't close temporary file %s", tmpfilename);
612
613 munge_file_in_editor(tmpfilename, line_number, column_number);
614
615 /* read back edited input, replacing real newline with substitute */
616 tmpfile_fd = open(tmpfilename, O_RDONLY);
617 if (tmpfile_fd < 0)
618 myerror(FATAL|USE_ERRNO, "could not read temp file %s", tmpfilename);
619 tmpfilesize = filesize(tmpfilename);
620 input = mymalloc(tmpfilesize + 1);
621 bytes_read = read(tmpfile_fd, input, tmpfilesize);
622 if (bytes_read < 0)
623 myerror(FATAL|USE_ERRNO, "unreadable temp file %s", tmpfilename);
624 input[bytes_read] = '\0';
625 rewritten_input = search_and_replace("\t", " ", input, 0, NULL, NULL); /* rlwrap cannot handle tabs in input lines */
626 rewritten_input2 =
627 search_and_replace("\n", multiline_separator, rewritten_input, 0, NULL, NULL);
628 for(p = rewritten_input2; *p ;p++)
629 if(*p >= 0 && *p < ' ') /* @@@FIXME: works for UTF8, but not UTF16 or UTF32 (Mention this in manpage?)*/
630 *p = ' '; /* replace all control characters (like \r) by spaces */
631
632
633 rl_delete_text(0, strlen(rl_line_buffer));
634 rl_point = 0;
635 clear_line();
636 cr();
637 my_putstr(saved_rl_state.cooked_prompt);
638 rl_insert_text(rewritten_input2);
639 rl_point = 0; /* leave cursor on predictable place */
640 rl_done = 1; /* accept line immediately */
641
642
643
644
645 /* wash those dishes */
646 if (unlink(tmpfilename))
647 myerror(FATAL|USE_ERRNO, "could not delete temporary file %s", tmpfilename);
648
649 free(tmpfilename);
650 free(text_to_edit);
651 free(input);
652 free(rewritten_input);
653 free(rewritten_input2);
654
655 return_key = (char)'\n';
656 return 0;
657 }
658
659 static int
direct_keypress(int UNUSED (count),int key)660 direct_keypress(int UNUSED(count), int key)
661 {
662 #ifdef HAVE_RL_EXECUTING_KEYSEQ /* i.e. if readline version is >= 6.3 */
663 MAYBE_UNUSED(key);
664 DPRINTF1(DEBUG_READLINE,"direct keypress: %s", M(rl_executing_keyseq));
665 put_in_output_queue(rl_executing_keyseq);
666 #else
667 char *key_as_str = mysavestring("?"); /* ? is just a placeholder */
668 *key_as_str = key;
669 DPRINTF1(DEBUG_READLINE,"direct keypress: %s", mangle_char_for_debug_log(key, TRUE));
670 put_in_output_queue(key_as_str);
671 free(key_as_str);
672 #endif
673 return 0;
674 }
675
676 static int
direct_prefix(int count,int key)677 direct_prefix(int count, int key)
678 {
679 char *key_as_str = mysavestring("?"); /* ? is just a placeholder */
680
681 /* process the keypress used to invoke the function */
682 direct_keypress(count, key);
683 /* read an extra key to pass on */
684 key = rl_read_key();
685 *key_as_str = key;
686 DPRINTF1(DEBUG_READLINE,"read key: %s", mangle_char_for_debug_log(key, TRUE));
687 put_in_output_queue(key_as_str);
688 free(key_as_str);
689
690 return 0;
691 }
692
entire_history_as_one_string(void)693 static char* entire_history_as_one_string(void) {
694 HIST_ENTRY **the_list = history_list(), **entryp;
695 char *big_string = mymalloc(history_total_bytes() + history_length + 1);
696 char *stringp = big_string;
697
698 if (!the_list) /* i.e. if there is no history */
699 return mysavestring("");
700
701 for (entryp = the_list; *entryp; entryp++) {
702 int length = strlen((*entryp)->line);
703 strcpy(stringp, (*entryp)->line);
704 stringp +=length;
705 *stringp++ = '\n';
706 }
707 *stringp = '\0';
708 DPRINTF1(DEBUG_READLINE, "stringified %d bytes of history", (int) strlen(big_string));
709 return big_string;
710 }
711
712 static int
debug_ad_hoc(int UNUSED (count),int UNUSED (hotkey))713 debug_ad_hoc(int UNUSED(count), int UNUSED(hotkey))
714 {
715 printf("\n%s", entire_history_as_one_string());
716 cleanup_rlwrap_and_exit(EXIT_SUCCESS);
717 return 42;
718 }
719
720
721
722 static int
handle_hotkey2(int UNUSED (count),int hotkey,int without_history)723 handle_hotkey2(int UNUSED(count), int hotkey, int without_history)
724 {
725 char *prefix, *postfix, *history, *histpos_as_string, *executing_keyseq;
726 char *new_prefix, *new_postfix, *new_history, *new_histpos_as_string, *message;
727 char *filter_food, *filtered, **fragments, *new_rl_line_buffer;
728 int nfilter_fields, length, new_histpos;
729 unsigned long int hash;
730
731 static const unsigned int MAX_HISTPOS_DIGITS = 6; /* one million history items should suffice */
732
733
734 #ifdef HAVE_RL_EXECUTING_KEYSEQ /* i.e. if readline version is >= 6.3 */
735 executing_keyseq = mysavestring(rl_executing_keyseq);
736 #else
737 executing_keyseq = mysavestring("?");
738 *executing_keyseq = hotkey; /* The filter will only get the *last* byte of the key sequence that triggered rl_handle_hotkey */
739 #endif
740
741
742 DPRINTF3(DEBUG_READLINE, "hotkey press (without_history == %d): %x (%s)", without_history, hotkey, M(executing_keyseq));
743
744 prefix = mysavestring(rl_line_buffer);
745 prefix[rl_point] = '\0'; /* chop off just before cursor */
746 postfix = mysavestring(rl_line_buffer + rl_point);
747
748 if (without_history) {
749 histpos_as_string = mysavestring("0");
750 history = mysavestring("");
751 } else {
752 histpos_as_string = as_string(where_history());
753 assert(strlen(histpos_as_string) <= MAX_HISTPOS_DIGITS);
754 history = entire_history_as_one_string();
755 hash = hash_multiple(2, history, histpos_as_string);
756 }
757
758 /* filter_food = <keyseq_length><keyseq><prefix_length><prefix><postfix_length><postfix><history_length><history><histpos_length><histpos> + '\0' */
759 filter_food = merge_fields(executing_keyseq, prefix, postfix, history, histpos_as_string, END_FIELD); /* this is the format that the filter expects */
760
761 /* let the filter filter ...! */
762 filtered= pass_through_filter(TAG_HOTKEY, filter_food);
763
764 /* OK, we now have to read back everything. After splitting the message, here should be exactly 5 components*/
765 fragments = split_filter_message(filtered, &nfilter_fields);
766 assert(nfilter_fields == 5);
767 message = fragments[0];
768 new_prefix = fragments[1];
769 new_postfix = fragments[2];
770 new_history = fragments[3];
771 new_histpos_as_string = fragments[4];
772
773 if (!without_history && hash_multiple(2, new_history, new_histpos_as_string) != hash) { /* history has been rewritten */
774 char **linep, **history_lines = split_on_single_char(new_history, '\n', 0);
775 DPRINTF3(DEBUG_READLINE, "hash=%lx, new_history is %d bytes long, histpos <%s>", hash, (int) strlen(new_history), new_histpos_as_string);
776 clear_history();
777 for (linep = history_lines; *linep; linep++)
778 add_history(*linep);
779 new_histpos = my_atoi(new_histpos_as_string);
780 history_set_pos(new_histpos);
781 free_splitlist(history_lines);
782 }
783 new_rl_line_buffer = add2strings(new_prefix, new_postfix);
784
785 if ( (length = strlen(new_rl_line_buffer)) > 0 && new_rl_line_buffer[length - 1] == '\n') {
786 new_rl_line_buffer[length - 1] = '\0';
787 rl_done = TRUE;
788 return_key = (char) '\n';
789 assert(strchr(new_rl_line_buffer, '\n') == NULL);
790 }
791
792 rl_delete_text(0, strlen(rl_line_buffer));
793 rl_point = 0;
794 rl_insert_text(new_rl_line_buffer);
795 rl_point = strlen(new_rl_line_buffer);
796
797
798
799 if (*message && *message != hotkey) { /* if message has been set (i.e. != hotkey) , and isn't empty: */
800 message = append_and_free_old(mysavestring(message), " "); /* put space (for readability) between the message and the input line .. */
801 message_in_echo_area(message); /* .. then write it to echo area */
802 }
803
804 clear_line();
805 rl_on_new_line();
806 rl_redisplay();
807
808
809 free_splitlist(fragments); /* this will free all the fragments (and the list itself) in one go */
810 free_multiple(prefix, postfix, filter_food, executing_keyseq, filtered, new_rl_line_buffer, history, histpos_as_string, FMEND);
811 return 0;
812 }
813
814
815 static int
handle_hotkey(int count,int hotkey)816 handle_hotkey(int count, int hotkey)
817 {
818 return handle_hotkey2(count, hotkey, FALSE);
819 }
820
821
822 static int
handle_hotkey_without_history(int count,int hotkey)823 handle_hotkey_without_history(int count, int hotkey)
824 {
825 return handle_hotkey2(count, hotkey, TRUE);
826 }
827
828
829 void
initialise_colour_codes(char * colour)830 initialise_colour_codes(char *colour)
831 {
832 int attributes, foreground, background;
833 DPRINTF1(DEBUG_READLINE, "initialise_colour_codes(\"%s\")", colour);
834 attributes = foreground = -1;
835 background = 40; /* don't need to specify background; 40 passes the test automatically */
836 sscanf(colour, "%d;%d;%d", &attributes, &foreground, &background);
837
838 #define OUTSIDE(lo,hi,val) (val < lo || val > hi)
839 if (OUTSIDE(0,8,attributes) || OUTSIDE(30,37,foreground) || OUTSIDE(40,47,background))
840 myerror(FATAL|NOERRNO, "\n"
841 " prompt colour spec should be <attr>;<fg>[;<bg>]\n"
842 " where <attr> ranges over [0...8], <fg> over [30...37] and <bg> over [40...47]\n"
843 " example: 0;33 for yellow on current background, 1;31;40 for bold red on black ");
844 colour_start= add3strings("\033[", colour,"m");
845 colour_end = "\033[0m";
846 }
847
848 /* returns a colourised copy of prompt, trailing space is not colourised */
849 char*
colourise(const char * prompt)850 colourise (const char *prompt)
851 {
852 char *prompt_copy, *trailing_space, *colour_end_with_space, *result, *p;
853 prompt_copy = mysavestring(prompt);
854 /* if (strchr(prompt_copy, '\033') || strchr(prompt_copy, RL_PROMPT_START_IGNORE) ) { /\* prompt contains escape codes? *\/ */
855 /* DPRINTF1(DEBUG_READLINE, "colourise %s: left as-is", prompt); */
856 /* return prompt_copy; /\* if so, leave prompt alone *\/ */
857 /* } */
858 for (p = prompt_copy + strlen(prompt_copy); p > prompt_copy && *(p-1) == ' '; p--)
859 ; /* skip back over trailing space */
860 trailing_space = mysavestring(p); /* p now points at first trailing space, or else the final NULL */
861 *p = '\0';
862 colour_end_with_space = add2strings(colour_end, trailing_space);
863 result = add3strings(colour_start, prompt_copy, colour_end_with_space);
864 free (prompt_copy); free(trailing_space); free(colour_end_with_space);
865 DPRINTF1(DEBUG_READLINE, "colourise %s: colour added ", prompt);
866 return result;
867 }
868
869 void
move_cursor_to_start_of_prompt(int erase)870 move_cursor_to_start_of_prompt(int erase)
871 {
872 int termwidth = winsize.ws_col;
873 int promptlen_on_screen, number_of_lines_in_prompt, curpos, count;
874 int cooked = (saved_rl_state.cooked_prompt != NULL);
875
876 DPRINTF2(DEBUG_READLINE,"prompt_is_still_uncooked: %d, impatient_prompt: %d", prompt_is_still_uncooked, impatient_prompt);
877 if (prompt_is_still_uncooked && ! impatient_prompt)
878 return; /* @@@ is this necessary ?*/
879
880
881 promptlen_on_screen = colourless_strlen_unmarked(saved_rl_state.cooked_prompt ? saved_rl_state.cooked_prompt : saved_rl_state.raw_prompt, termwidth);
882 curpos = (within_line_edit ? 1 : 0); /* if the user has pressed a key the cursor will be 1 past the current prompt */
883 assert(termwidth > 0);
884 number_of_lines_in_prompt = 1 + ((promptlen_on_screen + curpos -1) / termwidth); /* integer arithmetic! (e.g. 171/80 = 2) */
885 cr();
886 for (count = 0; count < number_of_lines_in_prompt -1; count++) {
887 if (erase)
888 clear_line();
889 curs_up();
890 }
891 clear_line();
892 DPRINTF4(DEBUG_READLINE,"moved cursor up %d lines (erase = %d, len=%d, cooked=%d)", number_of_lines_in_prompt - 1, erase, promptlen_on_screen, cooked);
893 }
894
895
896 int
prompt_is_single_line(void)897 prompt_is_single_line(void)
898 {
899 int homegrown_redisplay= FALSE;
900 int force_homegrown_redisplay = FALSE;
901 int retval;
902
903 #ifndef SPY_ON_READLINE
904 # define _rl_horizontal_scroll_mode FALSE
905 # define rl_variable_value(s) "off"
906 #endif
907
908 #ifdef HOMEGROWN_REDISPLAY
909 homegrown_redisplay=TRUE;
910 #endif
911
912 #ifdef DEBUG
913 force_homegrown_redisplay = debug & FORCE_HOMEGROWN_REDISPLAY;
914 #endif
915
916 retval = _rl_horizontal_scroll_mode ||
917 strncmp(rl_variable_value("horizontal-scroll-mode"),"on",3) == 0 ||
918 homegrown_redisplay ||
919 force_homegrown_redisplay;
920
921 DPRINTF1(DEBUG_READLINE, "prompt is %s-line", (retval ? "single" : "multi"));
922 return retval;
923 }
924
925
process_new_output(const char * buffer,struct rl_state * UNUSED (state))926 char *process_new_output(const char* buffer, struct rl_state* UNUSED(state)) {
927 char*last_nl, *old_prompt_plus_new_output, *new_prompt, *result;
928
929 old_prompt_plus_new_output = append_and_free_old(saved_rl_state.raw_prompt, buffer);
930 last_nl = strrchr(old_prompt_plus_new_output, '\n');
931 if (last_nl != NULL) { /* newline seen, will get new prompt: */
932 new_prompt = mysavestring(last_nl +1); /* chop off the part after the last newline - this will be the new prompt */
933 *last_nl = '\0';
934 old_prompt_plus_new_output = append_and_free_old (old_prompt_plus_new_output, "\n");
935 result = (impatient_prompt ? mysavestring (old_prompt_plus_new_output): pass_through_filter(TAG_OUTPUT, old_prompt_plus_new_output));
936 if (remember_for_completion) {
937 feed_line_into_completion_list(result); /* feed output into completion list *after* filtering */
938 }
939 } else {
940 new_prompt = mysavestring(old_prompt_plus_new_output);
941 result = mysavestring("");
942 }
943 free(old_prompt_plus_new_output);
944
945 saved_rl_state.raw_prompt = new_prompt;
946 if (saved_rl_state.cooked_prompt) {
947 free (saved_rl_state.cooked_prompt);
948 saved_rl_state.cooked_prompt = NULL;
949 }
950 return result;
951 }
952
953
954
955
cook_prompt_if_necessary()956 int cook_prompt_if_necessary () {
957 char *pre_cooked, *slightly_cooked, *rubbish_from_alternate_screen, *filtered, *uncoloured, *cooked, *p, *non_rubbish = NULL;
958 static char **term_ctrl_seqs[]
959 = {&term_rmcup, &term_rmkx, NULL}; /* (NULL-terminated) list of (pointers to) term control sequences that may be
960 used by clients to return from an 'alternate screen'. If we spot one of those,
961 assume that it, and anything before it, is rubbish and better left untouched */
962
963 char ***tcptr;
964 filtered = NULL;
965
966 DPRINTF2(DEBUG_READLINE, "Prompt <%s>: %s", saved_rl_state.raw_prompt, prompt_is_still_uncooked ? "still raw" : "cooked already");
967
968 if (saved_rl_state.cooked_prompt) /* if (!prompt_is_still_uncooked) bombs with multi-line paste. Apparently
969 prompt_is_still_uncooked can be FALSE while saved_rl_state.cooked_prompt = NULL. Ouch!@@@! */
970 return FALSE; /* cooked already */
971
972 pre_cooked = mysavestring(saved_rl_state.raw_prompt);
973
974
975 for (tcptr = term_ctrl_seqs; *tcptr; tcptr++) {
976 /* find last occurence of one of term_ctrl_seq */
977 if (**tcptr && (p = mystrstr(pre_cooked, **tcptr))) {
978 p += strlen(**tcptr); /* p now points 1 char past term control sequence */
979 if (p > non_rubbish)
980 non_rubbish = p;
981 }
982 }
983 /* non_rubbish now points 1 past the last 'alternate screen terminating' control char in prompt */
984 if (non_rubbish) {
985 rubbish_from_alternate_screen = pre_cooked;
986 pre_cooked = mysavestring(non_rubbish);
987 *non_rubbish = '\0'; /* 0-terminate rubbish_from_alternate_screen */
988 } else {
989 rubbish_from_alternate_screen = mysavestring("");
990 }
991
992 slightly_cooked = ansi_colour_aware ? protect_or_cleanup(pre_cooked, FALSE) : mysavestring(pre_cooked);
993 free(pre_cooked);
994
995 unbackspace(slightly_cooked); /* programs that display a running counter would otherwise make rlwrap keep prompts
996 like " 1%\r 2%\r 3%\ ......" */
997
998 if ( /* raw prompt doesn't match '--only-cook' regexp */
999 (prompt_regexp && ! match_regexp(slightly_cooked, prompt_regexp, FALSE)) ||
1000 /* now filter it, but filter may "refuse" the prompt */
1001 (strcmp((filtered = pass_through_filter(TAG_PROMPT, slightly_cooked)), "_THIS_CANNOT_BE_A_PROMPT_")== 0)) {
1002 /* don't cook, eat raw (and eat nothing if patient) */
1003 saved_rl_state.cooked_prompt = (impatient_prompt ? mysavestring(slightly_cooked) : mysavestring(""));
1004 /* NB: if impatient, the rubbish_from_alternate_screen has been output already, no need to send it again */
1005 free(slightly_cooked);
1006 free(filtered); /* free(NULL) is never an error */
1007 return FALSE;
1008 }
1009 free(slightly_cooked);
1010 if(substitute_prompt) {
1011 uncoloured = mysavestring(substitute_prompt);
1012 free(filtered);
1013 } else {
1014 uncoloured = filtered;
1015 }
1016 if (colour_the_prompt) {
1017 cooked = colourise(uncoloured);
1018 free(uncoloured);
1019 } else {
1020 cooked = uncoloured;
1021 }
1022 if (! impatient_prompt) /* in this case our rubbish hasn't been output yet. Output it now, but don't store
1023 it in the prompt, as this may be re-printed e.g. after resuming a suspended rlwrap */
1024 write_patiently(STDOUT_FILENO,rubbish_from_alternate_screen, strlen(rubbish_from_alternate_screen), "to stdout");
1025
1026 free(rubbish_from_alternate_screen);
1027 saved_rl_state.cooked_prompt = cooked;
1028 return TRUE;
1029 }
1030
1031
1032
1033 /* Utility functions for binding keys. */
1034
please_update(const char * varname)1035 static int please_update(const char *varname) {
1036 myerror(FATAL|NOERRNO, "since version 0.35, the readline function '%s' is called '%s'\n"
1037 "(hyphens instead of underscores). Please update your .inputrc",
1038 varname, search_and_replace("_","-", varname, 0, NULL, NULL));
1039 return 0;
1040 }
1041
please_update_alaf(int UNUSED (count),int UNUSED (key))1042 static int please_update_alaf(int UNUSED(count), int UNUSED(key)) {
1043 return please_update("rlwrap_accept_line_and_forget");
1044 }
1045
please_update_ce(int UNUSED (count),int UNUSED (key))1046 static int please_update_ce(int UNUSED(count), int UNUSED(key)) {
1047 return please_update("rlwrap_call_editor");
1048 }
1049
1050
1051
1052 /*
1053 From the readline documentation: Acceptable keymap names are emacs, emacs-standard, emacs-meta, emacs-ctlx, vi,
1054 vi-move, vi-command, and vi-insert. vi is equivalent to vi-command; emacs is equivalent to emacs-standard. The
1055 default value is emacs. The value of the editing-mode variable also affects the default keymap.
1056 */
1057
1058
getmap(const char * name)1059 static Keymap getmap(const char *name) {
1060 Keymap km = rl_get_keymap_by_name(name);
1061 if (!km)
1062 myerror(FATAL|NOERRNO, "Could not get keymap '%s'", name);
1063 return km;
1064 }
1065
bindkey(int key,rl_command_func_t * function,const char * function_name,const char * maplist)1066 static void bindkey(int key, rl_command_func_t *function, const char *function_name, const char *maplist) {
1067 char *mapnames[] = {"emacs-standard","emacs-ctlx","emacs-meta","vi-insert","vi-move","vi-command",NULL};
1068 char **mapname;
1069 for (mapname = mapnames; *mapname; mapname++)
1070 if(strstr(maplist, *mapname)) {
1071 Keymap kmap = getmap(*mapname);
1072 DPRINTF4(DEBUG_READLINE,"Binding key %d (%s) in keymap '%s' to %s()", key, mangle_char_for_debug_log(key,TRUE), *mapname, function_name);
1073 if (rl_bind_key_in_map(key, function, kmap))
1074 myerror(FATAL|NOERRNO, "Could not bind key %d (%s) in keymap '%s'", key, mangle_char_for_debug_log(key,TRUE), *mapname);
1075 }
1076 }
1077
1078
1079 #ifdef UNIT_TEST
1080
my_line_handler(char * line)1081 void my_line_handler(char *line) { /* This function (callback) gets called by readline
1082 whenever rl_callback_read_char sees an ENTER */
1083 printf("\nYou typed: '%s'\n", line);
1084 if (!line)
1085 exit(0);
1086 }
1087
1088
TESTFUNC(simple_callback,argc,argv,stage)1089 TESTFUNC(simple_callback, argc, argv, stage) {
1090 char byte_read;
1091 ONLY_AT_STAGE(TEST_AFTER_OPTION_PARSING);
1092 init_readline("Weh! ");
1093 set_echo(FALSE);
1094 rl_callback_handler_install("Type anything: ", &my_line_handler);
1095 while (TRUE) {
1096 if (!read(STDIN_FILENO, &byte_read, 1))
1097 exit(0);
1098 DPRINTF1(DEBUG_READLINE, "Byte read: %s", mangle_char_for_debug_log(byte_read, TRUE));
1099 rl_stuff_char(byte_read);
1100 rl_callback_read_char();
1101 }
1102 }
1103
TESTFUNC(standard_readline,argc,argv,stage)1104 TESTFUNC(standard_readline, argc, argv, stage) {
1105 ONLY_AT_STAGE(TEST_AFTER_OPTION_PARSING);
1106 while(TRUE) {
1107 char *line_read = readline ("go ahead: ");
1108 printf("\nYou typed: '%s'\n", line_read);
1109 if (!line_read || !*line_read)
1110 break;
1111 free(line_read);
1112 }
1113 exit(0);
1114 }
1115
1116 #endif /* UNIT_TEST */
1117