Change release version from 21.4 to 22.1 throughout.
[bpt/emacs.git] / lisp / net / tramp.el
index 7f04a94..583b5fa 100644 (file)
@@ -1,7 +1,7 @@
 ;;; -*- mode: Emacs-Lisp; coding: iso-2022-7bit; -*-
 ;;; tramp.el --- Transparent Remote Access, Multiple Protocol
 
-;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
 ;; Author: kai.grossjohann@gmx.net
 ;; Keywords: comm, processes
@@ -34,7 +34,7 @@
 ;;
 ;; Notes:
 ;; -----
-;; 
+;;
 ;; This package only works for Emacs 20 and higher, and for XEmacs 21
 ;; and higher.  (XEmacs 20 is missing the `with-timeout' macro.  Emacs
 ;; 19 is reported to have other problems.  For XEmacs 21, you need the
@@ -159,7 +159,8 @@ Nil means to use a separate filename syntax for Tramp.")
 
 (defgroup tramp nil
   "Edit remote files with a combination of rsh and rcp or similar programs."
-  :group 'files)
+  :group 'files
+  :version "22.1")
 
 (defcustom tramp-verbose 9
   "*Verbosity level for tramp.el.  0 means be silent, 10 is most verbose."
@@ -204,7 +205,7 @@ file name, the backup directory is prepended with Tramp file name prefix
 
 gives the same backup policy for Tramp files on their hosts like the
 policy for local files."
-      :type '(repeat 
+      :type '(repeat
              (list (regexp :tag "File regexp")
                    (string :tag "Backup Dir")
                    (set :inline t
@@ -505,7 +506,7 @@ This variable defaults to the value of `tramp-encoding-shell'."
              (tramp-copy-args            nil)
              (tramp-copy-keep-date-arg   "-p")
              (tramp-password-end-of-line "xy")) ;see docstring for "xy"
