Subject: Re: bug#69085: 29.2; Tramp: Extend tramp-make-copy-program-file-name via tramp-methods

From: Michael Albinus <[email-masked]>
Date: Sat, 17 Feb 2024 18:57:51 +0100

Sean Devlin <[email-masked]> writes:

> Hi Michael,

Hi Sean,

> The patch does work as described, but it doesn’t integrate well with typical
> workflows.
>
> So while this approach does work for Docker containers, it adds some
> ergonomic overhead for the user making it inconvenient to use.

I see your point. Appended is a new patch, which addresses your
inconvenience. Could you pls apply it on top of Tramp 2.7 instead of the
other patch? The method "dockercp" allows now user names, which are
ignored when calling "docker cp ...".

> Thanks,
> Sean

Best regards, Michael.

diff --git a/lisp/tramp-container.el b/lisp/tramp-container.el
index 1f578949..ff2e11b5 100644
--- a/lisp/tramp-container.el
+++ b/lisp/tramp-container.el
@@ -31,15 +31,20 @@
 ;; Open a file on a running Docker container:
 ;;
 ;;     C-x C-f /docker:USER@CONTAINER:/path/to/file
+;;     C-x C-f /dockercp:USER@CONTAINER:/path/to/file
 ;;
 ;; or Podman:
 ;;
 ;;     C-x C-f /podman:USER@CONTAINER:/path/to/file
+;;     C-x C-f /podmancp:USER@CONTAINER:/path/to/file
 ;;
 ;; Where:
 ;;     USER          is the user on the container to connect as (optional).
 ;;     CONTAINER     is the container to connect to.
 ;;
+;; "docker" and "podman" are inline methods, "dockercp" and "podmancp"
+;; are out-of-band methods.
+;;
 ;;
 ;;
 ;; Open file in a Kubernetes container:
@@ -141,10 +146,20 @@ If it is nil, the default context will be used."
 (defconst tramp-docker-method "docker"
   "Tramp method name to use to connect to Docker containers.")

