;;;###tramp-autoload
(defcustom tramp-remote-process-environment
- `("HISTFILE=$HOME/.tramp_history" "HISTSIZE=1" "TMOUT=0" "LC_ALL=C"
+ `("HISTFILE=$HOME/.tramp_history" "HISTSIZE=1" "TMOUT=0" "LC_CTYPE=''"
,(format "TERM=%s" tramp-terminal-type)
"EMACS=t" ;; Deprecated.
,(format "INSIDE_EMACS='%s,tramp:%s'" emacs-version tramp-version)
(defun tramp-sh-handle-set-file-times (filename &optional time)
"Like `set-file-times' for Tramp files."
- (if (file-remote-p filename)
+ (if (tramp-tramp-file-p filename)
(with-parsed-tramp-file-name filename nil
(tramp-flush-file-property v localname)
(let ((time (if (or (null time) (equal time '(0 0)))
;; the majority of cases.
;; Don't modify `last-coding-system-used' by accident.
(let ((last-coding-system-used last-coding-system-used))
- (if (file-remote-p filename)
+ (if (tramp-tramp-file-p filename)
(with-parsed-tramp-file-name filename nil
(if (and (zerop (user-uid)) (tramp-local-host-p v))
;; If we are root on the local host, we can do it directly.
(with-parsed-tramp-file-name filename v1
(with-parsed-tramp-file-name newname v2
(let ((ln (when v1 (tramp-get-remote-ln v1))))
- (when (and (not ok-if-already-exists)
+ (when (and (numberp ok-if-already-exists)
(file-exists-p newname)
- (not (numberp ok-if-already-exists))
- (y-or-n-p
+ (yes-or-no-p
(format
"File %s already exists; make it a new name anyway? "
newname)))
(tramp-error
- v2 'file-error
- "add-name-to-file: file %s already exists" newname))
+ v2 'file-error "add-name-to-file: file %s already exists" newname))
+ (when ok-if-already-exists (setq ln (concat ln " -f")))
(tramp-flush-file-property v2 (file-name-directory v2-localname))
(tramp-flush-file-property v2 v2-localname)
(tramp-barf-unless-okay
v1
- (format "%s %s %s" ln (tramp-shell-quote-argument v1-localname)
+ (format "%s %s %s" ln
+ (tramp-shell-quote-argument v1-localname)
(tramp-shell-quote-argument v2-localname))
"error with add-name-to-file, see buffer `%s' for details"
(buffer-name))))))
'copy-file (list filename newname ok-if-already-exists keep-date)))))
(defun tramp-sh-handle-copy-directory
- (dirname newname &optional keep-date parents _copy-contents)
+ (dirname newname &optional keep-date parents copy-contents)
"Like `copy-directory' for Tramp files."
(let ((t1 (tramp-tramp-file-p dirname))
(t2 (tramp-tramp-file-p newname)))
(with-parsed-tramp-file-name (if t1 dirname newname) nil
- (if (and (tramp-get-method-parameter method 'tramp-copy-recursive)
+ (if (and (not copy-contents)
+ (tramp-get-method-parameter method 'tramp-copy-recursive)
;; When DIRNAME and NEWNAME are remote, they must have
;; the same method.
(or (null t1) (null t2)
(string-equal
(tramp-file-name-method (tramp-dissect-file-name dirname))
- (tramp-file-name-method (tramp-dissect-file-name newname)))))
+ (tramp-file-name-method
+ (tramp-dissect-file-name newname)))))
;; scp or rsync DTRT.
(progn
(setq dirname (directory-file-name (expand-file-name dirname))
'copy dirname newname keep-date))
;; We must do it file-wise.
(tramp-run-real-handler
- 'copy-directory (list dirname newname keep-date parents)))
+ 'copy-directory
+ (if copy-contents
+ (list dirname newname keep-date parents copy-contents)
+ (list dirname newname keep-date parents))))
;; When newname did exist, we have wrong cached values.
(when t2
;; In case of `rename', we must flush the cache of the source file.
(when (and t1 (eq op 'rename))
(with-parsed-tramp-file-name filename v1
- (tramp-flush-file-property v1 (file-name-directory localname))
- (tramp-flush-file-property v1 localname)))
+ (tramp-flush-file-property v1 (file-name-directory v1-localname))
+ (tramp-flush-file-property v1 v1-localname)))
;; When newname did exist, we have wrong cached values.
(when t2
(with-parsed-tramp-file-name newname v2
- (tramp-flush-file-property v2 (file-name-directory localname))
- (tramp-flush-file-property v2 localname)))))))
+ (tramp-flush-file-property v2 (file-name-directory v2-localname))
+ (tramp-flush-file-property v2 v2-localname)))))))
(defun tramp-do-copy-or-rename-file-via-buffer (op filename newname keep-date)
"Use an Emacs buffer to copy or rename a file.
;; `jka-compr-inhibit' to t.
(let ((coding-system-for-write 'binary)
(jka-compr-inhibit t))
- (write-region (point-min) (point-max) newname)))
+ (write-region (point-min) (point-max) newname nil 'no-message)))
;; KEEP-DATE handling.
(when keep-date (set-file-times newname (nth 5 (file-attributes filename))))
;; Set the mode.
(tramp-message
orig-vec 6 "%s"
(mapconcat 'identity (process-command p) " "))
+ (tramp-set-connection-property p "vector" orig-vec)
(tramp-compat-set-process-query-on-exit-flag p nil)
(tramp-process-actions
p v nil tramp-actions-copy-out-of-band)
(re-search-backward "tramp_exit_status [0-9]+" nil t)
(tramp-error
orig-vec 'file-error
- "Couldn't find exit status of `%s'" (process-command p)))
+ "Couldn't find exit status of `%s'"
+ (mapconcat 'identity (process-command p) " ")))
(skip-chars-forward "^ ")
(unless (zerop (read (current-buffer)))
(forward-line -1)
(filename switches &optional wildcard full-directory-p)
"Like `insert-directory' for Tramp files."
(setq filename (expand-file-name filename))
+ (unless switches (setq switches ""))
(with-parsed-tramp-file-name filename nil
(if (and (featurep 'ls-lisp)
(not (symbol-value 'ls-lisp-use-insert-directory-program)))
(if full-directory-p "yes" "no"))
;; If `full-directory-p', we just say `ls -l FILENAME'.
;; Else we chdir to the parent directory, then say `ls -ld BASENAME'.
- ;; "--dired" returns byte positions. Therefore, the file names
- ;; must be encoded, which is guaranteed by "LC_ALL=en_US.utf8
- ;; LC_CTYPE=''".
(if full-directory-p
(tramp-send-command
v
- (format "env LC_ALL=en_US.utf8 LC_CTYPE='' %s %s %s 2>/dev/null"
+ (format "%s %s %s 2>/dev/null"
(tramp-get-ls-command v)
switches
(if wildcard
(tramp-run-real-handler 'file-name-directory (list localname))))
(tramp-send-command
v
- (format "env LC_ALL=en_US.utf8 LC_CTYPE='' %s %s %s 2>/dev/null"
+ (format "%s %s %s 2>/dev/null"
(tramp-get-ls-command v)
switches
(if (or wildcard
(tramp-shell-quote-argument
(tramp-run-real-handler
'file-name-nondirectory (list localname)))))))
- (let ((beg (point)))
- ;; We cannot use `insert-buffer-substring' because the Tramp
- ;; buffer changes its contents before insertion due to calling
- ;; `expand-file' and alike.
- (insert
- (with-current-buffer (tramp-get-buffer v)
- (buffer-string)))
-
- ;; Check for "--dired" output.
- (forward-line -2)
- (when (looking-at "//SUBDIRED//")
- (forward-line -1))
- (when (looking-at "//DIRED//\\s-+")
- (let ((databeg (match-end 0))
- (end (point-at-eol)))
- ;; Now read the numeric positions of file names.
- (goto-char databeg)
- (while (< (point) end)
- (let ((start (+ beg (read (current-buffer))))
- (end (+ beg (read (current-buffer)))))
- (if (memq (char-after end) '(?\n ?\ ))
- ;; End is followed by \n or by " -> ".
- (put-text-property start end 'dired-filename t))))))
- ;; Remove trailing lines.
- (goto-char (point-at-bol))
- (while (looking-at "//")
- (forward-line 1)
- (delete-region (match-beginning 0) (point)))
-
- ;; Some busyboxes are reluctant to discard colors.
- (unless (string-match "color" (tramp-get-connection-property v "ls" ""))
- (goto-char beg)
- (while (re-search-forward tramp-color-escape-sequence-regexp nil t)
- (replace-match "")))
-
- ;; Decode the output, it could be multibyte.
- (decode-coding-region
- beg (point-max)
- (or file-name-coding-system
- (and (boundp 'default-file-name-coding-system)
- (symbol-value 'default-file-name-coding-system))))
-
- ;; The inserted file could be from somewhere else.
- (when (and (not wildcard) (not full-directory-p))
- (goto-char (point-max))
- (when (file-symlink-p filename)
- (goto-char (search-backward "->" beg 'noerror)))
- (search-backward
- (if (zerop (length (file-name-nondirectory filename)))
- "."
- (file-name-nondirectory filename))
- beg 'noerror)
- (replace-match (file-relative-name filename) t))
- (goto-char (point-max))))))
+ (save-restriction
+ (let ((beg (point)))
+ (narrow-to-region (point) (point))
+ ;; We cannot use `insert-buffer-substring' because the Tramp
+ ;; buffer changes its contents before insertion due to calling
+ ;; `expand-file' and alike.
+ (insert
+ (with-current-buffer (tramp-get-buffer v)
+ (buffer-string)))
+
+ ;; Check for "--dired" output.
+ (forward-line -2)
+ (when (looking-at "//SUBDIRED//")
+ (forward-line -1))
+ (when (looking-at "//DIRED//\\s-+")
+ (let ((databeg (match-end 0))
+ (end (point-at-eol)))
+ ;; Now read the numeric positions of file names.
+ (goto-char databeg)
+ (while (< (point) end)
+ (let ((start (+ beg (read (current-buffer))))
+ (end (+ beg (read (current-buffer)))))
+ (if (memq (char-after end) '(?\n ?\ ))
+ ;; End is followed by \n or by " -> ".
+ (put-text-property start end 'dired-filename t))))))
+ ;; Remove trailing lines.
+ (goto-char (point-at-bol))
+ (while (looking-at "//")
+ (forward-line 1)
+ (delete-region (match-beginning 0) (point)))
+
+ ;; Some busyboxes are reluctant to discard colors.
+ (unless (string-match "color" (tramp-get-connection-property v "ls" ""))
+ (goto-char beg)
+ (while (re-search-forward tramp-color-escape-sequence-regexp nil t)
+ (replace-match "")))
+
+ ;; Decode the output, it could be multibyte.
+ (decode-coding-region
+ beg (point-max)
+ (or file-name-coding-system
+ (and (boundp 'default-file-name-coding-system)
+ (symbol-value 'default-file-name-coding-system))))
+
+ ;; The inserted file could be from somewhere else.
+ (when (and (not wildcard) (not full-directory-p))
+ (goto-char (point-max))
+ (when (file-symlink-p filename)
+ (goto-char (search-backward "->" beg 'noerror)))
+ (search-backward
+ (if (zerop (length (file-name-nondirectory filename)))
+ "."
+ (file-name-nondirectory filename))
+ beg 'noerror)
+ (replace-match (file-relative-name filename) t))
+
+ (goto-char (point-max)))))))
;; Canonicalization of file names.
;; epa-file gets confused.
(let (file-name-handler-alist
(coding-system-for-write 'binary))
- (write-region (point-min) (point-max) tmpfile)))
+ (write-region
+ (point-min) (point-max) tmpfile nil 'no-message)))
;; If tramp-decoding-function is not defined for this
;; method, we invoke tramp-decoding-command instead.
(let (file-name-handler-alist
(coding-system-for-write 'binary))
(with-current-buffer (tramp-get-buffer v)
- (write-region (point-min) (point-max) tmpfile2)))
+ (write-region
+ (point-min) (point-max) tmpfile2 nil 'no-message)))
(unwind-protect
(tramp-call-local-coding-command
loc-dec tmpfile2 tmpfile)
(setq file-name (expand-file-name file-name))
(with-parsed-tramp-file-name file-name nil
(let* ((default-directory (file-name-directory file-name))
- command events filter p)
+ command events filter p sequence)
(cond
;; gvfs-monitor-dir.
((setq command (tramp-get-remote-gvfs-monitor-dir v))
(setq filter 'tramp-sh-file-gvfs-monitor-dir-process-filter
- p (start-file-process
- "gvfs-monitor-dir" (generate-new-buffer " *gvfs-monitor-dir*")
- command localname)))
+ sequence `(,command ,localname)))
;; inotifywait.
((setq command (tramp-get-remote-inotifywait v))
(setq filter 'tramp-sh-file-inotifywait-process-filter
"create,modify,move,delete,attrib")
((memq 'change flags) "create,modify,move,delete")
((memq 'attribute-change flags) "attrib"))
- p (start-file-process
- "inotifywait" (generate-new-buffer " *inotifywait*")
- command "-mq" "-e" events localname)))
+ sequence `(,command "-mq" "-e" ,events ,localname)))
;; None.
(t (tramp-error
v 'file-notify-error
"No file notification program found on %s"
(file-remote-p file-name))))
+ ;; Start process.
+ (setq p (apply
+ 'start-file-process
+ (file-name-nondirectory command)
+ (generate-new-buffer
+ (format " *%s*" (file-name-nondirectory command)))
+ sequence))
;; Return the process object as watch-descriptor.
(if (not (processp p))
(tramp-error
- v 'file-notify-error "`%s' failed to start on remote host" command)
+ v 'file-notify-error
+ "`%s' failed to start on remote host"
+ (mapconcat 'identity sequence " "))
+ (tramp-message v 6 "Run `%s', %S" (mapconcat 'identity sequence " ") p)
+ (tramp-set-connection-property p "vector" v)
(tramp-compat-set-process-query-on-exit-flag p nil)
(set-process-filter p filter)
p))))
;; Set the environment.
(tramp-message vec 5 "Setting default environment")
- (let ((env (copy-sequence tramp-remote-process-environment))
+ (let ((env (append `(,(tramp-get-remote-locale vec))
+ (copy-sequence tramp-remote-process-environment)))
unset item)
(while env
(setq item (tramp-compat-split-string (car env) "="))
(condition-case err
(unless (and p (processp p) (memq (process-status p) '(run open)))
- ;; We call `tramp-get-buffer' in order to get a debug buffer
- ;; for messages from the beginning.
- (tramp-get-buffer vec)
-
;; If `non-essential' is non-nil, don't reopen a new connection.
(when (and (boundp 'non-essential) (symbol-value 'non-essential))
(throw 'non-essential 'non-essential))
(when (and p (processp p))
(delete-process p))
(setenv "TERM" tramp-terminal-type)
- (setenv "LC_ALL" "C")
+ (setenv "LC_ALL" "en_US.utf8")
(setenv "PROMPT_COMMAND")
(setenv "PS1" tramp-initial-end-of-output)
(let* ((target-alist (tramp-compute-multi-hops vec))
tramp-current-user (or g-user l-user)
tramp-current-host (or g-host l-host))
- ;; Replace login-args place holders.
+ ;; Replace `login-args' place holders.
(setq
l-host (or l-host "")
l-user (or l-user "")
x))
remote-path)))))
+(defun tramp-get-remote-locale (vec)
+ (with-tramp-connection-property vec "locale"
+ (tramp-send-command vec "locale -a")
+ (let ((candidates '("en_US.utf8" "C.utf8" "C"))
+ locale)
+ (with-current-buffer (tramp-get-connection-buffer vec)
+ (while candidates
+ (goto-char (point-min))
+ (if (string-match (concat "^" (car candidates) "$") (buffer-string))
+ (setq locale (car candidates)
+ candidates nil)
+ (setq candidates (cdr candidates)))))
+ ;; Return value.
+ (when locale (format "LC_ALL=%s" locale)))))
+
(defun tramp-get-ls-command (vec)
(with-tramp-connection-property vec "ls"
(tramp-message vec 5 "Finding a suitable `ls' command")
(defun tramp-get-remote-id (vec)
(with-tramp-connection-property vec "id"
(tramp-message vec 5 "Finding POSIX `id' command")
- (or
- (catch 'id-found
- (let ((dl (tramp-get-remote-path vec))
- result)
- (while (and dl (setq result (tramp-find-executable vec "id" dl t t)))
- ;; Check POSIX parameter.
- (when (tramp-send-command-and-check vec (format "%s -u" result))
- (throw 'id-found result))
- (setq dl (cdr dl)))))
- (tramp-error vec 'file-error "Couldn't find a POSIX `id' command"))))
+ (catch 'id-found
+ (let ((dl (tramp-get-remote-path vec))
+ result)
+ (while (and dl (setq result (tramp-find-executable vec "id" dl t t)))
+ ;; Check POSIX parameter.
+ (when (tramp-send-command-and-check vec (format "%s -u" result))
+ (throw 'id-found result))
+ (setq dl (cdr dl)))))))
+
+(defun tramp-get-remote-uid-with-id (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -u%s %s"
+ (tramp-get-remote-id vec)
+ (if (equal id-format 'integer) "" "n")
+ (if (equal id-format 'integer)
+ "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/"))))
+
+(defun tramp-get-remote-uid-with-perl (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -le '%s'"
+ (tramp-get-remote-perl vec)
+ (if (equal id-format 'integer)
+ "print $>"
+ "print \"\\\"\", scalar getpwuid($>), \"\\\"\""))))
+
+(defun tramp-get-remote-python (vec)
+ (with-tramp-connection-property vec "python"
+ (tramp-message vec 5 "Finding a suitable `python' command")
+ (tramp-find-executable vec "python" (tramp-get-remote-path vec))))
+
+(defun tramp-get-remote-uid-with-python (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -c \"%s\""
+ (tramp-get-remote-python vec)
+ (if (equal id-format 'integer)
+ "import os; print os.getuid()"
+ "import os, pwd; print '\\\"' + pwd.getpwuid(os.getuid())[0] + '\\\"'"))))
(defun tramp-get-remote-uid (vec id-format)
(with-tramp-connection-property vec (format "uid-%s" id-format)
- (let ((res (tramp-send-command-and-read
- vec
- (format "%s -u%s %s"
- (tramp-get-remote-id vec)
- (if (equal id-format 'integer) "" "n")
- (if (equal id-format 'integer)
- "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/")))))
+ (let ((res (cond
+ ((tramp-get-remote-id vec)
+ (tramp-get-remote-uid-with-id vec id-format))
+ ((tramp-get-remote-perl vec)
+ (tramp-get-remote-uid-with-perl vec id-format))
+ ((tramp-get-remote-python vec)
+ (tramp-get-remote-uid-with-python vec id-format))
+ (t (tramp-error
+ vec 'file-error "Cannot determine remote uid")))))
;; The command might not always return a number.
(if (and (equal id-format 'integer) (not (integerp res))) -1 res))))
+(defun tramp-get-remote-gid-with-id (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -g%s %s"
+ (tramp-get-remote-id vec)
+ (if (equal id-format 'integer) "" "n")
+ (if (equal id-format 'integer)
+ "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/"))))
+
+(defun tramp-get-remote-gid-with-perl (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -le '%s'"
+ (tramp-get-remote-perl vec)
+ (if (equal id-format 'integer)
+ "print ($)=~/(\\d+)/)"
+ "print \"\\\"\", scalar getgrgid($)), \"\\\"\""))))
+
+(defun tramp-get-remote-gid-with-python (vec id-format)
+ (tramp-send-command-and-read
+ vec
+ (format "%s -c \"%s\""
+ (tramp-get-remote-python vec)
+ (if (equal id-format 'integer)
+ "import os; print os.getgid()"
+ "import os, grp; print '\\\"' + grp.getgrgid(os.getgid())[0] + '\\\"'"))))
+
(defun tramp-get-remote-gid (vec id-format)
(with-tramp-connection-property vec (format "gid-%s" id-format)
- (let ((res (tramp-send-command-and-read
- vec
- (format "%s -g%s %s"
- (tramp-get-remote-id vec)
- (if (equal id-format 'integer) "" "n")
- (if (equal id-format 'integer)
- "" "| sed -e s/^/\\\"/ -e s/\$/\\\"/")))))
+ (let ((res (cond
+ ((tramp-get-remote-id vec)
+ (tramp-get-remote-gid-with-id vec id-format))
+ ((tramp-get-remote-perl vec)
+ (tramp-get-remote-gid-with-perl vec id-format))
+ ((tramp-get-remote-python vec)
+ (tramp-get-remote-gid-with-python vec id-format))
+ (t (tramp-error
+ vec 'file-error "Cannot determine remote gid")))))
;; The command might not always return a number.
(if (and (equal id-format 'integer) (not (integerp res))) -1 res))))