From b6afced66584328fe207ebdad2aa0f006b1d5b75 Mon Sep 17 00:00:00 2001 From: Roel van de Kraats Date: Tue, 4 Jul 2023 08:38:57 +0200 Subject: [PATCH 1/2] Handle history_expand return value 0 GNU readline doesn't seem to ever return 0, but (e.g.) BSD editline (libedit) can return 0 (indicating that no expansion took place), while leaving the output pointer NULL. Handle return value 0 for compatibility with both libraries. --- tclreadline.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tclreadline.c b/tclreadline.c index 32be8b8..65907b6 100644 --- a/tclreadline.c +++ b/tclreadline.c @@ -524,29 +524,32 @@ TclReadlineLineCompleteHandler(char* ptr) */ char* expansion = (char*) NULL; + char* expand_output = (char*) NULL; if (tclrl_use_history_expansion) { - int status = history_expand(ptr, &expansion); + int status = history_expand(ptr, &expand_output); if (status >= 2) { /* TODO: make this a valid tcl output */ - printf("%s\n", expansion); + printf("%s\n", expand_output); FREE(ptr); - FREE(expansion); + FREE(expand_output); return; } else if (status <= -1) { Tcl_AppendResult - (tclrl_interp, "error in history expansion: ", expansion, "\n", (char*) NULL); + (tclrl_interp, "error in history expansion: ", expand_output, "\n", (char*) NULL); TclReadlineTerminate(TCL_ERROR); FREE(ptr); - FREE(expansion); + FREE(expand_output); return; - } else { - Tcl_AppendResult(tclrl_interp, expansion, (char*) NULL); + } else if (status == 0) { + expansion = ptr; + } else { /* status == 1 */ + expansion = expand_output; } } else { - Tcl_AppendResult(tclrl_interp, ptr, (char*) NULL); expansion = ptr; } + Tcl_AppendResult(tclrl_interp, expansion, (char*) NULL); #ifdef EXECUTING_MACRO_NAME /** @@ -576,9 +579,7 @@ TclReadlineLineCompleteHandler(char* ptr) */ TclReadlineTerminate(LINE_COMPLETE); FREE(ptr); - if (tclrl_use_history_expansion) { - FREE(expansion); - } + FREE(expand_output); } } From e6c2b9624d25e803f44c14b085a57d3e6bd62fa4 Mon Sep 17 00:00:00 2001 From: Roel van de Kraats Date: Tue, 4 Jul 2023 08:53:05 +0200 Subject: [PATCH 2/2] Only use rl_extend_line_buffer if available The BSD editline library (libedit, version 3.1-20221030) doesn't implement the rl_extend_line_buffer function. Alternative approaches to replace the line buffer using the readline interface don't give the desired behavior. So simply don't use history expansion in the completion function if the rl_extend_line_buffer function is not available. --- config.h.in | 3 +++ configure | 31 +++++++++++++++++++++++++++++++ configure.ac | 12 ++++++++++++ tclreadline.c | 7 +++++++ 4 files changed, 53 insertions(+) diff --git a/config.h.in b/config.h.in index c32e14f..4c38f3f 100644 --- a/config.h.in +++ b/config.h.in @@ -9,6 +9,9 @@ /* Define the name of the executing macro variable in libreadline. */ #undef EXECUTING_MACRO_NAME +/* Define if rl_extend_line_buffer is resolved in libreadline. */ +#undef EXTEND_LINE_BUFFER + /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H diff --git a/configure b/configure index bca26f7..8893709 100755 --- a/configure +++ b/configure @@ -12689,6 +12689,37 @@ $as_echo "yes" >&6; }; $as_echo "#define CLEANUP_AFER_SIGNAL 1" >>confdefs.h +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +# check for readline's rl_extend_line_buffer + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rl_extend_line_buffer() in -lreadline" >&5 +$as_echo_n "checking for rl_extend_line_buffer() in -lreadline... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + extern void rl_extend_line_buffer(int len); + rl_extend_line_buffer(1); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; + +$as_echo "#define EXTEND_LINE_BUFFER 1" >>confdefs.h + else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } diff --git a/configure.ac b/configure.ac index 1dc1bc6..db76bae 100644 --- a/configure.ac +++ b/configure.ac @@ -241,6 +241,18 @@ AC_TRY_LINK(,[ [ Define if rl_cleanup_after_signal is resolved in libreadline. ]), AC_MSG_RESULT(no)) +# check for readline's rl_extend_line_buffer + +AC_MSG_CHECKING([for rl_extend_line_buffer() in -lreadline]) +AC_TRY_LINK(,[ + extern void rl_extend_line_buffer(int len); + rl_extend_line_buffer(1); +], + AC_MSG_RESULT(yes); + AC_DEFINE(EXTEND_LINE_BUFFER, 1, + [ Define if rl_extend_line_buffer is resolved in libreadline. ]), + AC_MSG_RESULT(no)) + AC_MSG_CHECKING([for the readline version number]) AC_TRY_RUN([ diff --git a/tclreadline.c b/tclreadline.c index 65907b6..3ce28e9 100644 --- a/tclreadline.c +++ b/tclreadline.c @@ -26,11 +26,13 @@ #endif +#ifdef EXTEND_LINE_BUFFER /* * this prototype may be missing * in readline.h */ void rl_extend_line_buffer(int len); +#endif #ifdef EXECUTING_MACRO_HACK /** @@ -700,6 +702,10 @@ TclReadlineCompletion(char* text, int start, int end) int status; rl_completion_append_character = ' '; /* reset, just in case ... */ + /* Only enable history expansion like '!!' if the rl_extend_line_buffer + * function is available; e.g. libedit doesn't provide it, and alternative + * approaches to replace the line buffer don't give the desired behavior */ +#ifdef EXTEND_LINE_BUFFER if (tclrl_use_history_expansion && text && ('!' == text[0] || (start && rl_line_buffer[start - 1] == '!' /* for '$' */))) { char* expansion = (char*) NULL; @@ -721,6 +727,7 @@ TclReadlineCompletion(char* text, int start, int end) } FREE(expansion); } +#endif if (tclrl_custom_completer) { char start_s[BUFSIZ], end_s[BUFSIZ];