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

From: Michael Albinus <[email-masked]>
Date: Mon, 27 Jul 2020 20:01:57 +0200

内藤 祐一郎 <[email-masked]> writes:

> Hi.

Hi,

> I could find a another way to solve this problem that avoids changing
> ~/.editrc and it's more simple.
> See my patch attached on this mail.
>
> This problem happens on the latest libedit and FreeBSD sh uses Emacs
> edit mode by default.
>
> If libedit runs on edit mode, libedit rewrites line by each control character.
> On '^H', libedit sends line feed and write characters on the line that
> is held in the internal screen buffer of libedit.
>
> If edit mode turns off, line edit works on terminal that is as same as
> previous behavior.
>
> Shell command `set +E` escapes from edit mode in spite of libedit has
> two edit mode (Emacs and Vi). `set +E` disables both of them.
>
> Although libedit has been developed and used by NetBSD, NetBSD doesn’t
> have this problem.
> Because NetBSD sh doesn't use edit mode by default.

Thanks for the report. However, your patch is not applicable in general,
because the "+E" option is not POSIX conform, and it isn't supported by
all shells. I thought already about this when I was fixing bug#39399.

But wait - "set +o emacs +o vi" might give the same effect, and it is
much more supported by shells. And reading the sources, Tramp calls it
already (might have been added by Kai, many years ago). Maybe it is
sufficient to move it earlier in the initialization machinery?

Appended is a patch for Tramp 2.4.4. You might install it from GNU
ELPA. Please check, whether this works for you.

> Yuichiro NAITO
> [email-masked]

Best regards, Michael.

*** /tmp/ediffbojDZA	2020-07-27 19:49:26.025530831 +0200
--- /home/albinus/src/tramp-2-4-stable/lisp/tramp-sh.el	2020-07-27 19:38:04.853551455 +0200
***************
*** 4104,4183 ****

  (defun tramp-open-shell (vec shell)
    "Open shell SHELL."
    (with-tramp-progress-reporter
        vec 5 (format-message "Opening remote shell `%s'" shell)
!     ;; Find arguments for this shell.
!     (let ((extra-args (tramp-get-sh-extra-args shell))
! 	  (p (tramp-get-connection-process vec)))
!       ;; 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 during shell invocation, see
!       ;; `tramp-sh-extra-args' (Bug#39399).
!       ;; The shell prompt might not be set yet, so we must read any
!       ;; prompt via `tramp-barf-if-no-shell-prompt'.
!       (unless extra-args
! 	(tramp-send-command vec "rm -f ~/.editrc.tramp" t t)
! 	(tramp-barf-if-no-shell-prompt p 10 "Couldn't find remote shell prompt")
! 	(tramp-send-command
! 	 vec "test -e ~/.editrc && mv -f ~/.editrc ~/.editrc.tramp" t t)
! 	(tramp-barf-if-no-shell-prompt p 10 "Couldn't find remote shell prompt")
! 	(tramp-send-command vec "echo 'edit off' >~/.editrc" t t)
! 	(tramp-barf-if-no-shell-prompt
! 	 p 10 "Couldn't find remote shell prompt"))
!       ;; 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
!       ;; will display a dollar sign.  The following command line sets
!       ;; $PS1 to a sane value, and works under Bourne-ish shells as
!       ;; well as csh-like shells.  We also unset the variable $ENV
!       ;; because that is read by some sh implementations (eg, bash
!       ;; when called as sh) on startup; this way, we avoid the startup
!       ;; file clobbering $PS1.  $PROMPT_COMMAND is another way to set
!       ;; the prompt in /bin/bash, it must be discarded as well.
!       ;; $HISTFILE is set according to `tramp-histfile-override'.
!       ;; $TERM and $INSIDE_EMACS set here to ensure they have the
!       ;; correct values when the shell starts, not just processes
!       ;; run within the shell.  (Which processes include our
!       ;; initial probes to ensure the remote shell is usable.)
!       (tramp-send-command
!        vec (format
! 	    (eval-when-compile
! 	      (concat
! 	       "exec env TERM='%s' INSIDE_EMACS='%s,tramp:%s' "
! 	       "ENV=%s %s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s"))
!             tramp-terminal-type
!             emacs-version tramp-version  ; INSIDE_EMACS
!             (or (getenv-internal "ENV" tramp-remote-process-environment) "")
! 	    (if (stringp tramp-histfile-override)
! 		(format "HISTFILE=%s"
! 			(tramp-shell-quote-argument tramp-histfile-override))
! 	      (if tramp-histfile-override
! 		  "HISTFILE='' HISTFILESIZE=0 HISTSIZE=0"
! 		""))
! 	    (tramp-shell-quote-argument tramp-end-of-output)
! 	    shell (or extra-args ""))
!        t)
!       ;; Reset ~/.editrc.
!       (unless extra-args
! 	(tramp-send-command vec "rm -f ~/.editrc" t)
! 	(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))
! 	(tramp-barf-unless-okay
! 	 vec
! 	 (format
! 	  "(cd %s)"
! 	  (tramp-shell-quote-argument
! 	   (file-name-directory tramp-histfile-override)))
! 	 "`tramp-histfile-override' uses invalid file `%s'"
! 	 tramp-histfile-override)))

      (tramp-set-connection-property
       (tramp-get-connection-process vec) "remote-shell" shell)))
