summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Albinus <michael.albinus@gmx.de>2026-04-29 08:51:08 +0200
committerMichael Albinus <michael.albinus@gmx.de>2026-04-29 08:51:08 +0200
commit16c625636811ab7b78ea238d063f0c7940196ddd (patch)
tree8084eb9daf22b5173c87dd75b572f27d17c5bb0b
parentfe4488160a4c40c0540b31997b95764571a9202f (diff)
Tramp ELPA version 2.8.1.4 releasedexternals/tramp
-rw-r--r--README6
-rw-r--r--test/tramp-tests.el149
-rw-r--r--texi/tramp.texi135
-rw-r--r--texi/trampver.texi2
-rw-r--r--tramp-cache.el14
-rw-r--r--tramp-container.el9
-rw-r--r--tramp-sh.el42
-rw-r--r--tramp-sshfs.el2
-rw-r--r--tramp.el214
-rw-r--r--trampver.el6
10 files changed, 433 insertions, 146 deletions
diff --git a/README b/README
index 31a29b3..b92dd8a 100644
--- a/README
+++ b/README
@@ -32,11 +32,11 @@ Emacs 28 or older
• Remove all byte-compiled Tramp files
- $ rm -f ~/.emacs.d/elpa/tramp-2.8.1.3/tramp*.elc
+ $ rm -f ~/.emacs.d/elpa/tramp-2.8.1.4/tramp*.elc
• Start Emacs with Tramp's source files
- $ emacs -L ~/.emacs.d/elpa/tramp-2.8.1.3 -l tramp
+ $ emacs -L ~/.emacs.d/elpa/tramp-2.8.1.4 -l tramp
This should not give you the error.
@@ -50,7 +50,7 @@ Mitigation of a bug in Emacs 29.1
---------------------------------
Due to a bug in Emacs 29.1, you must apply the following change prior
-installation or upgrading Tramp 2.8.1.3 from GNU ELPA:
+installation or upgrading Tramp 2.8.1.4 from GNU ELPA:
(when (string-equal emacs-version "29.1")
(with-current-buffer
diff --git a/test/tramp-tests.el b/test/tramp-tests.el
index 3972e5f..a19a17d 100644
--- a/test/tramp-tests.el
+++ b/test/tramp-tests.el
@@ -2287,6 +2287,8 @@ being the result.")
(ert-deftest tramp-test03-file-error ()
"Check that Tramp signals an error in case of connection problems."
+ (skip-unless (tramp-file-name-p tramp-test-vec))
+
;; Connect to a non-existing host.
(let ((vec (copy-tramp-file-name tramp-test-vec))
;; Don't poison it.
@@ -4473,6 +4475,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
;; Check, that files in symlinked directories still work.
(make-symbolic-link tmp-name4 tmp-name6)
(should (file-symlink-p tmp-name6))
+ (should (file-directory-p tmp-name6))
(should-not (file-regular-p tmp-name6))
(write-region "foo" nil (expand-file-name "foo" tmp-name6))
(delete-file (expand-file-name "foo" tmp-name6))
@@ -5393,7 +5396,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'."
;; boundaries are always incorrect before that.
(skip-unless (tramp--test-emacs31-p))
- (let ((remote (file-remote-p ert-remote-temporary-file-directory)))
+ (when-let* ((remote (file-remote-p ert-remote-temporary-file-directory)))
(dolist
(file `(,remote ,(concat remote "/~/")
,(concat remote "/usr//usr/") ,(concat remote remote "//usr/")))
@@ -8858,10 +8861,19 @@ process sentinels. They shall not disturb each other."
"Test operation."
"Test operation")
-(defun tramp--handler-for-test-operation (&optional _file)
+(defun tramp--handle-test-operation (&optional _file)
"Test operation handler."
"Test operation handler")
+(defun tramp--handle-process-id (process)
+ "Handler for `process-id'."
+ ;; Return something else.
+ (1+ (tramp-run-real-handler #'process-id (list process))))
+
+(defun tramp--test-operation-file-name-for-operation (_operation &optional _file)
+ "Helper function for `tramp--test-operation' handler."
+ default-directory)
+
(ert-deftest tramp-test49-external-backend-function ()
"Check that Tramp handles external functions for a given backend."
:tags '(:expensive-test)
@@ -8878,78 +8890,167 @@ process sentinels. They shall not disturb each other."
;; There is no backend specific code.
(should-not
(string-equal (tramp--test-operation ert-remote-temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
ert-remote-temporary-file-directory)))
(should-not
(string-equal (tramp--test-operation temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
temporary-file-directory)))
(let ((default-directory ert-remote-temporary-file-directory))
(should-not
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation))))
+ (tramp--handle-test-operation))))
(let ((default-directory temporary-file-directory))
(should-not
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation))))
+ (tramp--handle-test-operation))))
(should-error
(tramp-add-external-operation
- #'tramp--test-operation
- #'tramp--handler-for-test-operation 'foo)
+ #'tramp--test-operation #'tramp--handle-test-operation 'foo)
:type 'file-missing)
+ (should-error
+ (tramp-add-external-operation
+ #'tramp--test-operation #'tramp--handle-test-operation backend 'foo)
+ :type 'remote-file-error)
+ ;; This doesn't hurt.
(tramp-add-external-operation
- #'tramp--test-operation
- #'tramp--handler-for-test-operation backend)
+ #'tramp--test-operation #'tramp--handle-test-operation backend 'file)
+
;; The backend specific function is called.
(should
(string-equal (tramp--test-operation ert-remote-temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
ert-remote-temporary-file-directory)))
(should-not
(string-equal (tramp--test-operation temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
temporary-file-directory)))
(let ((default-directory ert-remote-temporary-file-directory))
(should
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation)))
+ (tramp--handle-test-operation)))
(should
(string-equal (tramp--test-operation "foo")
- (tramp--handler-for-test-operation "foo"))))
+ (tramp--handle-test-operation "foo"))))
(let ((default-directory temporary-file-directory))
(should-not
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation)))
+ (tramp--handle-test-operation)))
(should-not
(string-equal (tramp--test-operation "foo")
- (tramp--handler-for-test-operation "foo"))))
+ (tramp--handle-test-operation "foo"))))
- (tramp-remove-external-operation
- #'tramp--test-operation backend)
+ (tramp-remove-external-operation #'tramp--test-operation backend)
;; There is no backend specific code.
(should-not
(string-equal (tramp--test-operation ert-remote-temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
ert-remote-temporary-file-directory)))
(should-not
(string-equal (tramp--test-operation temporary-file-directory)
- (tramp--handler-for-test-operation
+ (tramp--handle-test-operation
temporary-file-directory)))
(let ((default-directory ert-remote-temporary-file-directory))
(should-not
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation)))
+ (tramp--handle-test-operation)))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+ (let ((default-directory temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation)
+ (tramp--handle-test-operation)))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+
+ ;; Test `default-directory' arg type.
+ (tramp-add-external-operation
+ #'tramp--test-operation #'tramp--handle-test-operation
+ backend 'default-directory)
+
+ ;; The backend specific function is called.
+ (let ((default-directory ert-remote-temporary-file-directory))
+ (should
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+ (let ((default-directory temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+
+ (tramp-remove-external-operation #'tramp--test-operation backend)
+ ;; There is no backend specific code.
+ (let ((default-directory ert-remote-temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation)
+ (tramp--handle-test-operation)))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+ (let ((default-directory temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation)
+ (tramp--handle-test-operation)))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+
+ ;; Test `process' arg type.
+ (when (and (tramp--test-supports-processes-p) (not (tramp--test-smb-p)))
+ (let ((default-directory ert-remote-temporary-file-directory)
+ proc command id)
+ (unwind-protect
+ (with-temp-buffer
+ (setq command '("cat")
+ proc
+ (make-process
+ :name "test" :buffer (current-buffer) :command command
+ :file-handler t))
+ (should (processp proc))
+ (should (eq (process-status proc) 'run))
+ (should (natnump (setq id (process-id proc))))
+ (tramp-add-external-operation
+ #'process-id #'tramp--handle-process-id backend 'process)
+ (should (= (process-id proc) (1+ id))))
+
+ ;; Cleanup.
+ (tramp-remove-external-operation #'process-id backend)
+ (ignore-errors (delete-process proc)))))
+
+ ;; Test function arg type.
+ (tramp-add-external-operation
+ #'tramp--test-operation #'tramp--handle-test-operation
+ backend #'tramp--test-operation-file-name-for-operation)
+
+ ;; The backend specific function is called.
+ (let ((default-directory ert-remote-temporary-file-directory))
+ (should
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+ (let ((default-directory temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation "foo")
+ (tramp--handle-test-operation "foo"))))
+
+ (tramp-remove-external-operation #'tramp--test-operation backend)
+ ;; There is no backend specific code.
+ (let ((default-directory ert-remote-temporary-file-directory))
+ (should-not
+ (string-equal (tramp--test-operation)
+ (tramp--handle-test-operation)))
(should-not
(string-equal (tramp--test-operation "foo")
- (tramp--handler-for-test-operation "foo"))))
+ (tramp--handle-test-operation "foo"))))
(let ((default-directory temporary-file-directory))
(should-not
(string-equal (tramp--test-operation)
- (tramp--handler-for-test-operation)))
+ (tramp--handle-test-operation)))
(should-not
(string-equal (tramp--test-operation "foo")
- (tramp--handler-for-test-operation "foo"))))))
+ (tramp--handle-test-operation "foo"))))))
;; This test is inspired by Bug#29163.
(ert-deftest tramp-test50-auto-load ()
diff --git a/texi/tramp.texi b/texi/tramp.texi
index 3af4dfb..79271d2 100644
--- a/texi/tramp.texi
+++ b/texi/tramp.texi
@@ -2042,6 +2042,12 @@ Access of a hadoop/hdfs file system. A file is accessed via
the user that you want to use, and @samp{node} is the name of the
hadoop server.
+@c @cindex method @option{rpc}
+@c @cindex @option{rpc} method
+@c @item tramp-rpc
+@c This is a @value{tramp} backend of its own. It uses a remote server
+@c process to serve the requests, which gains better performance.
+
@cindex method @option{vagrant}
@cindex @option{vagrant} method
@item vagrant-tramp
@@ -5556,6 +5562,8 @@ operations. For example, the GNU ELPA package @file{tramp-hlo}
implements specialized versions of @code{dir-locals--all-files},
@code{locate-dominating-file} and @code{dir-locals-find-file} for
@value{tramp}'s @code{tramp-sh} backend (@pxref{New operations}).
+@c The NonGNU ELPA package @file{tramp-rpc} implements an own
+@c @value{tramp} backend (@pxref{New operations}).
@end itemize
@@ -6728,6 +6736,45 @@ See the docstring of variable @code{tramp-methods} for possible
@code{foo-tramp-executable} in this example would be a Lisp constant,
which is the program name of @command{foo}.
+If a parameter doesn't have a static value but must be computed at
+runtime, a format specifier can be used, like @t{"%h"} in the example
+above. See the docstring of @code{tramp-methods}, which patterns are
+expanded in which parameter. Furthermore, other format specifiers can
+be added via the variable @code{tramp-extra-expand-args}.
+
+The following parameters expand format specifiers for the
+@code{tramp-sh} backend: @code{tramp-copy-args},
+@code{tramp-copy-env}, @code{tramp-copy-file-name},
+@code{tramp-login-args}, @code{tramp-login-program},
+@code{tramp-remote-copy-args}.
+
+The example above could use
+
+@lisp
+(tramp-login-program "%b")
+@end lisp
+
+And you could set @code{tramp-extra-expand-args} as connection-local value:
+
+@lisp
+@group
+(defun foo-tramp-get-login-program (vec)
+ "Return connection-local value of `tramp-login-program'."
+ @dots{})
+@end group
+
+@group
+(connection-local-set-profile-variables
+ 'foo-tramp-connection-local-default-profile
+ '((tramp-extra-expand-args
+ ?b (foo-tramp-get-login-program (car tramp-current-connection)))))
+
+(connection-local-set-profiles
+ '(:application tramp :protocol "foo")
+ foo-tramp-connection-local-default-profile)
+@end group
+@end lisp
+
Another initialization could tell @value{tramp} which are the default
user and host name for method @option{foo}. This is done by calling
@code{tramp-set-completion-function}:
@@ -6828,15 +6875,12 @@ For example, it could implement this by using an own shell script
which collects the information on the remote host for this very
special purpose with one round-trip per-call.
-@defun tramp-add-external-operation operation function backend
+@defun tramp-add-external-operation operation function backend &optional arg-type
This adds an implementation of @var{operation} to @value{tramp}'s
backend @var{backend}. @var{function} is the new implementation.
Both @var{operation} and @var{function} shall be function symbols.
-They must have the same argument list. The first argument is used to
-determine, whether @value{tramp} is invoked (check for remote file
-name syntax). It must be a string or nil, in the latter case
-@code{default-directory} is used for the check.
+They must have the same argument list.
@var{backend}, also a symbol, is the feature name of a @value{tramp}
backend (except @code{tramp-ftp}). The new implementation will be
@@ -6844,18 +6888,18 @@ applied only for this backend. Example:
@lisp
@group
-(defun test-operation (file)
+(defun my-test-operation (file)
(message "Original implementation for %s" file))
@end group
@group
-(defun handle-test-operation (file)
+(defun my-handle-test-operation (file)
(message "Handler implementation for %s" file))
@end group
@group
(tramp-add-external-operation
- #'test-operation #'handle-test-operation 'tramp-sh)
+ #'my-test-operation #'my-handle-test-operation 'tramp-sh)
@end group
@end lisp
@@ -6864,23 +6908,24 @@ Then we have the different use cases:
@lisp
@group
;; Local file name.
-(test-operation "/a/b")
+(my-test-operation "/a/b")
@result{} "Original implementation for /a/b"
@end group
@group
;; Remote file name, handled by `tramp-sh'.
-(test-operation "/ssh::/a/b")
+(my-test-operation "/ssh::/a/b")
@result{} "Handler implementation for /ssh::/a/b"
@end group
@group
;; Remote file name, handled by `tramp-gvfs'.
-(test-operation "/sftp::/a/b")
+(my-test-operation "/sftp::/a/b")
@result{} "Original implementation for /sftp::/a/b"
@end group
@end lisp
+@findex with-parsed-tramp-file-name
@var{function} is implemented like an ordinary @value{tramp} backend
handler, see the examples in @code{tramp-<backend>-handle-*} and
@code{tramp-handle-*}. It can expect, that the first argument (or
@@ -6888,9 +6933,71 @@ handler, see the examples in @code{tramp-<backend>-handle-*} and
syntax. It shall use @value{tramp} internal macros and functions like
@code{with-parsed-tramp-file-name} and the different cache functions.
+@findex tramp-run-real-handler
+If @var{function} must call the original function, this can be done
+via @code{tramp-run-real-handler}. The implementation of the example
+could look like:
+
+@lisp
+@group
+(defun my-handle-test-operation (file)
+ (message "Entry handler implementation for %s" file)
+ (tramp-run-real-handler #'my-test-operation (list file))
+ (message "Exit handler implementation for %s" file))
+@end group
+
+@group
+(my-test-operation "/ssh::/a/b")
+@result{} "Entry handler implementation for /ssh::/a/b
+ Original implementation for /ssh::/a/b
+ Exit handler implementation for /ssh::/a/b"
+@end group
+@end lisp
+
If the same @var{function} shall be used for different @value{tramp}
backends, @code{tramp-add-external-operation} must be called for every
backend, respectively.
+
+The optional argument @var{arg-type} specisfies, which argument of
+@var{operation} shall be used in order to determine, whether the
+handler @var{function} should be called. It can be
+
+@itemize @minus
+@item @code{file}@*
+The first argument of @var{operation} is the remote file name to be
+checked. This is the default, if @var{arg-type} is @code{nil}.
+
+@item @code{default-directory}@*
+@code{default-directory} is the remote file name to be checked.
+
+@item @code{process}@*
+@code{default-directory} of the process buffer of the first argument
+of @var{operation}, a process, is the remote file name to be checked.
+@end itemize
+
+If the first argument of @var{operation} is nil,
+@code{default-directory} is the remote file name to be checked in case
+of @var{arg-type} being @code{file} or @code{process}.
+
+@findex tramp-file-name-for-operation
+If @var{arg-type} is a function symbol, it will be called with the
+same arguments as @code{tramp-file-name-for-operation}. It must
+return a string, which is the remote file name to be checked.
+
+The example above could be changed like this:
+
+@lisp
+@group
+(defun my-file-name-for-test-operation (operation &rest args)
+ (if (stringp (car args)) (car args) default-directory))
+@end group
+
+@group
+(tramp-add-external-operation
+ #'my-test-operation #'my-handle-test-operation 'tramp-sh
+ #'my-file-name-for-test-operation)
+@end group
+@end lisp
@end defun
@defun tramp-remove-external-operation operation backend
@@ -6902,7 +7009,7 @@ they are kept. Example:
@lisp
@group
(tramp-remove-external-operation
- #'test-operation 'tramp-sh)
+ #'my-test-operation 'tramp-sh)
@end group
@end lisp
@end defun
@@ -6918,6 +7025,10 @@ An example implementing this mechanism is the GNU ELPA package
@code{dir-locals-find-file} for @value{tramp}'s @code{tramp-sh}
backend.
+@c Another example is the NonGNU ELPA package @file{tramp-rpc}. It
+@c provides an own @value{tramp} backend, using a server process on the
+@c remote host.
+
@node Traces and Profiles
@chapter How to Customize Traces
diff --git a/texi/trampver.texi b/texi/trampver.texi
index cf9aef6..cf3fd7a 100644
--- a/texi/trampver.texi
+++ b/texi/trampver.texi
@@ -7,7 +7,7 @@
@c In the Tramp GIT, the version number and the bug report address
@c are auto-frobbed from configure.ac.
-@set trampver 2.8.1.3
+@set trampver 2.8.1.4
@set trampurl https://www.gnu.org/software/tramp/
@set tramp-bug-report-address tramp-devel@@gnu.org
@set emacsver 28.1
diff --git a/tramp-cache.el b/tramp-cache.el
index 1fc3fb3..b47a2aa 100644
--- a/tramp-cache.el
+++ b/tramp-cache.el
@@ -161,6 +161,20 @@ If KEY is `tramp-cache-undefined', don't create anything, and return nil."
;; would fail.
(function-put #'tramp-get-hash-table 'tramp-suppress-trace t)
+(defsubst tramp-suppress-remote-file-name-inhibit-cache ()
+ "Weaken `remote-file-name-inhibit-cache'.
+This is meant to be let-bound for code over many cache operations, like
+in large directories."
+ ;; If `remote-file-name-inhibit-cache' is already `nil', keep it.
+ (cond
+ (;; A timestamp. Keep it.
+ (consp remote-file-name-inhibit-cache) remote-file-name-inhibit-cache)
+ (;; A number of seconds. Set a timestamp with the difference.
+ (numberp remote-file-name-inhibit-cache)
+ (time-subtract nil remote-file-name-inhibit-cache))
+ (;; Cache is disabled. Set a timestamp from now on.
+ t (current-time))))
+
;;;###tramp-autoload
(defun tramp-get-file-property (key file property &optional default)
"Get the PROPERTY of FILE from the cache context of KEY.
diff --git a/tramp-container.el b/tramp-container.el
index 91d9b23..fec2e16 100644
--- a/tramp-container.el
+++ b/tramp-container.el
@@ -266,7 +266,7 @@ BODY is the backend specific code."
tramp--last-hop-directory)
tramp-compat-temporary-file-directory))
(program (let ((tramp-verbose 0))
- (tramp-get-method-parameter
+ (tramp-expand-args
(make-tramp-file-name :method ,method)
'tramp-login-program)))
(vec (when (tramp-tramp-file-p default-directory)
@@ -656,10 +656,9 @@ see its function help for a description of the format."
'((tramp-config-check . tramp-kubernetes--current-context-data)
;; This variable will be eval'ed in `tramp-expand-args'.
(tramp-extra-expand-args
- . (?a (tramp-kubernetes--container (car tramp-current-connection))
- ?h (tramp-kubernetes--pod (car tramp-current-connection))
- ?x (tramp-kubernetes--context-namespace
- (car tramp-current-connection)))))
+ ?a (tramp-kubernetes--container (car tramp-current-connection))
+ ?h (tramp-kubernetes--pod (car tramp-current-connection))
+ ?x (tramp-kubernetes--context-namespace (car tramp-current-connection))))
"Default connection-local variables for remote kubernetes connections.")
(connection-local-set-profile-variables
diff --git a/tramp-sh.el b/tramp-sh.el
index 08a44c8..8cade9c 100644
--- a/tramp-sh.el
+++ b/tramp-sh.el
@@ -4688,19 +4688,22 @@ process to set up. VEC specifies the connection."
(tramp-send-command
vec (format "unset %s" (string-join unset " ")) t)))
+ ;; FIXME: This doesn't work with `tramp-test42-utf8' and "/ssh::tmp".
;; Set connection-local variable `command-line-max-length'.
;; `command-line-max-length' exists since Emacs 31.
;; `connection-local-profile-name-for-criteria' exists since Emacs 29.1.
;; We simulate it with `make-symbol'.
- (when (boundp 'command-line-max-length)
- (let* ((criteria (tramp-get-connection-local-criteria vec))
- (profile (if (fboundp 'connection-local-profile-name-for-criteria)
- (connection-local-profile-name-for-criteria criteria)
- (make-symbol "generated-profile-name"))))
- (connection-local-set-profile-variables
- profile
- `((command-line-max-length . ,(tramp-get-remote-pipe-buf vec))))
- (connection-local-set-profiles criteria profile)))))
+ ;; (when (boundp 'command-line-max-length)
+ ;; (let* ((arg-max (tramp-get-remote-arg-max vec))
+ ;; (criteria (tramp-get-connection-local-criteria vec))
+ ;; (profile (if (fboundp 'connection-local-profile-name-for-criteria)
+ ;; (connection-local-profile-name-for-criteria criteria)
+ ;; (make-symbol "generated-profile-name"))))
+ ;; (connection-local-set-profile-variables
+ ;; profile
+ ;; `((command-line-max-length . ,(if arg-max (floor arg-max 4) 4094))))
+ ;; (connection-local-set-profiles criteria profile)))))
+ ))
;; Old text from documentation of tramp-methods:
;; Using a uuencode/uudecode inline method is discouraged, please use one
@@ -5036,8 +5039,7 @@ Goes through the list `tramp-inline-compress-commands'."
;; Use plink options.
((string-match-p
- (rx "plink" (? ".exe") eol)
- (tramp-get-method-parameter vec 'tramp-login-program))
+ (rx "plink" (? ".exe") eol) (tramp-expand-args vec 'tramp-login-program))
(concat
(if (eq tramp-use-connection-share 'suppress)
"-noshare" "-share")
@@ -5397,9 +5399,7 @@ connection if a previous connection has died for some reason."
(tramp-get-method-parameter
hop 'tramp-connection-timeout
tramp-connection-timeout))
- (command
- (tramp-get-method-parameter
- hop 'tramp-login-program))
+ (command (tramp-expand-args hop 'tramp-login-program))
;; We don't create the temporary file. In
;; fact, it is just a prefix for the
;; ControlPath option of ssh; the real
@@ -5805,12 +5805,24 @@ Nonexistent directories are removed from spec."
remote-path :test #'string-equal :from-end t))
;; Remove non-existing directories.
- (let (remote-file-name-inhibit-cache)
+ (let ((remote-file-name-inhibit-cache
+ (tramp-suppress-remote-file-name-inhibit-cache)))
(tramp-bundle-read-file-names vec remote-path)
(cl-remove-if
(lambda (x) (not (tramp-get-file-property vec x "file-directory-p")))
remote-path))))))
+(defun tramp-get-remote-arg-max (vec)
+ "Return ARG_MAX config from the remote side."
+ (with-tramp-connection-property vec "arg-max"
+ (when-let* ((result
+ (tramp-send-command-and-read
+ vec (format "getconf ARG_MAX 2>%s"
+ (tramp-get-remote-null-device vec))
+ 'noerror))
+ ((natnump result)))
+ result)))
+
;; The PIPE_BUF in POSIX [1] can be as low as 512 [2]. Here are the values
;; on various platforms:
;; - 512 on macOS, FreeBSD, NetBSD, OpenBSD, MirBSD, native Windows.
diff --git a/tramp-sshfs.el b/tramp-sshfs.el
index 2cb5b5b..f407315 100644
--- a/tramp-sshfs.el
+++ b/tramp-sshfs.el
@@ -269,7 +269,7 @@ arguments to pass to the OPERATION."
(setq ret
(apply
#'tramp-call-process
- v (tramp-get-method-parameter v 'tramp-login-program)
+ v (tramp-expand-args v 'tramp-login-program)
nil outbuf display
(tramp-expand-args
v 'tramp-login-args nil
diff --git a/tramp.el b/tramp.el
index b54ae51..5705a2b 100644
--- a/tramp.el
+++ b/tramp.el
@@ -7,7 +7,7 @@
;; Maintainer: Michael Albinus <michael.albinus@gmx.de>
;; Keywords: comm, processes
;; Package: tramp
-;; Version: 2.8.1.3
+;; Version: 2.8.1.4
;; Package-Requires: ((emacs "28.1"))
;; Package-Type: multi
;; URL: https://www.gnu.org/software/tramp/
@@ -2424,7 +2424,22 @@ arguments to pass to the OPERATION."
(apply operation args)))
(defvar tramp-file-name-for-operation-external nil
- "List of operations added by external packages.")
+ "Alist of operations added by external packages.
+An entry has the form `(OPERATION . ARG-TYPE)'. ARG-TYPE can be the
+symbol
+
+- `file': the first argument of OERATION is the remote file name to be
+ checked.
+- `default-directory': `default-directory' is the remote file name to be
+ checked.
+- `process': `default-directory' of the process buffer of the first
+ argument of OPERATION is the remote file name to be checked.
+
+If the first argument of OPERATION is nil, `default-directory' is the
+remote file name to be checked in case of `file' and `process'.
+
+If ARG-TYPE is a function symbol, it will be called with the same
+arguments as `tramp-file-name-for-operation'. It must return a string.")
;; We handle here all file primitives. Most of them have the file
;; name as first parameter; nevertheless we check for them explicitly
@@ -2434,9 +2449,7 @@ arguments to pass to the OPERATION."
;; ease the life if `file-name-handler-alist' would support a decision
;; function as well but regexp only.
;; Operations added by external packages are kept in
-;; `tramp-file-name-for-operation-external'. They expect the file
-;; name to be checked as first argument or, if there isn't any
-;; argument, `default-directory'.
+;; `tramp-file-name-for-operation-external'.
(defun tramp-file-name-for-operation (operation &rest args)
"Return file name related to OPERATION file primitive.
ARGS are the arguments OPERATION has been called with.
@@ -2446,30 +2459,32 @@ first argument of `expand-file-name' is absolute and not remote.
Must be handled by the callers."
(cond
;; FILE resp DIRECTORY.
- ((memq operation
- '(access-file byte-compiler-base-file-name delete-directory
- delete-file diff-latest-backup-file directory-file-name
- directory-files directory-files-and-attributes dired-compress-file
- dired-uncache file-acl file-accessible-directory-p file-attributes
- file-directory-p file-executable-p file-exists-p file-local-copy
- file-locked-p file-modes file-name-as-directory
- file-name-case-insensitive-p file-name-directory
- file-name-nondirectory file-name-sans-versions
- file-notify-add-watch file-ownership-preserved-p file-readable-p
- file-regular-p file-remote-p file-selinux-context file-symlink-p
- file-system-info file-truename file-writable-p
- find-backup-file-name get-file-buffer
- insert-directory insert-file-contents load lock-file make-directory
- make-lock-file-name set-file-acl set-file-modes
- set-file-selinux-context set-file-times substitute-in-file-name
- unhandled-file-name-directory unlock-file vc-registered
- ;; Emacs 28- only.
- make-directory-internal
- ;; Emacs 29+ only.
- abbreviate-file-name
- ;; Tramp internal magic file name function.
- tramp-set-file-uid-gid))
- (if (file-name-absolute-p (nth 0 args))
+ ((or
+ (memq operation
+ '(access-file byte-compiler-base-file-name delete-directory
+ delete-file diff-latest-backup-file directory-file-name
+ directory-files directory-files-and-attributes dired-compress-file
+ dired-uncache file-acl file-accessible-directory-p file-attributes
+ file-directory-p file-executable-p file-exists-p file-local-copy
+ file-locked-p file-modes file-name-as-directory
+ file-name-case-insensitive-p file-name-directory
+ file-name-nondirectory file-name-sans-versions
+ file-notify-add-watch file-ownership-preserved-p file-readable-p
+ file-regular-p file-remote-p file-selinux-context file-symlink-p
+ file-system-info file-truename file-writable-p
+ find-backup-file-name get-file-buffer
+ insert-directory insert-file-contents load lock-file make-directory
+ make-lock-file-name set-file-acl set-file-modes
+ set-file-selinux-context set-file-times substitute-in-file-name
+ unhandled-file-name-directory unlock-file vc-registered
+ ;; Emacs 28- only.
+ make-directory-internal
+ ;; Emacs 29+ only.
+ abbreviate-file-name
+ ;; Tramp internal magic file name function.
+ tramp-set-file-uid-gid))
+ (eq (alist-get operation tramp-file-name-for-operation-external) 'file))
+ (if (and (stringp (nth 0 args)) (file-name-absolute-p (nth 0 args)))
(nth 0 args)
default-directory))
;; STRING FILE.
@@ -2502,31 +2517,45 @@ Must be handled by the callers."
(buffer-file-name
(if (bufferp (nth 0 args)) (nth 0 args) (current-buffer))))
;; COMMAND.
- ((memq operation
- '(exec-path make-nearby-temp-file make-process process-file
- shell-command start-file-process temporary-file-directory
- ;; Emacs 29+ only.
- list-system-processes memory-info process-attributes
- ;; Emacs 30+ only.
- file-group-gid file-user-uid))
+ ((or
+ (memq operation
+ '(exec-path make-nearby-temp-file make-process process-file
+ shell-command start-file-process temporary-file-directory
+ ;; Emacs 29+ only.
+ list-system-processes memory-info process-attributes
+ ;; Emacs 30+ only.
+ file-group-gid file-user-uid))
+ (eq (alist-get operation tramp-file-name-for-operation-external)
+ 'default-directory))
default-directory)
- ;; PROC.
- ((memq operation '(file-notify-rm-watch file-notify-valid-p))
- (when (processp (nth 0 args))
- (tramp-get-default-directory (process-buffer (nth 0 args)))))
+ ;; PROC or BUFFER.
+ ((or
+ (memq operation '(file-notify-rm-watch file-notify-valid-p))
+ (eq (alist-get operation tramp-file-name-for-operation-external) 'process))
+ (or (when-let* (((processp (nth 0 args)))
+ (vec (process-get (nth 0 args) 'tramp-vector)))
+ (tramp-make-tramp-file-name vec))
+ (when-let*
+ ((buf (cond
+ ((processp (nth 0 args)) (process-buffer (nth 0 args)))
+ ((bufferp (nth 0 args)) (get-buffer (nth 0 args)))
+ ((stringp (nth 0 args))
+ ;; Process or buffer name.
+ (or (get-process (nth 0 args)) (get-buffer (nth 0 args)))))))
+ (tramp-get-default-directory buf))
+ ""))
;; VEC.
((memq operation
'(tramp-get-home-directory tramp-get-remote-gid
tramp-get-remote-groups tramp-get-remote-uid))
(tramp-make-tramp-file-name (nth 0 args)))
- ;; FILE resp DIRECTORY.
- ((and (memq operation tramp-file-name-for-operation-external)
- (or (stringp (nth 0 args)) (null (nth 0 args))))
- (if (and (stringp (nth 0 args)) (file-name-absolute-p (nth 0 args)))
- (nth 0 args)
- default-directory))
+ ;; A function.
+ ((functionp (alist-get operation tramp-file-name-for-operation-external))
+ (apply
+ (alist-get operation tramp-file-name-for-operation-external)
+ operation args))
;; Unknown file primitive.
- (t (unless (member 'remote-file-error debug-ignored-errors)
+ (t (unless (memq 'remote-file-error debug-ignored-errors)
(tramp-error
nil 'remote-file-error "Unknown file I/O primitive: %s" operation)))))
@@ -2544,7 +2573,7 @@ Must be handled by the callers."
(funcall (setq func (car elt)) vec)
(error
(setcar elt #'ignore)
- (unless (member 'remote-file-error debug-ignored-errors)
+ (unless (memq 'remote-file-error debug-ignored-errors)
(tramp-error
vec 'remote-file-error
"Not a valid Tramp file name function `%s'" func))))
@@ -2552,22 +2581,32 @@ Must be handled by the callers."
res (cdr elt))))
res)))
-(defun tramp-add-external-operation (operation function backend)
+(defun tramp-add-external-operation
+ (operation function backend &optional arg-type)
"Add FUNCTION to Tramp BACKEND as handler for OPERATION.
OPERATION must not be one of the magic operations listed in Info
node `(elisp) Magic File Names'. FUNCTION must have the same argument
list as OPERATION. BACKEND, a symbol, must be one of the Tramp backend
-packages like `tramp-sh' (except `tramp-ftp')."
+packages like `tramp-sh' (except `tramp-ftp'). ARG-TYPE is either
+`file' (the default), `default-directory', `process' or a function
+symbol. It describes the type of the OPERATION argument to be checked.
+See the docstring of `tramp-file-name-for-operation-external' for its
+meaning."
(require backend)
(when-let* ((fnha
(intern-soft
(concat (symbol-name backend) "-file-name-handler-alist")))
- ((boundp fnha)))
+ ((boundp fnha))
+ (arg-type (or arg-type 'file)))
+ (unless (or (memq arg-type '(file default-directory process))
+ (functionp arg-type))
+ (tramp-error nil 'remote-file-error "Unknown arg type: %s" arg-type))
;; Make BACKEND aware of the new operation.
(add-to-list fnha (cons operation function))
- (unless (memq operation tramp-file-name-for-operation-external)
+ (unless (assq operation tramp-file-name-for-operation-external)
;; Make Tramp aware of the new operation.
- (add-to-list 'tramp-file-name-for-operation-external operation)
+ (add-to-list
+ 'tramp-file-name-for-operation-external (cons operation arg-type))
(put #'tramp-file-name-handler
'operations
(cons operation (get 'tramp-file-name-handler 'operations)))
@@ -2577,8 +2616,7 @@ packages like `tramp-sh' (except `tramp-ftp')."
`(lambda (orig-fun &rest args)
(if-let* ((handler
(find-file-name-handler
- (if (and (car args) (file-name-absolute-p (car args)))
- (car args) default-directory)
+ (apply #'tramp-file-name-for-operation #',operation args)
#',operation)))
(apply handler #',operation args)
(apply orig-fun args)))
@@ -2605,7 +2643,7 @@ Tramp backend packages like `tramp-sh'."
tramp-foreign-file-name-handler-alist)
;; Make Tramp unaware of OPERATION.
(setq tramp-file-name-for-operation-external
- (delq operation tramp-file-name-for-operation-external))
+ (assq-delete-all operation tramp-file-name-for-operation-external))
(put #'tramp-file-name-handler
'operations (delq operation (get 'tramp-file-name-handler 'operations)))
;; Remove the advice for OPERATION.
@@ -3016,6 +3054,8 @@ BODY is the backend specific code."
(when (file-directory-p ,directory)
(seq-uniq (delq nil
(let* ((case-fold-search read-file-name-completion-ignore-case)
+ (remote-file-name-inhibit-cache
+ (tramp-suppress-remote-file-name-inhibit-cache))
(result
(if (tramp-tramp-file-p ,directory)
(with-parsed-tramp-file-name
@@ -3027,16 +3067,14 @@ BODY is the backend specific code."
(format
"file-name-all-completions-%s"
tramp-fnac-add-trailing-slash)
- ;; Mark symlinked directories. Other
- ;; directories are already marked.
+ ;; Mark directories, including symlinks to
+ ;; directories.
(mapcar
(lambda (x)
(let ((f (file-name-concat ,directory x)))
(if (and tramp-fnac-add-trailing-slash
(not (string-suffix-p "/" x))
- (file-directory-p
- (if (file-symlink-p f)
- (file-truename f) f)))
+ (file-directory-p f))
(concat x "/") x)))
;; Some storage systems do not return "." and "..".
(seq-union
@@ -5189,11 +5227,17 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.")
(delete-file local-copy)))))
t)))
+(defvar tramp-multi-hop-p-hook nil
+ "Abnormal hook for `tramp-multi-hop-p'.
+This can be used by external Tramp backends to inform, that they are
+multi-hop capable.")
+
(defun tramp-multi-hop-p (vec)
"Whether the method of VEC is capable of multi-hops."
(let ((tramp-verbose 0))
- (and (tramp-sh-file-name-handler-p vec)
- (tramp-get-method-parameter vec 'tramp-login-args))))
+ (or (and (tramp-sh-file-name-handler-p vec)
+ (tramp-get-method-parameter vec 'tramp-login-args))
+ (run-hook-with-args-until-success 'tramp-multi-hop-p-hook vec))))
(defun tramp-add-hops (vec)
"Add ad-hoc proxy definitions to `tramp-default-proxies-alist'."
@@ -5325,7 +5369,7 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.")
(defvar tramp-extra-expand-args nil
"Method specific arguments.")
-(defun tramp-expand-args (vec parameter default &rest spec-list)
+(defun tramp-expand-args (vec parameter &optional default &rest spec-list)
"Expand login arguments as given by PARAMETER in `tramp-methods'.
PARAMETER is a symbol like `tramp-login-args', denoting a list of
list of strings from `tramp-methods', containing %-sequences for
@@ -5348,12 +5392,15 @@ a connection-local variable."
(setq spec-list (cddr spec-list)))
(setq spec (apply #'format-spec-make extra-spec-list))
;; Expand format spec.
- (flatten-tree
- (mapcar
- (lambda (x)
- (setq x (mapcar (lambda (y) (tramp-format-spec y spec)) x))
- (unless (member "" x) x))
- args))))
+ (cond
+ ((consp args)
+ (flatten-tree
+ (mapcar
+ (lambda (x)
+ (setq x (mapcar (lambda (y) (tramp-format-spec y spec)) x))
+ (unless (member "" x) x))
+ args)))
+ (args (tramp-format-spec args spec)))))
(defun tramp-post-process-creation (proc vec)
"Apply actions after creation of process PROC."
@@ -5458,6 +5505,10 @@ processes."
(setenv-internal env "HISTFILESIZE" "0" 'keep))
(t env))
env))
+ ;; Add TERM.
+ (env (if sh-file-name-handler-p
+ (setenv-internal env "TERM" tramp-terminal-type 'keep)
+ env))
;; Add INSIDE_EMACS.
(env (setenv-internal env "INSIDE_EMACS" (tramp-inside-emacs) 'keep))
(env (mapcar #'tramp-shell-quote-argument (delq nil env)))
@@ -5475,8 +5526,7 @@ processes."
(tramp-get-method-parameter v 'tramp-direct-async)
`(,(string-join command " ")))
command))
- (login-program
- (tramp-get-method-parameter v 'tramp-login-program))
+ (login-program (tramp-expand-args v 'tramp-login-program))
;; We don't create the temporary file. In fact, it is just
;; a prefix for the ControlPath option of ssh; the real
;; temporary file has another name, and it is created and
@@ -6333,7 +6383,7 @@ Mostly useful to protect BODY from being interrupted by timers."
(declare (indent 1) (debug t))
`(if (tramp-get-connection-property ,proc "locked")
;; Be kind for old versions of Emacs.
- (if (member 'remote-file-error debug-ignored-errors)
+ (if (memq 'remote-file-error debug-ignored-errors)
(throw 'non-essential 'non-essential)
;(tramp-backtrace ,proc 'force)
(tramp-error
@@ -6699,7 +6749,7 @@ If FILENAME is remote, a file name handler is called."
ID-FORMAT valid values are `string' and `integer'."
;; We use key nil for local connection properties.
(with-tramp-connection-property nil (format "uid-%s" id-format)
- (if (equal id-format 'integer) (user-uid) (user-login-name))))
+ (if (eq id-format 'integer) (user-uid) (user-login-name))))
(defun tramp-get-local-gid (id-format)
"The gid of the local user, in ID-FORMAT.
@@ -6707,8 +6757,8 @@ ID-FORMAT valid values are `string' and `integer'."
;; We use key nil for local connection properties.
(with-tramp-connection-property nil (format "gid-%s" id-format)
(cond
- ((equal id-format 'integer) (group-gid))
- ((equal id-format 'string) (group-name (group-gid)))
+ ((eq id-format 'integer) (group-gid))
+ ((eq id-format 'string) (group-name (group-gid)))
((file-attribute-group-id (file-attributes "~/" id-format))))))
(defun tramp-get-local-locale (&optional vec)
@@ -6904,8 +6954,8 @@ ID-FORMAT valid values are `string' and `integer'."
(with-tramp-connection-property vec (format "uid-%s" id-format)
(tramp-file-name-handler #'tramp-get-remote-uid vec id-format)))
;; Ensure there is a valid result.
- (and (equal id-format 'integer) tramp-unknown-id-integer)
- (and (equal id-format 'string) tramp-unknown-id-string)))
+ (and (eq id-format 'integer) tramp-unknown-id-integer)
+ (and (eq id-format 'string) tramp-unknown-id-string)))
(defun tramp-get-remote-gid (vec id-format)
"The gid of the remote connection VEC, in ID-FORMAT.
@@ -6914,8 +6964,8 @@ ID-FORMAT valid values are `string' and `integer'."
(with-tramp-connection-property vec (format "gid-%s" id-format)
(tramp-file-name-handler #'tramp-get-remote-gid vec id-format)))
;; Ensure there is a valid result.
- (and (equal id-format 'integer) tramp-unknown-id-integer)
- (and (equal id-format 'string) tramp-unknown-id-string)))
+ (and (eq id-format 'integer) tramp-unknown-id-integer)
+ (and (eq id-format 'string) tramp-unknown-id-string)))
(defun tramp-get-remote-groups (vec id-format)
"The list of groups of the remote connection VEC, in ID-FORMAT.
@@ -7229,9 +7279,9 @@ verbosity of 6."
(let ((default-directory temporary-file-directory))
(dolist (pid (list-system-processes))
(and-let* ((attributes (process-attributes pid))
- (comm (cdr (assoc 'comm attributes)))
+ (comm (cdr (assq 'comm attributes)))
((string-equal
- (cdr (assoc 'user attributes)) (user-login-name)))
+ (cdr (assq 'user attributes)) (user-login-name)))
;; The returned command name could be truncated
;; to 15 characters. Therefore, we cannot check
;; for `string-equal'.
diff --git a/trampver.el b/trampver.el
index bf84e89..d1c2533 100644
--- a/trampver.el
+++ b/trampver.el
@@ -7,7 +7,7 @@
;; Maintainer: Michael Albinus <michael.albinus@gmx.de>
;; Keywords: comm, processes
;; Package: tramp
-;; Version: 2.8.1.3
+;; Version: 2.8.1.4
;; Package-Requires: ((emacs "28.1"))
;; Package-Type: multi
;; URL: https://www.gnu.org/software/tramp/
@@ -40,7 +40,7 @@
;; ./configure" to change them.
;;;###tramp-autoload
-(defconst tramp-version "2.8.1.3"
+(defconst tramp-version "2.8.1.4"
"This version of Tramp.")
;;;###tramp-autoload
@@ -76,7 +76,7 @@
;; Check for Emacs version.
(let ((x (if (not (string-version-lessp emacs-version "28.1"))
"ok"
- (format "Tramp 2.8.1.3 is not fit for %s"
+ (format "Tramp 2.8.1.4 is not fit for %s"
(replace-regexp-in-string "\n" "" (emacs-version))))))
(unless (string-equal "ok" x) (error "%s" x)))