+;;;###tramp-autoload
+(defconst tramp-dockercp-method "dockercp"
+  "Tramp method name to use to connect to Docker containers.
+This is for out-of-band connections.")
+
 ;;;###tramp-autoload
 (defconst tramp-podman-method "podman"
   "Tramp method name to use to connect to Podman containers.")

+;;;###tramp-autoload
+(defconst tramp-podmancp-method "podmancp"
+  "Tramp method name to use to connect to Podman containers.
+This is for out-of-band connections.")
+
 ;;;###tramp-autoload
 (defconst tramp-kubernetes-method "kubernetes"
   "Tramp method name to use to connect to Kubernetes containers.")
@@ -183,7 +198,8 @@ BODY is the backend specific code."
 (defun tramp-container--completion-function (method)
   "List running containers available for connection.
 METHOD is the Tramp method to be used for \"ps\", either
-`tramp-docker-method' or `tramp-podman-method'.
+`tramp-docker-method', `tramp-dockercp-method', `tramp-podman-method',
+or `tramp-podmancp-method'.

 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
@@ -375,6 +391,22 @@ see its function help for a description of the format."
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))

+ (add-to-list 'tramp-methods
+              `(,tramp-dockercp-method
+                (tramp-login-program ,tramp-docker-program)
+                (tramp-login-args (("exec")
+                                   ("-it")
+                                   ("-u" "%u")
+                                   ("%h")
+			           ("%l")))
+		(tramp-direct-async (,tramp-default-remote-shell "-c"))
+                (tramp-remote-shell ,tramp-default-remote-shell)
+                (tramp-remote-shell-login ("-l"))
+                (tramp-remote-shell-args ("-i" "-c"))
+		(tramp-copy-program ,tramp-docker-program)
+		(tramp-copy-args (("cp")))
+		(tramp-copy-file-name (("%h" ":") ("%f")))))
+
  (add-to-list 'tramp-methods
               `(,tramp-podman-method
                 (tramp-login-program ,tramp-podman-program)
@@ -388,6 +420,22 @@ see its function help for a description of the format."
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))

+ (add-to-list 'tramp-methods
+              `(,tramp-podmancp-method
+                (tramp-login-program ,tramp-podman-program)
+                (tramp-login-args (("exec")
+                                   ("-it")
+                                   ("-u" "%u")
+                                   ("%h")
+			           ("%l")))
+		(tramp-direct-async (,tramp-default-remote-shell "-c"))
+                (tramp-remote-shell ,tramp-default-remote-shell)
+                (tramp-remote-shell-login ("-l"))
+                (tramp-remote-shell-args ("-i" "-c"))
+		(tramp-copy-program ,tramp-podman-program)
+		(tramp-copy-args (("cp")))
+		(tramp-copy-file-name (("%h" ":") ("%f")))))
+
  (add-to-list 'tramp-methods
               `(,tramp-kubernetes-method
                 (tramp-login-program ,tramp-kubernetes-program)
@@ -431,10 +479,18 @@ see its function help for a description of the format."
   tramp-docker-method
   `((tramp-container--completion-function ,tramp-docker-method)))

+ (tramp-set-completion-function
+  tramp-dockercp-method
+  `((tramp-container--completion-function ,tramp-dockercp-method)))
+
  (tramp-set-completion-function
   tramp-podman-method
   `((tramp-container--completion-function ,tramp-podman-method)))

+ (tramp-set-completion-function
+  tramp-podmancp-method
+  `((tramp-container--completion-function ,tramp-podmancp-method)))
+
  (tramp-set-completion-function
   tramp-kubernetes-method
   `((tramp-kubernetes--completion-function ,tramp-kubernetes-method)))
diff --git a/lisp/tramp-sh.el b/lisp/tramp-sh.el
index 3557b3a1..b3ab5609 100644
--- a/lisp/tramp-sh.el
+++ b/lisp/tramp-sh.el
@@ -282,6 +282,7 @@ The string is used in `tramp-methods'.")
                 (tramp-copy-program         "nc")
                 ;; We use "-v" for better error tracking.
                 (tramp-copy-args            (("-w" "1") ("-v") ("%h") ("%r")))
+                (tramp-copy-file-name       (("%f")))
                 (tramp-remote-copy-program  "nc")
                 ;; We use "-p" as required for newer busyboxes.  For older
                 ;; busybox/nc versions, the value must be (("-l") ("%r")).  This
@@ -2399,10 +2400,10 @@ The method used must be an out-of-band method."
 			#'file-name-as-directory
 		      #'identity)
 		    (if v1
-			(tramp-make-copy-program-file-name v1)
+			(tramp-make-copy-file-name v1)
 		      (file-name-unquote filename)))
 	    target (if v2
-		       (tramp-make-copy-program-file-name v2)
+		       (tramp-make-copy-file-name v2)
 		     (file-name-unquote newname)))

       ;; Check for listener port.
@@ -5559,13 +5560,17 @@ raises an error."
    string
    ""))

-(defun tramp-make-copy-program-file-name (vec)
+(defun tramp-make-copy-file-name (vec)
   "Create a file name suitable for `scp', `pscp', or `nc' and workalikes."
   (let ((method (tramp-file-name-method vec))
 	(user (tramp-file-name-user vec))
 	(host (tramp-file-name-host vec))
 	(localname
 	 (directory-file-name (tramp-file-name-unquote-localname vec))))
+    ;; Set the default value of connection property "copy-file-name".
+    (unless (tramp-get-method-parameter vec 'tramp-copy-file-name)
+      (tramp-set-connection-property
+       vec "copy-file-name" '(("%u" "@") ("%h" ":") ("%f"))))
     (when (string-match-p tramp-ipv6-regexp host)
       (setq host (format "[%s]" host)))
     ;; This does not work for MS Windows scp, if there are characters
@@ -5573,11 +5578,10 @@ raises an error."
     ;; checking in scp, we use it when available.
     (unless (string-match-p (rx "ftp" eos) method)
       (setq localname (tramp-unquote-shell-quote-argument localname)))
-    (cond
-     ((tramp-get-method-parameter vec 'tramp-remote-copy-program)
-      localname)
-     ((tramp-string-empty-or-nil-p user) (format "%s:%s" host localname))
-     (t (format "%s@%s:%s" user host localname)))))
+    (string-join
+     (apply #'tramp-expand-args vec 'tramp-copy-file-name
+	    (list ?h (or host "") ?u (or user "") ?f localname))
+     "")))

 (defun tramp-method-out-of-band-p (vec size)
   "Return t if this is an out-of-band method, nil otherwise."
diff --git a/lisp/tramp.el b/lisp/tramp.el
index 2efee234..0e64b779 100644
--- a/lisp/tramp.el
+++ b/lisp/tramp.el
@@ -301,6 +301,14 @@ pair of the form (KEY VALUE).  The following KEYs are defined:
     This specifies the list of parameters to pass to the above mentioned
     program, the hints for `tramp-login-args' also apply here.

+  * `tramp-copy-file-name'
+    The remote source or destination file name for out-of-band methods.
+    You can use the \"%u\" and \"%h\" like in `tramp-login-args'.
+    Additionally, \"%f\" denotes the local file name part.  It will be
+    expanded to a string without spaces between the elements of the list.
+
+    The default value is \\='((\"%u\" \"@\") (\"%h\" \":\") (\"%f\"))
+
   * `tramp-copy-env'
      A list of environment variables and their values, which will
      be set when calling `tramp-copy-program'.