--- 4104,4157 ----

  (defun tramp-open-shell (vec shell)
    "Open shell SHELL."
+   ;; Find arguments for this shell.
    (with-tramp-progress-reporter
        vec 5 (format-message "Opening remote shell `%s'" shell)
!     ;; 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 will display
!     ;; a dollar sign.  The following command line sets $PS1 to a sane
!     ;; value, and works under Bourne-ish shells as well as csh-like
!     ;; shells.  We also unset the variable $ENV because that is read
!     ;; by some sh implementations (eg, bash when called as sh) on
!     ;; startup; this way, we avoid the startup file clobbering $PS1.
!     ;; $PROMPT_COMMAND is another way to set the prompt in /bin/bash,
!     ;; it must be discarded as well.  $HISTFILE is set according to
!     ;; `tramp-histfile-override'.  $TERM and $INSIDE_EMACS set here to
!     ;; ensure they have the correct values when the shell starts, not
!     ;; just processes run within the shell.  (Which processes include
!     ;; our initial probes to ensure the remote shell is usable.)
!     (tramp-send-command
!      vec (format
! 	  (eval-when-compile
! 	    (concat
! 	     "exec env TERM='%s' INSIDE_EMACS='%s,tramp:%s' "
! 	     "ENV=%s %s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s"))
!           tramp-terminal-type
!           emacs-version tramp-version  ; INSIDE_EMACS
!           (or (getenv-internal "ENV" tramp-remote-process-environment) "")
! 	  (if (stringp tramp-histfile-override)
! 	      (format "HISTFILE=%s"
! 		      (tramp-shell-quote-argument tramp-histfile-override))
! 	    (if tramp-histfile-override
! 		"HISTFILE='' HISTFILESIZE=0 HISTSIZE=0"
! 	      ""))
! 	  (tramp-shell-quote-argument tramp-end-of-output)
! 	  shell (or (tramp-get-sh-extra-args shell) ""))
!      t)
!     ;; Check proper HISTFILE setting.  We give up when not working.
!     (when (and (stringp tramp-histfile-override)
! 	       (file-name-directory tramp-histfile-override))
!       (tramp-barf-unless-okay
!        vec
!        (format
! 	"(cd %s)"
! 	(tramp-shell-quote-argument
! 	 (file-name-directory tramp-histfile-override)))
!        "`tramp-histfile-override' uses invalid file `%s'"
!        tramp-histfile-override))

      (tramp-set-connection-property
       (tramp-get-connection-process vec) "remote-shell" shell)))
***************
*** 4248,4256 ****
    (let ((tramp-end-of-output tramp-initial-end-of-output)
  	(case-fold-search t))
      (tramp-open-shell vec (tramp-get-method-parameter vec 'tramp-remote-shell))

      ;; Disable echo expansion.
-     (tramp-message vec 5 "Setting up remote shell environment")
      (tramp-send-command
       vec "stty -inlcr -onlcr -echo kill '^U' erase '^H'" t)
      ;; Check whether the echo has really been disabled.  Some
--- 4222,4233 ----
    (let ((tramp-end-of-output tramp-initial-end-of-output)
  	(case-fold-search t))
      (tramp-open-shell vec (tramp-get-method-parameter vec 'tramp-remote-shell))
+     (tramp-message vec 5 "Setting up remote shell environment")
+
+     ;; Disable line editing.
+     (tramp-send-command vec "set +o vi +o emacs" t)

      ;; Disable echo expansion.
      (tramp-send-command
       vec "stty -inlcr -onlcr -echo kill '^U' erase '^H'" t)
      ;; Check whether the echo has really been disabled.  Some
***************
*** 4320,4327 ****
  	(tramp-message
  	 vec 5 "Setting coding system to `%s' and `%s'" cs-decode cs-encode)))

-     (tramp-send-command vec "set +o vi +o emacs" t)
-
      ;; Check whether the remote host suffers from buggy
      ;; `send-process-string'.  This is known for FreeBSD (see comment
      ;; in `send_process', file process.c).  I've tested sending 624
--- 4297,4302 ----