-     ("fcp"   
+     ("fcp"
              (tramp-connection-function  tramp-open-connection-rsh)
               (tramp-login-program        "fsh")
               (tramp-copy-program         "fcp")
@@ -632,7 +633,7 @@ variable `tramp-methods'."
     ("rsh"    tramp-multi-connect-rlogin "rsh %h -l %u%n")
     ("remsh"  tramp-multi-connect-rlogin "remsh %h -l %u%n")
     ("ssh"    tramp-multi-connect-rlogin "ssh %h -l %u%n")
-    ("ssht"   tramp-multi-connect-rlogin "ssh %h -e none -t -t -l %u%n")     
+    ("ssht"   tramp-multi-connect-rlogin "ssh %h -e none -t -t -l %u%n")
     ("su"     tramp-multi-connect-su     "su - %u%n")
     ("sudo"   tramp-multi-connect-su     "sudo -u %u -s -p Password:%n"))
   "*List of connection functions for multi-hop methods.
@@ -776,7 +777,7 @@ the info pages.")
       "sudo" tramp-completion-function-alist-su)
      (tramp-set-completion-function
       "multi" nil)
-     (tramp-set-completion-function 
+     (tramp-set-completion-function
       "scpx" tramp-completion-function-alist-ssh)
      (tramp-set-completion-function
       "sshx" tramp-completion-function-alist-ssh)
@@ -911,6 +912,15 @@ The answer will be provided by `tramp-action-terminal', which see."
   :group 'tramp
   :type 'regexp)
 
+(defcustom tramp-operation-not-permitted-regexp
+  (concat "\\(" "preserving times.*" "\\|" "set mode" "\\)" ":\\s-*"
+         (regexp-opt '("Operation not permitted") t))
+  "Regular expression matching keep-date problems in (s)cp operations.
+Copying has been performed successfully already, so this message can
+be ignored safely."
+  :group 'tramp
+  :type 'regexp)
+
 (defcustom tramp-process-alive-regexp
   ""
   "Regular expression indicating a process has finished.
@@ -1535,8 +1545,9 @@ cat /tmp/tramp.$$
 rm -f /tmp/tramp.$$
 }"
   "Shell function to implement `uudecode' to standard output.
-Many systems support `uudecode -o -' for this or `uudecode -p', but
-some systems don't, and for them we have this shell function.")
+Many systems support `uudecode -o /dev/stdout' or `uudecode -o -'
+for this or `uudecode -p', but some systems don't, and for them
+we have this shell function.")
 
 ;; Perl script to implement `file-attributes' in a Lisp `read'able
 ;; output.  If you are hacking on this, note that you get *no* output
@@ -1545,20 +1556,92 @@ some systems don't, and for them we have this shell function.")
 ;; The device number is returned as "-1", because there will be a virtual
 ;; device number set in `tramp-handle-file-attributes'
 (defconst tramp-perl-file-attributes "\
-\($f, $n) = @ARGV;
-@s = lstat($f);
-if (($s[2] & 0170000) == 0120000) { $l = readlink($f); $l = \"\\\"$l\\\"\"; }
-elsif (($s[2] & 0170000) == 040000) { $l = \"t\"; }
-else { $l = \"nil\" };
-$u = ($n eq \"nil\") ? $s[4] : getpwuid($s[4]);
-$g = ($n eq \"nil\") ? $s[5] : getgrgid($s[5]);
-printf(\"(%s %u %s %s (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) -1)\\n\",
-$l, $s[3], $u, $g, $s[8] >> 16 & 0xffff, $s[8] & 0xffff,
-$s[9] >> 16 & 0xffff, $s[9] & 0xffff, $s[10] >> 16 & 0xffff, $s[10] & 0xffff,
-$s[7], $s[2], $s[1] >> 16 & 0xffff, $s[1] & 0xffff);"
+@stat = lstat($ARGV[0]);
+if (($stat[2] & 0170000) == 0120000)
+{
+    $type = readlink($ARGV[0]);
+    $type = \"\\\"$type\\\"\";
+}
+elsif (($stat[2] & 0170000) == 040000)
+{
+    $type = \"t\";
+}
+else
+{
+    $type = \"nil\"
+};
+$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\",
+    $type,
+    $stat[3],
+    $uid,
+    $gid,
+    $stat[8] >> 16 & 0xffff,
+    $stat[8] & 0xffff,
+    $stat[9] >> 16 & 0xffff,
+    $stat[9] & 0xffff,
+    $stat[10] >> 16 & 0xffff,
+    $stat[10] & 0xffff,
+    $stat[7],
+    $stat[2],
+    $stat[1] >> 16 & 0xffff,
+    $stat[1] & 0xffff
+);"
   "Perl script to produce output suitable for use with `file-attributes'
 on the remote file system.")
 
+(defconst tramp-perl-directory-files-and-attributes "\
+chdir($ARGV[0]);
+opendir(DIR,\".\");
+@list = readdir(DIR);
+closedir(DIR);
+$n = scalar(@list);
+printf(\"(\\n\");
+for($i = 0; $i < $n; $i++)
+{
+    $filename = $list[$i];
+    @stat = lstat($filename);
+    if (($stat[2] & 0170000) == 0120000)
+    {
+        $type = readlink($filename);
+        $type = \"\\\"$type\\\"\";
+    }
+    elsif (($stat[2] & 0170000) == 040000)
+    {
+        $type = \"t\";
+    }
+    else
+    {
+        $type = \"nil\"
+    };
+    $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\",
+        $filename,
+        $type,
+        $stat[3],
+        $uid,
+        $gid,
+        $stat[8] >> 16 & 0xffff,
+        $stat[8] & 0xffff,
+        $stat[9] >> 16 & 0xffff,
+        $stat[9] & 0xffff,
+        $stat[10] >> 16 & 0xffff,
+        $stat[10] & 0xffff,
+        $stat[7],
+        $stat[2],
+        $stat[1] >> 16 & 0xffff,
+        $stat[1] & 0xffff,
+        $stat[0] >> 16 & 0xffff,
+        $stat[0] & 0xffff);
+}
+printf(\")\\n\");"
+  "Perl script implementing `directory-files-attributes' as Lisp `read'able
+output.")
+
 ;; ;; These two use uu encoding.
 ;; (defvar tramp-perl-encode "%s -e'\
 ;; print qq(begin 644 xxx\n);
@@ -1668,6 +1751,7 @@ while (my $data = <STDIN>) {
 
     my $len = length($pending);
     my $chunk = substr($pending, 0, $len & ~3);
+    $pending = substr($pending, $len & ~3 + 1);
 
     # Easy method: translate from chars to (pregenerated) six-bit packets, join,
     # split in 8-bit chunks and convert back to char.
@@ -1756,8 +1840,8 @@ on the FILENAME argument, even if VISIT was a string.")
     (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
     (file-attributes . tramp-handle-file-attributes)
     (file-modes . tramp-handle-file-modes)
-    (file-directory-files . tramp-handle-file-directory-files)
     (directory-files . tramp-handle-directory-files)
+    (directory-files-and-attributes . tramp-handle-directory-files-and-attributes)
     (file-name-all-completions . tramp-handle-file-name-all-completions)
     (file-name-completion . tramp-handle-file-name-completion)
     (add-name-to-file . tramp-handle-add-name-to-file)
@@ -1769,6 +1853,7 @@ on the FILENAME argument, even if VISIT was a string.")
     (delete-file . tramp-handle-delete-file)
     (directory-file-name . tramp-handle-directory-file-name)
     (shell-command . tramp-handle-shell-command)
+    (process-file . tramp-handle-process-file)
     (insert-directory . tramp-handle-insert-directory)
     (expand-file-name . tramp-handle-expand-file-name)
     (file-local-copy . tramp-handle-file-local-copy)
@@ -1883,7 +1968,10 @@ If VAR is nil, then we bind `v' to the structure and `multi-method',
 
 (put 'with-parsed-tramp-file-name 'lisp-indent-function 2)
 ;; To be activated for debugging containing this macro
-(def-edebug-spec with-parsed-tramp-file-name t)
+;; It works only when VAR is nil.  Otherwise, it can be deactivated by
+;; (put 'with-parsed-tramp-file-name 'edebug-form-spec 0)
+;; I'm too stupid to write a precise SPEC for it.
+(put 'with-parsed-tramp-file-name 'edebug-form-spec t)
 
 (defmacro tramp-let-maybe (variable value &rest body)
   "Let-bind VARIABLE to VALUE in BODY, but only if VARIABLE is not obsolete.
@@ -1976,7 +2064,7 @@ target of the symlink differ."
        (setq filename (tramp-file-name-localname
                        (tramp-dissect-file-name
                         (expand-file-name filename)))))
-    
+
       ;; Right, they are on the same host, regardless of user, method, etc.
       ;; We now make the link on the remote machine. This will occur as the user
       ;; that FILENAME belongs to.
@@ -1985,7 +2073,7 @@ target of the symlink differ."
        l-multi-method l-method l-user l-host
        (format "cd %s && %s -sf %s %s"
                cwd ln
-               filename 
+               filename
                l-localname)
        t)))))
 
@@ -2055,7 +2143,7 @@ target of the symlink differ."
 
 (defun tramp-handle-file-truename (filename &optional counter prev-dirs)
   "Like `file-truename' for tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (let* ((steps        (tramp-split-string localname "/"))
           (localnamedir (tramp-let-maybe directory-sep-char ?/ ;for XEmacs
                           (file-name-as-directory localname)))
@@ -2162,26 +2250,21 @@ target of the symlink differ."
 ;; Daniel Pittman <daniel@danann.net>
 (defun tramp-handle-file-attributes (filename &optional id-format)
   "Like `file-attributes' for tramp files."
-  (let ((nonnumeric (and id-format (equal id-format 'string)))
-       result)
+  (when (file-exists-p filename)
+    ;; file exists, find out stuff
+    (unless id-format (setq id-format 'integer))
     (with-parsed-tramp-file-name filename nil
-      (when (file-exists-p filename)
-       ;; file exists, find out stuff
-       (save-excursion
-         (if (tramp-get-remote-perl multi-method method user host)
-             (setq result
-                   (tramp-handle-file-attributes-with-perl
-                    multi-method method user host localname nonnumeric))
-           (setq result
-                 (tramp-handle-file-attributes-with-ls
-                  multi-method method user host localname nonnumeric)))
-         ;; set virtual device number
-         (setcar (nthcdr 11 result)
-                 (tramp-get-device multi-method method user host)))))
-    result))
+      (save-excursion
+        (tramp-convert-file-attributes
+         multi-method method user host
+         (if (tramp-get-remote-perl multi-method method user host)
+             (tramp-handle-file-attributes-with-perl multi-method method user host
+                                                     localname id-format)
+           (tramp-handle-file-attributes-with-ls multi-method method user host
+                                                 localname id-format)))))))
 
 (defun tramp-handle-file-attributes-with-ls
-  (multi-method method user host localname &optional nonnumeric)
+  (multi-method method user host localname &optional id-format)
   "Implement `file-attributes' for tramp files using the ls(1) command."
   (let (symlinkp dirp
                 res-inode res-filemodes res-numlinks
@@ -2194,7 +2277,7 @@ target of the symlink differ."
      multi-method method user host
      (format "%s %s %s"
             (tramp-get-ls-command multi-method method user host)
-            (if nonnumeric "-ild" "-ildn")
+            (if (eq id-format 'integer) "-ildn" "-ild")
             (tramp-shell-quote-argument localname)))
     (tramp-wait-for-output)
     ;; parse `ls -l' output ...
@@ -2221,7 +2304,7 @@ target of the symlink differ."
     ;; ... uid and gid
     (setq res-uid (read (current-buffer)))
     (setq res-gid (read (current-buffer)))
-    (unless nonnumeric
+    (when (eq id-format 'integer)
       (unless (numberp res-uid) (setq res-uid -1))
       (unless (numberp res-gid) (setq res-gid -1)))
     ;; ... size
@@ -2266,105 +2349,104 @@ target of the symlink differ."
      )))
 
 (defun tramp-handle-file-attributes-with-perl
-  (multi-method method user host localname &optional nonnumeric)
-  "Implement `file-attributes' for tramp files using a Perl script.
-
-The Perl command is sent to the remote machine when the connection
-is initially created and is kept cached by the remote shell."
+  (multi-method method user host localname &optional id-format)
+  "Implement `file-attributes' for tramp files using a Perl script."
   (tramp-message-for-buffer multi-method method user host 10
                            "file attributes with perl: %s"
                            (tramp-make-tramp-file-name
                             multi-method method user host localname))
-  (tramp-send-command
-   multi-method method user host
-   (format "tramp_file_attributes %s %s"
-          (tramp-shell-quote-argument localname) nonnumeric))
+  (tramp-maybe-send-perl-script multi-method method user host
+                               tramp-perl-file-attributes
+                                "tramp_file_attributes")
+  (tramp-send-command multi-method method user host
+                      (format "tramp_file_attributes %s %s"
+                              (tramp-shell-quote-argument localname) id-format))
   (tramp-wait-for-output)
-  (let ((result (read (current-buffer))))
-    (setcar (nthcdr 8 result)
-           (tramp-file-mode-from-int (nth 8 result)))
-    result))
-
-(defun tramp-get-device (multi-method method user host)
-  "Returns the virtual device number.
-If it doesn't exist, generate a new one."
-  (let ((string (tramp-make-tramp-file-name multi-method method user host "")))
-    (unless (assoc string tramp-devices)
-      (add-to-list 'tramp-devices
-                  (list string (length tramp-devices))))
-    (list -1 (nth 1 (assoc string tramp-devices)))))
+  (read (current-buffer)))
 
 (defun tramp-handle-set-visited-file-modtime (&optional time-list)
   "Like `set-visited-file-modtime' for tramp files."
   (unless (buffer-file-name)
     (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file"
           (buffer-name)))
-  (when time-list
-    (tramp-run-real-handler 'set-visited-file-modtime (list time-list)))
-  (let ((f (buffer-file-name))
-       (coding-system-used nil))
-    (with-parsed-tramp-file-name f nil
-      (let* ((attr (file-attributes f))
-            (modtime (nth 5 attr)))
-       ;; We use '(0 0) as a don't-know value.  See also
-       ;; `tramp-handle-file-attributes-with-ls'.
-       (when (boundp 'last-coding-system-used)
-         (setq coding-system-used last-coding-system-used))
-       (if (not (equal modtime '(0 0)))
-           (tramp-run-real-handler 'set-visited-file-modtime (list modtime))
-         (save-excursion
-           (tramp-send-command
-            multi-method method user host
-            (format "%s -ild %s"
-                    (tramp-get-ls-command multi-method method user host)
-                    (tramp-shell-quote-argument localname)))
-           (tramp-wait-for-output)
-           (setq attr (buffer-substring (point)
-                                        (progn (end-of-line) (point)))))
-         (setq tramp-buffer-file-attributes attr))
-       (when (boundp 'last-coding-system-used)
-         (setq last-coding-system-used coding-system-used))
-       nil))))
+  (if time-list
+      (tramp-run-real-handler 'set-visited-file-modtime (list time-list))
+    (let ((f (buffer-file-name))
+         (coding-system-used nil))
+      (with-parsed-tramp-file-name f nil
+       (let* ((attr (file-attributes f))
+              ;; '(-1 65535) means file doesn't exists yet.
+              (modtime (or (nth 5 attr) '(-1 65535))))
+         ;; We use '(0 0) as a don't-know value.  See also
+         ;; `tramp-handle-file-attributes-with-ls'.
+         (when (boundp 'last-coding-system-used)
+           (setq coding-system-used last-coding-system-used))
+         (if (not (equal modtime '(0 0)))
+             (tramp-run-real-handler 'set-visited-file-modtime (list modtime))
+           (save-excursion
+             (tramp-send-command
+              multi-method method user host
+              (format "%s -ild %s"
+                      (tramp-get-ls-command multi-method method user host)
+                      (tramp-shell-quote-argument localname)))
+             (tramp-wait-for-output)
+             (setq attr (buffer-substring (point)
+                                          (progn (end-of-line) (point)))))
+           (setq tramp-buffer-file-attributes attr))
+         (when (boundp 'last-coding-system-used)
+           (setq last-coding-system-used coding-system-used))
+       nil)))))
 
 ;; CCC continue here
 
 ;; This function makes the same assumption as
 ;; `tramp-handle-set-visited-file-modtime'.
 (defun tramp-handle-verify-visited-file-modtime (buf)
-  "Like `verify-visited-file-modtime' for tramp files."
+  "Like `verify-visited-file-modtime' for tramp files.
+At the time `verify-visited-file-modtime' calls this function, we
+already know that the buffer is visiting a file and that
+`visited-file-modtime' does not return 0.  Do not call this
+function directly, unless those two cases are already taken care
+of."
   (with-current-buffer buf
-    (let ((f (buffer-file-name)))
-      (with-parsed-tramp-file-name f nil
-       (let* ((attr (file-attributes f))
-              (modtime (nth 5 attr)))
-         (cond ((and attr (not (equal modtime '(0 0))))
-                ;; Why does `file-attributes' return a list (HIGH
-                ;; LOW), but `visited-file-modtime' returns a cons
-                ;; (HIGH . LOW)?
-                (let ((mt (visited-file-modtime)))
-                  (< (abs (tramp-time-diff
-                           modtime
-                           ;; For compatibility, deal with both the old
-                           ;; (HIGH . LOW) and the new (HIGH LOW)
-                           ;; return values of `visited-file-modtime'.
-                           (if (atom (cdr mt))
-                               (list (car mt) (cdr mt))
-                             mt)))
-                     2)))
-               (attr
-                (save-excursion
-                  (tramp-send-command
-                   multi-method method user host
-                   (format "%s -ild %s"
-                           (tramp-get-ls-command multi-method method
-                                                 user host)
-                           (tramp-shell-quote-argument localname)))
-                  (tramp-wait-for-output)
-                  (setq attr (buffer-substring
-                              (point) (progn (end-of-line) (point)))))
-                (equal tramp-buffer-file-attributes attr))
-               ;; If file does not exist, say it is not modified.
-               (t nil)))))))
+    ;; There is no file visiting the buffer, or the buffer has no
+    ;; recorded last modification time.
+    (if (or (not (buffer-file-name))
+           (eq (visited-file-modtime) 0))
+      t
+      (let ((f (buffer-file-name)))
+       (with-parsed-tramp-file-name f nil
+         (let* ((attr (file-attributes f))
+                (modtime (nth 5 attr))
+                (mt (visited-file-modtime)))
+
+           (cond
+            ;; file exists, and has a known modtime.
+            ((and attr (not (equal modtime '(0 0))))
+             (< (abs (tramp-time-diff
+                      modtime
+                      ;; For compatibility, deal with both the old
+                      ;; (HIGH . LOW) and the new (HIGH LOW)
+                      ;; return values of `visited-file-modtime'.
+                      (if (atom (cdr mt))
+                          (list (car mt) (cdr mt))
+                        mt)))
+                2))
+            ;; modtime has the don't know value.
+            (attr
+             (save-excursion
+               (tramp-send-command
+                multi-method method user host
+                (format "%s -ild %s"
+                        (tramp-get-ls-command multi-method method user host)
+                        (tramp-shell-quote-argument localname)))
+               (tramp-wait-for-output)
+               (setq attr (buffer-substring
+                           (point) (progn (end-of-line) (point)))))
+             (equal tramp-buffer-file-attributes attr))
+            ;; If file does not exist, say it is not modified
+            ;; if and only if that agrees with the buffer's record.
+            (t (equal mt '(-1 65535))))))))))
 
 (defadvice clear-visited-file-modtime (after tramp activate)
   "Set `tramp-buffer-file-attributes' back to nil.
@@ -2427,7 +2509,7 @@ if the remote host can't provide the modtime."
                 (fa2 (file-attributes file2)))
             (if (and (not (equal (nth 5 fa1) '(0 0)))
                      (not (equal (nth 5 fa2) '(0 0))))
-                (> 0 (tramp-time-diff (nth 5 fa1) (nth 5 fa2)))
+                (> 0 (tramp-time-diff (nth 5 fa2) (nth 5 fa1)))
               ;; If one of them is the dont-know value, then we can
               ;; still try to run a shell command on the remote host.
               ;; However, this only works if both files are Tramp
@@ -2613,6 +2695,38 @@ if the remote host can't provide the modtime."
                  (push item result)))))))
       result)))
 
+(defun tramp-handle-directory-files-and-attributes
+  (directory &optional full match nosort id-format)
+  "Like `directory-files-and-attributes' for tramp files."
+  (when (tramp-handle-file-exists-p directory)
+    (save-excursion
+      (setq directory (tramp-handle-expand-file-name directory))
+      (with-parsed-tramp-file-name directory nil
+        (tramp-maybe-send-perl-script multi-method method user host
+                                     tramp-perl-directory-files-and-attributes
+                                      "tramp_directory_files_and_attributes")
+        (tramp-send-command multi-method method user host
+                            (format "tramp_directory_files_and_attributes %s %s"
+                                    (tramp-shell-quote-argument localname)
+                                    (or id-format 'integer)))
+        (tramp-wait-for-output)
+        (let* ((root (cons nil (read (current-buffer))))
+               (cell root))
+          (while (cdr cell)
+            (if (and match (not (string-match match (caadr cell))))
+                ;; Remove from list
+                (setcdr cell (cddr cell))
+              ;; Include in list
+              (setq cell (cdr cell))
+              (let ((l (car cell)))
+                (tramp-convert-file-attributes multi-method method user host
+                                               (cdr l))
+                ;; If FULL, make file name absolute
+                (when full (setcar l (concat directory "/" (car l)))))))
+          (if nosort
+              (cdr root)
+            (sort (cdr root) (lambda (x y) (string< (car x) (car y))))))))))
+
 ;; This function should return "foo/" for directories and "bar" for
 ;; files.  We use `ls -ad' to get a list of files (including
 ;; directories), and `find . -type d \! -name . -prune' to get a list
@@ -2652,7 +2766,7 @@ if the remote host can't provide the modtime."
            (push (buffer-substring (point)
                                    (tramp-line-end-position))
                  result))
-       
+
          (tramp-send-command multi-method method user host "cd")
          (tramp-wait-for-output)
 
@@ -2717,10 +2831,8 @@ if the remote host can't provide the modtime."
   ;; At least one file a tramp file?
   (if (or (tramp-tramp-file-p filename)
           (tramp-tramp-file-p newname))
-      (let ((modes (file-modes filename)))
-       (tramp-do-copy-or-rename-file
-        'copy filename newname ok-if-already-exists keep-date)
-       (set-file-modes newname modes))
+      (tramp-do-copy-or-rename-file
+       'copy filename newname ok-if-already-exists keep-date)
     (tramp-run-real-handler
      'copy-file
      (list filename newname ok-if-already-exists keep-date))))
@@ -2868,8 +2980,9 @@ KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
       (when keep-date
        (when (and (not (null modtime))
                   (not (equal modtime '(0 0))))
-         (tramp-touch newname modtime))
-       (set-file-modes newname (file-modes filename))))
+         (tramp-touch newname modtime)))
+      ;; Set the mode.
+      (set-file-modes newname (file-modes filename)))
     ;; If the operation was `rename', delete the original file.
     (unless (eq op 'copy)
       (delete-file filename))))
@@ -2889,15 +3002,34 @@ If KEEP-DATE is non-nil, preserve the time stamp when copying."
                        "Unknown operation `%s', must be `copy' or `rename'"
                        op)))))
     (save-excursion
-      (tramp-barf-unless-okay
+      (tramp-send-command
        multi-method method user host
        (format "%s %s %s"
                cmd
                (tramp-shell-quote-argument localname1)
-               (tramp-shell-quote-argument localname2))
-       nil 'file-error
-       "Copying directly failed, see buffer `%s' for details."
-       (buffer-name)))))
+               (tramp-shell-quote-argument localname2)))
+      (tramp-wait-for-output)
+      (goto-char (point-min))
+      (unless
+         (or
+          (and (eq op 'copy) keep-date
+               ;; Mask cp -f error.
+               (re-search-forward tramp-operation-not-permitted-regexp nil t))
+          (zerop (tramp-send-command-and-check
+                  multi-method method user host nil nil)))
+       (pop-to-buffer (current-buffer))
+       (signal 'file-error
+               (format "Copying directly failed, see buffer `%s' for details."
+                       (buffer-name)))))
+    ;; Set the mode.
+    ;; CCC: Maybe `chmod --reference=localname1 localname2' could be used
+    ;;      where available?
+    (unless (or (eq op 'rename) keep-date)
+      (set-file-modes
+       (tramp-make-tramp-file-name multi-method method user host localname2)
+       (file-modes
+       (tramp-make-tramp-file-name
+        multi-method method user host localname1))))))
 
 (defun tramp-do-copy-or-rename-file-out-of-band (op filename newname keep-date)
   "Invoke rcp program to copy.
@@ -2995,6 +3127,12 @@ be a local filename.  The method used must be an out-of-band method."
 
     ;; Use an asynchronous process.  By this, password can be handled.
     (save-excursion
+
+      ;; Check for program.
+      (when (and (fboundp 'executable-find)
+                (not (executable-find copy-program)))
+       (error "Cannot find copy program: %s" copy-program))
+
       (set-buffer trampbuf)
       (setq tramp-current-multi-method multi-method
            tramp-current-method method
@@ -3011,7 +3149,11 @@ be a local filename.  The method used must be an out-of-band method."
                               tramp-actions-copy-out-of-band))
       (kill-buffer trampbuf)
       (tramp-message
-       5 "Transferring %s to file %s...done" filename newname))
+       5 "Transferring %s to file %s...done" filename newname)
+
+      ;; Set the mode.
+      (unless keep-date
+       (set-file-modes newname (file-modes filename))))
 
     ;; If the operation was `rename', delete the original file.
     (unless (eq op 'copy)
@@ -3069,15 +3211,15 @@ This is like `dired-recursive-delete-directory' for tramp files."
         'file-error
         (list "Removing old file name" "no such directory" filename)))
     ;; Which is better, -r or -R? (-r works for me <daniel@danann.net>)
-    (tramp-send-command multi-method method user host 
+    (tramp-send-command multi-method method user host
                        (format "rm -r %s" (tramp-shell-quote-argument localname)))
     ;; Wait for the remote system to return to us...
     ;; This might take a while, allow it plenty of time.
     (tramp-wait-for-output 120)
     ;; Make sure that it worked...
     (and (file-exists-p filename)
-        (error "Failed to recusively delete %s" filename))))
-        
+        (error "Failed to recursively delete %s" filename))))
+
 (defun tramp-handle-dired-call-process (program discard &rest arguments)
   "Like `dired-call-process' for tramp files."
   (with-parsed-tramp-file-name default-directory nil
@@ -3099,7 +3241,7 @@ This is like `dired-recursive-delete-directory' for tramp files."
          (tramp-send-command-and-check multi-method method user host nil)
        (tramp-send-command multi-method method user host "cd")
        (tramp-wait-for-output)))))
-        
+
 (defun tramp-handle-dired-compress-file (file &rest ok-flag)
   "Like `dired-compress-file' for tramp files."
   ;; OK-FLAG is valid for XEmacs only, but not implemented.
@@ -3171,83 +3313,87 @@ This is like `dired-recursive-delete-directory' for tramp files."
 (defun tramp-handle-insert-directory
   (filename switches &optional wildcard full-directory-p)
   "Like `insert-directory' for tramp files."
-  ;; For the moment, we assume that the remote "ls" program does not
-  ;; grok "--dired".  In the future, we should detect this on
-  ;; connection setup.
-  (when (string-match "^--dired\\s-+" switches)
-    (setq switches (replace-match "" nil t switches)))
-  (setq filename (expand-file-name filename))
-  (with-parsed-tramp-file-name filename nil
-    (tramp-message-for-buffer
-     multi-method method user host 10
-     "Inserting directory `ls %s %s', wildcard %s, fulldir %s"
-     switches filename (if wildcard "yes" "no")
-     (if full-directory-p "yes" "no"))
-    (when wildcard
-      (setq wildcard (file-name-nondirectory localname))
-      (setq localname (file-name-directory localname)))
-    (when (listp switches)
-      (setq switches (mapconcat 'identity switches " ")))
-    (unless full-directory-p
-      (setq switches (concat "-d " switches)))
-    (when wildcard
-      (setq switches (concat switches " " wildcard)))
-    (save-excursion
-      ;; If `full-directory-p', we just say `ls -l FILENAME'.
-      ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'.
-      (if full-directory-p
-         (tramp-send-command
-          multi-method method user host
-          (format "%s %s %s"
-                  (tramp-get-ls-command multi-method method user host)
-                  switches
-                  (if wildcard
-                      localname
-                    (tramp-shell-quote-argument (concat localname ".")))))
-       (tramp-barf-unless-okay
-        multi-method method user host
-        (format "cd %s" (tramp-shell-quote-argument
-                         (file-name-directory localname)))
-        nil 'file-error
-        "Couldn't `cd %s'"
-        (tramp-shell-quote-argument (file-name-directory localname)))
-       (tramp-send-command
-        multi-method method user host
-        (format "%s %s %s"
-                (tramp-get-ls-command multi-method method user host)
-                switches
-                (if wildcard
-                    localname
-                  (tramp-shell-quote-argument
-                   (file-name-nondirectory localname))))))
-      (sit-for 1)                      ;needed for rsh but not ssh?
-      (tramp-wait-for-output))
-    ;; The following let-binding is used by code that's commented
-    ;; out.  Let's leave the let-binding in for a while to see
-    ;; that the commented-out code is really not needed.  Commenting-out
-    ;; happened on 2003-03-13.
-    (let ((old-pos (point)))
-      (insert-buffer-substring
-       (tramp-get-buffer multi-method method user host))
-      ;; On XEmacs, we want to call (exchange-point-and-mark t), but
-      ;; that doesn't exist on Emacs, so we use this workaround instead.
-      ;; Since zmacs-region-stays doesn't exist in Emacs, this ought to
-      ;; be safe.  Thanks to Daniel Pittman <daniel@danann.net>.
-      ;;     (let ((zmacs-region-stays t))
-      ;;       (exchange-point-and-mark))
+  (if (and (boundp 'ls-lisp-use-insert-directory-program)
+           (not ls-lisp-use-insert-directory-program))
+      (tramp-run-real-handler 'insert-directory
+                              (list filename switches wildcard full-directory-p))
+    ;; For the moment, we assume that the remote "ls" program does not
+    ;; grok "--dired".  In the future, we should detect this on
+    ;; connection setup.
+    (when (string-match "^--dired\\s-+" switches)
+      (setq switches (replace-match "" nil t switches)))
+    (setq filename (expand-file-name filename))
+    (with-parsed-tramp-file-name filename nil
+      (tramp-message-for-buffer
+       multi-method method user host 10
+       "Inserting directory `ls %s %s', wildcard %s, fulldir %s"
+       switches filename (if wildcard "yes" "no")
+       (if full-directory-p "yes" "no"))
+      (when wildcard
+        (setq wildcard (file-name-nondirectory localname))
+        (setq localname (file-name-directory localname)))
+      (when (listp switches)
+        (setq switches (mapconcat 'identity switches " ")))
+      (unless full-directory-p
+        (setq switches (concat "-d " switches)))
+      (when wildcard
+        (setq switches (concat switches " " wildcard)))
       (save-excursion
-       (tramp-send-command multi-method method user host "cd")
-       (tramp-wait-for-output))
-      ;; For the time being, the XEmacs kludge is commented out.
-      ;; Please test it on various XEmacs versions to see if it works.
-;;       ;; Another XEmacs specialty follows.  What's the right way to do
-;;       ;; it?
-;;       (when (and (featurep 'xemacs)
-;;              (eq major-mode 'dired-mode))
-;;     (save-excursion
-;;       (require 'dired)
-;;       (dired-insert-set-properties old-pos (point))))
-      )))
+        ;; If `full-directory-p', we just say `ls -l FILENAME'.
+        ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'.
+        (if full-directory-p
+            (tramp-send-command
+             multi-method method user host
+             (format "%s %s %s"
+                     (tramp-get-ls-command multi-method method user host)
+                     switches
+                     (if wildcard
+                         localname
+                       (tramp-shell-quote-argument (concat localname ".")))))
+          (tramp-barf-unless-okay
+           multi-method method user host
+           (format "cd %s" (tramp-shell-quote-argument
+                            (file-name-directory localname)))
+           nil 'file-error
+           "Couldn't `cd %s'"
+           (tramp-shell-quote-argument (file-name-directory localname)))
+          (tramp-send-command
+           multi-method method user host
+           (format "%s %s %s"
+                   (tramp-get-ls-command multi-method method user host)
+                   switches
+                   (if wildcard
+                       localname
+                     (tramp-shell-quote-argument
+                      (file-name-nondirectory localname))))))
+        (sit-for 1)                    ;needed for rsh but not ssh?
+        (tramp-wait-for-output))
+      ;; The following let-binding is used by code that's commented
+      ;; out.  Let's leave the let-binding in for a while to see
+      ;; that the commented-out code is really not needed.  Commenting-out
+      ;; happened on 2003-03-13.
+      (let ((old-pos (point)))
+        (insert-buffer-substring
+         (tramp-get-buffer multi-method method user host))
+        ;; On XEmacs, we want to call (exchange-point-and-mark t), but
+        ;; that doesn't exist on Emacs, so we use this workaround instead.
+        ;; Since zmacs-region-stays doesn't exist in Emacs, this ought to
+        ;; be safe.  Thanks to Daniel Pittman <daniel@danann.net>.
+        ;;     (let ((zmacs-region-stays t))
+        ;;       (exchange-point-and-mark))
+        (save-excursion
+          (tramp-send-command multi-method method user host "cd")
+          (tramp-wait-for-output))
+        ;; For the time being, the XEmacs kludge is commented out.
+        ;; Please test it on various XEmacs versions to see if it works.
+        ;;       ;; Another XEmacs specialty follows.  What's the right way to do
+        ;;       ;; it?
+        ;;       (when (and (featurep 'xemacs)
+        ;;              (eq major-mode 'dired-mode))
+        ;;     (save-excursion
+        ;;       (require 'dired)
+        ;;       (dired-insert-set-properties old-pos (point))))
+        ))))
 
 ;; Continuation of kluge to pacify byte-compiler.
 ;;(eval-when-compile
@@ -3355,7 +3501,7 @@ Used in `tramp-handle-shell-command'")
 This will break if COMMAND prints a newline, followed by the value of
 `tramp-end-of-output', followed by another newline."
   ;; Asynchronous processes are far from being perfect.  But it works at least
-  ;; for `find-grep-dired' and `find-name-dired' in Emacs 21.4.
+  ;; for `find-grep-dired' and `find-name-dired' in Emacs 22.1.
   (if (tramp-tramp-file-p default-directory)
       (with-parsed-tramp-file-name default-directory nil
        (let ((asynchronous (string-match "[ \t]*&[ \t]*\\'" command))
@@ -3457,6 +3603,18 @@ This will break if COMMAND prints a newline, followed by the value of
     (tramp-run-real-handler 'shell-command
                            (list command output-buffer error-buffer))))
 
+(defun tramp-handle-process-file (program &optional infile buffer display &rest args)
+  "Like `process-file' for Tramp files."
+  (when infile (error "Implementation does not handle input from file"))
+  (when (and (numberp buffer) (zerop buffer))
+    (error "Implementation does not handle immediate return"))
+  (when (consp buffer) (error "Implementation does not handle error files"))
+  (shell-command
+   (mapconcat 'tramp-shell-quote-argument
+              (cons program args)
+              " ")
+   buffer))
+
 ;; File Editing.
 
 (defsubst tramp-make-temp-file ()
@@ -3806,8 +3964,11 @@ This will break if COMMAND prints a newline, followed by the value of
       (unless (equal curbuf (current-buffer))
        (error "Buffer has changed from `%s' to `%s'"
               curbuf (current-buffer)))
-      (when (eq visit t)
-       (set-visited-file-modtime))
+      (when (or (eq visit t) (stringp visit))
+       (set-visited-file-modtime
+        ;; We must pass modtime explicitely, because filename can be different
+        ;; from (buffer-file-name), f.e. if `file-precious-flag' is set.
+        (nth 5 (file-attributes filename))))
       ;; Make `last-coding-system-used' have the right value.
       (when (boundp 'last-coding-system-used)
        (setq last-coding-system-used coding-system-used))
@@ -3944,7 +4105,11 @@ ARGS are the arguments OPERATION has been called with."
      (if (bufferp (nth 0 args)) (nth 0 args) (current-buffer))))
    ; COMMAND
    ((member operation
-           (list 'dired-call-process 'shell-command
+           (list 'dired-call-process-command
+                  ; Emacs only
+                 'shell
+                  ; Post Emacs 21.3 only
+                  'process-file
                  ; XEmacs only
                  'dired-print-file 'dired-shell-call-process))
     default-directory)
@@ -4048,7 +4213,7 @@ Falls back to normal file name handler if no tramp file name handler exists."
 
 (defun tramp-repair-jka-compr ()
   "If jka-compr is already loaded, move it to the front of
-`file-name-handler-alist'.  On Emacs 21.4 or so this will not be
+`file-name-handler-alist'.  On Emacs 22.1 or so this will not be
 necessary anymore."
   (let ((jka (rassoc 'jka-compr-handler file-name-handler-alist)))
     (when jka
@@ -4128,7 +4293,7 @@ necessary anymore."
 ;; `tramp-completion-file-name-regexp-unified' aren't different.
 ;; If nil, `tramp-completion-run-real-handler' is called (i.e. forwarding to
 ;; `tramp-file-name-handler'). Otherwise, it takes `tramp-run-real-handler'.
-;; Using `last-input-event' is a little bit risky, because completing a file 
+;; Using `last-input-event' is a little bit risky, because completing a file
 ;; might require loading other files, like "~/.netrc", and for them it
 ;; shouldn't be decided based on that variable. On the other hand, those files
 ;; shouldn't have partial tramp file name syntax. Maybe another variable should
@@ -4232,7 +4397,7 @@ necessary anymore."
                               (funcall (nth 0 x) (nth 1 x)))))
               (tramp-get-completion-function m))
 
-             (setq result (append result 
+             (setq result (append result
                (mapcar
                 (lambda (x)
                   (tramp-get-completion-user-host
@@ -4273,7 +4438,7 @@ necessary anymore."
 ;; [nil nil "x" nil nil]
 ;; [nil "x" nil nil nil]
 
-;; "/x:"                    "/x:y"                   "/x:y:"                 
+;; "/x:"                    "/x:y"                   "/x:y:"
 ;; [nil nil nil "x" ""]     [nil nil nil "x" "y"]    [nil "x" nil "y" ""]
 ;;       "/[x/"                   "/[x/y"
 ;; [nil "x" nil "" nil]     [nil "x" nil "y" nil]
@@ -4647,6 +4812,29 @@ User may be nil."
 
 ;;; Internal Functions:
 
+(defun tramp-maybe-send-perl-script (multi-method method user host script name)
+  "Define in remote shell function NAME implemented as perl SCRIPT.
+Only send the definition if it has not already been done.
+Function may have 0-3 parameters."
+  (let ((remote-perl (tramp-get-remote-perl multi-method method user host)))
+    (unless remote-perl (error "No remote perl"))
+    (let ((perl-scripts (tramp-get-connection-property "perl-scripts" nil
+                                                       multi-method method user host)))
+      (unless (memq name perl-scripts)
+        (with-current-buffer (tramp-get-buffer multi-method method user host)
+          (tramp-message 5 (concat "Sending the Perl script `" name "'..."))
+          (tramp-send-string multi-method method user host
+                             (concat name
+                                     " () {\n"
+                                     remote-perl
+                                     " -e '"
+                                     script
+                                     "' \"$1\" \"$2\" \"$3\" 2>/dev/null\n}"))
+          (tramp-wait-for-output)
+          (tramp-set-connection-property "perl-scripts" (cons name perl-scripts)
+                                         multi-method method user host)
+          (tramp-message 5 (concat "Sending the Perl script `" name "'...done.")))))))
+
 (defun tramp-set-auto-save ()
   (when (and (buffer-file-name)
              (tramp-tramp-file-p (buffer-file-name))
@@ -4719,7 +4907,7 @@ TIME is an Emacs internal time value as returned by `current-time'."
                        "touch" nil (current-buffer) nil "-t" touch-time file))
              (pop-to-buffer (current-buffer))
              (error "tramp-touch: touch failed"))))))
+
 (defun tramp-buffer-name (multi-method method user host)
   "A name for the connection buffer for USER at HOST using METHOD."
   (if multi-method
@@ -4753,7 +4941,10 @@ USER the array of user names, HOST the array of host names."
 
 (defun tramp-get-buffer (multi-method method user host)
   "Get the connection buffer to be used for USER at HOST using METHOD."
-  (get-buffer-create (tramp-buffer-name multi-method method user host)))
+  (with-current-buffer
+      (get-buffer-create (tramp-buffer-name multi-method method user host))
+    (setq buffer-undo-list t)
+    (current-buffer)))
 
 (defun tramp-debug-buffer-name (multi-method method user host)
   "A name for the debug buffer for USER at HOST using METHOD."
@@ -4767,7 +4958,11 @@ USER the array of user names, HOST the array of host names."
 
 (defun tramp-get-debug-buffer (multi-method method user host)
   "Get the debug buffer for USER at HOST using METHOD."
-  (get-buffer-create (tramp-debug-buffer-name multi-method method user host)))
+  (with-current-buffer
+      (get-buffer-create
+       (tramp-debug-buffer-name multi-method method user host))
+    (setq buffer-undo-list t)
+    (current-buffer)))
 
 (defun tramp-find-executable (multi-method method user host
                                          progname dirlist ignore-tilde)
@@ -4877,7 +5072,7 @@ file exists and nonzero exit status otherwise."
                   (file-exists-p existing)
                   (not (file-exists-p nonexisting))))
       (error "Couldn't find command to check if file exists."))))
-    
+
 
 ;; CCC test ksh or bash found for tilde expansion?
 (defun tramp-find-shell (multi-method method user host)
@@ -4976,9 +5171,9 @@ Returns nil if none was found, else the command is returned."
    (tramp-check-ls-commands multi-method method user host "gnuls" tramp-remote-path)
    (tramp-check-ls-commands multi-method method user host "gls" tramp-remote-path)))
 
-;; ------------------------------------------------------------ 
-;; -- Functions for establishing connection -- 
-;; ------------------------------------------------------------ 
+;; ------------------------------------------------------------
+;; -- Functions for establishing connection --
+;; ------------------------------------------------------------
 
 ;; The following functions are actions to be taken when seeing certain
 ;; prompts from the remote host.  See the variable
@@ -5059,8 +5254,16 @@ The terminal type can be configured with `tramp-terminal-type'."
        ((or (and (memq (process-status p) '(stop exit))
                  (not (zerop (process-exit-status p))))
             (memq (process-status p) '(signal)))
-        (tramp-message 9 "Process has died.")
-        (throw 'tramp-action 'process-died))
+        ;; `scp' could have copied correctly, but set modes could have failed.
+        ;; This can be ignored.
+        (goto-char (point-min))
+        (if (re-search-forward tramp-operation-not-permitted-regexp nil t)
+            (progn
+              (tramp-message 10 "'set mode' error ignored.")
+              (tramp-message 9 "Process has finished.")
+              (throw 'tramp-action 'ok))
+          (tramp-message 9 "Process has died.")
+          (throw 'tramp-action 'process-died)))
        (t nil)))
 
 ;; The following functions are specifically for multi connections.
@@ -5219,7 +5422,7 @@ Maybe the different regular expressions need to be tuned.
     (when multi-method
       (error "Cannot multi-connect using telnet connection method"))
     (tramp-pre-connection multi-method method user host)
-    (tramp-message 7 "Opening connection for %s@%s using %s..." 
+    (tramp-message 7 "Opening connection for %s@%s using %s..."
                   (or user (user-login-name)) host method)
     (let ((process-environment (copy-sequence process-environment)))
       (setenv "TERM" tramp-terminal-type)
@@ -5253,7 +5456,7 @@ Maybe the different regular expressions need to be tuned.
          p multi-method method user host)
         (tramp-post-connection multi-method method user host)))))
 
-           
+
 (defun tramp-open-connection-rsh (multi-method method user host)
   "Open a connection using an rsh METHOD.
 This starts the command `rsh HOST -l USER'[*], then waits for a remote
@@ -5278,7 +5481,7 @@ arguments, and xx will be used as the host name to connect to.
       (error "Cannot multi-connect using rsh connection method"))
     (tramp-pre-connection multi-method method user host)
     (if (and user (not (string= user "")))
-       (tramp-message 7 "Opening connection for %s@%s using %s..." 
+       (tramp-message 7 "Opening connection for %s@%s using %s..."
                       user host method)
       (tramp-message 7 "Opening connection at %s using %s..." host method))
     (let ((process-environment (copy-sequence process-environment))
@@ -5307,9 +5510,9 @@ arguments, and xx will be used as the host name to connect to.
                                                   (> emacs-major-version 20))
                                        tramp-dos-coding-system))
              (p (if (and user (not (string= user "")))
-                    (apply #'start-process bufnam buf login-program  
+                    (apply #'start-process bufnam buf login-program
                            real-host "-l" user login-args)
-                  (apply #'start-process bufnam buf login-program 
+                  (apply #'start-process bufnam buf login-program
                          real-host login-args)))
              (found nil))
         (tramp-set-process-query-on-exit-flag p nil)
@@ -5379,10 +5582,10 @@ prompt than you do, so it is not at all unlikely that the variable
                               tramp-actions-before-shell)
         (tramp-open-connection-setup-interactive-shell
          p multi-method method user host)
-        (tramp-post-connection multi-method method 
+        (tramp-post-connection multi-method method
                                user host)))))
 
-;; HHH: Not Changed.  Multi method.  It is not clear to me how this can 
+;; HHH: Not Changed.  Multi method.  It is not clear to me how this can
 ;;      handle not giving a user name in the "file name".
 ;;
 ;;      This is more difficult than for the single-hop method.  In the
@@ -5452,7 +5655,7 @@ log in as u2 to h2."
         (tramp-post-connection multi-method method user host)))))
 
 ;; HHH: Changed.  Multi method.  Don't know how to handle this in the case
-;;      of no user name provided.  Hack to make it work as it did before:  
+;;      of no user name provided.  Hack to make it work as it did before:
 ;;      changed `user' to `(or user (user-login-name))' in the places where
 ;;      the value is actually used.
 (defun tramp-multi-connect-telnet (p method user host command)
@@ -5474,8 +5677,8 @@ If USER is nil, uses the return value of (user-login-name) instead."
     (tramp-process-multi-actions p method user host
                                 tramp-multi-actions)))
 
-;; HHH: Changed.  Multi method.  Don't know how to handle this in the case 
-;;      of no user name provided.  Hack to make it work as it did before:  
+;; HHH: Changed.  Multi method.  Don't know how to handle this in the case
+;;      of no user name provided.  Hack to make it work as it did before:
 ;;      changed `user' to `(or user (user-login-name))' in the places where
 ;;      the value is actually used.
 (defun tramp-multi-connect-rlogin (p method user host command)
@@ -5500,8 +5703,8 @@ If USER is nil, uses the return value of (user-login-name) instead."
     (tramp-process-multi-actions p method user host
                                 tramp-multi-actions)))
 
-;; HHH: Changed.  Multi method.  Don't know how to handle this in the case 
-;;      of no user name provided.  Hack to make it work as it did before:  
+;; HHH: Changed.  Multi method.  Don't know how to handle this in the case
+;;      of no user name provided.  Hack to make it work as it did before:
 ;;      changed `user' to `(or user (user-login-name))' in the places where
 ;;      the value is actually used.
 (defun tramp-multi-connect-su (p method user host command)
@@ -5827,6 +6030,7 @@ locale to C and sets up the remote shell search path."
   (tramp-wait-for-output)
   ;; Find a `perl'.
   (erase-buffer)
+  (tramp-set-connection-property "perl-scripts" nil multi-method method user host)
   (let ((tramp-remote-perl
         (or (tramp-find-executable multi-method method user host
                                    "perl5" tramp-remote-path nil)
@@ -5835,47 +6039,37 @@ locale to C and sets up the remote shell search path."
     (when tramp-remote-perl
       (tramp-set-connection-property "perl" tramp-remote-perl
                                     multi-method method user host)
-      ;; Set up stat in Perl if we can.
-      (when tramp-remote-perl
-       (tramp-message 5 "Sending the Perl `file-attributes' implementation.")
-       (tramp-send-string
-        multi-method method user host
-        (concat "tramp_file_attributes () {\n"
-                tramp-remote-perl
-                " -e '" tramp-perl-file-attributes "' $1 $2 2>/dev/null\n"
-                "}"))
-       (tramp-wait-for-output)
-       (unless (tramp-method-out-of-band-p multi-method method user host)
-         (tramp-message 5 "Sending the Perl `mime-encode' implementations.")
-         (tramp-send-string
-          multi-method method user host
-          (concat "tramp_encode () {\n"
-                  (format tramp-perl-encode tramp-remote-perl)
-                  " 2>/dev/null"
-                  "\n}"))
-         (tramp-wait-for-output)
-         (tramp-send-string
-          multi-method method user host
-          (concat "tramp_encode_with_module () {\n"
-                  (format tramp-perl-encode-with-module tramp-remote-perl)
-                  " 2>/dev/null"
-                  "\n}"))
-         (tramp-wait-for-output)
-         (tramp-message 5 "Sending the Perl `mime-decode' implementations.")
-         (tramp-send-string
-          multi-method method user host
-          (concat "tramp_decode () {\n"
-                  (format tramp-perl-decode tramp-remote-perl)
-                  " 2>/dev/null"
-                  "\n}"))
-         (tramp-wait-for-output)
-         (tramp-send-string
-          multi-method method user host
-          (concat "tramp_decode_with_module () {\n"
-                  (format tramp-perl-decode-with-module tramp-remote-perl)
-                  " 2>/dev/null"
-                  "\n}"))
-         (tramp-wait-for-output)))))
+      (unless (tramp-method-out-of-band-p multi-method method user host)
+        (tramp-message 5 "Sending the Perl `mime-encode' implementations.")
+        (tramp-send-string
+         multi-method method user host
+         (concat "tramp_encode () {\n"
+                 (format tramp-perl-encode tramp-remote-perl)
+                 " 2>/dev/null"
+                 "\n}"))
+        (tramp-wait-for-output)
+        (tramp-send-string
+         multi-method method user host
+         (concat "tramp_encode_with_module () {\n"
+                 (format tramp-perl-encode-with-module tramp-remote-perl)
+                 " 2>/dev/null"
+                 "\n}"))
+        (tramp-wait-for-output)
+        (tramp-message 5 "Sending the Perl `mime-decode' implementations.")
+        (tramp-send-string
+         multi-method method user host
+         (concat "tramp_decode () {\n"
+                 (format tramp-perl-decode tramp-remote-perl)
+                 " 2>/dev/null"
+                 "\n}"))
+        (tramp-wait-for-output)
+        (tramp-send-string
+         multi-method method user host
+         (concat "tramp_decode_with_module () {\n"
+                 (format tramp-perl-decode-with-module tramp-remote-perl)
+                 " 2>/dev/null"
+                 "\n}"))
+        (tramp-wait-for-output))))
   ;; Find ln(1)
   (erase-buffer)
   (let ((ln (tramp-find-executable multi-method method user host
@@ -5939,6 +6133,8 @@ locale to C and sets up the remote shell search path."
      base64-encode-region base64-decode-region)
     ("recode data..base64" "recode base64..data"
      base64-encode-region base64-decode-region)
+    ("uuencode xxx" "uudecode -o /dev/stdout"
+     tramp-uuencode-region uudecode-decode-region)
     ("uuencode xxx" "uudecode -o -"
      tramp-uuencode-region uudecode-decode-region)
     ("uuencode xxx" "uudecode -p"
@@ -6138,7 +6334,7 @@ Sends COMMAND, then waits 30 seconds for shell prompt."
   (tramp-barf-if-no-shell-prompt
    nil 30
    "Couldn't `%s', see buffer `%s'" command (buffer-name)))
-  
+
 (defun tramp-wait-for-output (&optional timeout)
   "Wait for output from remote rsh command."
   (let ((proc (get-buffer-process (current-buffer)))
@@ -6188,7 +6384,7 @@ Sends COMMAND, then waits 30 seconds for shell prompt."
       (save-excursion
        (goto-char start-point)
        (when (looking-at (regexp-quote tramp-last-cmd))
-         (delete-region (point) (forward-line 1)))))
+         (delete-region (point) (progn (forward-line 1) (point))))))
     ;; Add output to debug buffer if appropriate.
     (when tramp-debug-buffer
       (append-to-buffer
@@ -6382,6 +6578,26 @@ If `tramp-discard-garbage' is nil, just erase buffer."
          (t (error "Tenth char `%c' must be one of `xtT-'"
                    other-execute-or-sticky)))))))
 
+(defun tramp-convert-file-attributes (multi-method method user host attr)
+  "Convert file-attributes ATTR generated by perl script or ls.
+Convert file mode bits to string and set virtual device number.
+Return ATTR."
+  (unless (stringp (nth 8 attr))
+    ;; Convert file mode bits to string.
+    (setcar (nthcdr 8 attr) (tramp-file-mode-from-int (nth 8 attr))))
+  ;; Set virtual device number.
+  (setcar (nthcdr 11 attr)
+          (tramp-get-device multi-method method user host))
+  attr)
+
+(defun tramp-get-device (multi-method method user host)
+  "Returns the virtual device number.
+If it doesn't exist, generate a new one."
+  (let ((string (tramp-make-tramp-file-name multi-method method user host "")))
+    (unless (assoc string tramp-devices)
+      (add-to-list 'tramp-devices
+                  (list string (length tramp-devices))))
+    (list -1 (nth 1 (assoc string tramp-devices)))))
 
 (defun tramp-file-mode-from-int (mode)
   "Turn an integer representing a file mode into an ls(1)-like string."
@@ -6451,9 +6667,9 @@ Not actually used.  Use `(format \"%o\" i)' instead?"
    ""))
 
 
-;; ------------------------------------------------------------ 
-;; -- TRAMP file names -- 
-;; ------------------------------------------------------------ 
+;; ------------------------------------------------------------
+;; -- TRAMP file names --
+;; ------------------------------------------------------------
 ;; Conversion functions between external representation and
 ;; internal data structure.  Convenience functions for internal
 ;; data structure.
@@ -6464,7 +6680,7 @@ Not actually used.  Use `(format \"%o\" i)' instead?"
   "Return t iff NAME is a tramp file."
   (save-match-data
     (string-match tramp-file-name-regexp name)))
+
 ;; HHH: Changed.  Used to assign the return value of (user-login-name)
 ;;      to the `user' part of the structure if a user name was not
 ;;      provided, now it assigns nil.
@@ -6517,7 +6733,7 @@ This is MULTI-METHOD, if non-nil.  Otherwise, it is METHOD, if non-nil.
 If both MULTI-METHOD and METHOD are nil, do a lookup in
 `tramp-default-method-alist'."
   (or multi-method method (tramp-find-default-method user host)))
-    
+
 ;; HHH: Not Changed.  Multi method.  Will probably not handle the case where
 ;;      a user name is not provided in the "file name" very well.
 (defun tramp-dissect-multi-file-name (name)
@@ -6689,7 +6905,7 @@ as default."
     (if entry
        (second entry)
       (symbol-value param))))
-      
+
 
 ;; Auto saving to a special directory.
 
@@ -6721,6 +6937,31 @@ as default."
             (tramp-make-auto-save-file-name (buffer-file-name)))
     ad-do-it))
 
+;; In Emacs < 22.1 and XEmacs < 21.5 autosaved remote files have
+;; permission 666 minus umask. This is a security threat.
+
+(defun tramp-set-auto-save-file-modes ()
+  "Set permissions of autosaved remote files to the original permissions."
+  (let ((bfn (buffer-file-name)))
+    (when (and (stringp bfn)
+              (tramp-tramp-file-p bfn)
+              (stringp buffer-auto-save-file-name)
+              (not (equal bfn buffer-auto-save-file-name))
+              (not (file-exists-p buffer-auto-save-file-name)))
+      (write-region "" nil buffer-auto-save-file-name)
+      (set-file-modes buffer-auto-save-file-name (file-modes bfn)))))
+
+(unless (or (> emacs-major-version 21)
+           (and (featurep 'xemacs)
+                (= emacs-major-version 21)
+                (> emacs-minor-version 4))
+           (and (not (featurep 'xemacs))
+                (= emacs-major-version 21)
+                (or (> emacs-minor-version 3)
+                    (and (string-match "^21\\.3\\.\\([0-9]+\\)" emacs-version)
+                         (>= (string-to-int (match-string 1 emacs-version)) 50)))))
+  (add-hook 'auto-save-hook 'tramp-set-auto-save-file-modes))
+
 (defun tramp-subst-strs-in-string (alist string)
   "Replace all occurrences of the string FROM with TO in STRING.
 ALIST is of the form ((FROM . TO) ...)."
@@ -6856,9 +7097,9 @@ exiting if process is running."
             process flag)))
 
 
-;; ------------------------------------------------------------ 
-;; -- Kludges section -- 
-;; ------------------------------------------------------------ 
+;; ------------------------------------------------------------
+;; -- Kludges section --
+;; ------------------------------------------------------------
 
 ;; Currently (as of Emacs 20.5), the function `shell-quote-argument'
 ;; does not deal well with newline characters.  Newline is replaced by
@@ -7121,7 +7362,7 @@ report.
 ;;   strange when doing zerop, we should kill the process and start
 ;;   again.  (Greg Stark)
 ;; * Add caching for filename completion.  (Greg Stark)
-;;   Of course, this has issues with usability (stale cache bites) 
+;;   Of course, this has issues with usability (stale cache bites)
 ;;      -- <daniel@danann.net>
 ;; * Provide a local cache of old versions of remote files for the rsync
 ;;   transfer method to use.  (Greg Stark)