]> code.delx.au - gnu-emacs/blobdiff - lisp/net/tramp.el
* net/tramp.el (top): Require cl.el, when `copy-tree' is not available
[gnu-emacs] / lisp / net / tramp.el
index 021d3db6faca8d58d82e62f085a9699bab90f362..9f326800f0e3b6bb98beb3b0af6fd24729c17d00 100644 (file)
@@ -1,4 +1,4 @@
-;;; -*- mode: Emacs-Lisp; coding: iso-2022-7bit; -*-
+;;; -*- mode: Emacs-Lisp; coding: utf-8; -*-
 ;;; tramp.el --- Transparent Remote Access, Multiple Protocol
 
 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
@@ -6,7 +6,7 @@
 
 ;; (copyright statements below in code to be updated with the above notice)
 
-;; Author: Kai Gro\e,A_\e(Bjohann <kai.grossjohann@gmx.net>
+;; Author: Kai Großjohann <kai.grossjohann@gmx.net>
 ;;         Michael Albinus <michael.albinus@gmx.de>
 ;; Keywords: comm, processes
 
@@ -14,8 +14,8 @@
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3 of the License, or
-;; (at your option) any later version.
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 (require 'shell)
 (require 'advice)
 
+;; `copy-tree' is part of subr.el since Emacs 22.
+(eval-when-compile
+  (unless (functionp 'copy-tree)
+    (require 'cl)))
+
 ;; Requiring 'tramp-cache results in an endless loop.
 (autoload 'tramp-get-file-property "tramp-cache")
 (autoload 'tramp-set-file-property "tramp-cache")
@@ -571,8 +576,9 @@ files conditionalize this setup based on the TERM environment variable."
     ("plinkx"
              (tramp-login-program        "plink")
             (tramp-login-args           (("-load" "%h") ("-t")
-                                         (,(format "env 'TERM=%s' 'PS1=$ '"
-                                                   tramp-terminal-type))
+                                         (,(format
+                                            "env 'TERM=%s' 'PROMPT_COMMAND=' 'PS1=$ '"
+                                            tramp-terminal-type))
                                          ("/bin/sh")))
             (tramp-remote-sh            "/bin/sh")
             (tramp-copy-program         nil)
@@ -1026,7 +1032,7 @@ The `sudo' program appears to insert a `^@' character into the prompt."
                        "Login incorrect"
                        "Login Incorrect"
                        "Connection refused"
-                       "Connection closed by foreign host."
+                       "Connection closed"
                        "Sorry, try again."
                        "Name or service not known"
                        "Host key verification failed."
@@ -1527,7 +1533,7 @@ else
 $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\";
 $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\";
 printf(
-    \"(%%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u %%u t (%%u . %%u) -1)\\n\",
+    \"(%%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u.0 %%u t (%%u . %%u) -1)\\n\",
     $type,
     $stat[3],
     $uid,
@@ -1576,7 +1582,7 @@ for($i = 0; $i < $n; $i++)
     $uid = ($ARGV[1] eq \"integer\") ? $stat[4] : \"\\\"\" . getpwuid($stat[4]) . \"\\\"\";
     $gid = ($ARGV[1] eq \"integer\") ? $stat[5] : \"\\\"\" . getgrgid($stat[5]) . \"\\\"\";
     printf(
-        \"(\\\"%%s\\\" %%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u %%u t (%%u . %%u) (%%u %%u))\\n\",
+        \"(\\\"%%s\\\" %%s %%u %%s %%s (%%u %%u) (%%u %%u) (%%u %%u) %%u.0 %%u t (%%u . %%u) (%%u %%u))\\n\",
         $filename,
         $type,
         $stat[3],
@@ -1772,6 +1778,7 @@ This is used to map a mode number to a permission string.")
     (copy-file . tramp-handle-copy-file)
     (rename-file . tramp-handle-rename-file)
     (set-file-modes . tramp-handle-set-file-modes)
+    (set-file-times . tramp-handle-set-file-times)
     (make-directory . tramp-handle-make-directory)
     (delete-directory . tramp-handle-delete-directory)
     (delete-file . tramp-handle-delete-file)
@@ -2388,7 +2395,7 @@ target of the symlink differ."
   (tramp-send-command-and-read
    vec
    (format
-    "%s -c '((\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s \"%%A\" t %%i.0 -1)' %s"
+    "%s -c '((\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s.0 \"%%A\" t %%i.0 -1)' %s"
     (tramp-get-remote-stat vec)
     (if (eq id-format 'integer) "%u" "\"%U\"")
     (if (eq id-format 'integer) "%g" "\"%G\"")
@@ -2446,6 +2453,7 @@ of."
        t
       (let ((f (buffer-file-name)))
        (with-parsed-tramp-file-name f nil
+         (tramp-flush-file-property v localname)
          (let* ((attr (file-attributes f))
                 (modtime (nth 5 attr))
                 (mt (visited-file-modtime)))
@@ -2493,6 +2501,40 @@ of."
       (tramp-error
        v 'file-error "Error while changing file's mode %s" filename))))
 
+(defun tramp-handle-set-file-times (filename &optional time)
+  "Like `set-file-times' for Tramp files."
+  (zerop
+   (if (file-remote-p filename)
+       (with-parsed-tramp-file-name filename nil
+        (let ((time (if (or (null time) (equal time '(0 0)))
+                        (current-time)
+                      time))
+              (utc
+               ;; With GNU Emacs, `format-time-string' has an
+               ;; optional parameter UNIVERSAL.  This is preferred,
+               ;; because we could handle the case when the remote
+               ;; host is located in a different time zone as the
+               ;; local host.
+               (and (functionp 'subr-arity)
+                    (subrp (symbol-function 'format-time-string))
+                    (= 3 (cdr (funcall (symbol-function 'subr-arity)
+                                       (symbol-function
+                                        'format-time-string)))))))
+          (tramp-send-command-and-check
+           v (format "%s touch -t %s %s"
+                     (if utc "TZ=UTC; export TZ;" "")
+                     (if utc
+                         (format-time-string "%Y%m%d%H%M.%S" time t)
+                       (format-time-string "%Y%m%d%H%M.%S" time))
+                     (tramp-shell-quote-argument localname)))))
+     ;; We handle also the local part, because in older Emacsen,
+     ;; without `set-file-times', this function is an alias for this.
+     ;; We are local, so we don't need the UTC settings.
+     (call-process
+      "touch" nil nil nil "-t"
+      (format-time-string "%Y%m%d%H%M.%S" time)
+      (tramp-shell-quote-argument filename)))))
+
 ;; Simple functions using the `test' command.
 
 (defun tramp-handle-file-executable-p (filename)
@@ -2704,7 +2746,7 @@ of."
    (format
     (concat
      "cd %s; echo \"(\"; (%s -ab | xargs "
-     "%s -c '(\"%%n\" (\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s \"%%A\" t %%i.0 -1)'); "
+     "%s -c '(\"%%n\" (\"%%N\") %%h %s %s %%X.0 %%Y.0 %%Z.0 %%s.0 \"%%A\" t %%i.0 -1)'); "
      "echo \")\"")
     (tramp-shell-quote-argument localname)
     (tramp-get-ls-command vec)
@@ -2926,10 +2968,8 @@ KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
                (jka-compr-inhibit t))
            (write-region (point-min) (point-max) newname))))
     ;; KEEP-DATE handling.
-    (when keep-date
-      (when (and (not (null modtime))
-                (not (equal modtime '(0 0))))
-       (tramp-touch newname modtime)))
+    (when (and keep-date (functionp 'set-file-times))
+      (apply 'set-file-times (list newname modtime)))
     ;; Set the mode.
     (set-file-modes newname (file-modes filename))
     ;; If the operation was `rename', delete the original file.
@@ -3078,8 +3118,9 @@ be a local filename.  The method used must be an out-of-band method."
       (tramp-message v 0 "Transferring %s to %s...done" filename newname)
 
       ;; Handle KEEP-DATE argument.
-      (when (and keep-date (not copy-keep-date))
-       (set-file-times newname (nth 5 (file-attributes filename))))
+      (when (and keep-date (not copy-keep-date) (functionp 'set-file-times))
+       (apply 'set-file-times
+              (list newname (nth 5 (file-attributes filename)))))
 
       ;; Set the mode.
       (unless (and keep-date copy-keep-date)
@@ -3282,8 +3323,7 @@ This is like `dired-recursive-delete-directory' for Tramp files."
 ;; CCC is this the right thing to do?
 (defun tramp-handle-unhandled-file-name-directory (filename)
   "Like `unhandled-file-name-directory' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
-    (expand-file-name (tramp-make-tramp-file-name method user host "~/"))))
+  (expand-file-name "~/"))
 
 ;; Canonicalization of file names.
 
@@ -3432,7 +3472,7 @@ beginning of local filename are not substituted."
 (defun tramp-handle-executable-find (command)
   "Like `executable-find' for Tramp files."
   (with-parsed-tramp-file-name default-directory nil
-    (tramp-find-executable v command tramp-remote-path t)))
+    (tramp-find-executable v command (tramp-get-remote-path v) t)))
 
 ;; We use BUFFER also as connection buffer during setup. Because of
 ;; this, its original contents must be saved, and restored once
@@ -3446,9 +3486,8 @@ beginning of local filename are not substituted."
          (tramp-set-connection-property v "process-name" name)
          (tramp-set-connection-property
           v "process-buffer"
-          (get-buffer-create
-           ;; BUFFER can be nil.
-           (or buffer (generate-new-buffer-name (tramp-buffer-name v)))))
+          ;; BUFFER can be nil.
+          (get-buffer-create (or buffer (current-buffer))))
          ;; Activate narrowing in order to save BUFFER contents.
          (with-current-buffer (tramp-get-connection-buffer v)
            (narrow-to-region (point-max) (point-max)))
@@ -3466,7 +3505,9 @@ beginning of local filename are not substituted."
          ;; Return process.
          (tramp-get-connection-process v))
       ;; Save exit.
-      (with-current-buffer (tramp-get-connection-buffer v) (widen))
+      (with-current-buffer (tramp-get-connection-buffer v)
+       (widen)
+       (goto-char (point-max)))
       (tramp-set-connection-property v "process-name" nil)
       (tramp-set-connection-property v "process-buffer" nil))))
 
@@ -3545,8 +3586,11 @@ beginning of local filename are not substituted."
              (tramp-send-command v command)
            ;; We should show the output anyway.
            (when outbuf
-             (with-current-buffer outbuf
-               (insert-buffer-substring (tramp-get-connection-buffer v)))
+             (let ((output-string
+                    (with-current-buffer (tramp-get-connection-buffer v)
+                      (buffer-substring (point-min) (point-max)))))
+               (with-current-buffer outbuf
+                 (insert output-string)))
              (when display (display-buffer outbuf))))
        ;; When the user did interrupt, we should do it also.
        (error
@@ -3575,12 +3619,44 @@ beginning of local filename are not substituted."
 (defun tramp-handle-shell-command
   (command &optional output-buffer error-buffer)
   "Like `shell-command' for Tramp files."
-  (with-parsed-tramp-file-name default-directory nil
-    (let ((shell-file-name
-          (tramp-get-connection-property v "remote-shell" "/bin/sh"))
-         (shell-command-switch "-c"))
-      (tramp-run-real-handler
-       'shell-command (list command output-buffer error-buffer)))))
+  (let* ((asynchronous (string-match "[ \t]*&[ \t]*\\'" command))
+        (args (split-string (substring command 0 asynchronous) " "))
+        (output-buffer
+         (cond
+          ((bufferp output-buffer) output-buffer)
+          ((stringp output-buffer) (get-buffer-create output-buffer))
+          (output-buffer (current-buffer))
+          (t (generate-new-buffer
+              (if asynchronous
+                  "*Async Shell Command*"
+                "*Shell Command Output*")))))
+        (error-buffer
+         (cond
+          ((bufferp error-buffer) error-buffer)
+          ((stringp error-buffer) (get-buffer-create error-buffer))))
+        (buffer
+         (if (and (not asynchronous) error-buffer)
+             (with-parsed-tramp-file-name default-directory nil
+               (list output-buffer (tramp-make-tramp-temp-file v)))
+           output-buffer)))
+
+    (prog1
+       ;; Run the process.  We cannot use `process-file' and
+       ;; `start-file-process', because these functions might not
+       ;; exist in older Emacsen.
+       (if (integerp asynchronous)
+           (apply 'tramp-handle-start-file-process
+                  "*Async Shell*" buffer args)
+         (apply 'tramp-handle-process-file
+                (car args) nil buffer nil (cdr args)))
+      ;; Insert error messages if they were separated.
+      (when (listp buffer)
+       (with-current-buffer error-buffer
+         (insert-file-contents (cadr buffer)))
+       (delete-file (buffer-file-name (cadr buffer))))
+      ;; There's some output, display it.
+      (when (with-current-buffer output-buffer (> (point-max) (point-min)))
+       (display-message-or-buffer output-buffer)))))
 
 ;; File Editing.
 
@@ -3657,14 +3733,18 @@ beginning of local filename are not substituted."
       (run-hooks 'tramp-handle-file-local-copy-hook)
       tmpfil)))
 
-(defun tramp-handle-file-remote-p (filename &optional connected)
+(defun tramp-handle-file-remote-p (filename &optional identification connected)
   "Like `file-remote-p' for Tramp files."
   (when (tramp-tramp-file-p filename)
     (with-parsed-tramp-file-name filename nil
       (and (or (not connected)
               (let ((p (tramp-get-connection-process v)))
                 (and p (processp p) (memq (process-status p) '(run open)))))
-          (tramp-make-tramp-file-name method user host "")))))
+          (cond
+           ((eq identification 'method) method)
+           ((eq identification 'user) user)
+           ((eq identification 'host) host)
+           (t (tramp-make-tramp-file-name method user host "")))))))
 
 (defun tramp-handle-insert-file-contents
   (filename &optional visit beg end replace)
@@ -4025,6 +4105,8 @@ ARGS are the arguments OPERATION has been called with."
                  'load 'make-directory 'make-directory-internal
                  'set-file-modes 'substitute-in-file-name
                  'unhandled-file-name-directory 'vc-registered
+                 ; Emacs 22 only
+                 'set-file-times
                  ; XEmacs only
                  'abbreviate-file-name 'create-file-buffer
                  'dired-file-modtime 'dired-make-compressed-filename
@@ -4114,8 +4196,12 @@ Falls back to normal file name handler if no tramp file name handler exists."
         ((and completion (zerop (length localname))
               (memq operation '(file-name-as-directory)))
          filename)
-        ;; Call the backend function.
-        (foreign (apply foreign operation args))
+        ;; Call the backend function.  Set a connection property
+        ;; first, it will be reused for user/host name completion.
+        (foreign
+         (unless (zerop (length localname))
+           (tramp-set-connection-property v "started" nil))
+         (apply foreign operation args))
         ;; Nothing to do for us.
         (t (tramp-run-real-handler operation args)))))))
 
@@ -4886,40 +4972,6 @@ hosts, or files, disagree."
               (tramp-shell-quote-argument v1-localname)
               (tramp-shell-quote-argument v2-localname))))))
 
-(defun tramp-touch (file time)
-  "Set the last-modified timestamp of the given file.
-TIME is an Emacs internal time value as returned by `current-time'."
-  (let* ((utc
-         ;; With GNU Emacs, `format-time-string' has an optional
-         ;; parameter UNIVERSAL.  This is preferred.
-         (and (functionp 'subr-arity)
-              (subrp (symbol-function 'format-time-string))
-              (= 3 (cdr (funcall (symbol-function 'subr-arity)
-                                 (symbol-function 'format-time-string))))))
-        (touch-time
-         (if utc
-             (format-time-string "%Y%m%d%H%M.%S" time t)
-           (format-time-string "%Y%m%d%H%M.%S" time)))
-        (default-directory (file-name-directory file)))
-
-    (if (eq (tramp-find-foreign-file-name-handler file)
-           'tramp-sh-file-name-handler)
-       (with-parsed-tramp-file-name file nil
-         (tramp-send-command
-          v (format "%s touch -t %s %s"
-                    (if utc "TZ=UTC; export TZ;" "")
-                    touch-time
-                    (tramp-shell-quote-argument localname))))
-      (with-temp-buffer
-       (shell-command
-        (format "%s touch -t %s %s"
-                (if utc "TZ=UTC; export TZ;" "")
-                touch-time
-                (tramp-shell-quote-argument
-                 (if (tramp-tramp-file-p file)
-                     (with-parsed-tramp-file-name file nil localname) file)))
-        (current-buffer))))))
-
 (defun tramp-buffer-name (vec)
   "A name for the connection buffer VEC."
   ;; We must use `tramp-file-name-real-host', because for gateway
@@ -5052,53 +5104,9 @@ I.e., for each directory in `tramp-remote-path', it is tested
 whether it exists and if so, it is added to the environment
 variable PATH."
   (tramp-message vec 5 (format "Setting $PATH environment variable"))
-
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    (set (make-local-variable 'tramp-remote-path)
-        (copy-tree tramp-remote-path))
-    (let* ((elt (memq 'tramp-default-remote-path tramp-remote-path))
-          (tramp-default-remote-path
-           (with-connection-property vec "default-remote-path"
-             (when elt
-               (condition-case nil
-                   (symbol-name
-                    (tramp-send-command-and-read vec "getconf PATH"))
-                 ;; Default if "getconf" is not available.
-                 (error
-                  (tramp-message
-                   vec 3
-                   "`getconf PATH' not successful, using default value \"%s\"."
-                   "/bin:/usr/bin")
-                  "/bin:/usr/bin"))))))
-      (when elt
-       ;; Replace place holder `tramp-default-remote-path'.
-       (setcdr elt
-               (append
-                (tramp-split-string tramp-default-remote-path ":")
-                (cdr elt)))
-       (setq tramp-remote-path
-             (delq 'tramp-default-remote-path tramp-remote-path))))
-
-    ;; Check for existence of directories.
-    (setq tramp-remote-path
-         (delq
-          nil
-          (mapcar
-           (lambda (x)
-             (and
-              (with-connection-property vec x
-                (file-directory-p
-                 (tramp-make-tramp-file-name
-                  (tramp-file-name-method vec)
-                  (tramp-file-name-user vec)
-                  (tramp-file-name-host vec)
-                  x)))
-              x))
-           tramp-remote-path)))
-    (tramp-send-command
-     vec
-     (format "PATH=%s; export PATH"
-            (mapconcat 'identity tramp-remote-path ":")))))
+  (tramp-send-command
+   vec (format "PATH=%s; export PATH"
+              (mapconcat 'identity (tramp-get-remote-path vec) ":"))))
 
 ;; -- communication with external shell --
 
@@ -5163,8 +5171,10 @@ file exists and nonzero exit status otherwise."
        (cond
         ((string-match "^~root$" (buffer-string))
          (setq shell
-               (or (tramp-find-executable vec "bash" tramp-remote-path t)
-                   (tramp-find-executable vec "ksh" tramp-remote-path t)))
+               (or (tramp-find-executable
+                    vec "bash" (tramp-get-remote-path vec) t)
+                   (tramp-find-executable
+                    vec "ksh" (tramp-get-remote-path vec) t)))
          (unless shell
            (tramp-error
             vec 'file-error
@@ -5179,7 +5189,8 @@ file exists and nonzero exit status otherwise."
            (when extra-args (setq shell (concat shell " " extra-args))))
          (tramp-message
           vec 5 "Starting remote shell `%s' for tilde expansion..." shell)
-         (tramp-send-command-internal vec (concat "PS1='$ ' exec " shell))
+         (tramp-send-command-internal
+          vec (concat "PROMPT_COMMAND='' PS1='$ ' exec " shell))
          (tramp-message vec 5 "Setting remote shell prompt...")
          ;; Douglas Gray Stephens <DGrayStephens@slb.com> says that we
          ;; must use "\n" here, not tramp-rsh-end-of-line.  Kai left the
@@ -5187,7 +5198,7 @@ file exists and nonzero exit status otherwise."
          ;; as well.
          (tramp-send-command
           vec
-          (format "PS1='%s%s%s'; PS2=''; PS3=''"
+          (format "PROMPT_COMMAND=''; PS1='%s%s%s'; PS2=''; PS3=''"
                   tramp-rsh-end-of-line
                   tramp-end-of-output
                   tramp-rsh-end-of-line))
@@ -5455,10 +5466,11 @@ process to set up.  VEC specifies the connection."
   ;; makes it work under `rc', too.  We also unset the variable $ENV
   ;; because that is read by some sh implementations (eg, bash when
   ;; called as sh) on startup; this way, we avoid the startup file
-  ;; clobbering $PS1.
+  ;; clobbering $PS1.  $PROMP_COMMAND is another way to set the prompt
+  ;; in /bin/bash, it must be discarded as well.
   (tramp-send-command-internal
    vec
-   (format "exec env 'ENV=' 'PS1=$ ' %s"
+   (format "exec env 'ENV=' 'PROMPT_COMMAND=' 'PS1=$ ' %s"
           (tramp-get-method-parameter
            (tramp-file-name-method vec) 'tramp-remote-sh)))
   (tramp-message vec 5 "Setting up remote shell environment")
@@ -5512,7 +5524,7 @@ process to set up.  VEC specifies the connection."
   ;; send "echo are you awake".
   (tramp-send-command
    vec
-   (format "PS1='%s%s%s'; PS2=''; PS3=''"
+   (format "PROMPT_COMMAND=''; PS1='%s%s%s'; PS2=''; PS3=''"
           tramp-rsh-end-of-line
            tramp-end-of-output
           tramp-rsh-end-of-line))
@@ -5893,6 +5905,7 @@ connection if a previous connection has died for some reason."
       (when (and p (processp p))
        (delete-process p))
       (setenv "TERM" tramp-terminal-type)
+      (setenv "PROMPT_COMMAND")
       (setenv "PS1" "$ ")
       (let* ((target-alist (tramp-compute-multi-hops vec))
             (process-environment (copy-sequence process-environment))
@@ -6084,7 +6097,8 @@ In case there is no valid Lisp expression, it raises an error"
     (condition-case nil
        (prog1 (read (current-buffer))
          ;; Error handling.
-         (when (re-search-forward "\\S-" nil t) (error)))
+         (when (re-search-forward "\\S-" (tramp-line-end-position) t)
+           (error)))
       (error (tramp-error
              vec 'file-error
              "`%s' does not return a valid Lisp expression: `%s'"
@@ -6093,7 +6107,7 @@ In case there is no valid Lisp expression, it raises an error"
 ;; It seems that Tru64 Unix does not like it if long strings are sent
 ;; to it in one go.  (This happens when sending the Perl
 ;; `file-attributes' implementation, for instance.)  Therefore, we
-;; have this function which waits a bit at each line.
+;; have this function which sends the string in chunks.
 (defun tramp-send-string (vec string)
   "Send the STRING via connection VEC.
 
@@ -6111,7 +6125,7 @@ the remote host use line-endings as defined in the variable
       ;; Clean up the buffer.  We cannot call `erase-buffer' because
       ;; narrowing might be in effect.
       (let (buffer-read-only) (delete-region (point-min) (point-max)))
-      ;; replace "\n" by `tramp-rsh-end-of-line'
+      ;; Replace "\n" by `tramp-rsh-end-of-line'.
       (setq string
            (mapconcat 'identity
                       (split-string string "\n")
@@ -6119,7 +6133,7 @@ the remote host use line-endings as defined in the variable
       (unless (or (string= string "")
                  (string-equal (substring string -1) tramp-rsh-end-of-line))
        (setq string (concat string tramp-rsh-end-of-line)))
-      ;; send the string
+      ;; Send the string.
       (if (and chunksize (not (zerop chunksize)))
          (let ((pos 0)
                (end (length string)))
@@ -6222,6 +6236,11 @@ Return ATTR."
     (setcar (nthcdr 6 attr)
            (list (floor (nth 6 attr) 65536)
                  (floor (mod (nth 6 attr) 65536)))))
+  ;; Convert file size.
+  (when (< (nth 7 attr) 0)
+    (setcar (nthcdr 7 attr) -1))
+  (when (and (floatp (nth 7 attr)) (<= (nth 7 attr) most-positive-fixnum))
+    (setcar (nthcdr 7 attr) (round (nth 7 attr))))
   ;; Convert file mode bits to string.
   (unless (stringp (nth 8 attr))
     (setcar (nthcdr 8 attr) (tramp-file-mode-from-int (nth 8 attr))))
@@ -6243,17 +6262,24 @@ Return ATTR."
   ;; Convert inode.
   (unless (listp (nth 10 attr))
     (setcar (nthcdr 10 attr)
-            (list (floor (nth 10 attr) 65536)
-                  (floor (mod (nth 10 attr) 65536)))))
+           (condition-case nil
+               (list (floor (nth 10 attr) 65536)
+                     (floor (mod (nth 10 attr) 65536)))
+             ;; Inodes can be incredible huge.  We must hide this.
+             (error (tramp-get-inode vec)))))
   ;; Set virtual device number.
   (setcar (nthcdr 11 attr)
           (tramp-get-device vec))
   attr)
 
-(defun tramp-get-inode (file)
+(defun tramp-get-inode (vec)
   "Returns the virtual inode number.
 If it doesn't exist, generate a new one."
-  (let ((string (directory-file-name file)))
+  (let ((string (tramp-make-tramp-file-name
+                (tramp-file-name-method vec)
+                (tramp-file-name-user vec)
+                (tramp-file-name-host vec)
+                "")))
     (unless (assoc string tramp-inodes)
       (add-to-list 'tramp-inodes
                   (list string (length tramp-inodes))))
@@ -6490,6 +6516,46 @@ necessary only.  This function will be used in file name completion."
 
 ;; Variables local to connection.
 
+(defun tramp-get-remote-path (vec)
+  (with-connection-property vec "remote-path"
+    (let* ((remote-path (copy-tree tramp-remote-path))
+          (elt (memq 'tramp-default-remote-path remote-path))
+          (default-remote-path
+            (when elt
+              (condition-case nil
+                  (symbol-name
+                   (tramp-send-command-and-read vec "getconf PATH"))
+                ;; Default if "getconf" is not available.
+                (error
+                 (tramp-message
+                  vec 3
+                  "`getconf PATH' not successful, using default value \"%s\"."
+                  "/bin:/usr/bin")
+                 "/bin:/usr/bin")))))
+      (when elt
+       ;; Replace place holder `tramp-default-remote-path'.
+       (setcdr elt
+               (append
+                (tramp-split-string default-remote-path ":")
+                (cdr elt)))
+       (setq remote-path (delq 'tramp-default-remote-path remote-path)))
+
+      ;; Remove non-existing directories.
+      (delq
+       nil
+       (mapcar
+       (lambda (x)
+         (and
+          (with-connection-property vec x
+            (file-directory-p
+             (tramp-make-tramp-file-name
+              (tramp-file-name-method vec)
+              (tramp-file-name-user vec)
+              (tramp-file-name-host vec)
+              x)))
+          x))
+       remote-path)))))
+
 (defun tramp-get-ls-command (vec)
   (with-connection-property vec "ls"
     (with-current-buffer (tramp-get-buffer vec)
@@ -6497,7 +6563,7 @@ necessary only.  This function will be used in file name completion."
       (or
        (catch 'ls-found
         (dolist (cmd '("ls" "gnuls" "gls"))
-          (let ((dl tramp-remote-path)
+          (let ((dl (tramp-get-remote-path vec))
                 result)
             (while
                 (and
@@ -6508,14 +6574,6 @@ necessary only.  This function will be used in file name completion."
               (when (zerop (tramp-send-command-and-check
                             vec (format "%s -lnd /" result)))
                 (throw 'ls-found result))
-              ;; Remove unneeded directories from path.
-              (while
-                  (and
-                   dl
-                   (not
-                    (string-equal
-                     result (expand-file-name-as-directory cmd (car dl)))))
-                (setq dl (cdr dl)))
               (setq dl (cdr dl))))))
        (tramp-error vec 'file-error "Couldn't find a proper `ls' command")))))
 
@@ -6525,7 +6583,7 @@ necessary only.  This function will be used in file name completion."
       (tramp-message vec 5 "Finding a suitable `test' command")
       (if (zerop (tramp-send-command-and-check vec "test 0"))
          "test"
-       (tramp-find-executable vec "test" tramp-remote-path)))))
+       (tramp-find-executable vec "test" (tramp-get-remote-path vec))))))
 
 (defun tramp-get-test-nt-command (vec)
   ;; Does `test A -nt B' work?  Use abominable `find' construct if it
@@ -6559,20 +6617,21 @@ necessary only.  This function will be used in file name completion."
   (with-connection-property vec "ln"
     (with-current-buffer (tramp-get-buffer vec)
       (tramp-message vec 5 "Finding a suitable `ln' command")
-      (tramp-find-executable vec "ln" tramp-remote-path))))
+      (tramp-find-executable vec "ln" (tramp-get-remote-path vec)))))
 
 (defun tramp-get-remote-perl (vec)
   (with-connection-property vec "perl"
     (with-current-buffer (tramp-get-buffer vec)
       (tramp-message vec 5 "Finding a suitable `perl' command")
-      (or (tramp-find-executable vec "perl5" tramp-remote-path)
-         (tramp-find-executable vec "perl" tramp-remote-path)))))
+      (or (tramp-find-executable vec "perl5" (tramp-get-remote-path vec))
+         (tramp-find-executable vec "perl" (tramp-get-remote-path vec))))))
 
 (defun tramp-get-remote-stat (vec)
   (with-connection-property vec "stat"
     (with-current-buffer (tramp-get-buffer vec)
       (tramp-message vec 5 "Finding a suitable `stat' command")
-      (let ((result (tramp-find-executable vec "stat" tramp-remote-path))
+      (let ((result (tramp-find-executable
+                    vec "stat" (tramp-get-remote-path vec)))
            tmp)
        ;; Check whether stat(1) returns usable syntax.
        (when result
@@ -6594,7 +6653,7 @@ necessary only.  This function will be used in file name completion."
       (tramp-message vec 5 "Finding POSIX `id' command")
       (or
        (catch 'id-found
-        (let ((dl tramp-remote-path)
+        (let ((dl (tramp-get-remote-path vec))
               result)
           (while
               (and
@@ -6605,15 +6664,6 @@ necessary only.  This function will be used in file name completion."
             (when (zerop (tramp-send-command-and-check
                           vec (format "%s -u" result)))
               (throw 'id-found result))
-            ;; Remove unneeded directories from path.
-            (while
-                (and
-                 dl
-                 (not
-                  (string-equal
-                   result
-                   (concat (file-name-as-directory (car dl)) "id"))))
-              (setq dl (cdr dl)))
             (setq dl (cdr dl)))))
        (tramp-error vec 'file-error "Couldn't find a POSIX `id' command")))))
 
@@ -6993,7 +7043,6 @@ Only works for Bourne-like shells."
               tramp-default-user-alist
               tramp-rsh-end-of-line
               tramp-default-password-end-of-line
-              tramp-remote-path
               tramp-login-prompt-regexp
               ;; Mask non-7bit characters
               (tramp-password-prompt-regexp . tramp-reporter-dump-variable)
@@ -7308,7 +7357,6 @@ please ensure that the buffers are attached to your email.\n\n")
 ;; * Grok `append' parameter for `write-region'.
 ;; * Test remote ksh or bash for tilde expansion in `tramp-find-shell'?
 ;; * abbreviate-file-name
-;; * grok ~ in tramp-remote-path  (Henrik Holm <henrikh@tele.ntnu.no>)
 ;; * better error checking.  At least whenever we see something
 ;;   strange when doing zerop, we should kill the process and start
 ;;   again.  (Greg Stark)
@@ -7316,7 +7364,7 @@ please ensure that the buffers are attached to your email.\n\n")
 ;;   transfer method to use.  (Greg Stark)
 ;; * Remove unneeded parameters from methods.
 ;; * Invoke rsync once for copying a whole directory hierarchy.
-;;   (Francesco Potort\e,Al\e(B)
+;;   (Francesco Potortì)
 ;; * Make it work for different encodings, and for different file name
 ;;   encodings, too.  (Daniel Pittman)
 ;; * Clean up unused *tramp/foo* buffers after a while.  (Pete Forman)