X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/d2a2c17f0f3735238953df26f42b4d18cb04bf4d..65e86587ab836aaa86b12ce30b219bcb4fcbaa06:/lisp/net/tramp.el diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index e3ad395959..822a995230 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2,7 +2,7 @@ ;;; tramp.el --- Transparent Remote Access, Multiple Protocol ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, -;; 2005 Free Software Foundation, Inc. +;; 2005, 2006 Free Software Foundation, Inc. ;; Author: Kai Gro,A_(Bjohann ;; Michael Albinus @@ -67,8 +67,15 @@ ;; The Tramp version number and bug report address, as prepared by configure. (require 'trampver) +(add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'trampver) + (unload-feature 'trampver 'force)))) + +(if (featurep 'xemacs) + (require 'timer-funcs) + (require 'timer)) -(require 'timer) (require 'format-spec) ;from Gnus 5.8, also in tar ball ;; As long as password.el is not part of (X)Emacs, it shouldn't ;; be mandatory @@ -87,6 +94,10 @@ (autoload 'tramp-uuencode-region "tramp-uu" "Implementation of `uuencode' in Lisp.") +(add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-uu) + (unload-feature 'tramp-uu 'force)))) (unless (fboundp 'uudecode-decode-region) (autoload 'uudecode-decode-region "uudecode")) @@ -102,7 +113,7 @@ ;;;###autoload (defvar tramp-unified-filenames (not (featurep 'xemacs)) "Non-nil means to use unified Ange-FTP/Tramp filename syntax. -Nil means to use a separate filename syntax for Tramp.") +Otherwise, use a separate filename syntax for Tramp.") ;; Load foreign methods. Because they do require Tramp internally, this ;; must be done with the `eval-after-load' trick. @@ -110,10 +121,20 @@ Nil means to use a separate filename syntax for Tramp.") ;; tramp-ftp supports Ange-FTP only. Not suited for XEmacs therefore. (unless (featurep 'xemacs) (eval-after-load "tramp" - '(require 'tramp-ftp))) + '(progn + (require 'tramp-ftp) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-ftp) + (unload-feature 'tramp-ftp 'force))))))) (when (and tramp-unified-filenames (featurep 'xemacs)) (eval-after-load "tramp" - '(require 'tramp-efs))) + '(progn + (require 'tramp-efs) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-efs) + (unload-feature 'tramp-efs 'force))))))) ;; tramp-smb uses "smbclient" from Samba. ;; Not available under Cygwin and Windows, because they don't offer @@ -121,14 +142,18 @@ Nil means to use a separate filename syntax for Tramp.") ;; UNC file names like "//host/share/localname". (unless (memq system-type '(cygwin windows-nt)) (eval-after-load "tramp" - '(require 'tramp-smb))) - -(eval-when-compile - (require 'cl) - (require 'custom) - ;; Emacs 19.34 compatibility hack -- is this needed? - (or (>= emacs-major-version 20) - (load "cl-seq"))) + '(progn + (require 'tramp-smb) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-smb) + (unload-feature 'tramp-smb 'force))))))) + +(require 'cl) +(require 'custom) +;; Emacs 19.34 compatibility hack -- is this needed? +(or (>= emacs-major-version 20) + (load "cl-seq")) (unless (boundp 'custom-print-functions) (defvar custom-print-functions nil)) ; not autoloaded before Emacs 20.4 @@ -136,7 +161,7 @@ Nil means to use a separate filename syntax for Tramp.") ;; Avoid byte-compiler warnings if the byte-compiler supports this. ;; Currently, XEmacs supports this. (eval-when-compile - (when (fboundp 'byte-compiler-options) + (when (featurep 'xemacs) (let (unused-vars) ; Pacify Emacs byte-compiler (defalias 'warnings 'identity) ; Pacify Emacs byte-compiler (byte-compiler-options (warnings (- unused-vars)))))) @@ -447,6 +472,17 @@ This variable defaults to the value of `tramp-encoding-shell'." (tramp-copy-args nil) (tramp-copy-keep-date-arg nil) (tramp-password-end-of-line nil)) + ("scpc" (tramp-connection-function tramp-open-connection-rsh) + (tramp-login-program "ssh") + (tramp-copy-program "scp") + (tramp-remote-sh "/bin/sh") + (tramp-login-args ("-o" "ControlPath=%t.%%r@%%h:%%p" + "-o" "ControlMaster=yes" + "-e" "none")) + (tramp-copy-args ("-o" "ControlPath=%t.%%r@%%h:%%p" + "-o" "ControlMaster=auto")) + (tramp-copy-keep-date-arg "-p") + (tramp-password-end-of-line nil)) ("scpx" (tramp-connection-function tramp-open-connection-rsh) (tramp-login-program "ssh") (tramp-copy-program "scp") @@ -541,6 +577,7 @@ pair of the form (KEY VALUE). The following KEYs are defined: If `tramp-connection-function' is `tramp-open-connection-su', then \"%u\" in this list is replaced by the user name, and \"%%\" can be used to obtain a literal percent character. + \"%t\" is replaced by the temporary file name for `scp'-like methods. * `tramp-copy-program' This specifies the name of the program to use for remotely copying the file; this might be the absolute filename of rcp or the name of @@ -647,10 +684,39 @@ various functions for details." :type '(repeat (list string function string))) (defcustom tramp-default-method - (if (and (fboundp 'executable-find) - (executable-find "plink")) - "plink" - "ssh") + ;; An external copy method seems to be preferred, because it is much + ;; more performant for large files, and it hasn't too serious delays + ;; for small files. But it must be ensured that there aren't + ;; permanent password queries. Either a password agent like + ;; "ssh-agent" or "Pageant" shall run, or the optional password.el + ;; package shall be active for password caching. "scpc" would be + ;; another good choice because of the "ControlMaster" option, but + ;; this is a more modern alternative in OpenSSH 4, which cannot be + ;; taken as default. + (let ((e-f (fboundp 'executable-find))) + (cond + ;; PuTTY is installed. + ((and e-f (funcall 'executable-find "pscp")) + (if (or (fboundp 'password-read) + ;; Pageant is running. + (and (fboundp 'w32-window-exists-p) + (funcall 'w32-window-exists-p "Pageant" "Pageant"))) + "pscp" + "plink")) + ;; There is an ssh installation. + ((and e-f (funcall 'executable-find "scp")) + (if (or (fboundp 'password-read) + ;; ssh-agent is running. + (getenv "SSH_AUTH_SOCK") + (getenv "SSH_AGENT_PID")) + "scp" + "ssh")) + ;; Under Emacs 20, `executable-find' does not exists. So we + ;; couldn't check whether there is an ssh implementation. Let's + ;; hope the best. + ((not e-f) "ssh") + ;; Fallback. + (t "ftp"))) "*Default method to use for transferring files. See `tramp-methods' for possibilities. Also see `tramp-default-method-alist'." @@ -810,8 +876,10 @@ The default value is to use the same value as `tramp-rsh-end-of-line'." :type 'string) (defcustom tramp-remote-path - '("/bin" "/usr/bin" "/usr/sbin" "/usr/local/bin" "/usr/ccs/bin" - "/local/bin" "/local/freeware/bin" "/local/gnu/bin" + ;; "/usr/xpg4/bin" has been placed first, because on Solaris a POSIX + ;; compatible "id" is needed. + '("/usr/xpg4/bin" "/bin" "/usr/bin" "/usr/sbin" "/usr/local/bin" + "/usr/ccs/bin" "/local/bin" "/local/freeware/bin" "/local/gnu/bin" "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin") "*List of directories to search for executables on remote host. Please notify me about other semi-standard directories to include here. @@ -822,9 +890,11 @@ tilde expansion, all directory names starting with `~' will be ignored." :type '(repeat string)) (defcustom tramp-login-prompt-regexp - ".*ogin: *" + ".*ogin\\( .*\\)?: *" "*Regexp matching login-like prompts. -The regexp should match at end of buffer." +The regexp should match at end of buffer. + +Sometimes the prompt is reported to look like \"login as:\"." :group 'tramp :type 'regexp) @@ -842,7 +912,7 @@ which should work well in many cases." :type 'regexp) (defcustom tramp-password-prompt-regexp - "^.*\\([pP]assword\\|passphrase.*\\):\^@? *" + "^.*\\([pP]assword\\|passphrase\\).*:\^@? *" "*Regexp matching password-like prompts. The regexp should match at end of buffer. @@ -884,8 +954,10 @@ See also `tramp-yn-prompt-regexp'." :type 'regexp) (defcustom tramp-yn-prompt-regexp - (concat (regexp-opt '("Store key in cache? (y/n)") t) - "\\s-*") + (concat + (regexp-opt '("Store key in cache? (y/n)" + "Update cached key? (y/n, Return cancels connection)") t) + "\\s-*") "Regular expression matching all y/n queries which need to be confirmed. The confirmation should be done with y or n. The regexp should match at end of buffer. @@ -914,6 +986,17 @@ be ignored safely." :group 'tramp :type 'regexp) +(defcustom tramp-copy-failed-regexp + (concat "\\(.+: " + (regexp-opt '("Permission denied" + "not a regular file" + "is a directory" + "No such file or directory") t) + "\\)\\s-*") + "Regular expression matching copy problems in (s)cp operations." + :group 'tramp + :type 'regexp) + (defcustom tramp-process-alive-regexp "" "Regular expression indicating a process has finished. @@ -1311,6 +1394,7 @@ corresponding PATTERN matches, the ACTION function is called." (defcustom tramp-actions-copy-out-of-band '((tramp-password-prompt-regexp tramp-action-password) (tramp-wrong-passwd-regexp tramp-action-permission-denied) + (tramp-copy-failed-regexp tramp-action-copy-failed) (tramp-process-alive-regexp tramp-action-out-of-band)) "List of pattern/action pairs. This list is used for copying/renaming with out-of-band methods. @@ -1356,32 +1440,62 @@ autocorrect\" to the remote host." (when (and (not (featurep 'xemacs)) (memq system-type '(hpux))) 500) +;; Parentheses in docstring starting at beginning of line are escaped. +;; Fontification is messed up when +;; `open-paren-in-column-0-is-defun-start' set to t. "*If non-nil, chunksize for sending input to local process. It is necessary only on systems which have a buggy `process-send-string' implementation. The necessity, whether this variable must be set, can be checked via the following code: (with-temp-buffer - (let ((bytes 1000) - (proc (start-process (buffer-name) (current-buffer) \"wc\" \"-c\"))) - (process-send-string proc (make-string bytes ?x)) - (process-send-eof proc) - (process-send-eof proc) - (accept-process-output proc 1) - (goto-char (point-min)) - (re-search-forward \"\\\\w+\") - (message \"Bytes sent: %s\\tBytes received: %s\" bytes (match-string 0)))) - -In the Emacs normally running Tramp, evaluate the above code. -You can do this, for example, by pasting it into the `*scratch*' -buffer and then hitting C-j with the cursor after the last -closing parenthesis. - -If your Emacs is buggy, the sent and received numbers will be -different. In that case, you'll want to set this variable to -some number. For those people who have needed it, the value 500 -seems to have worked well. There is no way to predict what value -you need; maybe you could just experiment a bit. + (let* ((user \"xxx\") (host \"yyy\") + (init 0) (step 50) + (sent init) (received init)) + (while (= sent received) + (setq sent (+ sent step)) + (erase-buffer) + (let ((proc (start-process (buffer-name) (current-buffer) + \"ssh\" \"-l\" user host \"wc\" \"-c\"))) + (when (memq (process-status proc) '(run open)) + (process-send-string proc (make-string sent ?\\ )) + (process-send-eof proc) + (process-send-eof proc)) + (while (not (progn (goto-char (point-min)) + (re-search-forward \"\\\\w+\" (point-max) t))) + (accept-process-output proc 1)) + (when (memq (process-status proc) '(run open)) + (setq received (string-to-number (match-string 0))) + (delete-process proc) + (message \"Bytes sent: %s\\tBytes received: %s\" sent received) + (sit-for 0)))) + (if (> sent (+ init step)) + (message \"You should set `tramp-chunksize' to a maximum of %s\" + (- sent step)) + (message \"Test does not work\") + (display-buffer (current-buffer)) + (sit-for 30)))) + +In the Emacs normally running Tramp, evaluate the above code +\(replace \"xxx\" and \"yyy\" by the remote user and host name, +respectively). You can do this, for example, by pasting it into +the `*scratch*' buffer and then hitting C-j with the cursor after the +last closing parenthesis. Note that it works only if you have configured +\"ssh\" to run without password query, see ssh-agent(1). + +You will see the number of bytes sent successfully to the remote host. +If that number exceeds 1000, you can stop the execution by hitting +C-g, because your Emacs is likely clean. + +If your Emacs is buggy, the code stops and gives you an indication +about the value `tramp-chunksize' should be set. Maybe you could just +experiment a bit, e.g. changing the values of `init' and `step' +in the third line of the code. + +When it is necessary to set `tramp-chunksize', you might consider to +use an out-of-the-band method (like \"scp\") instead of an internal one +\(like \"ssh\"), because setting `tramp-chunksize' to non-nil decreases +performance. Please raise a bug report via \"M-x tramp-bug\" if your system needs this variable to be set as well." @@ -1413,7 +1527,7 @@ the visited file modtime.") ((fboundp 'md5-encode) (lambda (x) (base64-encode-string (funcall (symbol-function 'md5-encode) x)))) - (t (error "Coulnd't find an `md5' function"))) + (t (error "Couldn't find an `md5' function"))) "Function to call for running the MD5 algorithm.") (defvar tramp-end-of-output @@ -1588,8 +1702,8 @@ printf( on the remote file system.") (defconst tramp-perl-directory-files-and-attributes "\ -chdir($ARGV[0]); -opendir(DIR,\".\"); +chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), exit(); +opendir(DIR,\".\") or printf(\"\\\"Cannot open directory $ARGV[0]: $''!''\\\"\\n\"), exit(); @list = readdir(DIR); closedir(DIR); $n = scalar(@list); @@ -1677,7 +1791,7 @@ on the remote host.") (defvar tramp-perl-encode "%s -e ' # This script contributed by Juanma Barranquero . -# Copyright (C) 2002 Free Software Foundation, Inc. +# Copyright (C) 2002, 2006 Free Software Foundation, Inc. use strict; my %%trans = do { @@ -1719,7 +1833,7 @@ This string is passed to `format', so percent characters need to be doubled.") (defvar tramp-perl-decode "%s -e ' # This script contributed by Juanma Barranquero . -# Copyright (C) 2002 Free Software Foundation, Inc. +# Copyright (C) 2002, 2006 Free Software Foundation, Inc. use strict; my %%trans = do { @@ -1761,8 +1875,6 @@ while (my $data = ) { Escape sequence %s is replaced with name of Perl binary. This string is passed to `format', so percent characters need to be doubled.") -; These values conform to `file-attributes' from XEmacs 21.2. -; GNU Emacs and other tools not checked. (defconst tramp-file-mode-type-map '((0 . "-") ; Normal file (SVID-v2 and XPG2) (1 . "p") ; fifo (2 . "c") ; character device @@ -1856,6 +1968,7 @@ on the FILENAME argument, even if VISIT was a string.") (insert-file-contents . tramp-handle-insert-file-contents) (write-region . tramp-handle-write-region) (find-backup-file-name . tramp-handle-find-backup-file-name) + (make-auto-save-file-name . tramp-handle-make-auto-save-file-name) (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory) (dired-compress-file . tramp-handle-dired-compress-file) (dired-call-process . tramp-handle-dired-call-process) @@ -1863,20 +1976,15 @@ on the FILENAME argument, even if VISIT was a string.") . tramp-handle-dired-recursive-delete-directory) (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime)) - "Alist of handler functions. + "Alist of handler functions. Operations not mentioned here will be handled by the normal Emacs functions.") -;; Handlers for partial tramp file names. For GNU Emacs just -;; `file-name-all-completions' is needed. The other ones are necessary -;; for XEmacs. +;; Handlers for partial tramp file names. For Emacs just +;; `file-name-all-completions' is needed. +;;;###autoload (defconst tramp-completion-file-name-handler-alist - '( - (file-name-directory . tramp-completion-handle-file-name-directory) - (file-name-nondirectory . tramp-completion-handle-file-name-nondirectory) - (file-exists-p . tramp-completion-handle-file-exists-p) - (file-name-all-completions . tramp-completion-handle-file-name-all-completions) - (file-name-completion . tramp-completion-handle-file-name-completion) - (expand-file-name . tramp-completion-handle-expand-file-name)) + '((file-name-all-completions . tramp-completion-handle-file-name-all-completions) + (file-name-completion . tramp-completion-handle-file-name-completion)) "Alist of completion handler functions. Used for file names matching `tramp-file-name-regexp'. Operations not mentioned here will be handled by `tramp-file-name-handler-alist' or the @@ -1964,11 +2072,12 @@ If VAR is nil, then we bind `v' to the structure and `multi-method', ,@body)) (put 'with-parsed-tramp-file-name 'lisp-indent-function 2) -;; To be activated for debugging containing this macro -;; 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) +;; Enable debugging. +(def-edebug-spec with-parsed-tramp-file-name (form symbolp body)) +;; Highlight as keyword. +(when (functionp 'font-lock-add-keywords) + (funcall 'font-lock-add-keywords + 'emacs-lisp-mode '("\\"))) (defmacro tramp-let-maybe (variable value &rest body) "Let-bind VARIABLE to VALUE in BODY, but only if VARIABLE is not obsolete. @@ -1979,6 +2088,7 @@ The intent is to protect against `obsolete variable' warnings." (let ((,variable ,value)) ,@body))) (put 'tramp-let-maybe 'lisp-indent-function 2) +(put 'tramp-let-maybe 'edebug-form-spec t) ;;; Config Manipulation Functions: @@ -2110,28 +2220,11 @@ target of the symlink differ." ;; Localname manipulation functions that grok TRAMP localnames... (defun tramp-handle-file-name-directory (file) "Like `file-name-directory' but aware of TRAMP files." - ;; everything except the last filename thing is the directory + ;; Everything except the last filename thing is the directory. (with-parsed-tramp-file-name file nil - ;; For the following condition, two possibilities should be tried: - ;; (1) (string= localname "") - ;; (2) (or (string= localname "") (string= localname "/")) - ;; The second variant fails when completing a "/" directory on - ;; the remote host, that is a filename which looks like - ;; "/user@host:/". But maybe wildcards fail with the first variant. - ;; We should do some investigation. - (if (string= localname "") - ;; For a filename like "/[foo]", we return "/". The `else' - ;; case would return "/[foo]" unchanged. But if we do that, - ;; then `file-expand-wildcards' ceases to work. It's not - ;; quite clear to me what's the intuition that tells that this - ;; behavior is the right behavior, but oh, well. - "/" - ;; run the command on the localname portion only - ;; CCC: This should take into account the remote machine type, no? - ;; --daniel - (tramp-make-tramp-file-name multi-method method user host - ;; This will not recurse... - (or (file-name-directory localname) ""))))) + ;; Run the command on the localname portion only. + (tramp-make-tramp-file-name + multi-method method user host (file-name-directory (or localname ""))))) (defun tramp-handle-file-name-nondirectory (file) "Like `file-name-nondirectory' but aware of TRAMP files." @@ -2337,8 +2430,8 @@ target of the symlink differ." ;; 8. File modes, as a string of ten letters or dashes as in ls -l. res-filemodes ;; 9. t iff file's gid would change if file were deleted and - ;; recreated. - nil ;hm? + ;; recreated. Will be set in `tramp-convert-file-attributes' + t ;; 10. inode number. res-inode ;; 11. Device number. Will be replaced by a virtual device number. @@ -2368,11 +2461,14 @@ target of the symlink differ." (buffer-name))) (if time-list (tramp-run-real-handler 'set-visited-file-modtime (list time-list)) - (let ((f (buffer-file-name))) + (let ((f (buffer-file-name)) + coding-system-used) (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)))) + (when (boundp 'last-coding-system-used) + (setq coding-system-used (symbol-value 'last-coding-system-used))) ;; We use '(0 0) as a don't-know value. See also ;; `tramp-handle-file-attributes-with-ls'. (if (not (equal modtime '(0 0))) @@ -2387,6 +2483,8 @@ target of the symlink differ." (setq attr (buffer-substring (point) (progn (end-of-line) (point))))) (setq tramp-buffer-file-attributes attr)) + (when (boundp 'last-coding-system-used) + (set 'last-coding-system-used coding-system-used)) nil))))) ;; CCC continue here @@ -2589,9 +2687,12 @@ of." (defun tramp-handle-file-ownership-preserved-p (filename) "Like `file-ownership-preserved-p' for tramp files." (with-parsed-tramp-file-name filename nil - (or (not (file-exists-p filename)) - ;; Existing files must be writable. - (zerop (tramp-run-test "-O" filename))))) + (let ((attributes (file-attributes filename))) + ;; Return t if the file doesn't exist, since it's true that no + ;; information would be lost by an (attempted) delete and create. + (or (null attributes) + (= (nth 2 attributes) + (tramp-get-remote-uid multi-method method user host)))))) ;; Other file name ops. @@ -2696,7 +2797,10 @@ of." (tramp-shell-quote-argument localname) (or id-format 'integer))) (tramp-wait-for-output) - (let* ((root (cons nil (read (current-buffer)))) + (let* ((root (cons nil (let ((object (read (current-buffer)))) + (when (stringp object) + (error object)) + object))) (cell root)) (while (cdr cell) (if (and match (not (string-match match (caadr cell)))) @@ -2763,17 +2867,18 @@ of." ;; The following isn't needed for Emacs 20 but for 19.34? -(defun tramp-handle-file-name-completion (filename directory) +(defun tramp-handle-file-name-completion + (filename directory &optional predicate) "Like `file-name-completion' for tramp files." (unless (tramp-tramp-file-p directory) (error "tramp-handle-file-name-completion invoked on non-tramp directory `%s'" directory)) - (with-parsed-tramp-file-name directory nil - (try-completion - filename - (mapcar (lambda (x) (cons x nil)) - (file-name-all-completions filename directory))))) + (try-completion + filename + (mapcar 'list (file-name-all-completions filename directory)) + (when predicate + (lambda (x) (funcall predicate (expand-file-name (car x) directory)))))) ;; cp, mv and ln @@ -2856,7 +2961,7 @@ and `rename'. FILENAME and NEWNAME must be absolute file names." (unless ok-if-already-exists (when (file-exists-p newname) (signal 'file-already-exists - (list newname)))) + (list "File already exists" newname)))) (let ((t1 (tramp-tramp-file-p filename)) (t2 (tramp-tramp-file-p newname)) v1-multi-method v1-method v1-user v1-host v1-localname @@ -2929,10 +3034,10 @@ and `rename'. FILENAME and NEWNAME must be absolute file names." ;; copy-program can be invoked. (if (and (not v1-multi-method) (not v2-multi-method) - (or (tramp-method-out-of-band-p - v1-multi-method v1-method v1-user v1-host) - (tramp-method-out-of-band-p - v2-multi-method v2-method v2-user v2-host))) + (or (and t1 (tramp-method-out-of-band-p + v1-multi-method v1-method v1-user v1-host)) + (and t2 (tramp-method-out-of-band-p + v2-multi-method v2-method v2-user v2-host)))) (tramp-do-copy-or-rename-file-out-of-band op filename newname keep-date) ;; Use the generic method via a Tramp buffer. @@ -3093,6 +3198,14 @@ be a local filename. The method used must be an out-of-band method." v2-user v2-host (tramp-shell-quote-argument v2-localname)))) + ;; Handle ControlMaster/ControlPath + (setq copy-args + (mapcar + (lambda (x) + (format-spec + x `((?t . ,(format "/tmp/%s" tramp-temp-name-prefix))))) + copy-args)) + ;; Handle keep-date argument (when keep-date (if t1 @@ -3127,12 +3240,13 @@ be a local filename. The method used must be an out-of-band method." (message "Transferring %s to %s..." filename newname) ;; Use rcp-like program for file transfer. - (let ((p (apply 'start-process (buffer-name trampbuf) trampbuf - copy-program copy-args))) - (tramp-set-process-query-on-exit-flag p nil) - (tramp-process-actions p multi-method method user host - tramp-actions-copy-out-of-band)) - (kill-buffer trampbuf) + (unwind-protect + (let ((p (apply 'start-process (buffer-name trampbuf) trampbuf + copy-program copy-args))) + (tramp-set-process-query-on-exit-flag p nil) + (tramp-process-actions p multi-method method user host + tramp-actions-copy-out-of-band)) + (kill-buffer trampbuf)) (message "Transferring %s to %s...done" filename newname) ;; Set the mode. @@ -3446,6 +3560,17 @@ the result will be a local, non-Tramp, filename." (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" localname) (let ((uname (match-string 1 localname)) (fname (match-string 2 localname))) + ;; We cannot simply apply "~/", because under sudo "~/" is + ;; expanded to the local user home directory but to the + ;; root home directory. On the other hand, using always + ;; the default user name for tilde expansion is not + ;; appropriate either, because ssh and companions might + ;; use a user name from the config file. + (when (and (string-equal uname "~") + (string-match + "\\`su\\(do\\)?\\'" + (tramp-find-method multi-method method user host))) + (setq uname (concat uname (or user "root")))) ;; CCC fanatic error checking? (set-buffer (tramp-get-buffer multi-method method user host)) (erase-buffer) @@ -3458,17 +3583,24 @@ the result will be a local, non-Tramp, filename." (setq uname (buffer-substring (point) (tramp-line-end-position))) (setq localname (concat uname fname)) (erase-buffer))) + ;; There might be a double slash, for example when "~/" + ;; expands to "/". Remove this. + (while (string-match "//" localname) + (setq localname (replace-match "/" t t localname))) ;; No tilde characters in file name, do normal ;; expand-file-name (this does "/./" and "/../"). We bind - ;; directory-sep-char here for XEmacs on Windows, which - ;; would otherwise use backslash. + ;; directory-sep-char here for XEmacs on Windows, which would + ;; otherwise use backslash. `default-directory' is bound to + ;; "/", because on Windows there would be problems with UNC + ;; shares or Cygwin mounts. (tramp-let-maybe directory-sep-char ?/ - (tramp-make-tramp-file-name - multi-method (or method (tramp-find-default-method user host)) - user host - (tramp-drop-volume-letter - (tramp-run-real-handler 'expand-file-name - (list localname))))))))) + (let ((default-directory "/")) + (tramp-make-tramp-file-name + multi-method (or method (tramp-find-default-method user host)) + user host + (tramp-drop-volume-letter + (tramp-run-real-handler 'expand-file-name + (list localname)))))))))) ;; old version follows. it uses ".." to cross file handler ;; boundaries. @@ -3493,6 +3625,10 @@ the result will be a local, non-Tramp, filename." "Global variable keeping asynchronous process object. Used in `tramp-handle-shell-command'") +(defvar tramp-display-shell-command-buffer t + "Whether to display output buffer of `shell-command'. +This is necessary for handling DISPLAY of `process-file'.") + (defun tramp-handle-shell-command (command &optional output-buffer error-buffer) "Like `shell-command' for tramp files. This will break if COMMAND prints a newline, followed by the value of @@ -3501,7 +3637,8 @@ This will break if COMMAND prints a newline, followed by the value of ;; for `find-grep-dired' and `find-name-dired' in Emacs 22. (if (tramp-tramp-file-p default-directory) (with-parsed-tramp-file-name default-directory nil - (let ((asynchronous (string-match "[ \t]*&[ \t]*\\'" command)) + (let ((curbuf (current-buffer)) + (asynchronous (string-match "[ \t]*&[ \t]*\\'" command)) status) (unless output-buffer (setq output-buffer @@ -3601,7 +3738,9 @@ This will break if COMMAND prints a newline, followed by the value of (skip-chars-forward "^ ") (setq status (read (current-buffer))))) (unless (zerop (buffer-size)) - (display-buffer output-buffer)) + (when tramp-display-shell-command-buffer + (display-buffer output-buffer))) + (set-buffer curbuf) status)) ;; The following is only executed if something strange was ;; happening. Emit a helpful message and do it anyway. @@ -3616,11 +3755,10 @@ This will break if COMMAND prints a newline, followed by the value of (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)) + (let ((tramp-display-shell-command-buffer display)) + (shell-command + (mapconcat 'tramp-shell-quote-argument (cons program args) " ") + buffer))) ;; File Editing. @@ -3678,7 +3816,7 @@ This will break if COMMAND prints a newline, followed by the value of (let ((tmpbuf (get-buffer-create " *tramp tmp*"))) (set-buffer tmpbuf) (erase-buffer) - (insert-buffer tramp-buf) + (insert-buffer-substring tramp-buf) (tramp-message-for-buffer multi-method method user host 6 "Decoding remote file %s with function %s..." @@ -3747,7 +3885,7 @@ This will break if COMMAND prints a newline, followed by the value of 'insert-file-contents) 'file-local-copy))) (file-local-copy filename))) - (result nil)) + coding-system-used result) (when visit (setq buffer-file-name filename) (set-visited-file-modtime) @@ -3756,10 +3894,15 @@ This will break if COMMAND prints a newline, followed by the value of multi-method method user host 9 "Inserting local temp file `%s'..." local-copy) (setq result (insert-file-contents local-copy nil beg end replace)) + ;; Now `last-coding-system-used' has right value. Remember it. + (when (boundp 'last-coding-system-used) + (setq coding-system-used (symbol-value 'last-coding-system-used))) (tramp-message-for-buffer multi-method method user host 9 "Inserting local temp file `%s'...done" local-copy) (delete-file local-copy) + (when (boundp 'last-coding-system-used) + (set 'last-coding-system-used coding-system-used)) (list (expand-file-name filename) (second result)))))) @@ -3807,6 +3950,54 @@ This will break if COMMAND prints a newline, followed by the value of (tramp-run-real-handler 'find-backup-file-name (list filename))))) +(defun tramp-handle-make-auto-save-file-name () + "Like `make-auto-save-file-name' for tramp files. +Returns a file name in `tramp-auto-save-directory' for autosaving this file." + (let ((tramp-auto-save-directory tramp-auto-save-directory)) + ;; File name must be unique. This is ensured with Emacs 22 (see + ;; UNIQUIFY element of `auto-save-file-name-transforms'); but for + ;; all other cases we must do it ourselves. + (when (boundp 'auto-save-file-name-transforms) + (mapcar + '(lambda (x) + (when (and (string-match (car x) buffer-file-name) + (not (car (cddr x)))) + (setq tramp-auto-save-directory + (or tramp-auto-save-directory temporary-file-directory)))) + (symbol-value 'auto-save-file-name-transforms))) + ;; Create directory. + (when tramp-auto-save-directory + (unless (file-exists-p tramp-auto-save-directory) + (make-directory tramp-auto-save-directory t))) + ;; jka-compr doesn't like auto-saving, so by appending "~" to the + ;; file name we make sure that jka-compr isn't used for the + ;; auto-save file. + (let ((buffer-file-name + (if tramp-auto-save-directory + (expand-file-name + (tramp-subst-strs-in-string + '(("_" . "|") + ("/" . "_a") + (":" . "_b") + ("|" . "__") + ("[" . "_l") + ("]" . "_r")) + (buffer-file-name)) + tramp-auto-save-directory) + (buffer-file-name)))) + ;; Run plain `make-auto-save-file-name'. There might be an advice when + ;; it is not a magic file name operation (since Emacs 22). + ;; We must deactivate it temporarily. + (if (not (ad-is-active 'make-auto-save-file-name)) + (tramp-run-real-handler + 'make-auto-save-file-name nil) + ;; else + (ad-deactivate 'make-auto-save-file-name) + (prog1 + (tramp-run-real-handler + 'make-auto-save-file-name nil) + (ad-activate 'make-auto-save-file-name)))))) + ;; CCC grok APPEND, LOCKNAME, CONFIRM (defun tramp-handle-write-region @@ -3838,6 +4029,13 @@ This will break if COMMAND prints a newline, followed by the value of (loc-dec (tramp-get-local-decoding multi-method method user host)) (trampbuf (get-buffer-create "*tramp output*")) (modes (file-modes filename)) + ;; We use this to save the value of `last-coding-system-used' + ;; after writing the tmp file. At the end of the function, + ;; we set `last-coding-system-used' to this saved value. + ;; This way, any intermediary coding systems used while + ;; talking to the remote shell or suchlike won't hose this + ;; variable. This approach was snarfed from ange-ftp.el. + coding-system-used tmpfil) ;; Write region into a tmp file. This isn't really needed if we ;; use an encoding function, but currently we use it always @@ -3854,6 +4052,9 @@ This will break if COMMAND prints a newline, followed by the value of (if confirm ; don't pass this arg unless defined for backward compat. (list start end tmpfil append 'no-message lockname confirm) (list start end tmpfil append 'no-message lockname))) + ;; Now, `last-coding-system-used' has the right value. Remember it. + (when (boundp 'last-coding-system-used) + (setq coding-system-used (symbol-value 'last-coding-system-used))) ;; The permissions of the temporary file should be set. If ;; filename does not exist (eq modes nil) it has been renamed to ;; the backup file. This case `save-buffer' handles @@ -3960,6 +4161,9 @@ This will break if COMMAND prints a newline, followed by the value of ;; 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) + (set 'last-coding-system-used coding-system-used)) (when (or (eq visit t) (eq visit nil) (stringp visit)) @@ -3996,7 +4200,8 @@ This will break if COMMAND prints a newline, followed by the value of ;; (inhibit-file-name-operation operation)) ;; (apply operation args))) -(defun tramp-run-real-handler (operation args) +;;;###autoload +(progn (defun tramp-run-real-handler (operation args) "Invoke normal file name handler for OPERATION. First arg specifies the OPERATION, second arg is a list of arguments to pass to the OPERATION." @@ -4009,13 +4214,14 @@ pass to the OPERATION." ,(and (eq inhibit-file-name-operation operation) inhibit-file-name-handlers))) (inhibit-file-name-operation operation)) - (apply operation args))) + (apply operation args)))) ;; This function is used from `tramp-completion-file-name-handler' functions ;; only, if `tramp-completion-mode' is true. But this cannot be checked here ;; because the check is based on a full filename, not available for all ;; basic I/O operations. -(defun tramp-completion-run-real-handler (operation args) +;;;###autoload +(progn (defun tramp-completion-run-real-handler (operation args) "Invoke `tramp-file-name-handler' for OPERATION. First arg specifies the OPERATION, second arg is a list of arguments to pass to the OPERATION." @@ -4027,7 +4233,7 @@ pass to the OPERATION." ,(and (eq inhibit-file-name-operation operation) inhibit-file-name-handlers))) (inhibit-file-name-operation operation)) - (apply operation args))) + (apply operation args)))) ;; We handle here all file primitives. Most of them have the file ;; name as first parameter; nevertheless we check for them explicitly @@ -4086,8 +4292,9 @@ ARGS are the arguments OPERATION has been called with." (nth 2 args)) ; BUF ((member operation - (list 'set-visited-file-modtime 'verify-visited-file-modtime - ; XEmacs only + (list 'make-auto-save-file-name + 'set-visited-file-modtime 'verify-visited-file-modtime + ; XEmacs only 'backup-buffer)) (buffer-file-name (if (bufferp (nth 0 args)) (nth 0 args) (current-buffer)))) @@ -4123,12 +4330,25 @@ ARGS are the arguments OPERATION has been called with." (defun tramp-file-name-handler (operation &rest args) "Invoke Tramp file name handler. Falls back to normal file name handler if no tramp file name handler exists." +;; (setq edebug-trace t) +;; (edebug-trace "%s" (with-output-to-string (backtrace))) (save-match-data (let* ((filename (apply 'tramp-file-name-for-operation operation args)) + (completion (tramp-completion-mode filename)) (foreign (tramp-find-foreign-file-name-handler filename))) - (cond - (foreign (apply foreign operation args)) - (t (tramp-run-real-handler operation args)))))) + (with-parsed-tramp-file-name filename nil + (cond + ;; When we are in completion mode, some operations shouldn' be + ;; handled by backend. + ((and completion (memq operation '(expand-file-name))) + (tramp-run-real-handler operation args)) + ((and completion (zerop (length localname)) + (memq operation '(file-exists-p file-directory-p))) + t) + ;; Call the backend function. + (foreign (apply foreign operation args)) + ;; Nothing to do for us. + (t (tramp-run-real-handler operation args))))))) ;; In Emacs, there is some concurrency due to timers. If a timer @@ -4176,38 +4396,56 @@ Fall back to normal file name handler if no Tramp handler exists." (setq tramp-locked tl)))) ;;;###autoload -(defun tramp-completion-file-name-handler (operation &rest args) +(progn (defun tramp-completion-file-name-handler (operation &rest args) "Invoke tramp file name completion handler. Falls back to normal file name handler if no tramp file name handler exists." -;; (setq tramp-debug-buffer t) -;; (tramp-message 1 "%s %s" operation args) -;; (tramp-message 1 "%s %s\n%s" -;; operation args (with-output-to-string (backtrace))) +;; (setq edebug-trace t) +;; (edebug-trace "%s" (with-output-to-string (backtrace))) (let ((fn (assoc operation tramp-completion-file-name-handler-alist))) (if fn (save-match-data (apply (cdr fn) args)) - (tramp-completion-run-real-handler operation args)))) + (tramp-completion-run-real-handler operation args))))) ;;;###autoload -(put 'tramp-completion-file-name-handler 'safe-magic t) - -;; Register in file name handler alist -;;;###autoload -(add-to-list 'file-name-handler-alist - (cons tramp-file-name-regexp 'tramp-file-name-handler)) -(add-to-list 'file-name-handler-alist - (cons tramp-completion-file-name-regexp - 'tramp-completion-file-name-handler)) - -(defun tramp-repair-jka-compr () - "If jka-compr is already loaded, move it to the front of -`file-name-handler-alist'. On Emacs 22 or so this will not be -necessary anymore." +(defsubst tramp-register-file-name-handlers () + "Add tramp file name handlers to `file-name-handler-alist'." + (add-to-list 'file-name-handler-alist + (cons tramp-file-name-regexp 'tramp-file-name-handler)) + ;; `partial-completion-mode' is unknown in XEmacs. So we should + ;; load it unconditionally there. In the GNU Emacs case, method/ + ;; user/host name completion shall be bound to `partial-completion-mode'. + (when (or (not (boundp 'partial-completion-mode)) + (symbol-value 'partial-completion-mode) + (featurep 'ido)) + (add-to-list 'file-name-handler-alist + (cons tramp-completion-file-name-regexp + 'tramp-completion-file-name-handler)) + (put 'tramp-completion-file-name-handler 'safe-magic t)) + ;; If jka-compr is already loaded, move it to the front of + ;; `file-name-handler-alist'. (let ((jka (rassoc 'jka-compr-handler file-name-handler-alist))) (when jka (setq file-name-handler-alist (cons jka (delete jka file-name-handler-alist)))))) -(tramp-repair-jka-compr) + +;; During autoload, it shall be checked whether +;; `partial-completion-mode' is active. Therefore registering will be +;; delayed. +;;;###autoload(add-hook +;;;###autoload 'after-init-hook +;;;###autoload '(lambda () (tramp-register-file-name-handlers))) +(tramp-register-file-name-handlers) + +;;;###autoload +(defun tramp-unload-file-name-handlers () + (setq file-name-handler-alist + (delete (rassoc 'tramp-file-name-handler + file-name-handler-alist) + (delete (rassoc 'tramp-completion-file-name-handler + file-name-handler-alist) + file-name-handler-alist)))) + +(add-hook 'tramp-unload-hook 'tramp-unload-file-name-handlers) ;;; Interactions with other packages: @@ -4258,25 +4496,22 @@ necessary anymore." (read (current-buffer)))))) (list (expand-file-name name)))))) -;; Check for complete.el and override PC-expand-many-files if appropriate. -(eval-and-compile - (defun tramp-save-PC-expand-many-files (name))); avoid compiler warning - -(defun tramp-setup-complete () - (fset 'tramp-save-PC-expand-many-files - (symbol-function 'PC-expand-many-files)) - (defun PC-expand-many-files (name) - (if (tramp-tramp-file-p name) - (funcall (symbol-function 'expand-many-files) name) - (tramp-save-PC-expand-many-files name)))) - -;; Why isn't eval-after-load sufficient? -(if (fboundp 'PC-expand-many-files) - (tramp-setup-complete) - (eval-after-load "complete" '(tramp-setup-complete))) +(eval-after-load "complete" + '(progn + (defadvice PC-expand-many-files + (around tramp-advice-PC-expand-many-files (name) activate) + "Invoke `tramp-handle-expand-many-files' for tramp files." + (if (tramp-tramp-file-p name) + (setq ad-return-value (tramp-handle-expand-many-files name)) + ad-do-it)) + (add-hook 'tramp-unload-hook + '(lambda () (ad-unadvise 'PC-expand-many-files))))) ;;; File name handler functions for completion mode +(defvar tramp-completion-mode nil + "If non-nil, we are in file name completion mode.") + ;; Necessary because `tramp-file-name-regexp-unified' and ;; `tramp-completion-file-name-regexp-unified' aren't different. ;; If nil, `tramp-completion-run-real-handler' is called (i.e. forwarding to @@ -4292,7 +4527,7 @@ necessary anymore." (defun tramp-completion-mode (file) "Checks whether method / user name / host name completion is active." (cond - ((not tramp-unified-filenames) t) + (tramp-completion-mode t) ((string-match "^/.*:.*:$" file) nil) ((string-match (concat tramp-prefix-regexp @@ -4300,118 +4535,117 @@ necessary anymore." file) (member (match-string 1 file) (mapcar 'car tramp-methods))) ((or (equal last-input-event 'tab) - ;; Emacs - (and (integerp last-input-event) - (not (event-modifiers last-input-event)) - (or (char-equal last-input-event ?\?) - (char-equal last-input-event ?\t) ; handled by 'tab already? - (char-equal last-input-event ?\ ))) + ;; Emacs + (and (wholenump last-input-event) + (or + ;; ?\t has event-modifier 'control + (char-equal last-input-event ?\t) + (and (not (event-modifiers last-input-event)) + (or (char-equal last-input-event ?\?) + (char-equal last-input-event ?\ ))))) ;; XEmacs (and (featurep 'xemacs) - (not (event-modifiers last-input-event)) - (or (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\?) - (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\t) - (char-equal - (funcall (symbol-function 'event-to-character) - last-input-event) ?\ )))) + (or + ;; ?\t has event-modifier 'control + (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\t) + (and (not (event-modifiers last-input-event)) + (or (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\?) + (char-equal + (funcall (symbol-function 'event-to-character) + last-input-event) ?\ )))))) t))) -(defun tramp-completion-handle-file-exists-p (filename) - "Like `file-exists-p' for tramp files." - (if (tramp-completion-mode filename) - (tramp-run-real-handler - 'file-exists-p (list filename)) - (tramp-completion-run-real-handler - 'file-exists-p (list filename)))) - -;; Localname manipulation in case of partial TRAMP file names. -(defun tramp-completion-handle-file-name-directory (file) - "Like `file-name-directory' but aware of TRAMP files." - (if (tramp-completion-mode file) - "/" - (tramp-completion-run-real-handler - 'file-name-directory (list file)))) - -;; Localname manipulation in case of partial TRAMP file names. -(defun tramp-completion-handle-file-name-nondirectory (file) - "Like `file-name-nondirectory' but aware of TRAMP files." - (substring - file (length (tramp-completion-handle-file-name-directory file)))) - ;; Method, host name and user name completion. ;; `tramp-completion-dissect-file-name' returns a list of ;; tramp-file-name structures. For all of them we return possible completions. +;;;###autoload (defun tramp-completion-handle-file-name-all-completions (filename directory) "Like `file-name-all-completions' for partial tramp files." - (let* - ((fullname (concat directory filename)) - ;; local files - (result - (if (tramp-completion-mode fullname) - (tramp-run-real-handler - 'file-name-all-completions (list filename directory)) - (tramp-completion-run-real-handler - 'file-name-all-completions (list filename directory)))) - ;; possible completion structures - (v (tramp-completion-dissect-file-name fullname))) - - (while v - (let* ((car (car v)) - (multi-method (tramp-file-name-multi-method car)) - (method (tramp-file-name-method car)) - (user (tramp-file-name-user car)) - (host (tramp-file-name-host car)) - (localname (tramp-file-name-localname car)) - (m (tramp-find-method multi-method method user host)) - (tramp-current-user user) ; see `tramp-parse-passwd' - all-user-hosts) - - (unless (or multi-method ;; Not handled (yet). - localname) ;; Nothing to complete - - (if (or user host) - - ;; Method dependent user / host combinations - (progn - (mapcar - (lambda (x) - (setq all-user-hosts - (append all-user-hosts - (funcall (nth 0 x) (nth 1 x))))) - (tramp-get-completion-function m)) - - (setq result (append result - (mapcar - (lambda (x) - (tramp-get-completion-user-host - method user host (nth 0 x) (nth 1 x))) - (delq nil all-user-hosts))))) - - ;; Possible methods - (setq result - (append result (tramp-get-completion-methods m))))) - - (setq v (delq car v)))) - - ;;; unify list, remove nil elements - (let (result1) - (while result - (let ((car (car result))) - (when car (add-to-list 'result1 car)) - (setq result (delq car result)))) - - result1))) + (unwind-protect + ;; We need to reset `tramp-completion-mode'. + (progn + (setq tramp-completion-mode t) + (let* + ((fullname (concat directory filename)) + ;; possible completion structures + (v (tramp-completion-dissect-file-name fullname)) + result result1) + + (while v + (let* ((car (car v)) + (multi-method (tramp-file-name-multi-method car)) + (method (tramp-file-name-method car)) + (user (tramp-file-name-user car)) + (host (tramp-file-name-host car)) + (localname (tramp-file-name-localname car)) + (m (tramp-find-method multi-method method user host)) + (tramp-current-user user) ; see `tramp-parse-passwd' + all-user-hosts) + + (unless (or multi-method ;; Not handled (yet). + localname) ;; Nothing to complete + + (if (or user host) + + ;; Method dependent user / host combinations + (progn + (mapcar + (lambda (x) + (setq all-user-hosts + (append all-user-hosts + (funcall (nth 0 x) (nth 1 x))))) + (tramp-get-completion-function m)) + + (setq result (append result + (mapcar + (lambda (x) + (tramp-get-completion-user-host + method user host (nth 0 x) (nth 1 x))) + (delq nil all-user-hosts))))) + + ;; Possible methods + (setq result + (append result (tramp-get-completion-methods m))))) + + (setq v (cdr v)))) + + ;; unify list, remove nil elements + (while result + (let ((car (car result))) + (when car (add-to-list + 'result1 (substring car (length directory)))) + (setq result (cdr result)))) + + ;; Complete local parts + (append + result1 + (condition-case nil + (if result1 + ;; "/ssh:" does not need to be expanded as hostname. + (tramp-run-real-handler + 'file-name-all-completions (list filename directory)) + ;; No method/user/host found to be expanded. + (tramp-completion-run-real-handler + 'file-name-all-completions (list filename directory))) + (error nil))))) + ;; unwindform + (setq tramp-completion-mode nil))) ;; Method, host name and user name completion for a file. -(defun tramp-completion-handle-file-name-completion (filename directory) +;;;###autoload +(defun tramp-completion-handle-file-name-completion + (filename directory &optional predicate) "Like `file-name-completion' for tramp files." - (try-completion filename - (mapcar 'list (file-name-all-completions filename directory)))) + (try-completion + filename + (mapcar 'list (file-name-all-completions filename directory)) + (when predicate + (lambda (x) (funcall predicate (expand-file-name (car x) directory)))))) ;; I misuse a little bit the tramp-file-name structure in order to handle ;; completion possibilities for partial methods / user names / host names. @@ -4534,8 +4768,7 @@ remote host and localname (filename on remote host)." (lambda (method) (and method (string-match (concat "^" (regexp-quote partial-method)) method) - ;; we must remove leading "/". - (substring (tramp-make-tramp-file-name nil method nil nil nil) 1))) + (tramp-make-tramp-file-name nil method nil nil nil))) (delete "multi" (mapcar 'car tramp-methods)))) ;; Compares partial user and host names with possible completions. @@ -4568,8 +4801,7 @@ PARTIAL-USER must match USER, PARTIAL-HOST must match HOST." host nil))) (unless (zerop (+ (length user) (length host))) - ;; we must remove leading "/". - (substring (tramp-make-tramp-file-name nil method user host nil) 1))) + (tramp-make-tramp-file-name nil method user host nil))) (defun tramp-parse-rhosts (filename) "Return a list of (user host) tuples allowed to access. @@ -4788,15 +5020,6 @@ User may be nil." (forward-line 1) result)) -(defun tramp-completion-handle-expand-file-name (name &optional dir) - "Like `expand-file-name' for tramp files." - (let ((fullname (concat (or dir default-directory) name))) - (if (tramp-completion-mode fullname) - (tramp-run-real-handler - 'expand-file-name (list name dir)) - (tramp-completion-run-real-handler - 'expand-file-name (list name dir))))) - ;;; Internal Functions: (defun tramp-maybe-send-perl-script (multi-method method user host script name) @@ -4831,6 +5054,9 @@ Function may have 0-3 parameters." auto-save-default) (auto-save-mode 1))) (add-hook 'find-file-hooks 'tramp-set-auto-save t) +(add-hook 'tramp-unload-hook + '(lambda () + (remove-hook 'find-file-hooks 'tramp-set-auto-save))) (defun tramp-run-test (switch filename) "Run `test' on the remote system, given a SWITCH and a FILENAME. @@ -4879,15 +5105,26 @@ hosts, or files, disagree." (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 ((touch-time (format-time-string "%Y%m%d%H%M.%S" time))) + (let* ((utc + ;; With GNU Emacs, `format-time-string' has an optional + ;; parameter UNIVERSAL. This is preferred. + (and (functionp 'subr-arity) + (= 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)))) (if (tramp-tramp-file-p file) (with-parsed-tramp-file-name file nil (let ((buf (tramp-get-buffer multi-method method user host))) (unless (zerop (tramp-send-command-and-check multi-method method user host - (format "touch -t %s %s" + (format "%s touch -t %s %s" + (if utc "TZ=UTC; export TZ;" "") touch-time - localname))) + (tramp-shell-quote-argument localname)) + t)) (pop-to-buffer buf) (error "tramp-touch: touch failed, see buffer `%s' for details" buf)))) @@ -5176,7 +5413,10 @@ Returns nil if none was found, else the command is returned." (defun tramp-action-password (p multi-method method user host) "Query the user for a password." - (let ((pw-prompt (match-string 0))) + (let ((pw-prompt + (format "Password for %s " + (tramp-make-tramp-file-name + nil method user host "")))) (tramp-message 9 "Sending password") (tramp-enter-password p pw-prompt user host))) @@ -5193,6 +5433,11 @@ Returns nil if none was found, else the command is returned." (kill-process p) (throw 'tramp-action 'permission-denied)) +(defun tramp-action-copy-failed (p multi-method method user host) + "Signal copy failed." + (kill-process p) + (error "%s" (match-string 1))) + (defun tramp-action-yesno (p multi-method method user host) "Ask the user for confirmation using `yes-or-no-p'. Send \"yes\" to remote process on confirmation, abort otherwise. @@ -5263,8 +5508,12 @@ The terminal type can be configured with `tramp-terminal-type'." (defun tramp-multi-action-password (p method user host) "Query the user for a password." - (tramp-message 9 "Sending password") - (tramp-enter-password p (match-string 0) user host)) + (let ((pw-prompt + (format "Password for %s " + (tramp-make-tramp-file-name + nil method user host "")))) + (tramp-message 9 "Sending password") + (tramp-enter-password p pw-prompt user host))) (defun tramp-multi-action-succeed (p method user host) "Signal success in finding shell prompt." @@ -5309,6 +5558,7 @@ The terminal type can be configured with `tramp-terminal-type'." (defun tramp-process-actions (p multi-method method user host actions) "Perform actions until success." + (tramp-message 10 "%s" (mapconcat 'identity (process-command p) " ")) (let (exit) (while (not exit) (tramp-message 9 "Waiting for prompts from remote shell") @@ -5413,6 +5663,7 @@ Maybe the different regular expressions need to be tuned. (or user (user-login-name)) host method) (let ((process-environment (copy-sequence process-environment))) (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") (let* ((default-directory (tramp-temporary-file-directory)) ;; If we omit the conditional here, then we would use ;; `undecided-dos' in some cases. With the conditional, @@ -5478,10 +5729,14 @@ arguments, and xx will be used as the host name to connect to. multi-method (tramp-find-method multi-method method user host) user host 'tramp-login-program)) - (login-args (tramp-get-method-parameter - multi-method - (tramp-find-method multi-method method user host) - user host 'tramp-login-args)) + (login-args (mapcar + (lambda (x) + (format-spec + x `((?t . ,(format "/tmp/%s" tramp-temp-name-prefix))))) + (tramp-get-method-parameter + multi-method + (tramp-find-method multi-method method user host) + user host 'tramp-login-args))) (real-host host)) ;; The following should be changed. We need a more general ;; mechanism to parse extra host args. @@ -5489,6 +5744,7 @@ arguments, and xx will be used as the host name to connect to. (setq login-args (cons "-p" (cons (match-string 2 host) login-args))) (setq real-host (match-string 1 host))) (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") (let* ((default-directory (tramp-temporary-file-directory)) ;; If we omit the conditional, we would use ;; `undecided-dos' in some cases. With the conditional, @@ -5540,6 +5796,7 @@ prompt than you do, so it is not at all unlikely that the variable (or user "") method) (let ((process-environment (copy-sequence process-environment))) (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") (let* ((default-directory (tramp-temporary-file-directory)) ;; If we omit the conditional, we use `undecided-dos' in ;; some cases. With the conditional, we use nil in these @@ -5604,6 +5861,7 @@ log in as u2 to h2." (tramp-message 7 "Opening `%s' connection..." multi-method) (let ((process-environment (copy-sequence process-environment))) (setenv "TERM" tramp-terminal-type) + (setenv "PS1" "$ ") (let* ((default-directory (tramp-temporary-file-directory)) ;; If we omit the conditional, we use `undecided-dos' in ;; some cases. With the conditional, we use nil in these @@ -6084,8 +6342,17 @@ locale to C and sets up the remote shell search path." "ln" tramp-remote-path nil))) (when ln (tramp-set-connection-property "ln" ln multi-method method user host))) + ;; Set uid and gid. (erase-buffer) + (tramp-send-command multi-method method user host "id -u; id -g") + (tramp-wait-for-output) + (goto-char (point-min)) + (tramp-set-connection-property + "uid" (read (current-buffer)) multi-method method user host) + (tramp-set-connection-property + "gid" (read (current-buffer)) multi-method method user host) ;; Find the right encoding/decoding commands to use. + (erase-buffer) (unless (tramp-method-out-of-band-p multi-method method user host) (tramp-find-inline-encoding multi-method method user host)) ;; If encoding/decoding command are given, test to see if they work. @@ -6294,7 +6561,8 @@ connection if a previous connection has died for some reason." p (processp p) (memq (process-status p) '(run open))) (tramp-send-command multi-method method user host "echo are you awake" nil t) - (unless (tramp-wait-for-output 10) + (unless (and (memq (process-status p) '(run open)) + (tramp-wait-for-output 10)) (delete-process p) (setq p nil)) (erase-buffer))) @@ -6578,9 +6846,14 @@ If `tramp-discard-garbage' is nil, just erase buffer." "Convert file-attributes ATTR generated by perl script or ls. Convert file mode bits to string and set virtual device number. Return ATTR." + ;; Convert file mode bits to string. (unless (stringp (nth 8 attr)) - ;; Convert file mode bits to string. (setcar (nthcdr 8 attr) (tramp-file-mode-from-int (nth 8 attr)))) + ;; Set file's gid change bit. Possible only when id-format is 'integer. + (when (numberp (nth 3 attr)) + (setcar (nthcdr 9 attr) + (not (eql (nth 3 attr) + (tramp-get-remote-gid multi-method method user host))))) ;; Set virtual device number. (setcar (nthcdr 11 attr) (tramp-get-device multi-method method user host)) @@ -6717,8 +6990,8 @@ localname (file name on remote host)." item) (while choices (setq item (pop choices)) - (when (and (string-match (nth 0 item) (or host "")) - (string-match (nth 1 item) (or user ""))) + (when (and (string-match (or (nth 0 item) "") (or host "")) + (string-match (or (nth 1 item) "") (or user ""))) (setq method (nth 2 item)) (setq choices nil))) method)) @@ -6838,6 +7111,12 @@ If both MULTI-METHOD and METHOD are nil, do a lookup in (defun tramp-get-remote-ln (multi-method method user host) (tramp-get-connection-property "ln" nil multi-method method user host)) +(defun tramp-get-remote-uid (multi-method method user host) + (tramp-get-connection-property "uid" nil multi-method method user host)) + +(defun tramp-get-remote-gid (multi-method method user host) + (tramp-get-connection-property "gid" nil multi-method method user host)) + ;; Get a property of a TRAMP connection. (defun tramp-get-connection-property (property default multi-method method user host) @@ -6848,7 +7127,7 @@ If the value is not set for the connection, return `default'" (let (error) (condition-case nil (symbol-value (intern (concat "tramp-connection-property-" property))) - (error default))))) + (error default))))) ;; Set a property of a TRAMP connection. (defun tramp-set-connection-property @@ -6905,33 +7184,30 @@ as default." ;; Auto saving to a special directory. -(defun tramp-make-auto-save-file-name (fn) - "Returns a file name in `tramp-auto-save-directory' for autosaving this file." - (when tramp-auto-save-directory - (unless (file-exists-p tramp-auto-save-directory) - (make-directory tramp-auto-save-directory t))) - ;; jka-compr doesn't like auto-saving, so by appending "~" to the - ;; file name we make sure that jka-compr isn't used for the - ;; auto-save file. - (let ((buffer-file-name (expand-file-name - (tramp-subst-strs-in-string '(("_" . "|") - ("/" . "_a") - (":" . "_b") - ("|" . "__") - ("[" . "_l") - ("]" . "_r")) - fn) - tramp-auto-save-directory))) - (make-auto-save-file-name))) - -(defadvice make-auto-save-file-name - (around tramp-advice-make-auto-save-file-name () activate) - "Invoke `tramp-make-auto-save-file-name' for tramp files." - (if (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name)) - tramp-auto-save-directory) - (setq ad-return-value - (tramp-make-auto-save-file-name (buffer-file-name))) - ad-do-it)) +(defun tramp-exists-file-name-handler (operation &rest args) + (let ((buffer-file-name "/") + (fnha file-name-handler-alist) + (check-file-name-operation operation) + (file-name-handler-alist + (list + (cons "/" + '(lambda (operation &rest args) + "Returns OPERATION if it is the one to be checked" + (if (equal check-file-name-operation operation) + operation + (let ((file-name-handler-alist fnha)) + (apply operation args)))))))) + (eq (apply operation args) operation))) + +(unless (tramp-exists-file-name-handler 'make-auto-save-file-name) + (defadvice make-auto-save-file-name + (around tramp-advice-make-auto-save-file-name () activate) + "Invoke `tramp-handle-make-auto-save-file-name' for tramp files." + (if (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name))) + (setq ad-return-value (tramp-handle-make-auto-save-file-name)) + ad-do-it)) + (add-hook 'tramp-unload-hook + '(lambda () (ad-unadvise 'make-auto-save-file-name)))) ;; In Emacs < 22 and XEmacs < 21.5 autosaved remote files have ;; permission 0666 minus umask. This is a security threat. @@ -6949,13 +7225,16 @@ as default." ;; auto-saved file belonging to another original file. This could ;; be a security threat. (set-file-modes buffer-auto-save-file-name - (or (file-modes bfn) #o600))))) + (or (file-modes bfn) (tramp-octal-to-decimal "0600")))))) (unless (or (> emacs-major-version 21) (and (featurep 'xemacs) (= emacs-major-version 21) (> emacs-minor-version 4))) - (add-hook 'auto-save-hook 'tramp-set-auto-save-file-modes)) + (add-hook 'auto-save-hook 'tramp-set-auto-save-file-modes) + (add-hook 'tramp-unload-hook + '(lambda () + (remove-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. @@ -7024,10 +7303,7 @@ Invokes `password-read' if available, `read-passwd' else." (defun tramp-time-diff (t1 t2) "Return the difference between the two times, in seconds. -T1 and T2 are time values (as returned by `current-time' for example). - -NOTE: This function will fail if the time difference is too large to -fit in an integer." +T1 and T2 are time values (as returned by `current-time' for example)." ;; Pacify byte-compiler with `symbol-function'. (cond ((and (fboundp 'subtract-time) (fboundp 'float-time)) @@ -7038,10 +7314,9 @@ fit in an integer." (funcall (symbol-function 'time-to-seconds) (funcall (symbol-function 'subtract-time) t1 t2))) ((fboundp 'itimer-time-difference) - (floor (funcall - (symbol-function 'itimer-time-difference) - (if (< (length t1) 3) (append t1 '(0)) t1) - (if (< (length t2) 3) (append t2 '(0)) t2)))) + (funcall (symbol-function 'itimer-time-difference) + (if (< (length t1) 3) (append t1 '(0)) t1) + (if (< (length t2) 3) (append t2 '(0)) t2))) (t ;; snarfed from Emacs 21 time-date.el; combining ;; time-to-seconds and subtract-time @@ -7182,7 +7457,9 @@ Only works for Bourne-like shells." (setq ad-return-value (list name)))) ;; If it is not a Tramp file, just run the original function. (let ((res ad-do-it)) - (setq ad-return-value (or res (list name)))))))) + (setq ad-return-value (or res (list name))))))) + (add-hook 'tramp-unload-hook + '(lambda () (ad-unadvise 'file-expand-wildcards)))) ;; Tramp version is useful in a number of situations. @@ -7407,6 +7684,26 @@ Therefore, the contents of files might be included in the debug buffer(s).") (defalias 'tramp-submit-bug 'tramp-bug) +;; Checklist for `tramp-unload-hook' +;; - Unload all `tramp-*' packages +;; - Reset `file-name-handler-alist' +;; - Cleanup hooks where Tramp functions are in +;; - Cleanup advised functions +;; - Cleanup autoloads +;;;###autoload +(defun tramp-unload-tramp () + "Discard Tramp from loading remote files." + (interactive) + ;; When Tramp is not loaded yet, its autoloads are still active. + (tramp-unload-file-name-handlers) + ;; ange-ftp settings must be enabled. + (when (functionp 'tramp-ftp-enable-ange-ftp) + (funcall (symbol-function 'tramp-ftp-enable-ange-ftp))) + ;; `tramp-util' unloads also `tramp'. + (condition-case nil ;; maybe its not loaded yet. + (unload-feature (if (featurep 'tramp-util) 'tramp-util 'tramp) 'force) + (error nil))) + (provide 'tramp) ;; Make sure that we get integration with the VC package. @@ -7414,7 +7711,12 @@ Therefore, the contents of files might be included in the debug buffer(s).") ;; This must come after (provide 'tramp) because tramp-vc.el ;; requires tramp. (eval-after-load "vc" - '(require 'tramp-vc)) + '(progn + (require 'tramp-vc) + (add-hook 'tramp-unload-hook + '(lambda () + (when (featurep 'tramp-vc) + (unload-feature 'tramp-vc 'force)))))) ;;; TODO: