When readline's display of completion matches is interrupted, the match list is only freed for the '?' completion type but not for '!' and '@' types. I couldn't find a specific reason for that to be the case so hope this patch is an OK fix.
Also add freeing saved_line_buffer from rl_complete_internal to the signal handler and freeing of matches in one spot where menu completion doesn't do it. * lib/readline/complete.c - complete_sigcleanarg_t: new struct to hold _rl_complete_sigcleanup argument - _rl_complete_sigcleanup: use new struct instead of *matches pointer, now also handles freeing saved_line - rl_complete_internal: set up _rl_sigcleanup whenever display_matches is called so we no longer leak matches and saved_line_buffer when getting a SIGINT - rl_menu_complete: free contents of matches instead of just pointer when completion-query-items exceeded --- lib/readline/complete.c | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/lib/readline/complete.c b/lib/readline/complete.c index bf7cc856..f8f08aa6 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -494,12 +494,19 @@ _rl_reset_completion_state (void) rl_completion_quote_character = 0; } +typedef struct { + char **matches; + char *saved_line; +} complete_sigcleanarg_t; + static void _rl_complete_sigcleanup (int sig, void *ptr) { + complete_sigcleanarg_t *arg = ptr; if (sig == SIGINT) /* XXX - for now */ { - _rl_free_match_list ((char **)ptr); + _rl_free_match_list (arg->matches); + FREE (arg->saved_line); _rl_complete_display_matches_interrupt = 1; } } @@ -2008,7 +2015,8 @@ rl_complete_internal (int what_to_do) int start, end, delimiter, found_quote, i, nontrivial_lcd; char *text, *saved_line_buffer; char quote_char; - int tlen, mlen, saved_last_completion_failed; + int tlen, mlen, saved_last_completion_failed, do_display; + complete_sigcleanarg_t cleanarg; RL_SETSTATE(RL_STATE_COMPLETING); @@ -2088,6 +2096,8 @@ rl_complete_internal (int what_to_do) if (matches && matches[0] && *matches[0]) last_completion_failed = 0; + do_display = 0; + switch (what_to_do) { case TAB: @@ -2121,13 +2131,13 @@ rl_complete_internal (int what_to_do) { if (what_to_do == '!') { - display_matches (matches); + do_display = 1; break; } else if (what_to_do == '@') { if (nontrivial_lcd == 0) - display_matches (matches); + do_display = 1; break; } else if (rl_editing_mode != vi_mode) @@ -2151,23 +2161,7 @@ rl_complete_internal (int what_to_do) append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); break; } - - if (rl_completion_display_matches_hook == 0) - { - _rl_sigcleanup = _rl_complete_sigcleanup; - _rl_sigcleanarg = matches; - _rl_complete_display_matches_interrupt = 0; - } - display_matches (matches); - if (_rl_complete_display_matches_interrupt) - { - matches = 0; /* already freed by rl_complete_sigcleanup */ - _rl_complete_display_matches_interrupt = 0; - if (rl_signal_event_hook) - (*rl_signal_event_hook) (); /* XXX */ - } - _rl_sigcleanup = 0; - _rl_sigcleanarg = 0; + do_display = 1; break; default: @@ -2180,6 +2174,29 @@ rl_complete_internal (int what_to_do) return 1; } + if (do_display) + { + if (rl_completion_display_matches_hook == 0) + { + _rl_sigcleanup = _rl_complete_sigcleanup; + cleanarg.matches = matches; + cleanarg.saved_line = saved_line_buffer; + _rl_sigcleanarg = &cleanarg; + _rl_complete_display_matches_interrupt = 0; + } + display_matches (matches); + if (_rl_complete_display_matches_interrupt) + { + matches = 0; /* already freed by rl_complete_sigcleanup */ + saved_line_buffer = 0; + _rl_complete_display_matches_interrupt = 0; + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); /* XXX */ + } + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + } + _rl_free_match_list (matches); /* Check to see if the line has changed through all of this manipulation. */ @@ -2864,7 +2881,7 @@ rl_menu_complete (int count, int ignore) if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) { rl_ding (); - FREE (matches); + _rl_free_match_list (matches); matches = (char **)0; full_completion = 1; return (0);
From 8eef36cd74caae425e536ead84b1a8cb1cad44b7 Mon Sep 17 00:00:00 2001 From: Grisha Levit <grishale...@gmail.com> Date: Sat, 3 Jun 2023 23:31:16 -0400 Subject: [PATCH] completion display interrupt leak handling When readline's display of completion matches is interrupted, the match list is only freed for the '?' completion type but not for '!' and '@' types. I couldn't find a specific reason for that to be the case so hope this patch is an OK fix. Also add freeing saved_line_buffer from rl_complete_internal to the signal handler and freeing of matches in one spot where menu completion doesn't do it. * lib/readline/complete.c - complete_sigcleanarg_t: new struct to hold _rl_complete_sigcleanup argument - _rl_complete_sigcleanup: use new struct instead of *matches pointer, now also handles freeing saved_line - rl_complete_internal: set up _rl_sigcleanup whenever display_matches is called so we no longer leak matches and saved_line_buffer when getting a SIGINT - rl_menu_complete: free contents of matches instead of just pointer when completion-query-items exceeded --- lib/readline/complete.c | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/lib/readline/complete.c b/lib/readline/complete.c index bf7cc856..f8f08aa6 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -494,12 +494,19 @@ _rl_reset_completion_state (void) rl_completion_quote_character = 0; } +typedef struct { + char **matches; + char *saved_line; +} complete_sigcleanarg_t; + static void _rl_complete_sigcleanup (int sig, void *ptr) { + complete_sigcleanarg_t *arg = ptr; if (sig == SIGINT) /* XXX - for now */ { - _rl_free_match_list ((char **)ptr); + _rl_free_match_list (arg->matches); + FREE (arg->saved_line); _rl_complete_display_matches_interrupt = 1; } } @@ -2008,7 +2015,8 @@ rl_complete_internal (int what_to_do) int start, end, delimiter, found_quote, i, nontrivial_lcd; char *text, *saved_line_buffer; char quote_char; - int tlen, mlen, saved_last_completion_failed; + int tlen, mlen, saved_last_completion_failed, do_display; + complete_sigcleanarg_t cleanarg; RL_SETSTATE(RL_STATE_COMPLETING); @@ -2088,6 +2096,8 @@ rl_complete_internal (int what_to_do) if (matches && matches[0] && *matches[0]) last_completion_failed = 0; + do_display = 0; + switch (what_to_do) { case TAB: @@ -2121,13 +2131,13 @@ rl_complete_internal (int what_to_do) { if (what_to_do == '!') { - display_matches (matches); + do_display = 1; break; } else if (what_to_do == '@') { if (nontrivial_lcd == 0) - display_matches (matches); + do_display = 1; break; } else if (rl_editing_mode != vi_mode) @@ -2151,23 +2161,7 @@ rl_complete_internal (int what_to_do) append_to_match (matches[0], delimiter, quote_char, nontrivial_lcd); break; } - - if (rl_completion_display_matches_hook == 0) - { - _rl_sigcleanup = _rl_complete_sigcleanup; - _rl_sigcleanarg = matches; - _rl_complete_display_matches_interrupt = 0; - } - display_matches (matches); - if (_rl_complete_display_matches_interrupt) - { - matches = 0; /* already freed by rl_complete_sigcleanup */ - _rl_complete_display_matches_interrupt = 0; - if (rl_signal_event_hook) - (*rl_signal_event_hook) (); /* XXX */ - } - _rl_sigcleanup = 0; - _rl_sigcleanarg = 0; + do_display = 1; break; default: @@ -2180,6 +2174,29 @@ rl_complete_internal (int what_to_do) return 1; } + if (do_display) + { + if (rl_completion_display_matches_hook == 0) + { + _rl_sigcleanup = _rl_complete_sigcleanup; + cleanarg.matches = matches; + cleanarg.saved_line = saved_line_buffer; + _rl_sigcleanarg = &cleanarg; + _rl_complete_display_matches_interrupt = 0; + } + display_matches (matches); + if (_rl_complete_display_matches_interrupt) + { + matches = 0; /* already freed by rl_complete_sigcleanup */ + saved_line_buffer = 0; + _rl_complete_display_matches_interrupt = 0; + if (rl_signal_event_hook) + (*rl_signal_event_hook) (); /* XXX */ + } + _rl_sigcleanup = 0; + _rl_sigcleanarg = 0; + } + _rl_free_match_list (matches); /* Check to see if the line has changed through all of this manipulation. */ @@ -2864,7 +2881,7 @@ rl_menu_complete (int count, int ignore) if (rl_completion_query_items > 0 && match_list_size >= rl_completion_query_items) { rl_ding (); - FREE (matches); + _rl_free_match_list (matches); matches = (char **)0; full_completion = 1; return (0); -- 2.41.0