Function: tramp-do-copy-or-rename-file-out-of-band

tramp-do-copy-or-rename-file-out-of-band is a byte-compiled function defined in tramp-sh.el.gz.

Signature

(tramp-do-copy-or-rename-file-out-of-band OP FILENAME NEWNAME OK-IF-ALREADY-EXISTS KEEP-DATE)

Documentation

Invoke scp program to copy.

The method used must be an out-of-band method.

Source Code

;; Defined in /usr/src/emacs/lisp/net/tramp-sh.el.gz
(defun tramp-do-copy-or-rename-file-out-of-band
    (op filename newname ok-if-already-exists keep-date)
  "Invoke `scp' program to copy.
The method used must be an out-of-band method."
  ;; FILENAME and NEWNAME are already expanded.
  (let* ((v1 (and (tramp-tramp-file-p filename)
		  (tramp-dissect-file-name filename)))
	 (v2 (and (tramp-tramp-file-p newname)
		  (tramp-dissect-file-name newname)))
	 (v (or v1 v2))
	 copy-program copy-args copy-env listener spec
	 options source target remote-copy-program remote-copy-args p)

    (if (and v1 v2 (string-empty-p (tramp-scp-direct-remote-copying v1 v2)))

	;; Both are Tramp files.  We cannot use direct remote copying.
	(let* ((dir-flag (file-directory-p filename))
	       (tmpfile (tramp-compat-make-temp-file
			 (tramp-file-name-localname v1) dir-flag)))
	  (if dir-flag
	      (setq tmpfile
		    (expand-file-name
		     (file-name-nondirectory newname) tmpfile)))
	  (unwind-protect
	      (progn
		(tramp-do-copy-or-rename-file-out-of-band
		 op filename tmpfile ok-if-already-exists keep-date)
		(tramp-do-copy-or-rename-file-out-of-band
		 'rename tmpfile newname ok-if-already-exists keep-date))
	    ;; Save exit.
	    (ignore-errors
	      (if dir-flag
		  (delete-directory
		   (expand-file-name ".." tmpfile) 'recursive)
		(delete-file tmpfile)))))

      ;; Check which ones of source and target are Tramp files.
      (setq source (funcall
		    (if (and (string-equal (tramp-file-name-method v) "rsync")
			     (file-directory-p filename)
			     (not (file-exists-p newname)))
			#'file-name-as-directory
		      #'identity)
		    (if v1
			(tramp-make-copy-file-name v1)
		      (file-name-unquote filename)))
	    target (if v2
		       (tramp-make-copy-file-name v2)
		     (file-name-unquote newname)))

      ;; Check for listener port.
      (when (tramp-get-method-parameter v 'tramp-remote-copy-args)
	(setq listener (number-to-string (+ 50000 (random 10000))))
	(while
	    (zerop (tramp-call-process
		    v "nc" nil nil nil "-z" (tramp-file-name-host v) listener))
	  (setq listener (number-to-string (+ 50000 (random 10000))))))

      ;; Compose copy command.
      (setq options
	    (format-spec
	     (tramp-ssh-or-plink-options v)
	     (format-spec-make
	      ?t (tramp-get-connection-property
		  (tramp-get-connection-process v) "temp-file" "")))
	    spec (list
		  ;; "%h" and "%u" do not happen in `tramp-copy-args'
		  ;; of `scp', so it is save to use `v'.
		  ?h (or (tramp-file-name-host v) "")
		  ?u (or (tramp-file-name-user v)
			 ;; There might be an interactive setting.
			 (tramp-get-connection-property v "login-as")
			 "")
		  ;; For direct remote copying, the port must be the
		  ;; same for source and target.
		  ?p (or (tramp-file-name-port v) "")
		  ?r listener ?c options ?k (if keep-date " " "")
                  ?n (concat "2>" (tramp-get-remote-null-device v))
		  ?x (tramp-scp-strict-file-name-checking v)
		  ?y (tramp-scp-force-scp-protocol v)
		  ?z (tramp-scp-direct-remote-copying v1 v2))
	    copy-program (tramp-get-method-parameter v 'tramp-copy-program)
	    copy-args
	    ;; " " has either been a replacement of "%k" (when
	    ;; KEEP-DATE argument is non-nil), or a replacement for
	    ;; the whole keep-date sublist.
	    (delete " " (apply #'tramp-expand-args v 'tramp-copy-args nil spec))
	    ;; `tramp-ssh-controlmaster-options' is a string instead
	    ;; of a list.  Unflatten it.
	    copy-args
	    (flatten-tree
	     (mapcar
	      (lambda (x) (if (string-search " " x) (split-string x) x))
	      copy-args))
	    copy-env (apply #'tramp-expand-args v 'tramp-copy-env nil spec)
	    remote-copy-program
	    (tramp-get-method-parameter v 'tramp-remote-copy-program)
	    remote-copy-args
	    (apply #'tramp-expand-args v 'tramp-remote-copy-args nil spec))

      ;; Check for local copy program.
      (unless (executable-find copy-program)
	(tramp-error
	 v 'file-error "Cannot find local copy program: %s" copy-program))

      ;; Install listener on the remote side.  The prompt must be
      ;; consumed later on, when the process does not listen anymore.
      (when remote-copy-program
	(unless (with-tramp-connection-property
		    v (concat "remote-copy-program-" remote-copy-program)
		  (tramp-find-executable
		   v remote-copy-program (tramp-get-remote-path v)))
	  (tramp-error
	   v 'file-error
	   "Cannot find remote listener: %s" remote-copy-program))
	(setq remote-copy-program
	      (string-join
	       (append
		(list remote-copy-program) remote-copy-args
		(list (if v1 (concat "<" source) (concat ">" target)) "&"))
	       " "))
	(tramp-send-command v remote-copy-program)
	(with-timeout
	    (60 (tramp-error
		 v 'file-error
		 "Listener process not running on remote host: `%s'"
		 remote-copy-program))
	  (tramp-send-command v (format "netstat -l | grep -q :%s" listener))
	  (while (not (tramp-send-command-and-check v nil))
	    (tramp-send-command
	     v (format "netstat -l | grep -q :%s" listener)))))

      (with-temp-buffer
	(unwind-protect
	    (with-tramp-saved-connection-properties
		v '(" process-name" " process-buffer")
	      ;; The default directory must be remote.
	      (let ((default-directory
		     (file-name-directory (if v1 filename newname)))
		    (process-environment (copy-sequence process-environment)))
		;; Set the transfer process properties.
		(tramp-set-connection-property
		 v " process-name" (buffer-name (current-buffer)))
		(tramp-set-connection-property
		 v " process-buffer" (current-buffer))
		(when copy-env
		  (tramp-message
		   v 6 "%s=\"%s\""
		   (car copy-env) (string-join (cdr copy-env) " "))
		  (setenv (car copy-env) (string-join (cdr copy-env) " ")))
		(setq
		 copy-args
		 (append
		  copy-args
		  (if remote-copy-program
		      (list (if v1 (concat ">" target) (concat "<" source)))
		    (list source target)))
		 ;; Use an asynchronous process.  By this, password
		 ;; can be handled.  We don't set a timeout, because
		 ;; the copying of large files can last longer than 60
		 ;; secs.
		 p (apply
		    #'tramp-start-process v
		    (tramp-get-connection-name v)
		    (tramp-get-connection-buffer v)
		    copy-program copy-args))

		;; We must adapt `tramp-local-end-of-line' for sending
		;; the password.  Also, we indicate that perhaps
		;; several password prompts might appear.
		(let ((tramp-local-end-of-line tramp-rsh-end-of-line)
		      (tramp-password-prompt-not-unique (and v1 v2)))
		  (tramp-process-actions
		   p v nil tramp-actions-copy-out-of-band))))

	  ;; Clear the remote prompt.
	  (when (and remote-copy-program
		     (not (tramp-send-command-and-check v nil)))
	    ;; Houston, we have a problem!  Likely, the listener is
	    ;; still running, so let's clear everything (but the
	    ;; cached password).
	    (tramp-cleanup-connection v 'keep-debug 'keep-password)))))

    ;; If the operation was `rename', delete the original file.
    (unless (eq op 'copy)
      (if (file-regular-p filename)
	  (delete-file filename)
	(delete-directory filename 'recursive)))))