Subject: bug#39399: tramp depends on unstable details of shell command line processing

From: Michael Albinus <[email-masked]>
Date: Tue, 04 Feb 2020 09:34:26 +0100

Michael Albinus <[email-masked]> writes:

Hi John,

>> The terminal output from a FreeBSD 13.0 development branch system is
>>
>> _echo
>> #$ _ech ^H
>> #$ _ec ^H
>> #$ _e ^H
>> #$ _ ^H
>> #$  ^Hstty icanon erase ^H cols 32767_echo
>> #$ stty icanon erase ^H cols 32767_ech ^H
>> #$ stty icanon erase ^H cols 32767_ec ^H
>> #$ stty icanon erase ^H cols 32767_e ^H
>> #$ stty icanon erase ^H cols 32767_ ^H
>> #$ stty icanon erase ^H cols 32767 ^H
>> #$
>>
>> with carriage returns between lines.  This does not make sense to
>> emacs, which hangs waiting for something it recognizes.  I can recover
>> by hitting control-G which aborts the tramp connection.
>>
>> This issue is also reported to FreeBSD at
>> https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=243807.
>
> IIUC the discussion in that bug, it is due to the changed libedit of
> FreeBSD 13. Hmm, don't know this beast. But wouldn't it help, if Tramps
> sends initially "stty cooked"?

I've digged further. Likely, the problem can be avoided if we disable
command line editing of the shell. Tramp does it already if the remote
shell is zsh; I've added a similar parameter to the bash invocation. For
other shells, like ksh, I haven't found a simple parameter.

For that class of shells, I've added code which installs a temporary
~/.editrc file, containing the line 'edit off'.

Could you please migrate to Tramp 2.4.3.1 from GNU ELPA? On top of this,
install the appended patch.

And pls report, whether it works for you.

Best regards, Michael.

diff --git a/lisp/tramp-sh.el b/lisp/tramp-sh.el
index 661e344d..49519926 100644
--- a/lisp/tramp-sh.el
+++ b/lisp/tramp-sh.el
@@ -537,12 +537,13 @@ based on the Tramp and Emacs versions, and should not be set here."

 ;;;###tramp-autoload
 (defcustom tramp-sh-extra-args
-  '(("/bash\\'" . "-norc -noprofile")
+  '(("/bash\\'" . "-noediting -norc -noprofile")
     ("/zsh\\'" . "-f +Z -V"))
   "Alist specifying extra arguments to pass to the remote shell.
 Entries are (REGEXP . ARGS) where REGEXP is a regular expression
 matching the shell file name and ARGS is a string specifying the
-arguments.
+arguments.  These arguments shall disable line editing, see
+`tramp-open-shell'.

 This variable is only used when Tramp needs to start up another shell
 for tilde expansion.  The extra arguments should typically prevent the
@@ -4107,6 +4108,24 @@ file exists and nonzero exit status otherwise."
       vec 5 (format-message "Opening remote shell `%s'" shell)
     ;; Find arguments for this shell.
     (let ((extra-args (tramp-get-sh-extra-args shell)))
+      ;; The readline library can disturb Tramp.  For example, the
+      ;; very recent version of libedit, the *BSD implementation of
+      ;; readline, confuses Tramp.  So we disable line editing.  Since
+      ;; $EDITRC is not supported on all target systems, we must move
+      ;; ~/.editrc temporarily somewhere else.  For bash and zsh we
+      ;; have disabled this already, see `tramp-sh-extra-args'.  Bug#39399.
+      (unless extra-args
+	(tramp-send-command
+	 vec (eval-when-compile
+	       (concat
+		"(rm -f ~/.editrc.tramp; "
+		"mv -f ~/.editrc ~/.editrc.tramp; "
+		"echo 'edit off' >~/.editrc)"))
+	 t t)
+	(while
+	    (tramp-accept-process-output (tramp-get-connection-process vec) 0)))
+      ;; It is useful to set the prompt in the following command
+      ;; because some people have a setting for $PS1 which /bin/sh
       ;; doesn't know about and thus /bin/sh will display a strange
       ;; prompt.  For example, if $PS1 has "${CWD}" in the value, then
       ;; ksh will display the current working directory but /bin/sh
@@ -4140,6 +4159,9 @@ file exists and nonzero exit status otherwise."
 	    (tramp-shell-quote-argument tramp-end-of-output)
 	    shell (or extra-args ""))
        t)
+      (unless extra-args
+	(tramp-send-command
+	 vec "test -e ~/.editrc.tramp && mv -f ~/.editrc.tramp ~/.editrc" t))
       ;; Check proper HISTFILE setting.  We give up when not working.
       (when (and (stringp tramp-histfile-override)
 		 (file-name-directory tramp-histfile-override))