]> code.delx.au - gnu-emacs/blobdiff - lisp/net/tramp-sh.el
nnimap respool fix
[gnu-emacs] / lisp / net / tramp-sh.el
index 314c1a6f8e7773b4daf85411d66fe6e550fdd209..a6bfe7236b1264e19410531e2ef99411de6fca8b 100644 (file)
@@ -1,6 +1,6 @@
 ;;; tramp-sh.el --- Tramp access functions for (s)sh-like connections
 
-;; Copyright (C) 1998-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1998-2014 Free Software Foundation, Inc.
 
 ;; (copyright statements below in code to be updated with the above notice)
 
@@ -60,6 +60,7 @@ files conditionalize this setup based on the TERM environment variable."
   :group 'tramp
   :type 'string)
 
+;;;###tramp-autoload
 (defconst tramp-color-escape-sequence-regexp "\e[[;0-9]+m"
   "Escape sequences produced by the \"ls\" command.")
 
@@ -79,6 +80,9 @@ detected as prompt when being sent on echoing hosts, therefore.")
 (defconst tramp-initial-end-of-output "#$ "
   "Prompt when establishing a connection.")
 
+(defconst tramp-end-of-heredoc (md5 tramp-end-of-output)
+  "String used to recognize end of heredoc strings.")
+
 ;; Initialize `tramp-methods' with the supported methods.
 ;;;###tramp-autoload
 (add-to-list 'tramp-methods
@@ -685,7 +689,7 @@ on the remote host.")
 (defconst tramp-perl-encode
   "%s -e '
 # This script contributed by Juanma Barranquero <lektu@terra.es>.
-# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
 use strict;
 
 my %%trans = do {
@@ -723,7 +727,7 @@ This string is passed to `format', so percent characters need to be doubled.")
 (defconst tramp-perl-decode
   "%s -e '
 # This script contributed by Juanma Barranquero <lektu@terra.es>.
-# Copyright (C) 2002-2013 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
 use strict;
 
 my %%trans = do {
@@ -934,8 +938,11 @@ target of the symlink differ."
 
 (defun tramp-sh-handle-file-truename (filename)
   "Like `file-truename' for Tramp files."
-  (with-parsed-tramp-file-name (expand-file-name filename) nil
-    (tramp-make-tramp-file-name method user host
+  (format
+   "%s%s"
+   (with-parsed-tramp-file-name (expand-file-name filename) nil
+     (tramp-make-tramp-file-name
+      method user host
       (with-tramp-file-property v localname "file-truename"
        (let ((result nil))                     ; result steps in reverse order
          (tramp-message v 4 "Finding true name for `%s'" filename)
@@ -1038,7 +1045,10 @@ target of the symlink differ."
                  (setq result (concat result "/"))))))
 
          (tramp-message v 4 "True name of `%s' is `%s'" localname result)
-         result)))))
+         result))))
+
+   ;; Preserve trailing "/".
+   (if (string-equal (file-name-nondirectory filename) "") "/" "")))
 
 ;; Basic functions.
 
