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