Function: tramp-sh-handle-write-region

tramp-sh-handle-write-region is a byte-compiled function defined in tramp-sh.el.gz.

Signature

(tramp-sh-handle-write-region START END FILENAME &optional APPEND VISIT LOCKNAME MUSTBENEW)

Documentation

Like write-region for Tramp files.

Source Code

;; Defined in /usr/src/emacs/lisp/net/tramp-sh.el.gz
(defun tramp-sh-handle-write-region
  (start end filename &optional append visit lockname mustbenew)
  "Like `write-region' for Tramp files."
  (tramp-skeleton-write-region start end filename append visit lockname mustbenew
    ;; If `start' is the empty string, it is likely that a temporary
    ;; file is created.  Do it directly.
    (if (and (stringp start) (string-empty-p start))
	(tramp-send-command
	 v (format "cat <%s >%s"
		   (tramp-get-remote-null-device v)
		   (tramp-shell-quote-argument localname)))

      ;; Short track: if we are on the local host, we can run directly.
      (if (and (tramp-local-host-p v)
	       ;; `file-writable-p' calls `file-expand-file-name'.  We
	       ;; cannot use `tramp-run-real-handler' therefore.
	       (file-writable-p (file-name-directory localname))
	       (or (file-directory-p localname)
		   (file-writable-p localname)))
	  (let ((create-lockfiles (not file-locked)))
	    (write-region start end localname append 'no-message lockname))

	(let* ((modes (tramp-default-file-modes
		       filename (and (eq mustbenew 'excl) 'nofollow)))
	       ;; Write region into a tmp file.  This isn't really
	       ;; needed if we use an encoding function, but currently
	       ;; we use it always because this makes the logic
	       ;; simpler.  We must also set
	       ;; `temporary-file-directory', because it could point
	       ;; to a remote directory.
	       (temporary-file-directory
		tramp-compat-temporary-file-directory)
	       (tmpfile (or tramp-temp-buffer-file-name
			    (tramp-compat-make-temp-file filename))))

	  ;; If `append' is non-nil, we copy the file locally, and let
	  ;; the native `write-region' implementation do the job.
	  (when (and append (file-exists-p filename))
	    (copy-file filename tmpfile 'ok))

	  ;; We say `no-message' here because we don't want the
	  ;; visited file modtime data to be clobbered from the temp
	  ;; file.  We call `set-visited-file-modtime' ourselves later
	  ;; on.  We must ensure that `file-coding-system-alist'
	  ;; matches `tmpfile'.
	  (let ((file-coding-system-alist
		 (tramp-find-file-name-coding-system-alist filename tmpfile))
		create-lockfiles)
	    (condition-case err
		(write-region start end tmpfile append 'no-message)
	      ((error quit)
	       (setq tramp-temp-buffer-file-name nil)
	       (delete-file tmpfile)
	       (signal (car err) (cdr err)))))

	  ;; Now, `last-coding-system-used' has the right value.
	  ;; Remember it.
	  (setq coding-system-used last-coding-system-used)

	  ;; The permissions of the temporary file should be set.  If
	  ;; FILENAME does not exist (eq modes nil) it has been
	  ;; renamed to the backup file.  This case `save-buffer'
	  ;; handles permissions.  Ensure that it is still readable.
	  (when modes
	    (set-file-modes tmpfile (logior (or modes 0) #o0400)))

	  ;; This is a bit lengthy due to the different methods
	  ;; possible for file transfer.  First, we check whether the
	  ;; method uses an scp program.  If so, we call it.
	  ;; Otherwise, both encoding and decoding command must be
	  ;; specified.  However, if the method _also_ specifies an
	  ;; encoding function, then that is used for encoding the
	  ;; contents of the tmp file.
	  (let* ((size (file-attribute-size (file-attributes tmpfile)))
		 (rem-dec (tramp-get-inline-coding v "remote-decoding" size))
		 (loc-enc (tramp-get-inline-coding v "local-encoding" size)))
	    (cond
	     ;; `copy-file' handles direct copy and out-of-band methods.
	     ((or (tramp-local-host-p v)
		  (tramp-method-out-of-band-p v size))
	      (if (and (not (stringp start))
		       (= (or end (point-max)) (point-max))
		       (= (or start (point-min)) (point-min))
		       (tramp-get-method-parameter
			v 'tramp-copy-keep-tmpfile))
		  (progn
		    (setq tramp-temp-buffer-file-name tmpfile)
		    (condition-case err
			;; We keep the local file for performance
			;; reasons, useful for "rsync".
			(copy-file tmpfile filename t)
		      ((error quit)
		       (setq tramp-temp-buffer-file-name nil)
		       (delete-file tmpfile)
		       (signal (car err) (cdr err)))))
		(setq tramp-temp-buffer-file-name nil)
		;; Don't rename, in order to keep context in SELinux.
		(unwind-protect
		    (copy-file tmpfile filename t)
		  (delete-file tmpfile))))

	     ;; Use inline file transfer.
	     (rem-dec
	      ;; Encode tmpfile.
	      (unwind-protect
		  (with-temp-buffer
		    (set-buffer-multibyte nil)
		    ;; Use encoding function or command.
		    (with-tramp-progress-reporter
			v 3 (format-message
			     "Encoding local file `%s' using `%s'"
			     tmpfile loc-enc)
		      (if (functionp loc-enc)
			  ;; The following `let' is a workaround for
			  ;; the base64.el that comes with pgnus-0.84.
			  ;; If both of the following conditions are
			  ;; satisfied, it tries to write to a local
			  ;; file in default-directory, but at this
			  ;; point, default-directory is remote.
			  ;; (`call-process-region' can't write to
			  ;; remote files, it seems.)  The file in
			  ;; question is a tmp file anyway.
			  (let ((coding-system-for-read 'binary)
				(default-directory
				 tramp-compat-temporary-file-directory))
			    (insert-file-contents-literally tmpfile)
			    (funcall loc-enc (point-min) (point-max)))

			(unless (zerop (tramp-call-local-coding-command
					loc-enc tmpfile t))
			  (tramp-error
			   v 'file-error
			   (concat "Cannot write to `%s', "
				   "local encoding command `%s' failed")
			   filename loc-enc))))

		    ;; Send buffer into remote decoding command which
		    ;; writes to remote file.  Because this happens on
		    ;; the remote host, we cannot use the function.
		    (with-tramp-progress-reporter
			v 3 (format-message
			     "Decoding remote file `%s' using `%s'"
			     filename rem-dec)
		      (goto-char (point-max))
		      (unless (bolp) (newline))
		      (tramp-barf-unless-okay
		       v  (format
			   (concat rem-dec " <<'%s'\n%s%s")
			   (tramp-shell-quote-argument localname)
			   tramp-end-of-heredoc
			   (buffer-string)
			   tramp-end-of-heredoc)
		       "Couldn't write region to `%s', decode using `%s' failed"
		       filename rem-dec)
		      ;; When `file-precious-flag' is set, the region
		      ;; is written to a temporary file.  Check that
		      ;; the checksum is equal to that from the local
		      ;; tmpfile.
		      (when file-precious-flag
			(erase-buffer)
			(and
			 ;; cksum runs locally, if possible.
			 (zerop (tramp-call-process v "cksum" tmpfile t))
			 ;; cksum runs remotely.
			 (tramp-send-command-and-check
			  v (format
			     "cksum <%s" (tramp-shell-quote-argument localname)))
			 ;; ... they are different.
			 (not
			  (string-equal
			   (buffer-string)
			   (tramp-get-buffer-string (tramp-get-buffer v))))
			 (tramp-error
			  v 'file-error
			  (concat "Couldn't write region to `%s',"
				  " decode using `%s' failed")
			  filename rem-dec)))))

		;; Save exit.
		(delete-file tmpfile)))

	     ;; That's not expected.
	     (t
	      (tramp-error
	       v 'file-error
	       (concat "Method `%s' should specify both encoding and "
		       "decoding command or an scp program")
	       method)))))))))