@@ -1302,22 +1312,29 @@ of."
   "Like `set-file-times' for Tramp files."
   (if (tramp-tramp-file-p filename)
       (with-parsed-tramp-file-name filename nil
-       (tramp-flush-file-property v localname)
-       (let ((time (if (or (null time) (equal time '(0 0)))
-                       (current-time)
-                     time))
-             ;; 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.
-             (utc (not (featurep 'xemacs))))
-         (tramp-send-command-and-check
-          v (format "%s touch -t %s %s"
-                    (if utc "env TZ=UTC" "")
-                    (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)))))
+       (when (tramp-get-remote-touch v)
+         (tramp-flush-file-property v localname)
+         (let ((time (if (or (null time) (equal time '(0 0)))
+                         (current-time)
+                       time))
+               ;; 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.
+               (utc (not (featurep 'xemacs))))
+           (tramp-send-command-and-check
+            v (format
+               "%s %s %s %s"
+               (if utc "env TZ=UTC" "")
+               (tramp-get-remote-touch v)
+               (if (tramp-get-connection-property v "touch-t" nil)
+                   (format "-t %s"
+                           (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.
@@ -1443,8 +1460,11 @@ be non-negative integers."
     (if (and (stringp acl-string) (tramp-remote-acl-p v)
             (progn
               (tramp-send-command
-               v (format "setfacl --set-file=- %s <<'EOF'\n%s\nEOF\n"
-                         (tramp-shell-quote-argument localname) acl-string))
+               v (format "setfacl --set-file=- %s <<'%s'\n%s\n%s\n"
+                         (tramp-shell-quote-argument localname)
+                         tramp-end-of-heredoc
+                         acl-string
+                         tramp-end-of-heredoc))
               (tramp-send-command-and-check v nil)))
        ;; Success.
        (progn
@@ -1556,39 +1576,45 @@ be non-negative integers."
 (defun tramp-sh-handle-directory-files-and-attributes
   (directory &optional full match nosort id-format)
   "Like `directory-files-and-attributes' for Tramp files."
-  (unless id-format (setq id-format 'integer))
-  (when (file-directory-p directory)
-    (setq directory (expand-file-name directory))
-    (let* ((temp
-           (copy-tree
-            (with-parsed-tramp-file-name directory nil
-              (with-tramp-file-property
-                  v localname
-                  (format "directory-files-and-attributes-%s" id-format)
-                (save-excursion
-                  (mapcar
-                   (lambda (x)
-                     (cons (car x)
-                           (tramp-convert-file-attributes v (cdr x))))
-                   (cond
-                    ((tramp-get-remote-stat v)
-                     (tramp-do-directory-files-and-attributes-with-stat
-                      v localname id-format))
-                    ((tramp-get-remote-perl v)
-                     (tramp-do-directory-files-and-attributes-with-perl
-                      v localname id-format)))))))))
-          result item)
-
-      (while temp
-       (setq item (pop temp))
-       (when (or (null match) (string-match match (car item)))
-         (when full
-           (setcar item (expand-file-name (car item) directory)))
-         (push item result)))
-
-      (if nosort
-         result
-       (sort result (lambda (x y) (string< (car x) (car y))))))))
+  (if (with-parsed-tramp-file-name directory nil
+       (not (or (tramp-get-remote-stat v) (tramp-get-remote-perl v))))
+      (tramp-handle-directory-files-and-attributes
+       directory full match nosort id-format)
+
+    ;; Do it directly.
+    (unless id-format (setq id-format 'integer))
+    (when (file-directory-p directory)
+      (setq directory (expand-file-name directory))
+      (let* ((temp
+             (copy-tree
+              (with-parsed-tramp-file-name directory nil
+                (with-tramp-file-property
+                    v localname
+                    (format "directory-files-and-attributes-%s" id-format)
+                  (save-excursion
+                    (mapcar
+                     (lambda (x)
+                       (cons (car x)
+                             (tramp-convert-file-attributes v (cdr x))))
+                     (cond
+                      ((tramp-get-remote-stat v)
+                       (tramp-do-directory-files-and-attributes-with-stat
+                        v localname id-format))
+                      ((tramp-get-remote-perl v)
+                       (tramp-do-directory-files-and-attributes-with-perl
+                        v localname id-format)))))))))
+            result item)
+
+       (while temp
+         (setq item (pop temp))
+         (when (or (null match) (string-match match (car item)))
+           (when full
+             (setcar item (expand-file-name (car item) directory)))
+           (push item result)))
+
+       (if nosort
+           result
+         (sort result (lambda (x y) (string< (car x) (car y)))))))))
 
 (defun tramp-do-directory-files-and-attributes-with-perl
   (vec localname &optional id-format)
@@ -2496,8 +2522,8 @@ This is like `dired-recursive-delete-directory' for Tramp files."
   (with-parsed-tramp-file-name filename nil
     (if (and (featurep 'ls-lisp)
             (not (symbol-value 'ls-lisp-use-insert-directory-program)))
-       (tramp-run-real-handler
-        'insert-directory (list filename switches wildcard full-directory-p))
+       (tramp-handle-insert-directory
+        filename switches wildcard full-directory-p)
       (when (stringp switches)
         (setq switches (split-string switches)))
       (when (and (member "--dired" switches)
@@ -2705,16 +2731,17 @@ the result will be a local, non-Tramp, filename."
                   (cdr args)))
           (command
            (when (stringp program)
-             (format "cd %s; exec %s env PS1=%s %s"
+             (format "cd %s && exec %s env PS1=%s %s"
                      (tramp-shell-quote-argument localname)
-                     (if heredoc "<<EOF" "")
+                     (if heredoc (format "<<'%s'" tramp-end-of-heredoc) "")
                      ;; Use a human-friendly prompt, for example for `shell'.
                      (tramp-shell-quote-argument
                       (format "%s %s"
                               (file-remote-p default-directory)
                               tramp-initial-end-of-output))
                      (if heredoc
-                         (format "%s\n%s\nEOF" program (car args))
+                         (format "%s\n(\n%s\n) </dev/tty\n%s"
+                                 program (car args) tramp-end-of-heredoc)
                        (mapconcat 'tramp-shell-quote-argument
                                   (cons program args) " ")))))
           (tramp-process-connection-type
@@ -2751,7 +2778,7 @@ the result will be a local, non-Tramp, filename."
                ;; `verify-visited-file-modtime'.
                (let ((buffer-undo-list t)
                      (buffer-read-only nil)
-                     (mark (point)))
+                     (mark (point-max)))
                  (clear-visited-file-modtime)
                  (narrow-to-region (point-max) (point-max))
                  ;; We call `tramp-maybe-open-connection', in order
@@ -3182,9 +3209,11 @@ the result will be a local, non-Tramp, filename."
                      (tramp-send-command
                       v
                       (format
-                       (concat rem-dec " <<'EOF'\n%sEOF")
+                       (concat rem-dec " <<'%s'\n%s%s")
                        (tramp-shell-quote-argument localname)
-                       (buffer-string)))
+                       tramp-end-of-heredoc
+                       (buffer-string)
+                       tramp-end-of-heredoc))
                      (tramp-barf-unless-okay
                       v nil
                       "Couldn't write region to `%s', decode using `%s' failed"
@@ -3277,38 +3306,49 @@ the result will be a local, non-Tramp, filename."
       (with-tramp-progress-reporter
          v 3 (format "Checking `vc-registered' for %s" file)
 
-       ;; There could be new files, created by the vc backend.  We
-       ;; cannot reuse the old cache entries, therefore.
-       (let (tramp-vc-registered-file-names
-             (remote-file-name-inhibit-cache (current-time))
-             (file-name-handler-alist
-              `((,tramp-file-name-regexp . tramp-vc-file-name-handler))))
-
-         ;; Here we collect only file names, which need an operation.
-         (ignore-errors (tramp-run-real-handler 'vc-registered (list file)))
-         (tramp-message v 10 "\n%s" tramp-vc-registered-file-names)
-
-         ;; Send just one command, in order to fill the cache.
-         (when tramp-vc-registered-file-names
-           (tramp-maybe-send-script
-            v
-            (format tramp-vc-registered-read-file-names
-                    (tramp-get-file-exists-command v)
-                    (format "%s -r" (tramp-get-test-command v)))
-            "tramp_vc_registered_read_file_names")
-
-           (dolist
-               (elt
-                (tramp-send-command-and-read
-                 v
-                 (format
-                  "tramp_vc_registered_read_file_names <<'EOF'\n%s\nEOF\n"
-                  (mapconcat 'tramp-shell-quote-argument
-                             tramp-vc-registered-file-names
-                             "\n"))))
-
-             (tramp-set-file-property
-              v (car elt) (cadr elt) (cadr (cdr elt))))))
+       (unless remote-file-name-inhibit-cache
+         ;; There could be new files, created by the vc backend.  We
+         ;; cannot reuse the old cache entries, therefore.
+         (let (tramp-vc-registered-file-names
+               (remote-file-name-inhibit-cache (current-time))
+               (file-name-handler-alist
+                `((,tramp-file-name-regexp . tramp-vc-file-name-handler))))
+
+           ;; Here we collect only file names, which need an operation.
+           (ignore-errors (tramp-run-real-handler 'vc-registered (list file)))
+           (tramp-message v 10 "\n%s" tramp-vc-registered-file-names)
+
+           ;; Send just one command, in order to fill the cache.
+           (when tramp-vc-registered-file-names
+             (tramp-maybe-send-script
+              v
+              (format tramp-vc-registered-read-file-names
+                      (tramp-get-file-exists-command v)
+                      (format "%s -r" (tramp-get-test-command v)))
+              "tramp_vc_registered_read_file_names")
+
+             (dolist
+                 (elt
+                  (ignore-errors
+                    ;; We cannot use `tramp-send-command-and-read',
+                    ;; because this does not cooperate well with
+                    ;; heredoc documents.
+                    (tramp-send-command
+                     v
+                     (format
+                      "tramp_vc_registered_read_file_names <<'%s'\n%s\n%s\n"
+                      tramp-end-of-heredoc
+                      (mapconcat 'tramp-shell-quote-argument
+                                 tramp-vc-registered-file-names
+                                 "\n")
+                      tramp-end-of-heredoc))
+                    (with-current-buffer (tramp-get-connection-buffer v)
+                      ;; Read the expression.
+                      (goto-char (point-min))
+                      (read (current-buffer)))))
+
+               (tramp-set-file-property
+                v (car elt) (cadr elt) (cadr (cdr elt)))))))
 
        ;; Second run.  Now all `file-exists-p' or `file-readable-p'
        ;; calls shall be answered from the file cache.  We unset
@@ -3324,17 +3364,18 @@ the result will be a local, non-Tramp, filename."
 Fall back to normal file name handler if no Tramp handler exists."
   (when (and tramp-locked (not tramp-locker))
     (setq tramp-locked nil)
-    (signal 'file-error (list "Forbidden reentrant call of Tramp")))
+    (tramp-error
+     (car-safe tramp-current-connection) 'file-error
+     "Forbidden reentrant call of Tramp"))
   (let ((tl tramp-locked))
+    (setq tramp-locked t)
     (unwind-protect
-       (progn
-         (setq tramp-locked t)
-         (let ((tramp-locker t))
-           (save-match-data
-             (let ((fn (assoc operation tramp-sh-file-name-handler-alist)))
-               (if fn
-                   (apply (cdr fn) args)
-                 (tramp-run-real-handler operation args))))))
+       (let ((tramp-locker t))
+         (save-match-data
+           (let ((fn (assoc operation tramp-sh-file-name-handler-alist)))
+             (if fn
+                 (apply (cdr fn) args)
+               (tramp-run-real-handler operation args)))))
       (setq tramp-locked tl))))
 
 (defun tramp-vc-file-name-handler (operation &rest args)
@@ -3580,9 +3621,12 @@ This function expects to be in the right *tramp* buffer."
         (format (concat "while read d; "
                         "do if test -x $d/%s -a -f $d/%s; "
                         "then echo tramp_executable $d/%s; "
-                        "break; fi; done <<'EOF'\n"
-                        "%s\nEOF")
-                progname progname progname (mapconcat 'identity dirlist "\n")))
+                        "break; fi; done <<'%s'\n"
+                        "%s\n%s")
+                progname progname progname
+                tramp-end-of-heredoc
+                (mapconcat 'identity dirlist "\n")
+                tramp-end-of-heredoc))
        (goto-char (point-max))
        (when (search-backward "tramp_executable " nil t)
          (skip-chars-forward "^ ")
@@ -4557,14 +4601,6 @@ function waits for output unless NOOUTPUT is set."
       ;; We mark the command string that it can be erased in the output buffer.
       (tramp-set-connection-property p "check-remote-echo" t)
       (setq command (format "%s%s%s" tramp-echo-mark command tramp-echo-mark)))
-    ;; Some busyboxes tend to close the connection when we use the
-    ;; following syntax for here-documents.  This we cannot test; it
-    ;; shall be set via `tramp-connection-properties'.
-    (when (and (string-match "<<'EOF'" command)
-              (not (tramp-get-connection-property vec "busybox" nil)))
-      ;; Unset $PS1 when using here documents, in order to avoid
-      ;; multiple prompts.
-      (setq command (concat "(PS1= ; " command "\n)")))
     ;; Send the command.
     (tramp-message vec 6 "%s" command)
     (tramp-send-string vec command)
@@ -4811,7 +4847,7 @@ Return ATTR."
       (when elt1
        (setcdr elt1
                (append
-                (tramp-compat-split-string default-remote-path ":")
+                (tramp-compat-split-string (or default-remote-path "") ":")
                 (cdr elt1)))
        (setq remote-path (delq 'tramp-default-remote-path remote-path)))
 
@@ -4819,7 +4855,7 @@ Return ATTR."
       (when elt2
        (setcdr elt2
                (append
-                (tramp-compat-split-string own-remote-path ":")
+                (tramp-compat-split-string (or own-remote-path "") ":")
                 (cdr elt2)))
        (setq remote-path (delq 'tramp-own-remote-path remote-path)))
 
@@ -4984,6 +5020,30 @@ Return ATTR."
     (tramp-message vec 5 "Finding a suitable `trash' command")
     (tramp-find-executable vec "trash" (tramp-get-remote-path vec))))
 
+(defun tramp-get-remote-touch (vec)
+  (with-tramp-connection-property vec "touch"
+    (tramp-message vec 5 "Finding a suitable `touch' command")
+    (let ((result (tramp-find-executable
+                  vec "touch" (tramp-get-remote-path vec)))
+         (tmpfile
+          (make-temp-name
+           (expand-file-name
+            tramp-temp-name-prefix (tramp-get-remote-tmpdir vec)))))
+      ;; Busyboxes do support the "-t" option only when they have been
+      ;; built with the DESKTOP config option.  Let's check it.
+      (when result
+       (tramp-set-connection-property
+        vec "touch-t"
+        (tramp-send-command-and-check
+         vec
+         (format
+          "%s -t %s %s"
+          result
+          (format-time-string "%Y%m%d%H%M.%S" (current-time))
+          (tramp-file-name-handler 'file-remote-p tmpfile 'localname))))
+       (delete-file tmpfile))
+      result)))
+
 (defun tramp-get-remote-gvfs-monitor-dir (vec)
   (with-tramp-connection-property vec "gvfs-monitor-dir"
     (tramp-message vec 5 "Finding a suitable `gvfs-monitor-dir' command")