--- /dev/null
+;;; tramp.el --- Transparent Remote Access, Multiple Protocol -*- coding: iso-8859-1; -*-
+
+;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+
+;; Author: Kai.Grossjohann@CS.Uni-Dortmund.DE
+;; Keywords: comm, processes
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This package provides remote file editing, similar to ange-ftp.
+;; The difference is that ange-ftp uses FTP to transfer files between
+;; the local and the remote host, whereas tramp.el uses a combination
+;; of rsh and rcp or other work-alike programs, such as ssh/scp.
+;;
+;; For more detailed instructions, please see the info file, which is
+;; included in the file `tramp.tar.gz' mentioned below.
+;;
+;; 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
+;; package `fsf-compat' for the `with-timeout' macro.)
+;;
+;; This version might not work with pre-Emacs 21 VC unless VC is
+;; loaded before tramp.el. Could you please test this and tell me about
+;; the result? Thanks.
+;;
+;; Also see the todo list at the bottom of this file.
+;;
+;; The current version of tramp.el can be retrieved from the following
+;; URL: ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz
+;; For your convenience, the *.el file is available separately from
+;; the same directory.
+;;
+;; There's a mailing list for this, as well. Its name is:
+;; tramp-devel@lists.sourceforge.net
+;; Send a mail with `help' in the subject (!) to the administration
+;; address for instructions on joining the list. The administration
+;; address is:
+;; tramp-devel-request@lists.sourceforge.net
+;; You can also use the Web to subscribe, under the following URL:
+;; http://lists.sourceforge.net/lists/listinfo/tramp-devel
+;;
+;; For the adventurous, the current development sources are available
+;; via CVS. You can find instructions about this at the following URL:
+;; http://sourceforge.net/projects/tramp/
+;; Click on "CVS" in the navigation bar near the top.
+;;
+;; Don't forget to put on your asbestos longjohns, first!
+
+;;; Code:
+
+(defconst tramp-version "2.0.0"
+ "This version of tramp.")
+(defconst tramp-bug-report-address "tramp-devel@mail.freesoftware.fsf.org"
+ "Email address to send bug reports to.")
+
+(require 'timer)
+(require 'format-spec) ;from Gnus 5.8, also in tar ball
+(require 'base64) ;for the mimencode methods
+(require 'shell)
+(require 'advice)
+
+;; ;; It does not work to load EFS after loading TRAMP.
+;; (when (fboundp 'efs-file-handler-function)
+;; (require 'efs))
+
+(eval-when-compile
+ (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
+
+;;; User Customizable Internal Variables:
+
+(defgroup tramp nil
+ "Edit remote files with a combination of rsh and rcp or similar programs."
+ :group 'files)
+
+(defcustom tramp-verbose 10
+ "*Verbosity level for tramp.el. 0 means be silent, 10 is most verbose."
+ :group 'tramp
+ :type 'integer)
+
+(defcustom tramp-debug-buffer nil
+ "*Whether to send all commands and responses to a debug buffer."
+ :group 'tramp
+ :type 'boolean)
+
+(defcustom tramp-auto-save-directory nil
+ "*Put auto-save files in this directory, if set.
+The idea is to use a local directory so that auto-saving is faster."
+ :group 'tramp
+ :type '(choice (const nil)
+ string))
+
+(defcustom tramp-sh-program "/bin/sh"
+ "*Use this program for shell commands on the local host.
+This MUST be a Bourne-like shell. This shell is used to execute
+the encoding and decoding command on the local host, so if you
+want to use `~' in those commands, you should choose a shell here
+which groks tilde expansion. `/bin/sh' normally does not
+understand tilde expansion.
+
+Note that this variable is not used for remote commands. There are
+mechanisms in tramp.el which automatically determine the right shell to
+use for the remote host."
+ :group 'tramp
+ :type '(file :must-match t))
+
+;; CCC I have changed all occurrences of comint-quote-filename with
+;; tramp-shell-quote-argument, except in tramp-handle-expand-many-files.
+;; There, comint-quote-filename was removed altogether. If it turns
+;; out to be necessary there, something will need to be done.
+;;-(defcustom tramp-file-name-quote-list
+;;- '(?] ?[ ?\| ?& ?< ?> ?\( ?\) ?\; ?\ ?\* ?\? ?\! ?\" ?\' ?\` ?# ?\@ ?\+ )
+;;- "*Protect these characters from the remote shell.
+;;-Any character in this list is quoted (preceded with a backslash)
+;;-because it means something special to the shell. This takes effect
+;;-when sending file and directory names to the remote shell.
+;;-
+;;-See `comint-file-name-quote-list' for details."
+;;- :group 'tramp
+;;- :type '(repeat character))
+
+(defcustom tramp-methods
+ '( ("rcp" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "rsh")
+ (tramp-rcp-program "rcp")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("scp" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program "scp")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("scp1" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh1")
+ (tramp-rcp-program "scp1")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("scp2" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh2")
+ (tramp-rcp-program "scp2")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("rsync" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program "rsync")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args ("-e" "ssh"))
+ (tramp-rcp-keep-date-arg "-t")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("ru" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "rsh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("su" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("su1" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh1")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("su2" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh2")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("rm" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "rsh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("sm" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("smp" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "tramp_mimencode")
+ (tramp-decoding-command "tramp_mimedecode")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil))
+ ("sm1" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh1")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("sm2" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh2")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("tm" (tramp-connection-function tramp-open-connection-telnet)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program "telnet")
+ (tramp-telnet-args nil))
+ ("tu" (tramp-connection-function tramp-open-connection-telnet)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program "telnet")
+ (tramp-telnet-args nil))
+ ("sum" (tramp-connection-function tramp-open-connection-su)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program "su")
+ (tramp-su-args ("-" "%u"))
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("suu" (tramp-connection-function tramp-open-connection-su)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program "su")
+ (tramp-su-args ("-" "%u"))
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("sudm" (tramp-connection-function tramp-open-connection-su)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program "sudo")
+ (tramp-su-args ("-u" "%u" "-s"))
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("sudu" (tramp-connection-function tramp-open-connection-su)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program "sudo")
+ (tramp-su-args ("-u" "%u" "-s"))
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("multi" (tramp-connection-function tramp-open-connection-multi)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("multiu" (tramp-connection-function tramp-open-connection-multi)
+ (tramp-rsh-program nil)
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args nil)
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("scpx" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program "scp")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("smx" (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "ssh")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("km"
+ (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "krlogin")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-x"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("plinku"
+ (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "plink")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-ssh")) ;optionally add "-v"
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "uuencode xxx")
+ (tramp-decoding-command
+ "( uudecode -o - 2>/dev/null || uudecode -p 2>/dev/null )")
+ (tramp-encoding-function nil)
+ (tramp-decoding-function uudecode-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("plinkm"
+ (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "plink")
+ (tramp-rcp-program nil)
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-ssh")) ;optionally add "-v"
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg nil)
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command "mimencode -b")
+ (tramp-decoding-command "mimencode -u -b")
+ (tramp-encoding-function base64-encode-region)
+ (tramp-decoding-function base64-decode-region)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("pscp"
+ (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "plink")
+ (tramp-rcp-program "pscp")
+ (tramp-remote-sh "/bin/sh")
+ (tramp-rsh-args ("-ssh"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ ("fcp"
+ (tramp-connection-function tramp-open-connection-rsh)
+ (tramp-rsh-program "fsh")
+ (tramp-rcp-program "fcp")
+ (tramp-remote-sh "/bin/sh -i")
+ (tramp-rsh-args ("sh" "-i"))
+ (tramp-rcp-args nil)
+ (tramp-rcp-keep-date-arg "-p")
+ (tramp-su-program nil)
+ (tramp-su-args nil)
+ (tramp-encoding-command nil)
+ (tramp-decoding-command nil)
+ (tramp-encoding-function nil)
+ (tramp-decoding-function nil)
+ (tramp-telnet-program nil)
+ (tramp-telnet-args nil))
+ )
+ "*Alist of methods for remote files.
+This is a list of entries of the form (NAME PARAM1 PARAM2 ...).
+Each NAME stands for a remote access method. Each PARAM is a
+pair of the form (KEY VALUE). The following KEYs are defined:
+ * `tramp-connection-function'
+ This specifies the function to use to connect to the remote host.
+ Currently, `tramp-open-connection-rsh', `tramp-open-connection-telnet'
+ and `tramp-open-connection-su' are defined. See the documentation
+ of these functions for more details.
+ * `tramp-remote-sh'
+ This specifies the Bourne shell to use on the remote host. This
+ MUST be a Bourne-like shell. It is normally not necessary to set
+ this to any value other than \"/bin/sh\": tramp wants to use a shell
+ which groks tilde expansion, but it can search for it. Also note
+ that \"/bin/sh\" exists on all Unixen, this might not be true for
+ the value that you decide to use. You Have Been Warned.
+ * `tramp-rsh-program'
+ This specifies the name of the program to use for rsh; this might be
+ the full path to rsh or the name of a workalike program.
+ * `tramp-rsh-args'
+ This specifies the list of arguments to pass to the above
+ mentioned program. Please note that this is a list of arguments,
+ that is, normally you don't want to put \"-a -b\" or \"-f foo\"
+ here. Instead, you want two list elements, one for \"-a\" and one
+ for \"-b\", or one for \"-f\" and one for \"foo\".
+ * `tramp-rcp-program'
+ This specifies the name of the program to use for rcp; this might be
+ the full path to rcp or the name of a workalike program.
+ * `tramp-rcp-args'
+ This specifies the list of parameters to pass to the above mentioned
+ program, the hints for `tramp-rsh-args' also apply here.
+ * `tramp-rcp-keep-date-arg'
+ This specifies the parameter to use for `rcp' when the timestamp
+ of the original file should be kept. For `rcp', use `-p', for
+ `rsync', use `-t'.
+ * `tramp-su-program'
+ This specifies the name of the program to use for `su'.
+ * `tramp-su-args'
+ This specifies the list of arguments to pass to `su'.
+ \"%u\" is replaced by the user name, use \"%%\" for a literal
+ percent character.
+ * `tramp-encoding-command'
+ This specifies a command to use to encode the file contents for
+ transfer. The command should read the raw file contents from
+ standard input and write the encoded file contents to standard
+ output. In this string, the percent escape \"%f\" should be used
+ to indicate the file to convert. Use \"%%\" if you need a literal
+ percent character in your command.
+ * `tramp-decoding-command'
+ This specifies a command to use to decode file contents encoded
+ with `tramp-encoding-command'. The command should read from standard
+ input and write to standard output.
+ * `tramp-encoding-function'
+ This specifies a function to be called to encode the file contents
+ on the local side. This function should accept two arguments
+ START and END, the beginning and end of the region to encode. The
+ region should be replaced with the encoded contents.
+ * `tramp-decoding-function'
+ Same for decoding on the local side.
+ * `tramp-telnet-program'
+ Specifies the telnet program to use when using
+ `tramp-open-connection-telnet' to log in.
+ * `tramp-telnet-args'
+ Specifies list of arguments to pass to `telnet'. The hints for
+ `tramp-rsh-args' also apply here.
+
+What does all this mean? Well, you should specify `tramp-rsh-program',
+`tramp-telnet-program' or `tramp-su-program' for all methods; this program
+is used to log in to the remote site. Then, there are two ways to
+actually transfer the files between the local and the remote side.
+One way is using an additional rcp-like program. If you want to do
+this, set `tramp-rcp-program' in the method.
+
+Another possibility for file transfer is inline transfer, i.e. the
+file is passed through the same buffer used by `tramp-rsh-program'. In
+this case, the file contents need to be protected since the
+`tramp-rsh-program' might use escape codes or the connection might not
+be eight-bit clean. Therefore, file contents are encoded for transit.
+
+Two possibilities for encoding are uuencode/uudecode and mimencode.
+For uuencode/uudecode you want to set `tramp-encoding-command' to
+something like \"uuencode\" and `tramp-decoding-command' to \"uudecode
+-p\". For mimencode you want to set `tramp-encoding-command' to
+something like \"mimencode -b\" and `tramp-decoding-command' to
+\"mimencode -b -u\".
+
+When using inline transfer, you can use a program or a Lisp function
+on the local side to encode or decode the file contents. Set the
+`tramp-encoding-function' and `tramp-decoding-function' parameters to nil
+in order to use the commands or to the function to use. It is
+possible to specify one function and the other parameter as nil.
+
+So, to summarize: if the method is an inline method, you must specify
+`tramp-encoding-command' and `tramp-decoding-command', and
+`tramp-rcp-program' must be nil. If the method is out of band, then
+you must specify `tramp-rcp-program' and `tramp-rcp-args' and
+`tramp-encoding-command' and `tramp-decoding-command' must be nil.
+Every method, inline or out of band, must specify
+`tramp-connection-function' plus the associated arguments (for
+example, the telnet program if you chose
+`tramp-open-connection-telnet').
+
+Notes:
+
+When using `tramp-open-connection-su' the phrase `open connection to a
+remote host' sounds strange, but it is used nevertheless, for
+consistency. No connection is opened to a remote host, but `su' is
+started on the local host. You are not allowed to specify a remote
+host other than `localhost' or the name of the local host.
+
+Using a uuencode/uudecode inline method is discouraged, please use one
+of the base64 methods instead since base64 encoding is much more
+reliable and the commands are more standardized between the different
+Unix versions. But if you can't use base64 for some reason, please
+note that the default uudecode command does not work well for some
+Unices, in particular AIX and Irix. For AIX, you might want to use
+the following command for uudecode:
+
+ sed '/^begin/d;/^[` ]$/d;/^end/d' | iconv -f uucode -t ISO8859-1
+
+For Irix, no solution is known yet."
+ :group 'tramp
+ :type '(repeat
+ (cons string
+ (set (list (const tramp-connection-function) function)
+ (list (const tramp-rsh-program)
+ (choice (const nil) string))
+ (list (const tramp-rcp-program)
+ (choice (const nil) string))
+ (list (const tramp-remote-sh)
+ (choice (const nil) string))
+ (list (const tramp-rsh-args) (repeat string))
+ (list (const tramp-rcp-args) (repeat string))
+ (list (const tramp-rcp-keep-date-arg)
+ (choice (const nil) string))
+ (list (const tramp-su-program)
+ (choice (const nil) string))
+ (list (const tramp-su-args) (repeat string))
+ (list (const tramp-encoding-command)
+ (choice (const nil) string))
+ (list (const tramp-decoding-command)
+ (choice (const nil) string))
+ (list (const tramp-encoding-function)
+ (choice (const nil) function))
+ (list (const tramp-decoding-function)
+ (choice (const nil) function))
+ (list (const tramp-telnet-program)
+ (choice (const nil) string))
+ (list (const tramp-telnet-args) (repeat string))))))
+
+(defcustom tramp-multi-methods '("multi" "multiu")
+ "*List of multi-hop methods.
+Each entry in this list should be a method name as mentioned in the
+variable `tramp-methods'."
+ :group 'tramp
+ :type '(repeat string))
+
+(defcustom tramp-multi-connection-function-alist
+ '(("telnet" tramp-multi-connect-telnet "telnet %h%n")
+ ("rsh" tramp-multi-connect-rlogin "rsh %h -l %u%n")
+ ("ssh" tramp-multi-connect-rlogin "ssh %h -l %u%n")
+ ("su" tramp-multi-connect-su "su - %u%n")
+ ("sudo" tramp-multi-connect-su "sudo -u %u -s%n"))
+ "*List of connection functions for multi-hop methods.
+Each list item is a list of three items (METHOD FUNCTION COMMAND),
+where METHOD is the name as used in the file name, FUNCTION is the
+function to be executed, and COMMAND is the shell command used for
+connecting.
+
+COMMAND may contain percent escapes. `%u' will be replaced with the
+user name, `%h' will be replaced with the host name, and `%n' will be
+replaced with an end-of-line character, as specified in the variable
+`tramp-rsh-end-of-line'. Use `%%' for a literal percent character.
+Note that the interpretation of the percent escapes also depends on
+the FUNCTION. For example, the `%u' escape is forbidden with the
+function `tramp-multi-connect-telnet'. See the documentation of the
+various functions for details."
+ :group 'tramp
+ :type '(repeat (list string function string)))
+
+(defcustom tramp-default-method "rcp"
+ "*Default method to use for transferring files.
+See `tramp-methods' for possibilities."
+ :group 'tramp
+ :type 'string)
+
+(defcustom tramp-rsh-end-of-line "\n"
+ "*String used for end of line in rsh connections.
+I don't think this ever needs to be changed, so please tell me about it
+if you need to change this."
+ :group 'tramp
+ :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/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.
+
+You can use `~' in this list, but when searching for a shell which groks
+tilde expansion, all directory names starting with `~' will be ignored."
+ :group 'tramp
+ :type '(repeat string))
+
+(defcustom tramp-login-prompt-regexp
+ ".*ogin: *$"
+ "*Regexp matching login-like prompts.
+The regexp should match the whole line."
+ :group 'tramp
+ :type 'regexp)
+
+(defcustom tramp-password-prompt-regexp
+ "^.*\\([pP]assword\\|passphrase.*\\):\^@? *$"
+ "*Regexp matching password-like prompts.
+The regexp should match the whole line.
+
+The `sudo' program appears to insert a `^@' character into the prompt."
+ :group 'tramp
+ :type 'regexp)
+
+(defcustom tramp-wrong-passwd-regexp
+ (concat "^.*\\(Permission denied.\\|Login [Ii]ncorrect\\|"
+ "Received signal [0-9]+\\|Connection \\(refused\\|closed\\)\\|"
+ "Sorry, try again.\\|Name or service not known\\).*$")
+ "*Regexp matching a `login failed' message.
+The regexp should match the whole line."
+ :group 'tramp
+ :type 'regexp)
+
+(defcustom tramp-temp-name-prefix "tramp."
+ "*Prefix to use for temporary files.
+If this is a relative file name (such as \"tramp.\"), it is considered
+relative to the directory name returned by the function
+`tramp-temporary-file-directory' (which see). It may also be an
+absolute file name; don't forget to include a prefix for the filename
+part, though."
+ :group 'tramp
+ :type 'string)
+
+(defcustom tramp-discard-garbage nil
+ "*If non-nil, try to discard garbage sent by remote shell.
+Some shells send such garbage upon connection setup."
+ :group 'tramp
+ :type 'boolean)
+
+;; File name format.
+
+(defcustom tramp-file-name-structure
+ (list "\\`/\\[\\(\\([a-zA-Z0-9]+\\)/\\)?\\(\\([-a-zA-Z0-9_#/:]+\\)@\\)?\\([-a-zA-Z0-9_#/:@.]+\\)\\]\\(.*\\)\\'"
+ 2 4 5 6)
+ "*List of five elements (REGEXP METHOD USER HOST FILE), detailing \
+the tramp file name structure.
+
+The first element REGEXP is a regular expression matching a tramp file
+name. The regex should contain parentheses around the method name,
+the user name, the host name, and the file name parts.
+
+The second element METHOD is a number, saying which pair of
+parentheses matches the method name. The third element USER is
+similar, but for the user name. The fourth element HOST is similar,
+but for the host name. The fifth element FILE is for the file name.
+These numbers are passed directly to `match-string', which see. That
+means the opening parentheses are counted to identify the pair.
+
+See also `tramp-file-name-regexp' and `tramp-make-tramp-file-format'."
+ :group 'tramp
+ :type '(list (regexp :tag "File name regexp")
+ (integer :tag "Paren pair for method name")
+ (integer :tag "Paren pair for user name ")
+ (integer :tag "Paren pair for host name ")
+ (integer :tag "Paren pair for file name ")))
+
+;;;###autoload
+(defcustom tramp-file-name-regexp "\\`/\\[.*\\]"
+ "*Regular expression matching file names handled by tramp.
+This regexp should match tramp file names but no other file names.
+\(When tramp.el is loaded, this regular expression is prepended to
+`file-name-handler-alist', and that is searched sequentially. Thus,
+if the tramp entry appears rather early in the `file-name-handler-alist'
+and is a bit too general, then some files might be considered tramp
+files which are not really tramp files.
+
+Please note that the entry in `file-name-handler-alist' is made when
+this file (tramp.el) is loaded. This means that this variable must be set
+before loading tramp.el. Alternatively, `file-name-handler-alist' can be
+updated after changing this variable.
+
+Also see `tramp-file-name-structure' and `tramp-make-tramp-file-format'."
+ :group 'tramp
+ :type 'regexp)
+
+(defcustom tramp-make-tramp-file-format "/[%m/%u@%h]%p"
+ "*Format string saying how to construct tramp file name.
+`%m' is replaced by the method name.
+`%u' is replaced by the user name.
+`%h' is replaced by the host name.
+`%p' is replaced by the file name.
+`%%' is replaced by %.
+
+Also see `tramp-file-name-structure' and `tramp-file-name-regexp'."
+ :group 'tramp
+ :type 'string)
+
+;; HHH: New. This format spec is made to handle the cases where the
+;; user does not provide a user name for the connection.
+(defcustom tramp-make-tramp-file-user-nil-format "/[%m/%h]%p"
+ "*Format string saying how to construct tramp file name when the user name is not known.
+`%m' is replaced by the method name.
+`%h' is replaced by the host name.
+`%p' is replaced by the file name.
+`%%' is replaced by %.
+
+Also see `tramp-make-tramp-file-format', `tramp-file-name-structure', and `tramp-file-name-regexp'."
+ :group 'tramp
+ :type 'string)
+
+(defcustom tramp-multi-file-name-structure
+ (list (concat
+ ;; prefix
+ "\\`/\\[\\(\\([a-z0-9]+\\)\\)?"
+ ;; regexp specifying a hop
+ "\\(\\(%s\\)+\\)"
+ ;; path name
+ "\\]\\(.*\\)\\'")
+ 2 ;number of pair to match method
+ 3 ;number of pair to match hops
+ -1) ;number of pair to match path
+
+ "*Describes the file name structure of `multi' files.
+Multi files allow you to contact a remote host in several hops.
+This is a list of four elements (REGEXP METHOD HOP PATH).
+
+The first element, REGEXP, gives a regular expression to match against
+the file name. In this regular expression, `%s' is replaced with the
+value of `tramp-multi-file-name-hop-structure'. (Note: in order to
+allow multiple hops, you normally want to use something like
+\"\\\\(\\\\(%s\\\\)+\\\\)\" in the regular expression. The outer pair
+of parentheses is used for the HOP element, see below.)
+
+All remaining elements are numbers. METHOD gives the number of the
+paren pair which matches the method name. HOP gives the number of the
+paren pair which matches the hop sequence. PATH gives the number of
+the paren pair which matches the path name on the remote host.
+
+PATH can also be negative, which means to count from the end. Ie, a
+value of -1 means the last paren pair.
+
+I think it would be good if the regexp matches the whole of the
+string, but I haven't actually tried what happens if it doesn't..."
+ :group 'tramp
+ :type '(list (regexp :tag "File name regexp")
+ (integer :tag "Paren pair for method name")
+ (integer :tag "Paren pair for hops")
+ (integer :tag "Paren pair to match path")))
+
+(defcustom tramp-multi-file-name-hop-structure
+ (list "/\\([a-z0-9_]+\\):\\([a-z0-9_]+\\)@\\([a-z0-9.-]+\\)"
+ 1 2 3)
+ "*Describes the structure of a hop in multi files.
+This is a list of four elements (REGEXP METHOD USER HOST). First
+element REGEXP is used to match against the hop. Pair number METHOD
+matches the method of one hop, pair number USER matches the user of
+one hop, pair number HOST matches the host of one hop.
+
+This regular expression should match exactly all of one hop."
+ :group 'tramp
+ :type '(list (regexp :tag "Hop regexp")
+ (integer :tag "Paren pair for method name")
+ (integer :tag "Paren pair for user name")
+ (integer :tag "Paren pair for host name")))
+
+(defcustom tramp-make-multi-tramp-file-format
+ (list "/[%m" "/%m:%u@%h" "]%p")
+ "*Describes how to construct a `multi' file name.
+This is a list of three elements PREFIX, HOP and PATH.
+
+The first element PREFIX says how to construct the prefix, the second
+element HOP specifies what each hop looks like, and the final element
+PATH says how to construct the path name.
+
+In PREFIX, `%%' means `%' and `%m' means the method name.
+
+In HOP, `%%' means `%' and `%m', `%u', `%h' mean the hop method, hop
+user and hop host, respectively.
+
+In PATH, `%%' means `%' and `%p' means the path name.
+
+The resulting file name always contains one copy of PREFIX and one
+copy of PATH, but there is one copy of HOP for each hop in the file
+name.
+
+Note: the current implementation requires the prefix to contain the
+method name, followed by all the hops, and the path name must come
+last."
+ :group 'tramp
+ :type '(list string string string))
+
+(defcustom tramp-terminal-type "dumb"
+ "*Value of TERM environment variable for logging in to remote host.
+Because Tramp wants to parse the output of the remote shell, it is easily
+confused by ANSI color escape sequences and suchlike. Often, shell init
+files conditionalize this setup based on the TERM environment variable."
+ :group 'tramp
+ :type 'string)
+
+(defcustom tramp-completion-without-shell-p nil
+ "*If nil, use shell wildcards for completion, else rely on Lisp only.
+Using shell wildcards for completions has the advantage that it can be
+fast even in large directories, but completion is always
+case-sensitive. Relying on Lisp only means that case-insensitive
+completion is possible (subject to the variable `completion-ignore-case'),
+but it might be slow on large directories."
+ :group 'tramp
+ :type 'boolean)
+
+;;; Internal Variables:
+
+(defvar tramp-buffer-file-attributes nil
+ "Holds the `ls -ild' output for the current buffer.
+This variable is local to each buffer. It is not used if the remote
+machine groks Perl. If it is used, it's used as an emulation for
+the visited file modtime.")
+(make-variable-buffer-local 'tramp-buffer-file-attributes)
+
+(defvar tramp-end-of-output "/////"
+ "String used to recognize end of output.")
+
+(defvar tramp-connection-function nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-remote-sh nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-rsh-program nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-rsh-args nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-rcp-program nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-rcp-args nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-rcp-keep-date-arg nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-encoding-command nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-decoding-command nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-encoding-function nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-decoding-function nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-telnet-program nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+(defvar tramp-telnet-args nil
+ "This internal variable holds a parameter for `tramp-methods'.
+In the connection buffer, this variable has the value of the like-named
+method parameter, as specified in `tramp-methods' (which see).")
+
+;; CCC `local in each buffer'?
+(defvar tramp-ls-command nil
+ "This command is used to get a long listing with numeric user and group ids.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-current-multi-method nil
+ "Name of `multi' connection method for this *tramp* buffer, or nil if not multi.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-current-method nil
+ "Connection method for this *tramp* buffer.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-current-user nil
+ "Remote login name for this *tramp* buffer.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-current-host nil
+ "Remote host for this *tramp* buffer.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-test-groks-nt nil
+ "Whether the `test' command groks the `-nt' switch.
+\(`test A -nt B' tests if file A is newer than file B.)
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+(defvar tramp-file-exists-command nil
+ "Command to use for checking if a file exists.
+This variable is automatically made buffer-local to each rsh process buffer
+upon opening the connection.")
+
+;; Perl script to implement `file-attributes' in a Lisp `read'able output.
+;; If you are hacking on this, note that you get *no* output unless this
+;; spits out a complete line, including the '\n' at the end.
+(defconst tramp-perl-file-attributes (concat
+ "$f = $ARGV[0];
+@s = lstat($f);
+if (($s[2] & 0170000) == 0120000) { $l = readlink($f); $l = \"\\\"$l\\\"\"; }
+elsif (($s[2] & 0170000) == 040000) { $l = \"t\"; }
+else { $l = \"nil\" };
+printf(\"(%s %u %u %u (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) (%u %u))\\n\",
+$l, $s[3], $s[4], $s[5], $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, $s[0] >> 16 & 0xffff, $s[0] & 0xffff);"
+ )
+ "Perl script to produce output suitable for use with `file-attributes'
+on the remote file system.")
+
+;; Perl script to implement `mime-encode'
+(defvar tramp-perl-mime-encode (concat
+ "sub encode_base64 ($);
+ my $buf;
+ while(read(STDIN, $buf, 60*57)) { print encode_base64($buf) }
+ sub encode_base64 ($) {
+ my $res = \"\";
+ my $eol = \"\n\";
+ pos($_[0]) = 0; # ensure start at the beginning
+ while ($_[0] =~ /(.{1,45})/gs) {
+ $res .= substr(pack(\"u\", $1), 1);
+ chop($res);
+ }
+ $res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs
+ # fix padding at the end
+ my $padding = (3 - length($_[0]) % 3) % 3;
+ $res =~ s/.{$padding}$/\"=\" x $padding/e if $padding;
+ # break encoded string into lines of no more than 76 characters each
+ if (length $eol) {
+ $res =~ s/(.{1,76})/$1$eol/g;
+ }
+ $res;}"))
+
+;; Perl script to implement `mime-decode'
+(defvar tramp-perl-mime-decode (concat
+ "sub decode_base64 ($);
+ my $buf;
+ while(read(STDIN, $buf, 60*57)) { print decode_base64($buf) }
+ sub decode_base64 ($) {
+ local($^W) = 0; # unpack(\"u\",...) gives bogus warning in 5.00[123]
+
+ my $str = shift;
+ my $res = \"\";
+
+ $str =~ tr|A-Za-z0-9+=/||cd; # remove non-base64 chars
+ if (length($str) % 4) {
+ warn(\"Length of base64 data not a multiple of 4\")
+ }
+ $str =~ s/=+$//; # remove padding
+ $str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format
+ while ($str =~ /(.{1,60})/gs) {
+ my $len = chr(32 + length($1)*3/4); # compute length byte
+ $res .= unpack(\"u\", $len . $1 ); # uudecode
+ }
+ $res;}"))
+
+; 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
+ (3 . "m") ; multiplexed character device (v7)
+ (4 . "d") ; directory
+ (5 . "?") ; Named special file (XENIX)
+ (6 . "b") ; block device
+ (7 . "?") ; multiplexed block device (v7)
+ (8 . "-") ; regular file
+ (9 . "n") ; network special file (HP-UX)
+ (10 . "l") ; symlink
+ (11 . "?") ; ACL shadow inode (Solaris, not userspace)
+ (12 . "s") ; socket
+ (13 . "D") ; door special (Solaris)
+ (14 . "w")) ; whiteout (BSD)
+ "A list of file types returned from the `stat' system call.
+This is used to map a mode number to a permission string.")
+
+(defvar tramp-dos-coding-system
+ (if (and (fboundp 'coding-system-p)
+ (funcall 'coding-system-p '(dos)))
+ 'dos
+ 'undecided-dos)
+ "Some Emacsen know the `dos' coding system, others need `undecided-dos'.")
+
+
+;; New handlers should be added here. The following operations can be
+;; handled using the normal primitives: file-name-as-directory,
+;; file-name-directory, file-name-nondirectory,
+;; file-name-sans-versions, get-file-buffer.
+(defconst tramp-file-name-handler-alist
+ '(
+ (load . tramp-handle-load)
+ (make-symbolic-link . tramp-handle-make-symbolic-link)
+ (file-name-directory . tramp-handle-file-name-directory)
+ (file-name-nondirectory . tramp-handle-file-name-nondirectory)
+ (file-truename . tramp-handle-file-truename)
+ (file-exists-p . tramp-handle-file-exists-p)
+ (file-directory-p . tramp-handle-file-directory-p)
+ (file-executable-p . tramp-handle-file-executable-p)
+ (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
+ (file-readable-p . tramp-handle-file-readable-p)
+ (file-regular-p . tramp-handle-file-regular-p)
+ (file-symlink-p . tramp-handle-file-symlink-p)
+ (file-writable-p . tramp-handle-file-writable-p)
+ (file-ownership-preserved-p . tramp-handle-file-ownership-preserved-p)
+ (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)
+ (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)
+ (copy-file . tramp-handle-copy-file)
+ (rename-file . tramp-handle-rename-file)
+ (set-file-modes . tramp-handle-set-file-modes)
+ (make-directory . tramp-handle-make-directory)
+ (delete-directory . tramp-handle-delete-directory)
+ (delete-file . tramp-handle-delete-file)
+ (directory-file-name . tramp-handle-directory-file-name)
+ (shell-command . tramp-handle-shell-command)
+ (insert-directory . tramp-handle-insert-directory)
+ (expand-file-name . tramp-handle-expand-file-name)
+ (file-local-copy . tramp-handle-file-local-copy)
+ (insert-file-contents . tramp-handle-insert-file-contents)
+ (write-region . tramp-handle-write-region)
+ (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
+ (dired-call-process . tramp-handle-dired-call-process)
+ (dired-recursive-delete-directory
+ . 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.
+Operations not mentioned here will be handled by the normal Emacs functions.")
+
+;;; For better error reporting.
+
+(defun tramp-version (arg)
+ "Print version number of tramp.el in minibuffer or current buffer."
+ (interactive "P")
+ (if arg (insert tramp-version) (message tramp-version)))
+
+;;; Internal functions which must come first.
+
+(defsubst tramp-message (level fmt-string &rest args)
+ "Emit a message depending on verbosity level.
+First arg LEVEL says to be quiet if `tramp-verbose' is less than LEVEL. The
+message is emitted only if `tramp-verbose' is greater than or equal to LEVEL.
+Calls function `message' with FMT-STRING as control string and the remaining
+ARGS to actually emit the message (if applicable).
+
+This function expects to be called from the tramp buffer only!"
+ (when (<= level tramp-verbose)
+ (apply #'message (concat "tramp: " fmt-string) args)
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer
+ (tramp-get-debug-buffer
+ tramp-current-multi-method tramp-current-method
+ tramp-current-user tramp-current-host))
+ (goto-char (point-max))
+ (tramp-insert-with-face
+ 'italic
+ (concat "# " (apply #'format fmt-string args) "\n"))))))
+
+(defun tramp-message-for-buffer
+ (multi-method method user host level fmt-string &rest args)
+ "Like `tramp-message' but temporarily switches to the tramp buffer.
+First three args METHOD, USER, and HOST identify the tramp buffer to use,
+remaining args passed to `tramp-message'."
+ (save-excursion
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (apply 'tramp-message level fmt-string args)))
+
+(defsubst tramp-line-end-position nil
+ "Return point at end of line.
+Calls `line-end-position' or `point-at-eol' if defined, else
+own implementation."
+ (cond
+ ((fboundp 'line-end-position) (funcall 'line-end-position))
+ ((fboundp 'point-at-eol) (funcall 'point-at-eol))
+ (t (save-excursion (end-of-line) (point)))))
+
+;;; File Name Handler Functions:
+
+;; The following file name handler ops are not implemented (yet?).
+
+(defun tramp-handle-make-symbolic-link
+ (filename linkname &optional ok-if-already-exists)
+ "Like `make-symbolic-link' for tramp files.
+This function will raise an error if FILENAME and LINKNAME are not
+on the same remote host."
+ (unless (or (tramp-tramp-file-p filename)
+ (tramp-tramp-file-p linkname))
+ (tramp-run-real-handler 'make-symbolic-link
+ (list filename linkname ok-if-already-exists)))
+ (let* ((file (tramp-dissect-file-name filename))
+ (link (tramp-dissect-file-name linkname))
+ (multi (tramp-file-name-multi-method file))
+ (method (tramp-file-name-method file))
+ (user (tramp-file-name-user file))
+ (host (tramp-file-name-host file))
+ (l-multi (tramp-file-name-multi-method link))
+ (l-meth (tramp-file-name-method link))
+ (l-user (tramp-file-name-user link))
+ (l-host (tramp-file-name-host link))
+ (ln (tramp-get-remote-ln multi method user host))
+ (cwd (file-name-directory (tramp-file-name-path file))))
+ (unless ln
+ (signal 'file-error (list "Making a symbolic link."
+ "ln(1) does not exist on the remote host.")))
+
+ ;; Check that method, user, host are the same.
+ (unless (equal host l-host)
+ (signal 'file-error (list "Can't make symlink across hosts" host l-host)))
+ (unless (equal user l-user)
+ (signal 'file-error (list "Can't make symlink for different users"
+ user l-user)))
+ (unless (and (equal multi l-multi)
+ (equal method l-meth))
+ (signal 'file-error (list "Method must be the same for making symlinks"
+ multi l-multi method l-meth)))
+
+ ;; Do the 'confirm if exists' thing.
+ (when (file-exists-p (tramp-file-name-path link))
+ ;; What to do?
+ (if (or (null ok-if-already-exists) ; not allowed to exist
+ (and (numberp ok-if-already-exists)
+ (not (yes-or-no-p
+ (format "File %s already exists; make it a link anyway? "
+ (tramp-file-name-path link))))))
+ (signal 'file-already-exists (list "File already exists"
+ (tramp-file-name-path link)))))
+
+ ;; 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.
+ (zerop
+ (tramp-send-command-and-check
+ multi method user host
+ (format "cd %s && %s -sf %s %s"
+ cwd ln
+ (tramp-file-name-path file) ; target
+ (tramp-file-name-path link)) ; link name
+ t))))
+
+
+(defun tramp-handle-load (file &optional noerror nomessage nosuffix must-suffix)
+ "Like `load' for tramp files. Not implemented!"
+ (unless (file-name-absolute-p file)
+ (error "Tramp cannot `load' files without absolute path name"))
+ (unless nosuffix
+ (cond ((file-exists-p (concat file ".elc"))
+ (setq file (concat file ".elc")))
+ ((file-exists-p (concat file ".el"))
+ (setq file (concat file ".el")))))
+ (when must-suffix
+ ;; The first condition is always true for absolute file names.
+ ;; Included for safety's sake.
+ (unless (or (file-name-directory file)
+ (string-match "\\.elc?\\'" file))
+ (error "File `%s' does not include a `.el' or `.elc' suffix"
+ file)))
+ (unless noerror
+ (when (not (file-exists-p file))
+ (error "Cannot load nonexistant file `%s'" file)))
+ (if (not (file-exists-p file))
+ nil
+ (unless nomessage
+ (message "Loading %s..." file))
+ (let ((local-copy (file-local-copy file)))
+ ;; MUST-SUFFIX doesn't exist on XEmacs, so let it default to nil.
+ (load local-copy noerror t t)
+ (delete-file local-copy))
+ (unless nomessage
+ (message "Loading %s...done" file))
+ t))
+
+;; Path manipulation functions that grok TRAMP paths...
+(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
+ (let* ((v (tramp-dissect-file-name file))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v)))
+ (if (or (string= path "") (string= path "/"))
+ ;; 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 path portion only
+ ;; CCC: This should take into account the remote machine type, no?
+ ;; --daniel <daniel@danann.net>
+ (tramp-make-tramp-file-name multi-method method user host
+ ;; This will not recurse...
+ (or (file-name-directory path) "")))))
+
+(defun tramp-handle-file-name-nondirectory (file)
+ "Like `file-name-nondirectory' but aware of TRAMP files."
+ (let ((v (tramp-dissect-file-name file)))
+ (file-name-nondirectory (tramp-file-name-path v))))
+
+(defun tramp-handle-file-truename (filename &optional counter prev-dirs)
+ "Like `file-truename' for tramp files."
+ (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (steps (tramp-split-string path "/"))
+ (pathdir (let ((directory-sep-char ?/))
+ (file-name-as-directory path)))
+ (is-dir (string= path pathdir))
+ (thisstep nil)
+ (numchase 0)
+ ;; Don't make the following value larger than necessary.
+ ;; People expect an error message in a timely fashion when
+ ;; something is wrong; otherwise they might think that Emacs
+ ;; is hung. Of course, correctness has to come first.
+ (numchase-limit 20)
+ (result nil) ;result steps in reverse order
+ (curstri "")
+ symlink-target)
+ (tramp-message-for-buffer
+ multi-method method user host
+ 10 "Finding true name for `%s'" filename)
+ (while (and steps (< numchase numchase-limit))
+ (setq thisstep (pop steps))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 10 "Check %s"
+ (mapconcat 'identity
+ (append '("") (reverse result) (list thisstep))
+ "/"))
+ (setq symlink-target
+ (nth 0 (tramp-handle-file-attributes
+ (tramp-make-tramp-file-name
+ multi-method method user host
+ (mapconcat 'identity
+ (append '("") (reverse result) (list thisstep))
+ "/")))))
+ (cond ((string= "." thisstep)
+ (tramp-message-for-buffer multi-method method user host
+ 10 "Ignoring step `.'"))
+ ((string= ".." thisstep)
+ (tramp-message-for-buffer multi-method method user host
+ 10 "Processing step `..'")
+ (pop result))
+ ((stringp symlink-target)
+ ;; It's a symlink, follow it.
+ (tramp-message-for-buffer
+ multi-method method user host
+ 10 "Follow symlink to %s" symlink-target)
+ (setq numchase (1+ numchase))
+ (when (file-name-absolute-p symlink-target)
+ (setq result nil))
+ (setq steps
+ (append (tramp-split-string symlink-target "/") steps)))
+ (t
+ ;; It's a file.
+ (setq result (cons thisstep result)))))
+ (when (>= numchase numchase-limit)
+ (error "Maximum number (%d) of symlinks exceeded" numchase-limit))
+ (setq result (reverse result))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 10 "True name of `%s' is `%s'"
+ filename (mapconcat 'identity (cons "" result) "/"))
+ (tramp-make-tramp-file-name
+ multi-method method user host
+ (concat (mapconcat 'identity (cons "" result) "/")
+ (if is-dir "/" "")))))
+
+;; Basic functions.
+
+(defun tramp-handle-file-exists-p (filename)
+ "Like `file-exists-p' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ multi-method method user host path)
+ (setq multi-method (tramp-file-name-multi-method v))
+ (setq method (tramp-file-name-method v))
+ (setq user (tramp-file-name-user v))
+ (setq host (tramp-file-name-host v))
+ (setq path (tramp-file-name-path v))
+ (save-excursion
+ (zerop (tramp-send-command-and-check
+ multi-method method user host
+ (format
+ (tramp-get-file-exists-command multi-method method user host)
+ (tramp-shell-quote-argument path)))))))
+
+;; CCC: This should check for an error condition and signal failure
+;; when something goes wrong.
+;; Daniel Pittman <daniel@danann.net>
+(defun tramp-handle-file-attributes (filename &optional nonnumeric)
+ "Like `file-attributes' for tramp files.
+Optional argument NONNUMERIC means return user and group name
+rather than as numbers."
+ (if (tramp-handle-file-exists-p filename)
+ ;; file exists, find out stuff
+ (save-excursion
+ (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v)))
+ (if (tramp-get-remote-perl multi-method method user host)
+ (tramp-handle-file-attributes-with-perl multi-method method user host path nonnumeric)
+ (tramp-handle-file-attributes-with-ls multi-method method user host path nonnumeric))))
+ nil)) ; no file
+
+
+(defun tramp-handle-file-attributes-with-ls
+ (multi-method method user host path &optional nonnumeric)
+ "Implement `file-attributes' for tramp files using the ls(1) command."
+ (let (symlinkp dirp
+ res-inode res-filemodes res-numlinks
+ res-uid res-gid res-size res-symlink-target)
+ (tramp-send-command
+ multi-method method user host
+ (format "%s %s %s"
+ (tramp-get-ls-command multi-method method user host)
+ (if nonnumeric "-ild" "-ildn")
+ (tramp-shell-quote-argument path)))
+ (tramp-wait-for-output)
+ ;; parse `ls -l' output ...
+ ;; ... inode
+ (setq res-inode
+ (condition-case err
+ (read (current-buffer))
+ (invalid-read-syntax
+ (when (and (equal (cadr err)
+ "Integer constant overflow in reader")
+ (string-match
+ "^[0-9]+\\([0-9][0-9][0-9][0-9][0-9]\\)\\'"
+ (caddr err)))
+ (let* ((big (read (substring (caddr err) 0
+ (match-beginning 1))))
+ (small (read (match-string 1 (caddr err))))
+ (twiddle (/ small 65536)))
+ (cons (+ big twiddle)
+ (- small (* twiddle 65536))))))))
+ ;; ... file mode flags
+ (setq res-filemodes (symbol-name (read (current-buffer))))
+ ;; ... number links
+ (setq res-numlinks (read (current-buffer)))
+ ;; ... uid and gid
+ (setq res-uid (read (current-buffer)))
+ (setq res-gid (read (current-buffer)))
+ (unless nonnumeric
+ (unless (numberp res-uid) (setq res-uid -1))
+ (unless (numberp res-gid) (setq res-gid -1)))
+ ;; ... size
+ (setq res-size (read (current-buffer)))
+ ;; From the file modes, figure out other stuff.
+ (setq symlinkp (eq ?l (aref res-filemodes 0)))
+ (setq dirp (eq ?d (aref res-filemodes 0)))
+ ;; if symlink, find out file name pointed to
+ (when symlinkp
+ (search-forward "-> ")
+ (setq res-symlink-target
+ (buffer-substring (point)
+ (tramp-line-end-position))))
+ ;; return data gathered
+ (list
+ ;; 0. t for directory, string (name linked to) for symbolic
+ ;; link, or nil.
+ (or dirp res-symlink-target nil)
+ ;; 1. Number of links to file.
+ res-numlinks
+ ;; 2. File uid.
+ res-uid
+ ;; 3. File gid.
+ res-gid
+ ;; 4. Last access time, as a list of two integers. First
+ ;; integer has high-order 16 bits of time, second has low 16
+ ;; bits.
+ ;; 5. Last modification time, likewise.
+ ;; 6. Last status change time, likewise.
+ '(0 0) '(0 0) '(0 0) ;CCC how to find out?
+ ;; 7. Size in bytes (-1, if number is out of range).
+ res-size
+ ;; 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?
+ ;; 10. inode number.
+ res-inode
+ ;; 11. Device number.
+ -1 ;hm?
+ )))
+
+(defun tramp-handle-file-attributes-with-perl
+ (multi-method method user host path &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."
+ (tramp-send-command
+ multi-method method user host
+ (format "tramp_file_attributes %s"
+ (tramp-shell-quote-argument path)))
+ (tramp-wait-for-output)
+ (let ((result (read (current-buffer))))
+ (setcar (nthcdr 8 result)
+ (tramp-file-mode-from-int (nth 8 result)))
+ result))
+
+(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* ((coding-system-used nil)
+ (f (buffer-file-name))
+ (v (tramp-dissect-file-name f))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (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 path)))
+ (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))
+
+;; 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."
+ (with-current-buffer buf
+ (let* ((f (buffer-file-name))
+ (v (tramp-dissect-file-name f))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (attr (file-attributes f))
+ (modtime (nth 5 attr)))
+ (if attr
+ (if (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 (list (car mt) (cdr mt)))) 2))
+ (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 path)))
+ (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.
+ nil))))
+
+(defadvice clear-visited-file-modtime (after tramp activate)
+ "Set `tramp-buffer-file-attributes' back to nil.
+Tramp uses this variable as an emulation for the actual modtime of the file,
+if the remote host can't provide the modtime."
+ (setq tramp-buffer-file-attributes nil))
+
+(defun tramp-handle-set-file-modes (filename mode)
+ "Like `set-file-modes' for tramp files."
+ (let ((v (tramp-dissect-file-name filename)))
+ (save-excursion
+ (unless (zerop (tramp-send-command-and-check
+ (tramp-file-name-multi-method v)
+ (tramp-file-name-method v)
+ (tramp-file-name-user v)
+ (tramp-file-name-host v)
+ (format "chmod %s %s"
+ (tramp-decimal-to-octal mode)
+ (tramp-shell-quote-argument
+ (tramp-file-name-path v)))))
+ (signal 'file-error
+ (list "Doing chmod"
+ ;; FIXME: extract the proper text from chmod's stderr.
+ "error while changing file's mode"
+ filename))))))
+
+;; Simple functions using the `test' command.
+
+(defun tramp-handle-file-executable-p (filename)
+ "Like `file-executable-p' for tramp files."
+ (zerop (tramp-run-test "-x" filename)))
+
+(defun tramp-handle-file-readable-p (filename)
+ "Like `file-readable-p' for tramp files."
+ (zerop (tramp-run-test "-r" filename)))
+
+(defun tramp-handle-file-accessible-directory-p (filename)
+ "Like `file-accessible-directory-p' for tramp files."
+ (and (zerop (tramp-run-test "-d" filename))
+ (zerop (tramp-run-test "-r" filename))
+ (zerop (tramp-run-test "-x" filename))))
+
+;; When the remote shell is started, it looks for a shell which groks
+;; tilde expansion. Here, we assume that all shells which grok tilde
+;; expansion will also provide a `test' command which groks `-nt' (for
+;; newer than). If this breaks, tell me about it and I'll try to do
+;; something smarter about it.
+(defun tramp-handle-file-newer-than-file-p (file1 file2)
+ "Like `file-newer-than-file-p' for tramp files."
+ (cond ((not (file-exists-p file1))
+ nil)
+ ((not (file-exists-p file2))
+ t)
+ ;; We are sure both files exist at this point.
+ (t
+ (save-excursion
+ (let* ((v1 (tramp-dissect-file-name file1))
+ (mm1 (tramp-file-name-multi-method v1))
+ (m1 (tramp-file-name-method v1))
+ (u1 (tramp-file-name-user v1))
+ (h1 (tramp-file-name-host v1))
+ (v2 (tramp-dissect-file-name file2))
+ (mm2 (tramp-file-name-multi-method v2))
+ (m2 (tramp-file-name-method v2))
+ (u2 (tramp-file-name-user v2))
+ (h2 (tramp-file-name-host v2)))
+ (unless (and (equal mm1 mm2)
+ (equal m1 m2)
+ (equal u1 u2)
+ (equal h1 h2))
+ (signal 'file-error
+ (list "Files must have same method, user, host"
+ file1 file2)))
+ (unless (and (tramp-tramp-file-p file1)
+ (tramp-tramp-file-p file2))
+ (signal 'file-error
+ (list "Files must be tramp files on same host"
+ file1 file2)))
+ (if (tramp-get-test-groks-nt mm1 m1 u1 h1)
+ (zerop (tramp-run-test2 "test" file1 file2 "-nt"))
+ (zerop (tramp-run-test2 "tramp_test_nt" file1 file2))))))))
+
+;; Functions implemented using the basic functions above.
+
+(defun tramp-handle-file-modes (filename)
+ "Like `file-modes' for tramp files."
+ (when (file-exists-p filename)
+ (tramp-mode-string-to-int
+ (nth 8 (tramp-handle-file-attributes filename)))))
+
+(defun tramp-handle-file-directory-p (filename)
+ "Like `file-directory-p' for tramp files."
+ ;; Care must be taken that this function returns `t' for symlinks
+ ;; pointing to directories. Surely the most obvious implementation
+ ;; would be `test -d', but that returns false for such symlinks.
+ ;; CCC: Stefan Monnier says that `test -d' follows symlinks. And
+ ;; I now think he's right. So we could be using `test -d', couldn't
+ ;; we?
+ ;;
+ ;; Alternatives: `cd %s', `test -d %s'
+ (save-excursion
+ (let ((v (tramp-dissect-file-name filename)))
+ (zerop
+ (tramp-send-command-and-check
+ (tramp-file-name-multi-method v) (tramp-file-name-method v)
+ (tramp-file-name-user v) (tramp-file-name-host v)
+ (format "test -d %s"
+ (tramp-shell-quote-argument (tramp-file-name-path v)))
+ t))))) ;run command in subshell
+
+(defun tramp-handle-file-regular-p (filename)
+ "Like `file-regular-p' for tramp files."
+ (and (tramp-handle-file-exists-p filename)
+ (eq ?- (aref (nth 8 (tramp-handle-file-attributes filename)) 0))))
+
+(defun tramp-handle-file-symlink-p (filename)
+ "Like `file-symlink-p' for tramp files."
+ (let ((x (car (tramp-handle-file-attributes filename))))
+ (when (stringp x) x)))
+
+(defun tramp-handle-file-writable-p (filename)
+ "Like `file-writable-p' for tramp files."
+ (if (tramp-handle-file-exists-p filename)
+ ;; Existing files must be writable.
+ (zerop (tramp-run-test "-w" filename))
+ ;; If file doesn't exist, check if directory is writable.
+ (and (zerop (tramp-run-test "-d" (tramp-handle-file-name-directory filename)))
+ (zerop (tramp-run-test "-w" (tramp-handle-file-name-directory filename))))))
+
+(defun tramp-handle-file-ownership-preserved-p (filename)
+ "Like `file-ownership-preserved-p' for tramp files."
+ (or (not (tramp-handle-file-exists-p filename))
+ ;; Existing files must be writable.
+ (zerop (tramp-run-test "-O" filename))))
+
+;; Other file name ops.
+
+;; ;; Matthias Köppe <mkoeppe@mail.math.uni-magdeburg.de>
+;; (defun tramp-handle-directory-file-name (directory)
+;; "Like `directory-file-name' for tramp files."
+;; (if (and (eq (aref directory (- (length directory) 1)) ?/)
+;; (not (eq (aref directory (- (length directory) 2)) ?:)))
+;; (substring directory 0 (- (length directory) 1))
+;; directory))
+
+;; Philippe Troin <phil@fifi.org>
+(defun tramp-handle-directory-file-name (directory)
+ "Like `directory-file-name' for tramp files."
+ (let ((directory-length-1 (1- (length directory))))
+ (save-match-data
+ (if (and (eq (aref directory directory-length-1) ?/)
+ (eq (string-match tramp-file-name-regexp directory) 0)
+ (/= (match-end 0) directory-length-1))
+ (substring directory 0 directory-length-1)
+ directory))))
+
+;; Directory listings.
+
+(defun tramp-handle-directory-files (directory &optional full match nosort)
+ "Like `directory-files' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name directory)))
+ multi-method method user host path result x)
+ (setq multi-method (tramp-file-name-multi-method v))
+ (setq method (tramp-file-name-method v))
+ (setq user (tramp-file-name-user v))
+ (setq host (tramp-file-name-host v))
+ (setq path (tramp-file-name-path v))
+ (save-excursion
+ (tramp-barf-unless-okay multi-method method user host
+ (concat "cd " (tramp-shell-quote-argument path))
+ nil
+ 'file-error
+ "tramp-handle-directory-files: couldn't `cd %s'"
+ (tramp-shell-quote-argument path))
+ (tramp-send-command
+ multi-method method user host
+ (concat (tramp-get-ls-command multi-method method user host)
+ " -a | cat"))
+ (tramp-wait-for-output)
+ (goto-char (point-max))
+ (while (zerop (forward-line -1))
+ (setq x (buffer-substring (point)
+ (tramp-line-end-position)))
+ (when (or (not match) (string-match match x))
+ (if full
+ (push (concat (file-name-as-directory directory)
+ x)
+ result)
+ (push x result))))
+ (tramp-send-command multi-method method user host "cd")
+ (tramp-wait-for-output))
+ result))
+
+;; 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
+;; of directories.
+(defun tramp-handle-file-name-all-completions (filename directory)
+ "Like `file-name-all-completions' for tramp files."
+ (unless (save-match-data (string-match "/" filename))
+ (let* ((v (tramp-dissect-file-name directory))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (nowild tramp-completion-without-shell-p)
+ result)
+ (save-excursion
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (format "cd %s" (tramp-shell-quote-argument path))
+ nil 'file-error
+ "tramp-handle-file-name-all-completions: Couldn't `cd %s'"
+ (tramp-shell-quote-argument path))
+
+ ;; Get a list of directories and files, including reliably
+ ;; tagging the directories with a trailing '/'. Because I
+ ;; rock. --daniel@danann.net
+ (tramp-send-command
+ multi-method method user host
+ (format (concat "%s -a %s 2>/dev/null | while read f; do "
+ "if test -d \"$f\" 2>/dev/null; "
+ "then echo \"$f/\"; else echo \"$f\"; fi; done")
+ (tramp-get-ls-command multi-method method user host)
+ (if (or nowild (zerop (length filename)))
+ ""
+ (format "-d %s*" (tramp-shell-quote-argument filename)))))
+
+ ;; Now grab the output.
+ (tramp-wait-for-output)
+ (goto-char (point-max))
+ (while (zerop (forward-line -1))
+ (push (buffer-substring (point)
+ (tramp-line-end-position))
+ result))
+
+ (tramp-send-command multi-method method user host "cd")
+ (tramp-wait-for-output)
+
+ ;; Return the list.
+ (if nowild
+ (all-completions filename (mapcar 'list result))
+ result)))))
+
+
+;; The following isn't needed for Emacs 20 but for 19.34?
+(defun tramp-handle-file-name-completion (filename directory)
+ "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))
+ ;(setq directory (tramp-handle-expand-file-name directory))
+ (try-completion
+ filename
+ (mapcar (lambda (x) (cons x nil))
+ (tramp-handle-file-name-all-completions filename directory))))
+
+;; cp, mv and ln
+
+(defun tramp-handle-add-name-to-file
+ (filename newname &optional ok-if-already-exists)
+ "Like `add-name-to-file' for tramp files."
+ (let* ((v1 (when (tramp-tramp-file-p filename)
+ (tramp-dissect-file-name (tramp-handle-expand-file-name filename))))
+ (v2 (when (tramp-tramp-file-p newname)
+ (tramp-dissect-file-name (tramp-handle-expand-file-name newname))))
+ (mmeth1 (when v1 (tramp-file-name-multi-method v1)))
+ (mmeth2 (when v2 (tramp-file-name-multi-method v2)))
+ (meth1 (when v1 (tramp-file-name-method v1)))
+ (meth2 (when v2 (tramp-file-name-method v2)))
+ (user1 (when v1 (tramp-file-name-user v1)))
+ (user2 (when v2 (tramp-file-name-user v2)))
+ (host1 (when v1 (tramp-file-name-host v1)))
+ (host2 (when v2 (tramp-file-name-host v2)))
+ (path1 (when v1 (tramp-file-name-path v1)))
+ (path2 (when v2 (tramp-file-name-path v2)))
+ (ln (when v1 (tramp-get-remote-ln mmeth1 meth1 user1 host1))))
+ (unless (and meth1 meth2 user1 user2 host1 host2
+ (equal mmeth1 mmeth2)
+ (equal meth1 meth2)
+ (equal user1 user2)
+ (equal host1 host2))
+ (error "add-name-to-file: %s"
+ "only implemented for same method, same user, same host"))
+ (when (and (not ok-if-already-exists)
+ (file-exists-p newname)
+ (not (numberp ok-if-already-exists))
+ (y-or-n-p
+ (format
+ "File %s already exists; make it a new name anyway? "
+ newname)))
+ (error "add-name-to-file: file %s already exists" newname))
+ (tramp-barf-unless-okay
+ mmeth1 meth1 user1 host1
+ (format "%s %s %s" ln (tramp-shell-quote-argument path1)
+ (tramp-shell-quote-argument path2))
+ nil 'file-error
+ "error with add-name-to-file, see buffer `%s' for details"
+ (buffer-name))))
+
+(defun tramp-handle-copy-file
+ (filename newname &optional ok-if-already-exists keep-date)
+ "Like `copy-file' for tramp files."
+ ;; Check if both files are local -- invoke normal copy-file.
+ ;; Otherwise, use tramp from local system.
+ (setq filename (expand-file-name filename))
+ (setq newname (expand-file-name newname))
+ ;; At least one file a tramp file?
+ (if (or (tramp-tramp-file-p filename)
+ (tramp-tramp-file-p newname))
+ (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))))
+
+(defun tramp-handle-rename-file
+ (filename newname &optional ok-if-already-exists)
+ "Like `rename-file' for tramp files."
+ ;; Check if both files are local -- invoke normal rename-file.
+ ;; Otherwise, use tramp from local system.
+ (setq filename (expand-file-name filename))
+ (setq newname (expand-file-name newname))
+ ;; At least one file a tramp file?
+ (if (or (tramp-tramp-file-p filename)
+ (tramp-tramp-file-p newname))
+ (tramp-do-copy-or-rename-file
+ 'rename filename newname ok-if-already-exists)
+ (tramp-run-real-handler 'rename-file
+ (list filename newname ok-if-already-exists))))
+
+(defun tramp-do-copy-or-rename-file
+ (op filename newname &optional ok-if-already-exists keep-date)
+ "Copy or rename a remote file.
+OP must be `copy' or `rename' and indicates the operation to perform.
+FILENAME specifies the file to copy or rename, NEWNAME is the name of
+the new file (for copy) or the new name of the file (for rename).
+OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already.
+KEEP-DATE means to make sure that NEWNAME has the same timestamp
+as FILENAME.
+
+This function is invoked by `tramp-handle-copy-file' and
+`tramp-handle-rename-file'. It is an error if OP is neither of `copy'
+and `rename'. FILENAME and NEWNAME must be absolute file names."
+ (unless (memq op '(copy rename))
+ (error "Unknown operation `%s', must be `copy' or `rename'" op))
+ (unless ok-if-already-exists
+ (when (file-exists-p newname)
+ (signal 'file-already-exists
+ (list newname))))
+ (let* ((v1 (when (tramp-tramp-file-p filename)
+ (tramp-dissect-file-name (tramp-handle-expand-file-name filename))))
+ (v2 (when (tramp-tramp-file-p newname)
+ (tramp-dissect-file-name (tramp-handle-expand-file-name newname))))
+ (mmeth1 (when v1 (tramp-file-name-multi-method v1)))
+ (mmeth2 (when v2 (tramp-file-name-multi-method v2)))
+ (meth1 (when v1 (tramp-file-name-method v1)))
+ (meth2 (when v2 (tramp-file-name-method v2)))
+ (mmeth (tramp-file-name-multi-method (or v1 v2)))
+ (meth (tramp-file-name-method (or v1 v2)))
+ (rcp-program (tramp-get-rcp-program mmeth meth))
+ (rcp-args (tramp-get-rcp-args mmeth meth))
+ (trampbuf (get-buffer-create "*tramp output*")))
+ ;; Check if we can use a shortcut.
+ (if (and meth1 meth2 (equal mmeth1 mmeth2) (equal meth1 meth2)
+ (equal (tramp-file-name-host v1)
+ (tramp-file-name-host v2))
+ (equal (tramp-file-name-user v1)
+ (tramp-file-name-user v2)))
+ ;; Shortcut: if method, host, user are the same for both
+ ;; files, we invoke `cp' or `mv' on the remote host directly.
+ (tramp-do-copy-or-rename-file-directly
+ op
+ (tramp-file-name-multi-method v1)
+ (tramp-file-name-method v1)
+ (tramp-file-name-user v1)
+ (tramp-file-name-host v1)
+ (tramp-file-name-path v1) (tramp-file-name-path v2)
+ keep-date)
+ ;; New algorithm: copy file first. Then, if operation is
+ ;; `rename', go back and delete the original file if the copy
+ ;; was successful.
+ (if rcp-program
+ ;; The following code uses a tramp program to copy the file.
+ (let ((f1 (if (not v1)
+ filename
+ (tramp-make-rcp-program-file-name
+ (tramp-file-name-user v1)
+ (tramp-file-name-host v1)
+ (tramp-shell-quote-argument (tramp-file-name-path v1)))))
+ (f2 (if (not v2)
+ newname
+ (tramp-make-rcp-program-file-name
+ (tramp-file-name-user v2)
+ (tramp-file-name-host v2)
+ (tramp-shell-quote-argument (tramp-file-name-path v2)))))
+ (default-directory
+ (if (tramp-tramp-file-p default-directory)
+ (tramp-temporary-file-directory)
+ default-directory)))
+ (when keep-date
+ (add-to-list 'rcp-args (tramp-get-rcp-keep-date-arg mmeth meth)))
+ (save-excursion (set-buffer trampbuf) (erase-buffer))
+ (unless
+ (equal 0 (apply #'call-process (tramp-get-rcp-program mmeth meth)
+ nil trampbuf nil (append rcp-args (list f1 f2))))
+ (pop-to-buffer trampbuf)
+ (error (concat "tramp-do-copy-or-rename-file: %s"
+ " didn't work, see buffer `%s' for details")
+ (tramp-get-rcp-program mmeth meth) trampbuf)))
+ ;; The following code uses an inline method for copying.
+ ;; Let's start with a simple-minded approach: we create a new
+ ;; buffer, insert the contents of the source file into it,
+ ;; then write out the buffer. This should work fine, whether
+ ;; the source or the target files are tramp files.
+ ;; CCC TODO: error checking
+ (when keep-date
+ (tramp-message 1 (concat "Warning: cannot preserve file time stamp"
+ " with inline copying across machines")))
+ (save-excursion
+ (set-buffer trampbuf) (erase-buffer)
+ (insert-file-contents-literally filename)
+ (let ((coding-system-for-write 'no-conversion))
+ (write-region (point-min) (point-max) newname))))
+
+ ;; If the operation was `rename', delete the original file.
+ (unless (eq op 'copy)
+ (delete-file filename)))))
+
+(defun tramp-do-copy-or-rename-file-directly
+ (op multi-method method user host path1 path2 keep-date)
+ "Invokes `cp' or `mv' on the remote system.
+OP must be one of `copy' or `rename', indicating `cp' or `mv',
+respectively. METHOD, USER, and HOST specify the connection.
+PATH1 and PATH2 specify the two arguments of `cp' or `mv'.
+If KEEP-DATE is non-nil, preserve the time stamp when copying."
+ ;; CCC: What happens to the timestamp when renaming?
+ (let ((cmd (cond ((and (eq op 'copy) keep-date) "cp -f -p")
+ ((eq op 'copy) "cp -f")
+ ((eq op 'rename) "mv -f")
+ (t (error
+ "Unknown operation `%s', must be `copy' or `rename'"
+ op)))))
+ (save-excursion
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (format "%s %s %s"
+ cmd
+ (tramp-shell-quote-argument path1)
+ (tramp-shell-quote-argument path2))
+ nil 'file-error
+ "Copying directly failed, see buffer `%s' for details."
+ (buffer-name)))))
+
+;; mkdir
+(defun tramp-handle-make-directory (dir &optional parents)
+ "Like `make-directory' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name dir))))
+ (tramp-barf-unless-okay
+ (tramp-file-name-multi-method v) (tramp-file-name-method v)
+ (tramp-file-name-user v) (tramp-file-name-host v)
+ (format " %s %s"
+ (if parents "mkdir -p" "mkdir")
+ (tramp-shell-quote-argument (tramp-file-name-path v)))
+ nil 'file-error
+ "Couldn't make directory %s" dir)))
+
+;; CCC error checking?
+(defun tramp-handle-delete-directory (directory)
+ "Like `delete-directory' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name directory))))
+ (save-excursion
+ (tramp-send-command
+ (tramp-file-name-multi-method v) (tramp-file-name-method v)
+ (tramp-file-name-user v) (tramp-file-name-host v)
+ (format "rmdir %s ; echo ok"
+ (tramp-shell-quote-argument (tramp-file-name-path v))))
+ (tramp-wait-for-output))))
+
+(defun tramp-handle-delete-file (filename)
+ "Like `delete-file' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename))))
+ (save-excursion
+ (unless (zerop (tramp-send-command-and-check
+ (tramp-file-name-multi-method v)
+ (tramp-file-name-method v)
+ (tramp-file-name-user v)
+ (tramp-file-name-host v)
+ (format "rm -f %s"
+ (tramp-shell-quote-argument
+ (tramp-file-name-path v)))))
+ (signal 'file-error "Couldn't delete Tramp file")))))
+
+;; Dired.
+
+;; CCC: This does not seem to be enough. Something dies when
+;; we try and delete two directories under TRAMP :/
+(defun tramp-handle-dired-recursive-delete-directory (filename)
+ "Recursively delete the directory given.
+This is like `dired-recursive-delete-directory' for tramp files."
+ (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v)))
+ ;; run a shell command 'rm -r <path>'
+ ;; Code shamelessly stolen for the dired implementation and, um, hacked :)
+ (or (tramp-handle-file-exists-p filename)
+ (signal
+ '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
+ (format "rm -r %s" (tramp-shell-quote-argument path)))
+ ;; 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 (tramp-handle-file-exists-p filename)
+ (error "Failed to recusively delete %s" filename))))
+
+
+(defun tramp-handle-dired-call-process (program discard &rest arguments)
+ "Like `dired-call-process' for tramp files."
+ (let ((v (tramp-dissect-file-name
+ (tramp-handle-expand-file-name default-directory)))
+ multi-method method user host path)
+ (setq multi-method (tramp-file-name-multi-method v))
+ (setq method (tramp-file-name-method v))
+ (setq user (tramp-file-name-user v))
+ (setq host (tramp-file-name-host v))
+ (setq path (tramp-file-name-path v))
+ (save-excursion
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (format "cd %s" (tramp-shell-quote-argument path))
+ nil 'file-error
+ "tramp-handle-dired-call-process: Couldn't `cd %s'"
+ (tramp-shell-quote-argument path))
+ (tramp-send-command
+ multi-method method user host
+ (mapconcat #'tramp-shell-quote-argument (cons program arguments) " "))
+ (tramp-wait-for-output))
+ (unless discard
+ (insert-buffer (tramp-get-buffer multi-method method user host)))
+ (save-excursion
+ (prog1
+ (tramp-send-command-and-check multi-method method user host nil)
+ (tramp-send-command multi-method method user host "cd")
+ (tramp-wait-for-output)))))
+
+;; Pacify byte-compiler. The function is needed on XEmacs only. I'm
+;; not sure at all that this is the right way to do it, but let's hope
+;; it works for now, and wait for a guru to point out the Right Way to
+;; achieve this.
+;;(eval-when-compile
+;; (unless (fboundp 'dired-insert-set-properties)
+;; (fset 'dired-insert-set-properties 'ignore)))
+;; Gerd suggests this:
+(eval-when-compile (require 'dired))
+;; Note that dired is required at run-time, too, when it is needed.
+;; It is only needed on XEmacs for the function
+;; `dired-insert-set-properties'.
+
+(defun tramp-handle-insert-directory
+ (filename switches &optional wildcard full-directory-p)
+ "Like `insert-directory' for tramp files."
+ (let ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ multi-method method user host path)
+ (setq multi-method (tramp-file-name-multi-method v))
+ (setq method (tramp-file-name-method v))
+ (setq user (tramp-file-name-user v))
+ (setq host (tramp-file-name-host v))
+ (setq path (tramp-file-name-path v))
+ (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 path))
+ (setq path (file-name-directory path)))
+ (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
+ path
+ (tramp-shell-quote-argument (concat path ".")))))
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (format "cd %s" (tramp-shell-quote-argument
+ (file-name-directory path)))
+ nil 'file-error
+ "Couldn't `cd %s'"
+ (tramp-shell-quote-argument (file-name-directory path)))
+ (tramp-send-command
+ multi-method method user host
+ (format "%s %s %s"
+ (tramp-get-ls-command multi-method method user host)
+ switches
+ (if full-directory-p
+ ;; Add "/." to make sure we got complete dir
+ ;; listing for symlinks, too.
+ (concat (file-name-as-directory
+ (file-name-nondirectory path)) ".")
+ (file-name-nondirectory path)))))
+ (sit-for 1) ;needed for rsh but not ssh?
+ (tramp-wait-for-output))
+ (insert-buffer (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))
+ ;; 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 (point) (mark t))))))
+
+;; Continuation of kluge to pacify byte-compiler.
+;;(eval-when-compile
+;; (when (eq (symbol-function 'dired-insert-set-properties) 'ignore)
+;; (fmakunbound 'dired-insert-set-properties)))
+
+;; CCC is this the right thing to do?
+(defun tramp-handle-unhandled-file-name-directory (filename)
+ "Like `unhandled-file-name-directory' for tramp files."
+ (expand-file-name "~/"))
+
+;; Canonicalization of file names.
+
+(defun tramp-drop-volume-letter (name)
+ "Cut off unnecessary drive letter from file NAME.
+The function `tramp-handle-expand-file-name' calls `expand-file-name'
+locally on a remote file name. When the local system is a W32 system
+but the remote system is Unix, this introduces a superfluous drive
+letter into the file name. This function removes it.
+
+Doesn't do anything if the NAME does not start with a drive letter."
+ (if (and (> (length name) 1)
+ (char-equal (aref name 1) ?:)
+ (let ((c1 (aref name 0)))
+ (or (and (>= c1 ?A) (<= c1 ?Z))
+ (and (>= c1 ?a) (<= c1 ?z)))))
+ (substring name 2)
+ name))
+
+(defun tramp-handle-expand-file-name (name &optional dir)
+ "Like `expand-file-name' for tramp files."
+ ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
+ (setq dir (or dir default-directory "/"))
+ ;; Unless NAME is absolute, concat DIR and NAME.
+ (unless (file-name-absolute-p name)
+ (setq name (concat (file-name-as-directory dir) name)))
+ ;; If NAME is not a tramp file, run the real handler
+ (if (not (tramp-tramp-file-p name))
+ (tramp-run-real-handler 'expand-file-name
+ (list name nil))
+ ;; Dissect NAME.
+ (let* ((v (tramp-dissect-file-name name))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v)))
+ (unless (file-name-absolute-p path)
+ (setq path (concat "~/" path)))
+ (save-excursion
+ ;; Tilde expansion if necessary. This needs a shell which
+ ;; groks tilde expansion! The function `tramp-find-shell' is
+ ;; supposed to find such a shell on the remote host. Please
+ ;; tell me about it when this doesn't work on your system.
+ (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" path)
+ (let ((uname (match-string 1 path))
+ (fname (match-string 2 path)))
+ ;; CCC fanatic error checking?
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (erase-buffer)
+ (tramp-send-command
+ multi-method method user host
+ (format "cd %s; pwd" uname)
+ t)
+ (tramp-wait-for-output)
+ (goto-char (point-min))
+ (setq uname (buffer-substring (point) (tramp-line-end-position)))
+ (setq path (concat uname fname))
+ (erase-buffer)))
+ ;; 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.
+ (let ((directory-sep-char ?/))
+ (tramp-make-tramp-file-name
+ multi-method method user host
+ (tramp-drop-volume-letter
+ (tramp-run-real-handler 'expand-file-name (list path)))))))))
+
+;; Remote commands.
+
+(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
+`tramp-end-of-output', followed by another newline."
+ (if (tramp-tramp-file-p default-directory)
+ (let* ((v (tramp-dissect-file-name
+ (tramp-handle-expand-file-name default-directory)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ status)
+ (when (string-match "&[ \t]*\\'" command)
+ (error "Tramp doesn't grok asynchronous shell commands, yet"))
+ (when error-buffer
+ (error "Tramp doesn't grok optional third arg ERROR-BUFFER, yet"))
+ (save-excursion
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (format "cd %s" (tramp-shell-quote-argument path))
+ nil 'file-error
+ "tramp-handle-shell-command: Couldn't `cd %s'"
+ (tramp-shell-quote-argument path))
+ (tramp-send-command multi-method method user host
+ (concat command "; tramp_old_status=$?"))
+ ;; This will break if the shell command prints "/////"
+ ;; somewhere. Let's just hope for the best...
+ (tramp-wait-for-output))
+ (unless output-buffer
+ (setq output-buffer (get-buffer-create "*Shell Command Output*"))
+ (set-buffer output-buffer)
+ (erase-buffer))
+ (unless (bufferp output-buffer)
+ (setq output-buffer (current-buffer)))
+ (set-buffer output-buffer)
+ (insert-buffer (tramp-get-buffer multi-method method user host))
+ (save-excursion
+ (tramp-send-command multi-method method user host "cd")
+ (tramp-wait-for-output)
+ (tramp-send-command
+ multi-method method user host
+ "tramp_set_exit_status $tramp_old_status; echo tramp_exit_status $?")
+ (tramp-wait-for-output)
+ (goto-char (point-max))
+ (unless (search-backward "tramp_exit_status " nil t)
+ (error "Couldn't find exit status of `%s'" command))
+ (skip-chars-forward "^ ")
+ (setq status (read (current-buffer))))
+ (unless (zerop (buffer-size))
+ (pop-to-buffer output-buffer))
+ status)
+ ;; The following is only executed if something strange was
+ ;; happening. Emit a helpful message and do it anyway.
+ (message "tramp-handle-shell-command called with non-tramp directory: `%s'"
+ default-directory)
+ (tramp-run-real-handler 'shell-command
+ (list command output-buffer error-buffer))))
+
+;; File Editing.
+
+(defsubst tramp-make-temp-file ()
+ (funcall (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name)
+ (expand-file-name tramp-temp-name-prefix
+ (tramp-temporary-file-directory))))
+
+(defun tramp-handle-file-local-copy (filename)
+ "Like `file-local-copy' for tramp files."
+ (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (trampbuf (get-buffer-create "*tramp output*"))
+ tmpfil)
+ (unless (file-exists-p filename)
+ (error "Cannot make local copy of non-existing file `%s'"
+ filename))
+ (setq tmpfil (tramp-make-temp-file))
+ (cond ((tramp-get-rcp-program multi-method method)
+ ;; Use tramp-like program for file transfer.
+ (tramp-message-for-buffer
+ multi-method method user host
+ 5 "Fetching %s to tmp file %s..." filename tmpfil)
+ (save-excursion (set-buffer trampbuf) (erase-buffer))
+ (unless (equal 0
+ (apply #'call-process
+ (tramp-get-rcp-program multi-method method)
+ nil trampbuf nil
+ (append (tramp-get-rcp-args multi-method method)
+ (list
+ (tramp-make-rcp-program-file-name
+ user host
+ (tramp-shell-quote-argument path))
+ tmpfil))))
+ (pop-to-buffer trampbuf)
+ (error (concat "tramp-handle-file-local-copy: `%s' didn't work, "
+ "see buffer `%s' for details")
+ (tramp-get-rcp-program multi-method method) trampbuf))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 5 "Fetching %s to tmp file %s...done" filename tmpfil))
+ ((and (tramp-get-encoding-command multi-method method)
+ (tramp-get-decoding-command multi-method method))
+ ;; Use inline encoding for file transfer.
+ (save-excursion
+ ;; Following line for setting tramp-current-method,
+ ;; tramp-current-user, tramp-current-host.
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (tramp-message 5 "Encoding remote file %s..." filename)
+ (tramp-barf-unless-okay
+ multi-method method user host
+ (concat (tramp-get-encoding-command multi-method method)
+ " < " (tramp-shell-quote-argument path))
+ nil 'file-error
+ "Encoding remote file failed, see buffer `%s' for details"
+ (tramp-get-buffer multi-method method user host))
+ ;; Remove trailing status code
+ (goto-char (point-max))
+ (delete-region (point) (progn (forward-line -1) (point)))
+
+ (tramp-message 5 "Decoding remote file %s..." filename)
+ (if (and (tramp-get-decoding-function multi-method method)
+ (fboundp (tramp-get-decoding-function multi-method method)))
+ ;; If tramp-decoding-function is defined for this
+ ;; method, we call it.
+ (let ((tmpbuf (get-buffer-create " *tramp tmp*")))
+ (set-buffer tmpbuf)
+ (erase-buffer)
+ (insert-buffer (tramp-get-buffer multi-method method
+ user host))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 6 "Decoding remote file %s with function %s..."
+ filename
+ (tramp-get-decoding-function multi-method method))
+ (set-buffer tmpbuf)
+ (let ((coding-system-for-write 'no-conversion))
+ (funcall (tramp-get-decoding-function multi-method method)
+ (point-min)
+ (point-max))
+ (write-region (point-min) (point-max) tmpfil))
+ (kill-buffer tmpbuf))
+ ;; If tramp-decoding-function is not defined for this
+ ;; method, we invoke tramp-decoding-command instead.
+ (let ((tmpfil2 (tramp-make-temp-file)))
+ (write-region (point-min) (point-max) tmpfil2)
+ (tramp-message
+ 6 "Decoding remote file %s with command %s..."
+ filename
+ (tramp-get-decoding-command multi-method method))
+ (call-process
+ tramp-sh-program
+ tmpfil2 ;input
+ nil ;output
+ nil ;display
+ "-c" (concat (tramp-get-decoding-command multi-method method)
+ " > " tmpfil))
+ (delete-file tmpfil2)))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 5 "Decoding remote file %s...done" filename)))
+
+ (t (error "Wrong method specification for `%s'" method)))
+ tmpfil))
+
+
+(defun tramp-handle-insert-file-contents
+ (filename &optional visit beg end replace)
+ "Like `insert-file-contents' for tramp files."
+ (barf-if-buffer-read-only)
+ (setq filename (expand-file-name filename))
+ (let* ((v (tramp-dissect-file-name (tramp-handle-expand-file-name filename)))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v)))
+ (if (not (tramp-handle-file-exists-p filename))
+ (progn
+ (when visit
+ (setq buffer-file-name filename)
+ (set-visited-file-modtime)
+ (set-buffer-modified-p nil))
+ (signal 'file-error
+ (format "File `%s' not found on remote host" filename))
+ (list (tramp-handle-expand-file-name filename) 0))
+ (let ((local-copy (tramp-handle-file-local-copy filename))
+ (coding-system-used nil)
+ (result nil))
+ (when visit
+ (setq buffer-file-name filename)
+ (set-visited-file-modtime)
+ (set-buffer-modified-p nil))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 9 "Inserting local temp file `%s'..." local-copy)
+ (setq result
+ (tramp-run-real-handler 'insert-file-contents
+ (list 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 last-coding-system-used))
+ (tramp-message 9 "Inserting local temp file `%s'...done" local-copy)
+ (delete-file local-copy)
+ (when (boundp 'last-coding-system-used)
+ (setq last-coding-system-used coding-system-used))
+ (list (expand-file-name filename)
+ (second result))))))
+
+;; CCC grok APPEND, LOCKNAME, CONFIRM
+(defun tramp-handle-write-region
+ (start end filename &optional append visit lockname confirm)
+ "Like `write-region' for tramp files."
+ (unless (eq append nil)
+ (error "Cannot append to file using tramp (`%s')" filename))
+ (setq filename (expand-file-name filename))
+;; Following part commented out because we don't know what to do about
+;; file locking, and it does not appear to be a problem to ignore it.
+;; Ange-ftp ignores it, too.
+; (when (and lockname (stringp lockname))
+; (setq lockname (expand-file-name lockname)))
+; (unless (or (eq lockname nil)
+; (string= lockname filename))
+; (error "tramp-handle-write-region: LOCKNAME must be nil or equal FILENAME"))
+ ;; XEmacs takes a coding system as the sevent argument, not `confirm'
+ (when (and (not (featurep 'xemacs))
+ confirm (file-exists-p filename))
+ (unless (y-or-n-p (format "File %s exists; overwrite anyway? "
+ filename))
+ (error "File not overwritten")))
+ (let* ((curbuf (current-buffer))
+ (v (tramp-dissect-file-name filename))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ (rcp-program (tramp-get-rcp-program multi-method method))
+ (rcp-args (tramp-get-rcp-args multi-method method))
+ (encoding-command (tramp-get-encoding-command multi-method method))
+ (encoding-function (tramp-get-encoding-function multi-method method))
+ (decoding-command (tramp-get-decoding-command multi-method method))
+ (trampbuf (get-buffer-create "*tramp output*"))
+ ;; 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
+ ;; because this makes the logic simpler.
+ (setq tmpfil (tramp-make-temp-file))
+ ;; We say `no-message' here because we don't want the visited file
+ ;; modtime data to be clobbered from the temp file. We call
+ ;; `set-visited-file-modtime' ourselves later on.
+ (tramp-run-real-handler
+ 'write-region
+ (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 last-coding-system-used))
+ ;; This is a bit lengthy due to the different methods possible for
+ ;; file transfer. First, we check whether the method uses an rcp
+ ;; program. If so, we call it. Otherwise, both encoding and
+ ;; decoding command must be specified. However, if the method
+ ;; _also_ specifies an encoding function, then that is used for
+ ;; encoding the contents of the tmp file.
+ (cond (rcp-program
+ ;; use rcp-like program for file transfer
+ (let ((argl (append rcp-args
+ (list
+ tmpfil
+ (tramp-make-rcp-program-file-name
+ user host
+ (tramp-shell-quote-argument path))))))
+ (tramp-message-for-buffer
+ multi-method method user host
+ 6 "Writing tmp file using `%s'..." rcp-program)
+ (save-excursion (set-buffer trampbuf) (erase-buffer))
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer (tramp-get-debug-buffer multi-method
+ method user host))
+ (goto-char (point-max))
+ (tramp-insert-with-face
+ 'bold (format "$ %s %s\n" rcp-program
+ (mapconcat 'identity argl " ")))))
+ (unless (equal 0
+ (apply #'call-process
+ rcp-program nil trampbuf nil argl))
+ (pop-to-buffer trampbuf)
+ (error "Cannot write region to file `%s', command `%s' failed"
+ filename rcp-program))
+ (tramp-message-for-buffer multi-method method user host
+ 6 "Transferring file using `%s'...done"
+ rcp-program)))
+ ((and encoding-command decoding-command)
+ ;; Use inline file transfer
+ (let ((tmpbuf (get-buffer-create " *tramp file transfer*")))
+ (save-excursion
+ ;; Encode tmpfil into tmpbuf
+ (tramp-message-for-buffer multi-method method user host
+ 5 "Encoding region...")
+ (set-buffer tmpbuf)
+ (erase-buffer)
+ ;; Use encoding function or command.
+ (if (and encoding-function
+ (fboundp encoding-function))
+ (progn
+ (tramp-message-for-buffer
+ multi-method method user host
+ 6 "Encoding region using function...")
+ (insert-file-contents-literally tmpfil)
+ ;; CCC. The following `let' is a workaround for
+ ;; the base64.el that comes with pgnus-0.84. If
+ ;; both of the following conditions are
+ ;; satisfied, it tries to write to a local file
+ ;; in default-directory, but at this point,
+ ;; default-directory is remote.
+ ;; (CALL-PROCESS-REGION can't write to remote
+ ;; files, it seems.) The file in question is a
+ ;; tmp file anyway.
+ (let ((default-directory (tramp-temporary-file-directory)))
+ (funcall encoding-function (point-min) (point-max)))
+ (goto-char (point-max))
+ (unless (bolp)
+ (newline)))
+ (tramp-message-for-buffer multi-method method user host
+ 6 "Encoding region using command...")
+ (unless (equal 0
+ (call-process
+ tramp-sh-program
+ tmpfil ;input = local tmp file
+ t ;output is current buffer
+ nil ;don't redisplay
+ "-c"
+ encoding-command))
+ (pop-to-buffer trampbuf)
+ (error (concat "Cannot write to `%s', local encoding"
+ " command `%s' failed")
+ filename encoding-command)))
+ ;; Send tmpbuf into remote decoding command which
+ ;; writes to remote file. Because this happens on the
+ ;; remote host, we cannot use the function.
+ (tramp-message-for-buffer
+ multi-method method user host
+ 5 "Decoding region into remote file %s..." filename)
+ (tramp-send-command
+ multi-method method user host
+ (format "%s >%s <<'EOF'"
+ decoding-command
+ (tramp-shell-quote-argument path)))
+ (set-buffer tmpbuf)
+ (tramp-message-for-buffer
+ multi-method method user host
+ 6 "Sending data to remote host...")
+ (tramp-send-region multi-method method user host
+ (point-min) (point-max))
+ ;; wait for remote decoding to complete
+ (tramp-message-for-buffer
+ multi-method method user host 6 "Sending end of data token...")
+ (tramp-send-command
+ multi-method method user host "EOF")
+ (tramp-message-for-buffer
+ multi-method method user host 6
+ "Waiting for remote host to process data...")
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (tramp-wait-for-output)
+ (tramp-barf-unless-okay
+ multi-method method user host nil nil 'file-error
+ (concat "Couldn't write region to `%s',"
+ " decode using `%s' failed")
+ filename decoding-command)
+ (tramp-message 5 "Decoding region into remote file %s...done"
+ filename)
+ (kill-buffer tmpbuf))))
+ (t
+ (error
+ (concat "Method `%s' should specify both encoding and "
+ "decoding command or an rcp program")
+ method)))
+ (delete-file tmpfil)
+ (unless (equal curbuf (current-buffer))
+ (error "Buffer has changed from `%s' to `%s'"
+ curbuf (current-buffer)))
+ (when (eq visit t)
+ (set-visited-file-modtime))
+ ;; Make `last-coding-system-used' have the right value.
+ (when (boundp 'last-coding-system-used)
+ (setq last-coding-system-used coding-system-used))
+ (when (or (eq visit t)
+ (eq visit nil)
+ (stringp visit))
+ (message "Wrote %s" filename))))
+
+;; Call down to the real handler.
+;; Because EFS does not play nicely with TRAMP (both systems match an
+;; TRAMP path) it is needed to disable efs as well as tramp for the
+;; operation.
+;;
+;; Other than that, this is the canon file-handler code that the doco
+;; says should be used here. Which is nice.
+;;
+;; Under XEmacs current, EFS also hooks in as
+;; efs-sifn-handler-function to handle any path with environment
+;; variables. This has two implications:
+;; 1) That EFS may not be completely dead (yet) for TRAMP paths
+;; 2) That TRAMP might want to do the same thing.
+;; Details as they come in.
+;;
+;; Daniel Pittman <daniel@danann.net>
+
+;; (defun tramp-run-real-handler (operation args)
+;; "Invoke normal file name handler for OPERATION.
+;; This inhibits EFS and Ange-FTP, too, because they conflict with tramp.
+;; First arg specifies the OPERATION, remaining ARGS are passed to the
+;; OPERATION."
+;; (let ((inhibit-file-name-handlers
+;; (list 'tramp-file-name-handler
+;; 'efs-file-handler-function
+;; 'ange-ftp-hook-function
+;; (and (eq inhibit-file-name-operation operation)
+;; inhibit-file-name-handlers)))
+;; (inhibit-file-name-operation operation))
+;; (apply operation args)))
+
+(defun tramp-run-real-handler (operation args)
+ "Invoke normal file name handler for OPERATION.
+First arg specifies the OPERATION, remaining ARGS are passed to the
+OPERATION."
+ (let ((inhibit-file-name-handlers
+ (list 'tramp-file-name-handler
+ (and (eq inhibit-file-name-operation operation)
+ inhibit-file-name-handlers)))
+ (inhibit-file-name-operation operation))
+ (apply operation args)))
+
+
+;; Main function.
+;;;###autoload
+(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."
+ (let ((fn (assoc operation tramp-file-name-handler-alist)))
+ ;(message "Handling %s using %s" operation fn)
+ (if fn
+ (save-match-data
+ (apply (cdr fn) args))
+ (tramp-run-real-handler operation args))))
+
+;; Register in file name handler alist
+;;;###autoload
+(add-to-list 'file-name-handler-alist
+ (cons tramp-file-name-regexp 'tramp-file-name-handler))
+
+;; If jka-compr is already loaded, move it to the front of
+;; `file-name-handler-alist'. On Emacs 21.3 or so this will not be
+;; necessary anymore.
+(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)))))
+
+;;; Interactions with other packages:
+
+;; -- complete.el --
+
+;; This function contributed by Ed Sabol
+(defun tramp-handle-expand-many-files (name)
+ "Like `PC-expand-many-files' for tramp files."
+ (save-match-data
+ (if (or (string-match "\\*" name)
+ (string-match "\\?" name)
+ (string-match "\\[.*\\]" name))
+ (save-excursion
+ ;; Dissect NAME.
+ (let* ((v (tramp-dissect-file-name name))
+ (multi-method (tramp-file-name-multi-method v))
+ (method (tramp-file-name-method v))
+ (user (tramp-file-name-user v))
+ (host (tramp-file-name-host v))
+ (path (tramp-file-name-path v))
+ bufstr)
+ ;; CCC: To do it right, we should quote certain characters
+ ;; in the file name, but since the echo command is going to
+ ;; break anyway when there are spaces in the file names, we
+ ;; don't bother.
+ ;;-(let ((comint-file-name-quote-list
+ ;;- (set-difference tramp-file-name-quote-list
+ ;;- '(?\* ?\? ?[ ?]))))
+ ;;- (tramp-send-command
+ ;;- multi-method method user host
+ ;;- (format "echo %s" (comint-quote-filename path)))
+ ;;- (tramp-wait-for-output))
+ (tramp-send-command multi-method method user host
+ (format "echo %s" path))
+ (tramp-wait-for-output)
+ (setq bufstr (buffer-substring (point-min)
+ (tramp-line-end-position)))
+ (goto-char (point-min))
+ (if (string-equal path bufstr)
+ nil
+ (insert "(\"")
+ (while (search-forward " " nil t)
+ (delete-backward-char 1)
+ (insert "\" \""))
+ (goto-char (point-max))
+ (delete-backward-char 1)
+ (insert "\")")
+ (goto-char (point-min))
+ (mapcar
+ (function (lambda (x)
+ (tramp-make-tramp-file-name multi-method method
+ user host x)))
+ (read (current-buffer))))))
+ (list (tramp-handle-expand-file-name name)))))
+
+;; Check for complete.el and override PC-expand-many-files if appropriate.
+(eval-when-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)
+ (tramp-handle-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)))
+
+
+
+
+;;; Internal Functions:
+
+(defun tramp-set-auto-save ()
+ (when (and (buffer-file-name)
+ (tramp-tramp-file-p (buffer-file-name))
+ auto-save-default)
+ (auto-save-mode 1)))
+(add-hook 'find-file-hooks 'tramp-set-auto-save t)
+
+(defun tramp-run-test (switch filename)
+ "Run `test' on the remote system, given a SWITCH and a FILENAME.
+Returns the exit code of the `test' program."
+ (let ((v (tramp-dissect-file-name filename)))
+ (save-excursion
+ (tramp-send-command-and-check
+ (tramp-file-name-multi-method v) (tramp-file-name-method v)
+ (tramp-file-name-user v) (tramp-file-name-host v)
+ (format "test %s %s" switch
+ (tramp-shell-quote-argument (tramp-file-name-path v)))))))
+
+(defun tramp-run-test2 (program file1 file2 &optional switch)
+ "Run `test'-like PROGRAM on the remote system, given FILE1, FILE2.
+The optional SWITCH is inserted between the two files.
+Returns the exit code of the `test' PROGRAM. Barfs if the methods,
+hosts, or files, disagree."
+ (let* ((v1 (tramp-dissect-file-name file1))
+ (v2 (tramp-dissect-file-name file2))
+ (mmethod1 (tramp-file-name-multi-method v1))
+ (mmethod2 (tramp-file-name-multi-method v2))
+ (method1 (tramp-file-name-method v1))
+ (method2 (tramp-file-name-method v2))
+ (user1 (tramp-file-name-user v1))
+ (user2 (tramp-file-name-user v2))
+ (host1 (tramp-file-name-host v1))
+ (host2 (tramp-file-name-host v2))
+ (path1 (tramp-file-name-path v1))
+ (path2 (tramp-file-name-path v2)))
+ (unless (and method1 method2 host1 host2
+ (equal mmethod1 mmethod2)
+ (equal method1 method2)
+ (equal user1 user2)
+ (equal host1 host2))
+ (error "tramp-run-test2: %s"
+ "only implemented for same method, same user, same host"))
+ (save-excursion
+ (tramp-send-command-and-check
+ mmethod1 method1 user1 host1
+ (format "%s %s %s %s"
+ program
+ (tramp-shell-quote-argument path1)
+ (or switch "")
+ (tramp-shell-quote-argument path2))))))
+
+(defun tramp-buffer-name (multi-method method user host)
+ "A name for the connection buffer for USER at HOST using METHOD."
+ (cond (multi-method
+ (tramp-buffer-name-multi-method "tramp" multi-method method user host))
+ (user
+ (format "*tramp/%s %s@%s*" method user host))
+ (t
+ (format "*tramp/%s %s*" method host))))
+
+(defun tramp-buffer-name-multi-method (prefix multi-method method user host)
+ "A name for the multi method connection buffer.
+MULTI-METHOD gives the multi method, METHOD the array of methods,
+USER the array of user names, HOST the array of host names."
+ (unless (and (= (length method) (length user))
+ (= (length method) (length host)))
+ (error "Syntax error in multi method (implementation error)"))
+ (let ((len (length method))
+ (i 0)
+ string-list)
+ (while (< i len)
+ (setq string-list
+ (cons (if (aref user i)
+ (format "%s#%s@%s:" (aref method i)
+ (aref user i) (aref host i))
+ (format "%s@%s:" (aref method i) (aref host i)))
+ string-list))
+ (incf i))
+ (format "*%s/%s %s*"
+ prefix multi-method
+ (apply 'concat (reverse string-list)))))
+
+(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)))
+
+(defun tramp-debug-buffer-name (multi-method method user host)
+ "A name for the debug buffer for USER at HOST using METHOD."
+ (cond (multi-method
+ (tramp-buffer-name-multi-method "debug tramp"
+ multi-method method user host))
+ (user
+ (format "*debug tramp/%s %s@%s*" method user host))
+ (t
+ (format "*debug tramp/%s %s*" method host))))
+
+(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)))
+
+(defun tramp-find-executable (multi-method method user host
+ progname dirlist ignore-tilde)
+ "Searches for PROGNAME in all directories mentioned in DIRLIST.
+First args METHOD, USER and HOST specify the connection, PROGNAME
+is the program to search for, and DIRLIST gives the list of directories
+to search. If IGNORE-TILDE is non-nil, directory names starting
+with `~' will be ignored.
+
+Returns the full path name of PROGNAME, if found, and nil otherwise.
+
+This function expects to be in the right *tramp* buffer."
+ (let (result)
+ (when ignore-tilde
+ ;; Remove all ~/foo directories from dirlist. In Emacs 20,
+ ;; `remove' is in CL, and we want to avoid CL dependencies.
+ (let (newdl d)
+ (while dirlist
+ (setq d (car dirlist))
+ (setq dirlist (cdr dirlist))
+ (unless (char-equal ?~ (aref d 0))
+ (setq newdl (cons d newdl))))
+ (setq dirlist (nreverse newdl))))
+ (tramp-send-command
+ multi-method method user host
+ (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'")
+ progname progname progname))
+ (mapcar (lambda (d)
+ (tramp-send-command multi-method method user host d))
+ dirlist)
+ (tramp-send-command multi-method method user host "EOF")
+ (tramp-wait-for-output)
+ (goto-char (point-max))
+ (when (search-backward "tramp_executable " nil t)
+ (skip-chars-forward "^ ")
+ (skip-chars-forward " ")
+ (buffer-substring (point) (tramp-line-end-position)))))
+
+(defun tramp-set-remote-path (multi-method method user host var dirlist)
+ "Sets the remote environment VAR to existing directories from DIRLIST.
+I.e., for each directory in DIRLIST, it is tested whether it exists and if
+so, it is added to the environment variable VAR."
+ (let ((existing-dirs
+ (mapcar
+ (lambda (x)
+ (when (and
+ (file-exists-p
+ (tramp-make-tramp-file-name multi-method method user host x))
+ (file-directory-p
+ (tramp-make-tramp-file-name multi-method method user host x)))
+ x))
+ dirlist)))
+ (tramp-send-command
+ multi-method method user host
+ (concat var "="
+ (mapconcat 'identity (delq nil existing-dirs) ":")
+ "; export " var))
+ (tramp-wait-for-output)))
+
+;; -- communication with external shell --
+
+(defun tramp-find-file-exists-command (multi-method method user host)
+ "Find a command on the remote host for checking if a file exists.
+Here, we are looking for a command which has zero exit status if the
+file exists and nonzero exit status otherwise."
+ (make-local-variable 'tramp-file-exists-command)
+ (tramp-message 10 "Finding command to check if file exists")
+ (let ((existing
+ (tramp-make-tramp-file-name
+ multi-method method user host
+ "/")) ;assume this file always exists
+ (nonexisting
+ (tramp-make-tramp-file-name
+ multi-method method user host
+ "/ this file does not exist "))) ;assume this never exists
+ ;; The algorithm is as follows: we try a list of several commands.
+ ;; For each command, we first run `$cmd /' -- this should return
+ ;; true, as the root directory always exists. And then we run
+ ;; `$cmd /this\ file\ does\ not\ exist', hoping that the file indeed
+ ;; does not exist. This should return false. We use the first
+ ;; command we find that seems to work.
+ ;; The list of commands to try is as follows:
+ ;; `ls -d' This works on most systems, but NetBSD 1.4
+ ;; has a bug: `ls' always returns zero exit
+ ;; status, even for files which don't exist.
+ ;; `test -e' Some Bourne shells have a `test' builtin
+ ;; which does not know the `-e' option.
+ ;; `/bin/test -e' For those, the `test' binary on disk normally
+ ;; provides the option. Alas, the binary
+ ;; is sometimes `/bin/test' and sometimes it's
+ ;; `/usr/bin/test'.
+ ;; `/usr/bin/test -e' In case `/bin/test' does not exist.
+ (unless (or
+ (and (setq tramp-file-exists-command "ls -d %s")
+ (tramp-handle-file-exists-p existing)
+ (not (tramp-handle-file-exists-p nonexisting)))
+ (and (setq tramp-file-exists-command "test -e %s")
+ (tramp-handle-file-exists-p existing)
+ (not (tramp-handle-file-exists-p nonexisting)))
+ (and (setq tramp-file-exists-command "/bin/test -e %s")
+ (tramp-handle-file-exists-p existing)
+ (not (tramp-handle-file-exists-p nonexisting)))
+ (and (setq tramp-file-exists-command "/usr/bin/test -e %s")
+ (tramp-handle-file-exists-p existing)
+ (not (tramp-handle-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)
+ "Find a shell on the remote host which groks tilde expansion."
+ (let ((shell nil))
+ (tramp-send-command multi-method method user host "echo ~root")
+ (tramp-wait-for-output)
+ (cond
+ ((string-match "^~root$" (buffer-string))
+ (setq shell
+ (or (tramp-find-executable multi-method method user host
+ "bash" tramp-remote-path t)
+ (tramp-find-executable multi-method method user host
+ "ksh" tramp-remote-path t)))
+ (unless shell
+ (error "Couldn't find a shell which groks tilde expansion"))
+ ;; Hack: avoid reading of ~/.bashrc. What we should do is have an
+ ;; alist for extra args to give to each shell...
+ (when (string-match "/bash\\'" shell)
+ (setq shell (concat shell " --norc")))
+ (tramp-message
+ 5 "Starting remote shell `%s' for tilde expansion..." shell)
+ (tramp-send-command
+ multi-method method user host
+ (concat "PS1='$ ' ; exec " shell))
+ (unless (tramp-wait-for-regexp
+ (get-buffer-process (current-buffer))
+ 60 (format "\\(\\$ *\\|\\(%s\\)\\'\\)" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't find remote `%s' prompt." shell))
+ (process-send-string nil (format "PS1='%s%s%s'; PS2=''; PS3=''%s"
+ tramp-rsh-end-of-line
+ tramp-end-of-output
+ tramp-rsh-end-of-line
+ tramp-rsh-end-of-line))
+ (tramp-wait-for-output)
+ (tramp-send-command multi-method method user host "echo hello")
+ (tramp-message 5 "Waiting for remote `%s' to start up..." shell)
+ (unless (tramp-wait-for-output 5)
+ (unless (tramp-wait-for-output 5)
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't start remote `%s', see buffer `%s' for details"
+ shell (buffer-name))))
+ (tramp-message 5 "Waiting for remote `%s' to start up...done" shell))
+ (t (tramp-message 5 "Remote `%s' groks tilde expansion, good"
+ (tramp-get-remote-sh multi-method method))))))
+
+(defun tramp-check-ls-command (multi-method method user host cmd)
+ "Checks whether the given `ls' executable groks `-n'.
+METHOD, USER and HOST specify the connection, CMD (the full path name of)
+the `ls' executable. Returns t if CMD supports the `-n' option, nil
+otherwise."
+ (tramp-message 9 "Checking remote `%s' command for `-n' option"
+ cmd)
+ (when (tramp-handle-file-executable-p
+ (tramp-make-tramp-file-name multi-method method user host cmd))
+ (let ((result nil))
+ (tramp-message 7 "Testing remote command `%s' for -n..." cmd)
+ (setq result
+ (tramp-send-command-and-check
+ multi-method method user host
+ (format "%s -lnd / >/dev/null"
+ cmd)))
+ (tramp-message 7 "Testing remote command `%s' for -n...%s"
+ cmd
+ (if (zerop result) "okay" "failed"))
+ (zerop result))))
+
+(defun tramp-check-ls-commands (multi-method method user host cmd dirlist)
+ "Checks whether the given `ls' executable in one of the dirs groks `-n'.
+Returns nil if none was found, else the command is returned."
+ (let ((dl dirlist)
+ (result nil))
+ ;; It would be better to use the CL function `find', but
+ ;; we don't want run-time dependencies on CL.
+ (while (and dl (not result))
+ (let ((x (concat (file-name-as-directory (car dl)) cmd)))
+ (when (tramp-check-ls-command multi-method method user host x)
+ (setq result x)))
+ (setq dl (cdr dl)))
+ result))
+
+(defun tramp-find-ls-command (multi-method method user host)
+ "Finds an `ls' command which groks the `-n' option, returning nil if failed.
+\(This option prints numeric user and group ids in a long listing.)"
+ (tramp-message 9 "Finding a suitable `ls' command")
+ (or
+ (tramp-check-ls-commands multi-method method user host "ls" tramp-remote-path)
+ (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 --
+;; ------------------------------------------------------------
+
+(defun tramp-process-actions
+ (multi-method method user host actions &optional timeout)
+ "Process given ACTIONS for login specified via first four args.
+ACTIONS is a list of items (REGEXP FUN), where REGEXP specifies what
+output from the remote end to look for, and FUN specifies the action
+to take when the regexp matches."
+ nil)
+
+(defun tramp-open-connection-telnet (multi-method method user host)
+ "Open a connection using a telnet METHOD.
+This starts the command `telnet HOST ARGS'[*], then waits for a remote
+login prompt, then sends the user name USER, then waits for a remote
+password prompt. It queries the user for the password, then sends the
+password to the remote host.
+
+If USER is nil, uses value returned by `(user-login-name)' instead.
+
+Recognition of the remote shell prompt is based on the variable
+`shell-prompt-pattern' which must be set up correctly.
+
+Please note that it is NOT possible to use this connection method
+together with an out-of-band transfer method! You must use an inline
+transfer method.
+
+Maybe the different regular expressions need to be tuned.
+
+* Actually, the telnet program as well as the args to be used can be
+ specified in the method parameters, see the variable `tramp-methods'."
+ (save-match-data
+ (when (tramp-method-out-of-band-p multi-method method)
+ (error "Cannot use out-of-band method `%s' with telnet connection method"
+ method))
+ (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..."
+ (or user (user-login-name)) host method)
+ (let ((process-environment (copy-sequence process-environment)))
+ (setenv "TERM" tramp-terminal-type)
+ (let* ((default-directory (tramp-temporary-file-directory))
+ (coding-system-for-read (unless (and (not (featurep 'xemacs))
+ (> emacs-major-version 20))
+ tramp-dos-coding-system))
+ (p (apply 'start-process
+ (tramp-buffer-name multi-method method user host)
+ (tramp-get-buffer multi-method method user host)
+ (tramp-get-telnet-program multi-method method)
+ host
+ (tramp-get-telnet-args multi-method method)))
+ (found nil)
+ (pw nil))
+ (process-kill-without-query p)
+ (tramp-message 9 "Waiting for login prompt...")
+ (unless (tramp-wait-for-regexp p nil tramp-login-prompt-regexp)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote login prompt"))
+ (erase-buffer)
+ ;; Remote login defaults to local one.
+ (tramp-message 9 "Sending login name %s" (or user (user-login-name)))
+ (process-send-string p (concat (or user (user-login-name))
+ tramp-rsh-end-of-line))
+ (tramp-message 9 "Waiting for password prompt...")
+ (unless (setq found (tramp-wait-for-regexp
+ p nil tramp-password-prompt-regexp))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote password prompt"))
+ (erase-buffer)
+ (setq pw (tramp-read-passwd (car found)))
+ (tramp-message 9 "Sending password")
+ (process-send-string p (concat pw tramp-rsh-end-of-line))
+ (tramp-message 9 "Waiting 30s for remote shell to come up...")
+ (unless (setq found
+ (tramp-wait-for-regexp
+ p 30 (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell prompt"))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Login failed: %s" (nth 1 found)))
+ (tramp-open-connection-setup-interactive-shell
+ p multi-method method user host)
+ (tramp-post-connection multi-method method user host)))))
+
+;; HHH: Changed to handle the case when USER is nil.
+(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
+password or shell prompt. If a password prompt is seen, the user is
+queried for a password, this function sends the password to the remote
+host and waits for a shell prompt.
+
+If USER is nil, start the command `rsh HOST'[*] instead
+
+Recognition of the remote shell prompt is based on the variable
+`shell-prompt-pattern' which must be set up correctly.
+
+Please note that it is NOT possible to use this connection method with
+an out-of-band transfer method if this function asks the user for a
+password! You must use an inline transfer method in this case.
+Sadly, the transfer method cannot be switched on the fly, instead you
+must specify the right method in the file name.
+
+* Actually, the rsh program to be used can be specified in the
+ method parameters, see the variable `tramp-methods'."
+ (save-match-data
+ (when multi-method
+ (error "Cannot multi-connect using rsh connection method"))
+ (tramp-pre-connection multi-method method user host)
+ (if user
+ (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)))
+ (setenv "TERM" tramp-terminal-type)
+ (let* ((default-directory (tramp-temporary-file-directory))
+ (coding-system-for-read (unless (and (not (featurep 'xemacs))
+ (> emacs-major-version 20))
+ tramp-dos-coding-system))
+ (p (if user
+ (apply #'start-process
+ (tramp-buffer-name multi-method method user host)
+ (tramp-get-buffer multi-method method user host)
+ (tramp-get-rsh-program multi-method method)
+ host "-l" user
+ (tramp-get-rsh-args multi-method method))
+ (apply #'start-process
+ (tramp-buffer-name multi-method method user host)
+ (tramp-get-buffer multi-method method user host)
+ (tramp-get-rsh-program multi-method method)
+ host
+ (tramp-get-rsh-args multi-method method))))
+ (found nil))
+ (process-kill-without-query p)
+ (tramp-message 9 "Waiting 60s for shell or passwd prompt from %s" host)
+ (setq found
+ (tramp-wait-for-regexp
+ p 60
+ (format
+ "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-password-prompt-regexp
+ shell-prompt-pattern)))
+ (unless found
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell or passwd prompt"))
+ (when (nth 1 found)
+ (when (tramp-method-out-of-band-p multi-method method)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error (concat "Out of band method `%s' not applicable"
+ " for remote shell asking for a password")
+ method))
+ (erase-buffer)
+ (tramp-message 9 "Sending password...")
+ (tramp-enter-password p (nth 1 found))
+ (tramp-message 9 "Sent password, waiting 60s for remote shell prompt")
+ (setq found (tramp-wait-for-regexp p 60
+ (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern))))
+ (unless found
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell prompt"))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Login failed: %s" (nth 1 found)))
+ (tramp-message 7 "Initializing remote shell")
+ (tramp-open-connection-setup-interactive-shell
+ p multi-method method user host)
+ (tramp-post-connection multi-method method user host)))))
+
+;; HHH: Changed. Now utilizes (or user (user-login-name)) instead of USER.
+(defun tramp-open-connection-su (multi-method method user host)
+ "Open a connection using the `su' program with METHOD.
+This starts `su - USER', then waits for a password prompt. The HOST
+name must be equal to the local host name or to `localhost'.
+
+If USER is nil, uses value returned by user-login-name instead.
+
+Recognition of the remote shell prompt is based on the variable
+`shell-prompt-pattern' which must be set up correctly. Note that the
+other user may have a different shell prompt than you do, so it is not
+at all unlikely that this variable is set up wrongly!"
+ (save-match-data
+ (when (tramp-method-out-of-band-p multi-method method)
+ (error "Cannot use out-of-band method `%s' with `su' connection method"
+ method))
+ (unless (or (string-match (concat "^" (regexp-quote host))
+ (system-name))
+ (string= "localhost" host))
+ (error
+ "Cannot connect to different host `%s' with `su' connection method"
+ host))
+ (when (not user)
+ (error "Must give user name for `su' connection method"))
+ (tramp-pre-connection multi-method method user host)
+ (tramp-message 7 "Opening connection for `%s' using `%s'..."
+ (or user (user-login-name)) method)
+ (let ((process-environment (copy-sequence process-environment)))
+ (setenv "TERM" tramp-terminal-type)
+ (let* ((default-directory (tramp-temporary-file-directory))
+ (coding-system-for-read (unless (and (not (featurep 'xemacs))
+ (> emacs-major-version 20))
+ tramp-dos-coding-system))
+ (p (apply 'start-process
+ (tramp-buffer-name multi-method method
+ user host)
+ (tramp-get-buffer multi-method method
+ user host)
+ (tramp-get-su-program multi-method method)
+ (mapcar
+ '(lambda (x)
+ (format-spec
+ x (list (cons ?u user))))
+ (tramp-get-su-args multi-method method))))
+ (found nil)
+ (pw nil))
+ (process-kill-without-query p)
+ (tramp-message 9 "Waiting 30s for shell or password prompt...")
+ (unless (setq found (tramp-wait-for-regexp
+ p 30
+ (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-password-prompt-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find shell or password prompt"))
+ (when (nth 1 found)
+ (erase-buffer)
+ (setq pw (tramp-read-passwd (car found)))
+ (tramp-message 9 "Sending password")
+ (process-send-string p (concat pw tramp-rsh-end-of-line))
+ (tramp-message 9 "Waiting 30s for remote shell to come up...")
+ (unless (setq found
+ (tramp-wait-for-regexp
+ p 30 (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell prompt"))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "`su' failed: %s" (nth 1 found))))
+ (tramp-open-connection-setup-interactive-shell
+ p multi-method method user host)
+ (tramp-post-connection multi-method method
+ user host)))))
+
+;; 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
+;; multi-hop-method, the desired behaviour should be that the
+;; user must specify names for the telnet hops of which the user
+;; name is different than the "original" name (or different from
+;; the previous hop.
+(defun tramp-open-connection-multi (multi-method method user host)
+ "Open a multi-hop connection using METHOD.
+This uses a slightly changed file name syntax. The idea is to say
+ [multi/telnet:u1@h1/rsh:u2@h2]/path/to/file
+This will use telnet to log in as u1 to h1, then use rsh from there to
+log in as u2 to h2."
+ (save-match-data
+ (unless multi-method
+ (error "Multi-hop open connection function called on non-multi method"))
+ (when (tramp-method-out-of-band-p multi-method method)
+ (error "No out of band multi-hop connections"))
+ (unless (and (arrayp method) (not (stringp method)))
+ (error "METHOD must be an array of strings for multi methods"))
+ (unless (and (arrayp user) (not (stringp user)))
+ (error "USER must be an array of strings for multi methods"))
+ (unless (and (arrayp host) (not (stringp host)))
+ (error "HOST must be an array of strings for multi methods"))
+ (unless (and (= (length method) (length user))
+ (= (length method) (length host)))
+ (error "Arrays METHOD, USER, HOST must have equal length"))
+ (tramp-pre-connection multi-method method user host)
+ (tramp-message 7 "Opening `%s' connection..." multi-method)
+ (let ((process-environment (copy-sequence process-environment)))
+ (setenv "TERM" tramp-terminal-type)
+ (let* ((default-directory (tramp-temporary-file-directory))
+ (coding-system-for-read (unless (and (not (featurep 'xemacs))
+ (> emacs-major-version 20))
+ tramp-dos-coding-system))
+ (p (start-process (tramp-buffer-name multi-method method user host)
+ (tramp-get-buffer multi-method method user host)
+ tramp-sh-program))
+ (num-hops (length method))
+ (i 0))
+ (process-kill-without-query p)
+ (tramp-message 9 "Waiting 60s for local shell to come up...")
+ (unless (tramp-wait-for-regexp
+ p 60 (format "%s\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find local shell prompt"))
+ ;; Now do all the connections as specified.
+ (while (< i num-hops)
+ (let* ((m (aref method i))
+ (u (aref user i))
+ (h (aref host i))
+ (entry (assoc m tramp-multi-connection-function-alist))
+ (multi-func (nth 1 entry))
+ (command (nth 2 entry)))
+ ;; The multi-funcs don't need to do save-match-data, as that
+ ;; is done here.
+ (funcall multi-func p m u h command)
+ (erase-buffer)
+ (incf i)))
+ (tramp-open-connection-setup-interactive-shell
+ p multi-method method user host)
+ (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:
+;; 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)
+ "Issue `telnet' command.
+Uses shell COMMAND to issue a `telnet' command to log in as USER to
+HOST. You can use percent escapes in COMMAND: `%h' is replaced with
+the host name, and `%n' is replaced with an end of line character, as
+set in `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent
+character.
+
+If USER is nil, uses the return value of (user-login-name) instead."
+ (let ((cmd (format-spec command (list (cons ?h host)
+ (cons ?n tramp-rsh-end-of-line))))
+ (cmd1 (format-spec command (list (cons ?h host)
+ (cons ?n ""))))
+ found pw)
+ (erase-buffer)
+ (tramp-message 9 "Sending telnet command `%s'" cmd1)
+ (process-send-string p cmd)
+ (tramp-message 9 "Waiting 30s for login prompt from %s" host)
+ (unless (tramp-wait-for-regexp p 30 tramp-login-prompt-regexp)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find login prompt from host %s" host))
+ (erase-buffer)
+ (tramp-message 9 "Sending login name %s" (or user (user-login-name)))
+ (process-send-string p (concat (or user (user-login-name)) tramp-rsh-end-of-line))
+ (tramp-message 9 "Waiting for password prompt")
+ (unless (setq found (tramp-wait-for-regexp p nil tramp-password-prompt-regexp))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find password prompt from host %s" host))
+ (erase-buffer)
+ (setq pw (tramp-read-passwd
+ (format "Password for %s@%s, %s" (or user (user-login-name)) host found)))
+ (tramp-message 9 "Sending password")
+ (process-send-string p (concat pw tramp-rsh-end-of-line))
+ (tramp-message 9 "Waiting 60s for remote shell to come up...")
+ (unless (setq found (tramp-wait-for-regexp
+ p 60 (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find shell prompt from host %s" host))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Login to %s failed: %s" (nth 2 found)))))
+
+;; 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)
+ "Issue `rlogin' command.
+Uses shell COMMAND to issue an `rlogin' command to log in as USER to
+HOST. You can use percent escapes in COMMAND. `%u' will be replaced
+with the user name, `%h' will be replaced with the host name, and `%n'
+will be replaced with the value of `tramp-rsh-end-of-line'. You can use
+`%%' if you want to use a literal percent character.
+
+If USER is nil, uses the return value of (user-login-name) instead."
+ (let ((cmd (format-spec command (list (cons ?h host)
+ (cons ?u (or user (user-login-name)))
+ (cons ?n tramp-rsh-end-of-line))))
+ (cmd1 (format-spec command (list (cons ?h host)
+ (cons ?u (or user (user-login-name)))
+ (cons ?n ""))))
+ found)
+ (erase-buffer)
+ (tramp-message 9 "Sending rlogin command `%s'" cmd1)
+ (process-send-string p cmd)
+ (tramp-message 9 "Waiting 60s for shell or passwd prompt from %s" host)
+ (unless (setq found
+ (tramp-wait-for-regexp p 60
+ (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-password-prompt-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell or passwd prompt"))
+ (when (nth 1 found)
+ (erase-buffer)
+ (tramp-message 9 "Sending password...")
+ (tramp-enter-password p (nth 1 found))
+ (tramp-message 9 "Sent password, waiting 60s for remote shell prompt")
+ (setq found (tramp-wait-for-regexp p 60
+ (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern))))
+ (unless found
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell prompt"))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Login failed: %s" (nth 1 found)))))
+
+;; 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)
+ "Issue `su' command.
+Uses shell COMMAND to issue a `su' command to log in as USER on
+HOST. The HOST name is ignored, this just changes the user id on the
+host currently logged in to.
+
+If USER is nil, uses the return value of (user-login-name) instead.
+
+You can use percent escapes in the COMMAND. `%u' is replaced with the
+user name, and `%n' is replaced with the value of
+`tramp-rsh-end-of-line'. Use `%%' if you want a literal percent
+character."
+ (let ((cmd (format-spec command (list (cons ?u (or user (user-login-name)))
+ (cons ?n tramp-rsh-end-of-line))))
+ (cmd1 (format-spec command (list (cons ?u (or user (user-login-name)))
+ (cons ?n ""))))
+ found)
+ (erase-buffer)
+ (tramp-message 9 "Sending su command `%s'" cmd1)
+ (process-send-string p cmd)
+ (tramp-message 9 "Waiting 60s for shell or passwd prompt for %s" (or user (user-login-name)))
+ (unless (setq found (tramp-wait-for-regexp
+ p 60 (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-password-prompt-regexp
+ shell-prompt-pattern)))
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find shell or passwd prompt for %s"
+ (or user (user-login-name))))
+ (when (nth 1 found)
+ (tramp-message 9 "Sending password...")
+ (tramp-enter-password p (nth 1 found))
+ (erase-buffer)
+ (tramp-message 9 "Sent password, waiting 60s for remote shell prompt")
+ (setq found (tramp-wait-for-regexp p 60
+ (format "\\(%s\\)\\|\\(%s\\)\\'"
+ tramp-wrong-passwd-regexp
+ shell-prompt-pattern))))
+ (unless found
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Couldn't find remote shell prompt"))
+ (when (nth 1 found)
+ (pop-to-buffer (buffer-name))
+ (kill-process p)
+ (error "Login failed: %s" (nth 1 found)))))
+
+;; Utility functions.
+
+(defun tramp-wait-for-regexp (proc timeout regexp)
+ "Wait for a REGEXP to appear from process PROC within TIMEOUT seconds.
+Expects the output of PROC to be sent to the current buffer. Returns
+the string that matched, or nil. Waits indefinitely if TIMEOUT is
+nil."
+ (let ((found nil)
+ (start-time (current-time)))
+ (cond (timeout
+ ;; Work around a bug in XEmacs 21, where the timeout
+ ;; expires faster than it should. This degenerates
+ ;; to polling for buggy XEmacsen, but oh, well.
+ (while (and (not found)
+ (< (tramp-time-diff (current-time) start-time)
+ timeout))
+ (with-timeout (timeout)
+ (while (not found)
+ (accept-process-output proc 1)
+ (goto-char (point-min))
+ (setq found (when (re-search-forward regexp nil t)
+ (tramp-match-string-list)))))))
+ (t
+ (while (not found)
+ (accept-process-output proc 1)
+ (goto-char (point-min))
+ (setq found (when (re-search-forward regexp nil t)
+ (tramp-match-string-list))))))
+ (when tramp-debug-buffer
+ (append-to-buffer
+ (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
+ tramp-current-user tramp-current-host)
+ (point-min) (point-max))
+ (when (not found)
+ (save-excursion
+ (set-buffer
+ (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
+ tramp-current-user tramp-current-host))
+ (goto-char (point-max))
+ (insert "[[Regexp `" regexp "' not found"
+ (if timeout (concat " in " timeout " secs") "")
+ "]]"))))
+ found))
+
+(defun tramp-enter-password (p prompt)
+ "Prompt for a password and send it to the remote end.
+Uses PROMPT as a prompt and sends the password to process P."
+ (let ((pw (tramp-read-passwd prompt)))
+ (process-send-string p (concat pw tramp-rsh-end-of-line))))
+
+;; HHH: Not Changed. This might handle the case where USER is not
+;; given in the "File name" very poorly. Then, the local
+;; variable tramp-current user will be set to nil.
+(defun tramp-pre-connection (multi-method method user host)
+ "Do some setup before actually logging in.
+METHOD, USER and HOST specify the connection."
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (set (make-local-variable 'tramp-current-multi-method) multi-method)
+ (set (make-local-variable 'tramp-current-method) method)
+ (set (make-local-variable 'tramp-current-user) user)
+ (set (make-local-variable 'tramp-current-host) host)
+ (set (make-local-variable 'inhibit-eol-conversion) nil)
+ (erase-buffer))
+
+(defun tramp-open-connection-setup-interactive-shell
+ (p multi-method method user host)
+ "Set up an interactive shell.
+Mainly sets the prompt and the echo correctly. P is the shell process
+to set up. METHOD, USER and HOST specify the connection."
+ ;; Wait a bit in case the remote end feels like sending a little
+ ;; junk first. It seems that fencepost.gnu.org does this when doing
+ ;; a Kerberos login.
+ (sit-for 1)
+ (tramp-discard-garbage-erase-buffer p multi-method method user host)
+ (process-send-string nil (format "exec %s%s"
+ (tramp-get-remote-sh multi-method method)
+ tramp-rsh-end-of-line))
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer (tramp-get-debug-buffer multi-method method user host))
+ (goto-char (point-max))
+ (tramp-insert-with-face
+ 'bold (format "$ exec %s\n" (tramp-get-remote-sh multi-method method)))))
+ (tramp-message 9 "Waiting 30s for remote `%s' to come up..."
+ (tramp-get-remote-sh multi-method method))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Remote `%s' didn't come up. See buffer `%s' for details"
+ (tramp-get-remote-sh multi-method method) (buffer-name)))
+ (tramp-message 9 "Setting up remote shell environment")
+ (tramp-discard-garbage-erase-buffer p multi-method method user host)
+ (process-send-string
+ nil (format "stty -inlcr -echo kill '^U'%s" tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `stty -echo', see buffer `%s'" (buffer-name)))
+ (erase-buffer)
+ (process-send-string nil (format "TERM=dumb; export TERM%s"
+ tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `TERM=dumb; export TERM', see buffer `%s'" (buffer-name)))
+ ;; Try to set up the coding system correctly.
+ ;; CCC this can't be the right way to do it. Hm.
+ (save-excursion
+ (erase-buffer)
+ (tramp-message 9 "Determining coding system")
+ (process-send-string nil (format "echo foo ; echo bar %s"
+ tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `echo foo; echo bar' to determine line endings'"))
+ (goto-char (point-min))
+ (if (featurep 'mule)
+ ;; Use MULE to select the right EOL convention for communicating
+ ;; with the process.
+ (let* ((cs (or (process-coding-system p) (cons 'undecided 'undecided)))
+ cs-decode cs-encode)
+ (when (symbolp cs) (setq cs (cons cs cs)))
+ (setq cs-decode (car cs))
+ (setq cs-encode (cdr cs))
+ (unless cs-decode (setq cs-decode 'undecided))
+ (unless cs-encode (setq cs-encode 'undecided))
+ (setq cs-encode (tramp-coding-system-change-eol-conversion
+ cs-encode 'unix))
+ (when (search-forward "\r" nil t)
+ (setq cs-decode (tramp-coding-system-change-eol-conversion
+ cs-decode 'dos)))
+ (set-buffer-process-coding-system cs-decode cs-encode))
+ ;; Look for ^M and do something useful if found.
+ (when (search-forward "\r" nil t)
+ ;; We have found a ^M but cannot frob the process coding system
+ ;; because we're running on a non-MULE Emacs. Let's try
+ ;; stty, instead.
+ (tramp-message 9 "Trying `stty -onlcr'")
+ (process-send-string nil (format "stty -onlcr%s" tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `stty -onlcr', see buffer `%s'" (buffer-name))))))
+ (erase-buffer)
+ (tramp-message
+ 9 "Waiting 30s for `HISTFILE=$HOME/.tramp_history; HISTSIZE=1'")
+ (process-send-string
+ nil (format "HISTFILE=$HOME/.tramp_history; HISTSIZE=1%s"
+ tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error (concat "Couldn't `HISTFILE=$HOME/.tramp_history; "
+ "HISTSIZE=1', see buffer `%s'")
+ (buffer-name)))
+ (erase-buffer)
+ (tramp-message 9 "Waiting 30s for `set +o vi +o emacs'")
+ (process-send-string
+ nil (format "set +o vi +o emacs%s" ;mustn't `>/dev/null' with AIX?
+ tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `set +o vi +o emacs', see buffer `%s'"
+ (buffer-name)))
+ (erase-buffer)
+ (tramp-message 9 "Waiting 30s for `unset MAIL MAILCHECK MAILPATH'")
+ (process-send-string
+ nil (format "unset MAIL MAILCHECK MAILPATH 1>/dev/null 2>/dev/null%s"
+ tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `unset MAIL MAILCHECK MAILPATH', see buffer `%s'"
+ (buffer-name)))
+ (erase-buffer)
+ (tramp-message 9 "Waiting 30s for `unset CDPATH'")
+ (process-send-string
+ nil (format "unset CDPATH%s" tramp-rsh-end-of-line))
+ (unless (tramp-wait-for-regexp
+ p 30 (format "\\(\\$ *\\|%s\\)\\'" shell-prompt-pattern))
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't `unset CDPATH', see buffer `%s'"
+ (buffer-name)))
+ (erase-buffer)
+ (tramp-message 9 "Setting shell prompt")
+ (tramp-send-command
+ multi-method method user host
+ (format "PS1='%s%s%s'; PS2=''; PS3=''"
+ tramp-rsh-end-of-line
+ tramp-end-of-output
+ tramp-rsh-end-of-line))
+ (tramp-wait-for-output)
+ (tramp-send-command multi-method method user host "echo hello")
+ (tramp-message 9 "Waiting for remote `%s' to come up..."
+ (tramp-get-remote-sh multi-method method))
+ (unless (tramp-wait-for-output 5)
+ (unless (tramp-wait-for-output 5)
+ (pop-to-buffer (buffer-name))
+ (error "Couldn't set remote shell prompt. See buffer `%s' for details"
+ (buffer-name))))
+ (tramp-message 7 "Waiting for remote `%s' to come up...done"
+ (tramp-get-remote-sh multi-method method)))
+
+(defun tramp-post-connection (multi-method method user host)
+ "Prepare a remote shell before being able to work on it.
+METHOD, USER and HOST specify the connection.
+Among other things, this finds a shell which groks tilde expansion,
+tries to find an `ls' command which groks the `-n' option, sets the
+locale to C and sets up the remote shell search path."
+ ;; Search for a good shell before searching for a command which
+ ;; checks if a file exists. This is done because Tramp wants to use
+ ;; "test foo; echo $?" to check if various conditions hold, and
+ ;; there are buggy /bin/sh implementations which don't execute the
+ ;; "echo $?" part if the "test" part has an error. In particular,
+ ;; the Solaris /bin/sh is a problem. I'm betting that all systems
+ ;; with buggy /bin/sh implementations will have a working bash or
+ ;; ksh. Whee...
+ (tramp-find-shell multi-method method user host)
+ (tramp-find-file-exists-command multi-method method user host)
+ (sit-for 1)
+ ;; Without (sit-for 0.1) at least, my machine will almost always blow
+ ;; up on 'not numberp /root' - a race that causes the 'echo ~root'
+ ;; output of (tramp-find-shell) to show up along with the output of
+ ;; (tramp-find-ls-command) testing.
+ ;;
+ ;; I can't work out why this is a problem though. The (tramp-wait-for-output)
+ ;; call in (tramp-find-shell) *should* make this not happen, I thought.
+ ;;
+ ;; After much debugging I couldn't find any problem with the implementation
+ ;; of that function though. The workaround stays for me at least. :/
+ ;;
+ ;; Daniel Pittman <daniel@danann.net>
+ (make-local-variable 'tramp-ls-command)
+ (setq tramp-ls-command (tramp-find-ls-command multi-method method user host))
+ (unless tramp-ls-command
+ (tramp-message
+ 1
+ "Danger! Couldn't find ls which groks -n. Muddling through anyway")
+ (setq tramp-ls-command
+ (tramp-find-executable multi-method method user host
+ "ls" tramp-remote-path nil)))
+ (unless tramp-ls-command
+ (error "Fatal error: Couldn't find remote executable `ls'"))
+ (tramp-message 5 "Using remote command `%s' for getting directory listings"
+ tramp-ls-command)
+ (tramp-send-command multi-method method user host
+ (concat "tramp_set_exit_status () {" tramp-rsh-end-of-line
+ "return $1" tramp-rsh-end-of-line
+ "}"))
+ (tramp-wait-for-output)
+ ;; Set remote PATH variable.
+ (tramp-set-remote-path multi-method method user host "PATH" tramp-remote-path)
+ ;; Tell remote shell to use standard time format, needed for
+ ;; parsing `ls -l' output.
+ (tramp-send-command multi-method method user host
+ "LC_TIME=C; export LC_TIME; echo huhu")
+ (tramp-wait-for-output)
+ (tramp-send-command multi-method method user host
+ "mesg n; echo huhu")
+ (tramp-wait-for-output)
+ (tramp-send-command multi-method method user host
+ "biff n ; echo huhu")
+ (tramp-wait-for-output)
+ ;; Unalias ls(1) to work around issues with those silly people who make it
+ ;; spit out ANSI escapes or whatever.
+ (tramp-send-command multi-method method user host
+ "unalias ls; echo huhu")
+ (tramp-wait-for-output)
+ ;; Does `test A -nt B' work? Use abominable `find' construct if it
+ ;; doesn't. BSD/OS 4.0 wants the parentheses around the command,
+ ;; for otherwise the shell crashes.
+ (erase-buffer)
+ (make-local-variable 'tramp-test-groks-nt)
+ (tramp-send-command multi-method method user host
+ "( test / -nt / )")
+ (tramp-wait-for-output)
+ (goto-char (point-min))
+ (setq tramp-test-groks-nt
+ (looking-at (format "\n%s\n" (regexp-quote tramp-end-of-output))))
+ (unless tramp-test-groks-nt
+ (tramp-send-command
+ multi-method method user host
+ (concat "tramp_test_nt () {" tramp-rsh-end-of-line
+ "test -n \"`find $1 -prune -newer $2 -print`\"" tramp-rsh-end-of-line
+ "}")))
+ (tramp-wait-for-output)
+ ;; Find a `perl'.
+ (erase-buffer)
+ (let ((tramp-remote-perl
+ (or (tramp-find-executable multi-method method user host
+ "perl5" tramp-remote-path nil)
+ (tramp-find-executable multi-method method user host
+ "perl" tramp-remote-path nil))))
+ (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-linewise
+ multi-method method user host
+ (concat "tramp_file_attributes () {\n"
+ tramp-remote-perl
+ " -e '" tramp-perl-file-attributes "' $1 2>/dev/null\n"
+ "}"))
+ (tramp-wait-for-output)
+ (when (string= (tramp-get-encoding-command multi-method method)
+ "tramp_mimencode")
+ (tramp-message 5 "Sending the Perl `mime-encode' implementation.")
+ (tramp-send-linewise
+ multi-method method user host
+ (concat "tramp_mimencode () {\n"
+ (if (tramp-find-executable multi-method method user host
+ "mimencode" tramp-remote-path t)
+ "mimencode -b $1"
+ (concat tramp-remote-perl
+ " -e '" tramp-perl-mime-encode "' $1 2>/dev/null"))
+ "\n}"))
+ (tramp-wait-for-output))
+ (when (string= (tramp-get-decoding-command multi-method method)
+ "tramp_mimedecode")
+ (tramp-message 5 "Sending the Perl `mime-decode' implementation.")
+ (tramp-send-linewise
+ multi-method method user host
+ (concat "tramp_mimedecode () {\n"
+ (if (tramp-find-executable multi-method method user host
+ "mimencode" tramp-remote-path t)
+ "mimencode -u -b $1"
+ (concat tramp-remote-perl
+ " -e '" tramp-perl-mime-decode "' $1 2>/dev/null"))
+ "\n}"))
+ (tramp-wait-for-output)))))
+ ;; Find ln(1)
+ (erase-buffer)
+ (let ((ln (tramp-find-executable multi-method method user host
+ "ln" tramp-remote-path nil)))
+ (when ln
+ (tramp-set-connection-property "ln" ln multi-method method user host)))
+ (erase-buffer)
+ ;; If encoding/decoding command are given, test to see if they work.
+ ;; CCC: Maybe it would be useful to run the encoder both locally and
+ ;; remotely to see if they produce the same result.
+ (let ((decoding (tramp-get-decoding-command multi-method method))
+ (encoding (tramp-get-encoding-command multi-method method))
+ (magic-string "xyzzy"))
+ (when (and (or decoding encoding) (not (and decoding encoding)))
+ (tramp-kill-process multi-method method user host)
+ (error
+ "Must give both decoding and encoding command in method definition"))
+ (when (and decoding encoding)
+ (tramp-message
+ 5
+ "Checking to see if encoding/decoding commands work on remote host...")
+ (tramp-send-command
+ multi-method method user host
+ (format "echo %s | %s | %s"
+ (tramp-shell-quote-argument magic-string) encoding decoding))
+ (tramp-wait-for-output)
+ (unless (looking-at (regexp-quote magic-string))
+ (tramp-kill-process multi-method method user host)
+ (error "Remote host cannot execute de/encoding commands. See buffer `%s' for details"
+ (buffer-name)))
+ (erase-buffer)
+ (tramp-message
+ 5 "Checking to see if encoding/decoding commands work on remote host...done"))))
+
+
+(defun tramp-maybe-open-connection (multi-method method user host)
+ "Maybe open a connection to HOST, logging in as USER, using METHOD.
+Does not do anything if a connection is already open, but re-opens the
+connection if a previous connection has died for some reason."
+ (let ((p (get-buffer-process (tramp-get-buffer multi-method method user host))))
+ (unless (and p
+ (processp p)
+ (memq (process-status p) '(run open)))
+ (when (and p (processp p))
+ (delete-process p))
+ (funcall (tramp-get-connection-function multi-method method)
+ multi-method method user host))))
+
+(defun tramp-send-command
+ (multi-method method user host command &optional noerase)
+ "Send the COMMAND to USER at HOST (logged in using METHOD).
+Erases temporary buffer before sending the command (unless NOERASE
+is true)."
+ (tramp-maybe-open-connection multi-method method user host)
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer (tramp-get-debug-buffer multi-method method user host))
+ (goto-char (point-max))
+ (tramp-insert-with-face 'bold (format "$ %s\n" command))))
+ (let ((proc nil))
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (unless noerase (erase-buffer))
+ (setq proc (get-buffer-process (current-buffer)))
+ (process-send-string proc
+ (concat command tramp-rsh-end-of-line))))
+
+;; It seems that Tru64 Unix does not like it if long strings are sent
+;; to it in one go. (This happens when sending the Perl
+;; `file-attributes' implementation, for instance.) Therefore, we
+;; have this function which waits a bit at each line.
+(defun tramp-send-linewise
+ (multi-method method user host string &optional noerase)
+ "Send the STRING to USER at HOST linewise.
+Erases temporary buffer before sending the STRING (unless NOERASE
+is true).
+
+The STRING is expected to use Unix line-endings, but the lines sent to
+the remote host use line-endings as defined in the variable
+`tramp-rsh-end-of-line'."
+ (tramp-maybe-open-connection multi-method method user host)
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer (tramp-get-debug-buffer multi-method method user host))
+ (goto-char (point-max))
+ (tramp-insert-with-face 'bold (format "$ %s\n" string))))
+ (let ((proc nil)
+ (lines (split-string string "\n")))
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ (unless noerase (erase-buffer))
+ (setq proc (get-buffer-process (current-buffer)))
+ (mapcar (lambda (x)
+ (sleep-for 0.1)
+ (process-send-string proc
+ (concat x tramp-rsh-end-of-line)))
+ lines)))
+
+(defun tramp-wait-for-output (&optional timeout)
+ "Wait for output from remote rsh command."
+ (let ((proc (get-buffer-process (current-buffer)))
+ (found nil)
+ (start-time (current-time))
+ (end-of-output (concat "^"
+ (regexp-quote tramp-end-of-output)
+ "$")))
+ ;; Algorithm: get waiting output. See if last line contains
+ ;; end-of-output sentinel. If not, wait a bit and again get
+ ;; waiting output. Repeat until timeout expires or end-of-output
+ ;; sentinel is seen. Will hang if timeout is nil and
+ ;; end-of-output sentinel never appears.
+ (save-match-data
+ (cond (timeout
+ ;; Work around an XEmacs bug, where the timeout expires
+ ;; faster than it should. This degenerates into polling
+ ;; for buggy XEmacsen, but oh, well.
+ (while (and (not found)
+ (< (tramp-time-diff (current-time) start-time)
+ timeout))
+ (with-timeout (timeout)
+ (while (not found)
+ (accept-process-output proc 1)
+ (goto-char (point-max))
+ (forward-line -1)
+ (setq found (looking-at end-of-output))))))
+ (t
+ (while (not found)
+ (accept-process-output proc 1)
+ (goto-char (point-max))
+ (forward-line -1)
+ (setq found (looking-at end-of-output))))))
+ ;; At this point, either the timeout has expired or we have found
+ ;; the end-of-output sentinel.
+ (when found
+ (goto-char (point-max))
+ (forward-line -2)
+ (delete-region (point) (point-max)))
+ ;; Add output to debug buffer if appropriate.
+ (when tramp-debug-buffer
+ (append-to-buffer
+ (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
+ tramp-current-user tramp-current-host)
+ (point-min) (point-max))
+ (when (not found)
+ (save-excursion
+ (set-buffer
+ (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
+ tramp-current-user tramp-current-host))
+ (goto-char (point-max))
+ (insert "[[Remote prompt `" end-of-output "' not found"
+ (if timeout (concat " in " timeout " secs") "")
+ "]]"))))
+ (goto-char (point-min))
+ ;; Return value is whether end-of-output sentinel was found.
+ found))
+
+(defun tramp-match-string-list (&optional string)
+ "Returns list of all match strings.
+That is, (list (match-string 0) (match-string 1) ...), according to the
+number of matches."
+ (let* ((nmatches (/ (length (match-data)) 2))
+ (i (- nmatches 1))
+ (res nil))
+ (while (>= i 0)
+ (setq res (cons (match-string i string) res))
+ (setq i (- i 1)))
+ res))
+
+(defun tramp-send-command-and-check (multi-method method user host command
+ &optional subshell)
+ "Run COMMAND and check its exit status.
+MULTI-METHOD and METHOD specify how to log in (as USER) to the remote HOST.
+Sends `echo $?' along with the COMMAND for checking the exit status. If
+COMMAND is nil, just sends `echo $?'. Returns the exit status found.
+
+If the optional argument SUBSHELL is non-nil, the command is executed in
+a subshell, ie surrounded by parentheses."
+ (tramp-send-command multi-method method user host
+ (concat (if subshell "( " "")
+ command
+ (if command " 2>/dev/null; " "")
+ "echo tramp_exit_status $?"
+ (if subshell " )" " ")))
+ (tramp-wait-for-output)
+ (goto-char (point-max))
+ (unless (search-backward "tramp_exit_status " nil t)
+ (error "Couldn't find exit status of `%s'" command))
+ (skip-chars-forward "^ ")
+ (read (current-buffer)))
+
+(defun tramp-barf-unless-okay (multi-method method user host command subshell
+ signal fmt &rest args)
+ "Run COMMAND, check exit status, throw error if exit status not okay.
+Similar to `tramp-send-command-and-check' but accepts two more arguments
+FMT and ARGS which are passed to `error'."
+ (unless (zerop (tramp-send-command-and-check
+ multi-method method user host command subshell))
+ ;; CCC: really pop-to-buffer? Maybe it's appropriate to be more
+ ;; silent.
+ (pop-to-buffer (current-buffer))
+ (funcall 'signal signal (apply 'format fmt args))))
+
+(defun tramp-send-region (multi-method method user host start end)
+ "Send the region from START to END to remote command
+running as USER on HOST using METHOD."
+ (let ((proc (get-buffer-process
+ (tramp-get-buffer multi-method method user host))))
+ (unless proc
+ (error "Can't send region to remote host -- not logged in"))
+ (process-send-region proc start end)
+ (when tramp-debug-buffer
+ (append-to-buffer
+ (tramp-get-debug-buffer multi-method method user host)
+ start end))))
+
+(defun tramp-send-eof (multi-method method user host)
+ "Send EOF to the remote end.
+METHOD, HOST and USER specify the the connection."
+ (let ((proc (get-buffer-process
+ (tramp-get-buffer multi-method method user host))))
+ (unless proc
+ (error "Can't send EOF to remote host -- not logged in"))
+ (process-send-eof proc)))
+; (process-send-string proc "\^D")))
+
+(defun tramp-kill-process (multi-method method user host)
+ "Kill the connection process used by Tramp.
+MULTI-METHOD, METHOD, USER, and HOST, specify the connection."
+ (let ((proc (get-buffer-process
+ (tramp-get-buffer multi-method method user host))))
+ (kill-process proc)))
+
+(defun tramp-discard-garbage-erase-buffer (p multi-method method user host)
+ "Erase buffer, then discard subsequent garbage.
+If `tramp-discard-garbage' is nil, just erase buffer."
+ (if (not tramp-discard-garbage)
+ (erase-buffer)
+ (while (prog1 (erase-buffer) (accept-process-output p 0.25))
+ (when tramp-debug-buffer
+ (save-excursion
+ (set-buffer (tramp-get-debug-buffer multi-method method user host))
+ (goto-char (point-max))
+ (tramp-insert-with-face
+ 'bold (format "Additional characters detected\n")))))))
+
+(defun tramp-mode-string-to-int (mode-string)
+ "Converts a ten-letter `drwxrwxrwx'-style mode string into mode bits."
+ (let* ((mode-chars (string-to-vector mode-string))
+ (owner-read (aref mode-chars 1))
+ (owner-write (aref mode-chars 2))
+ (owner-execute-or-setid (aref mode-chars 3))
+ (group-read (aref mode-chars 4))
+ (group-write (aref mode-chars 5))
+ (group-execute-or-setid (aref mode-chars 6))
+ (other-read (aref mode-chars 7))
+ (other-write (aref mode-chars 8))
+ (other-execute-or-sticky (aref mode-chars 9)))
+ (save-match-data
+ (logior
+ (case owner-read
+ (?r (tramp-octal-to-decimal "00400")) (?- 0)
+ (t (error "Second char `%c' must be one of `r-'" owner-read)))
+ (case owner-write
+ (?w (tramp-octal-to-decimal "00200")) (?- 0)
+ (t (error "Third char `%c' must be one of `w-'" owner-write)))
+ (case owner-execute-or-setid
+ (?x (tramp-octal-to-decimal "00100"))
+ (?S (tramp-octal-to-decimal "04000"))
+ (?s (tramp-octal-to-decimal "04100"))
+ (?- 0)
+ (t (error "Fourth char `%c' must be one of `xsS-'"
+ owner-execute-or-setid)))
+ (case group-read
+ (?r (tramp-octal-to-decimal "00040")) (?- 0)
+ (t (error "Fifth char `%c' must be one of `r-'" group-read)))
+ (case group-write
+ (?w (tramp-octal-to-decimal "00020")) (?- 0)
+ (t (error "Sixth char `%c' must be one of `w-'" group-write)))
+ (case group-execute-or-setid
+ (?x (tramp-octal-to-decimal "00010"))
+ (?S (tramp-octal-to-decimal "02000"))
+ (?s (tramp-octal-to-decimal "02010"))
+ (?- 0)
+ (t (error "Seventh char `%c' must be one of `xsS-'"
+ group-execute-or-setid)))
+ (case other-read
+ (?r (tramp-octal-to-decimal "00004")) (?- 0)
+ (t (error "Eighth char `%c' must be one of `r-'" other-read)))
+ (case other-write
+ (?w (tramp-octal-to-decimal "00002")) (?- 0)
+ (t (error "Nineth char `%c' must be one of `w-'" other-write)))
+ (case other-execute-or-sticky
+ (?x (tramp-octal-to-decimal "00001"))
+ (?T (tramp-octal-to-decimal "01000"))
+ (?t (tramp-octal-to-decimal "01001"))
+ (?- 0)
+ (t (error "Tenth char `%c' must be one of `xtT-'"
+ other-execute-or-sticky)))))))
+
+
+(defun tramp-file-mode-from-int (mode)
+ "Turn an integer representing a file mode into an ls(1)-like string."
+ (let ((type (cdr (assoc (logand (lsh mode -12) 15) tramp-file-mode-type-map)))
+ (user (logand (lsh mode -6) 7))
+ (group (logand (lsh mode -3) 7))
+ (other (logand (lsh mode -0) 7))
+ (suid (> (logand (lsh mode -9) 4) 0))
+ (sgid (> (logand (lsh mode -9) 2) 0))
+ (sticky (> (logand (lsh mode -9) 1) 0)))
+ (setq user (tramp-file-mode-permissions user suid "s"))
+ (setq group (tramp-file-mode-permissions group sgid "s"))
+ (setq other (tramp-file-mode-permissions other sticky "t"))
+ (concat type user group other)))
+
+
+(defun tramp-file-mode-permissions (perm suid suid-text)
+ "Convert a permission bitset into a string.
+This is used internally by `tramp-file-mode-from-int'."
+ (let ((r (> (logand perm 4) 0))
+ (w (> (logand perm 2) 0))
+ (x (> (logand perm 1) 0)))
+ (concat (or (and r "r") "-")
+ (or (and w "w") "-")
+ (or (and suid x suid-text) ; suid, execute
+ (and suid (upcase suid-text)) ; suid, !execute
+ (and x "x") "-")))) ; !suid
+
+
+(defun tramp-decimal-to-octal (i)
+ "Return a string consisting of the octal digits of I.
+Not actually used. Use `(format \"%o\" i)' instead?"
+ (cond ((< i 0) (error "Cannot convert negative number to octal"))
+ ((not (integerp i)) (error "Cannot convert non-integer to octal"))
+ ((zerop i) "0")
+ (t (concat (tramp-decimal-to-octal (/ i 8))
+ (number-to-string (% i 8))))))
+
+
+;;(defun tramp-octal-to-decimal (ostr)
+;; "Given a string of octal digits, return a decimal number."
+;; (cond ((null ostr) 0)
+;; ((string= "" ostr) 0)
+;; (t (let ((last (aref ostr (1- (length ostr))))
+;; (rest (substring ostr 0 (1- (length ostr)))))
+;; (unless (and (>= last ?0)
+;; (<= last ?7))
+;; (error "Not an octal digit: %c" last))
+;; (+ (- last ?0) (* 8 (tramp-octal-to-decimal rest)))))))
+;; Kudos to Gerd Moellmann for this suggestion.
+(defun tramp-octal-to-decimal (ostr)
+ "Given a string of octal digits, return a decimal number."
+ (let ((x (or ostr "")))
+ ;; `save-match' is in `tramp-mode-string-to-int' which calls this.
+ (unless (string-match "\\`[0-7]*\\'" x)
+ (error "Non-octal junk in string `%s'" x))
+ (string-to-number ostr 8)))
+
+(defun tramp-shell-case-fold (string)
+ "Converts STRING to shell glob pattern which ignores case."
+ (mapconcat
+ (lambda (c)
+ (if (equal (downcase c) (upcase c))
+ (vector c)
+ (format "[%c%c]" (downcase c) (upcase c))))
+ string
+ ""))
+
+
+;; ------------------------------------------------------------
+;; -- TRAMP file names --
+;; ------------------------------------------------------------
+;; Conversion functions between external representation and
+;; internal data structure. Convenience functions for internal
+;; data structure.
+
+(defstruct tramp-file-name multi-method method user host path)
+
+(defun tramp-tramp-file-p (name)
+ "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.
+(defun tramp-dissect-file-name (name)
+ "Return an `tramp-file-name' structure.
+The structure consists of remote method, remote user, remote host and
+remote path name."
+ (let (method)
+ (save-match-data
+ (unless (string-match (nth 0 tramp-file-name-structure) name)
+ (error "Not a tramp file name: %s" name))
+ (setq method (or (match-string (nth 1 tramp-file-name-structure) name)
+ tramp-default-method))
+ (if (member method tramp-multi-methods)
+ ;; If it's a multi method, the file name structure contains
+ ;; arrays of method, user and host.
+ (tramp-dissect-multi-file-name name)
+ ;; Normal method.
+ (make-tramp-file-name
+ :multi-method nil
+ :method method
+ :user (or (match-string (nth 2 tramp-file-name-structure) name)
+ nil)
+ :host (match-string (nth 3 tramp-file-name-structure) name)
+ :path (match-string (nth 4 tramp-file-name-structure) name))))))
+
+;; 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)
+ "Not implemented yet."
+ (let ((regexp (nth 0 tramp-multi-file-name-structure))
+ (method-index (nth 1 tramp-multi-file-name-structure))
+ (hops-index (nth 2 tramp-multi-file-name-structure))
+ (path-index (nth 3 tramp-multi-file-name-structure))
+ (hop-regexp (nth 0 tramp-multi-file-name-hop-structure))
+ (hop-method-index (nth 1 tramp-multi-file-name-hop-structure))
+ (hop-user-index (nth 2 tramp-multi-file-name-hop-structure))
+ (hop-host-index (nth 3 tramp-multi-file-name-hop-structure))
+ method hops len hop-methods hop-users hop-hosts path)
+ (unless (string-match (format regexp hop-regexp) name)
+ (error "Not a multi tramp file name: %s" name))
+ (setq method (match-string method-index name))
+ (setq hops (match-string hops-index name))
+ (setq len (/ (length (match-data t)) 2))
+ (when (< path-index 0) (incf path-index len))
+ (setq path (match-string path-index name))
+ (let ((index 0))
+ (while (string-match hop-regexp hops index)
+ (setq index (match-end 0))
+ (setq hop-methods
+ (cons (match-string hop-method-index hops) hop-methods))
+ (setq hop-users
+ (cons (match-string hop-user-index hops) hop-users))
+ (setq hop-hosts
+ (cons (match-string hop-host-index hops) hop-hosts))))
+ (make-tramp-file-name
+ :multi-method method
+ :method (apply 'vector (reverse hop-methods))
+ :user (apply 'vector (reverse hop-users))
+ :host (apply 'vector (reverse hop-hosts))
+ :path path)))
+
+(defun tramp-make-tramp-file-name (multi-method method user host path)
+ "Constructs a tramp file name from METHOD, USER, HOST and PATH."
+ (unless tramp-make-tramp-file-format
+ (error "`tramp-make-tramp-file-format' is nil"))
+ (if multi-method
+ (tramp-make-tramp-multi-file-name multi-method method user host path)
+ (if user
+ (format-spec tramp-make-tramp-file-format
+ (list (cons ?m method)
+ (cons ?u user)
+ (cons ?h host)
+ (cons ?p path)))
+ (format-spec tramp-make-tramp-file-user-nil-format
+ (list (cons ?m method)
+ (cons ?h host)
+ (cons ?p path))))))
+
+;; CCC: Henrik Holm: Not Changed. Multi Method. What should be done
+;; with this when USER is nil?
+(defun tramp-make-tramp-multi-file-name (multi-method method user host path)
+ "Constructs a tramp file name for a multi-hop method."
+ (unless tramp-make-multi-tramp-file-format
+ (error "`tramp-make-multi-tramp-file-format' is nil"))
+ (let* ((prefix-format (nth 0 tramp-make-multi-tramp-file-format))
+ (hop-format (nth 1 tramp-make-multi-tramp-file-format))
+ (path-format (nth 2 tramp-make-multi-tramp-file-format))
+ (prefix (format-spec prefix-format (list (cons ?m multi-method))))
+ (hops "")
+ (path (format-spec path-format (list (cons ?p path))))
+ (i 0)
+ (len (length method)))
+ (while (< i len)
+ (let ((m (aref method i))
+ (u (aref user i))
+ (h (aref host i)))
+ (setq hops (concat hops
+ (format-spec
+ hop-format
+ (list (cons ?m m)
+ (cons ?u u)
+ (cons ?h h)))))
+ (incf i)))
+ (concat prefix hops path)))
+
+;; HHH: Changed. Handles the case where no user name is given in the
+;; file name.
+(defun tramp-make-rcp-program-file-name (user host path)
+ "Create a file name suitable to be passed to `rcp'."
+ (if user
+ (format "%s@%s:%s" user host path)
+ (format "%s:%s" host path)))
+
+(defun tramp-method-out-of-band-p (multi-method method)
+ "Return t if this is an out-of-band method, nil otherwise.
+It is important to check for this condition, since it is not possible
+to enter a password for the `tramp-rcp-program'."
+ (tramp-get-rcp-program multi-method method))
+
+;; Variables local to connection.
+
+(defun tramp-get-ls-command (multi-method method user host)
+ (save-excursion
+ (tramp-maybe-open-connection multi-method method user host)
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ tramp-ls-command))
+
+(defun tramp-get-test-groks-nt (multi-method method user host)
+ (save-excursion
+ (tramp-maybe-open-connection multi-method method user host)
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ tramp-test-groks-nt))
+
+(defun tramp-get-file-exists-command (multi-method method user host)
+ (save-excursion
+ (tramp-maybe-open-connection multi-method method user host)
+ (set-buffer (tramp-get-buffer multi-method method user host))
+ tramp-file-exists-command))
+
+(defun tramp-get-remote-perl (multi-method method user host)
+ (tramp-get-connection-property "perl" nil multi-method method user host))
+
+(defun tramp-get-remote-ln (multi-method method user host)
+ (tramp-get-connection-property "ln" 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)
+ "Get the named property for the connection.
+If the value is not set for the connection, return `default'"
+ (tramp-maybe-open-connection multi-method method user host)
+ (with-current-buffer (tramp-get-buffer multi-method method user host)
+ (let (error)
+ (condition-case nil
+ (symbol-value (intern (concat "tramp-connection-property-" property)))
+ (error default)))))
+
+;; Set a property of a TRAMP connection.
+(defun tramp-set-connection-property (property value multi-method method user host)
+ "Set the named property of a TRAMP connection."
+ (tramp-maybe-open-connection multi-method method user host)
+ (with-current-buffer (tramp-get-buffer multi-method method user host)
+ (set (make-local-variable
+ (intern (concat "tramp-connection-property-" property)))
+ value)))
+
+
+
+(defun tramp-get-connection-function (multi-method method)
+ (second (or (assoc 'tramp-connection-function
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a connection function"
+ (or multi-method method)))))
+
+(defun tramp-get-remote-sh (multi-method method)
+ (second (or (assoc 'tramp-remote-sh
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a remote shell"
+ (or multi-method method)))))
+
+(defun tramp-get-rsh-program (multi-method method)
+ (second (or (assoc 'tramp-rsh-program
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify an rsh program"
+ (or multi-method method)))))
+
+(defun tramp-get-rsh-args (multi-method method)
+ (second (or (assoc 'tramp-rsh-args
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify rsh args"
+ (or multi-method method)))))
+
+(defun tramp-get-rcp-program (multi-method method)
+ (second (or (assoc 'tramp-rcp-program
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify an rcp program"
+ (or multi-method method)))))
+
+(defun tramp-get-rcp-args (multi-method method)
+ (second (or (assoc 'tramp-rcp-args
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify rcp args"
+ (or multi-method method)))))
+
+(defun tramp-get-rcp-keep-date-arg (multi-method method)
+ (second (or (assoc 'tramp-rcp-keep-date-arg
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify `keep-date' arg for tramp"
+ (or multi-method method)))))
+
+(defun tramp-get-su-program (multi-method method)
+ (second (or (assoc 'tramp-su-program
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a su program"
+ (or multi-method method)))))
+
+(defun tramp-get-su-args (multi-method method)
+ (second (or (assoc 'tramp-su-args
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify su args"
+ (or multi-method method)))))
+
+(defun tramp-get-encoding-command (multi-method method)
+ (second (or (assoc 'tramp-encoding-command
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify an encoding command"
+ (or multi-method method)))))
+
+(defun tramp-get-decoding-command (multi-method method)
+ (second (or (assoc 'tramp-decoding-command
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a decoding command"
+ (or multi-method method)))))
+
+(defun tramp-get-encoding-function (multi-method method)
+ (second (or (assoc 'tramp-encoding-function
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify an encoding function"
+ (or multi-method method)))))
+
+(defun tramp-get-decoding-function (multi-method method)
+ (second (or (assoc 'tramp-decoding-function
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a decoding function"
+ (or multi-method method)))))
+
+(defun tramp-get-telnet-program (multi-method method)
+ (second (or (assoc 'tramp-telnet-program
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify a telnet program"
+ (or multi-method method)))))
+
+(defun tramp-get-telnet-args (multi-method method)
+ (second (or (assoc 'tramp-telnet-args
+ (assoc (or multi-method method tramp-default-method)
+ tramp-methods))
+ (error "Method `%s' didn't specify telnet args"
+ (or multi-method method)))))
+
+;; 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-subst-strs-in-string (alist string)
+ "Replace all occurrences of the string FROM with TO in STRING.
+ALIST is of the form ((FROM . TO) ...)."
+ (save-match-data
+ (while alist
+ (let* ((pr (car alist))
+ (from (car pr))
+ (to (cdr pr)))
+ (while (string-match (regexp-quote from) string)
+ (setq string (replace-match to t t string)))
+ (setq alist (cdr alist))))
+ string))
+
+(defun tramp-insert-with-face (face string)
+ "Insert text with a specific face."
+ (let ((start (point)))
+ (insert string)
+ (add-text-properties start (point) (list 'face face))))
+
+;; ------------------------------------------------------------
+;; -- Compatibility functions section --
+;; ------------------------------------------------------------
+
+(defun tramp-temporary-file-directory ()
+ "Return name of directory for temporary files (compat function).
+For Emacs, this is the variable `temporary-file-directory', for XEmacs
+this is the function `temp-directory'."
+ (cond ((boundp 'temporary-file-directory)
+ (symbol-value 'temporary-file-directory))
+ ((fboundp 'temp-directory)
+ (funcall (symbol-function 'temp-directory))) ;pacify byte-compiler
+ ((let ((d (getenv "TEMP"))) (and d (file-directory-p d)))
+ (file-name-as-directory (getenv "TEMP")))
+ ((let ((d (getenv "TMP"))) (and d (file-directory-p d)))
+ (file-name-as-directory (getenv "TMP")))
+ ((let ((d (getenv "TMPDIR"))) (and d (file-directory-p d)))
+ (file-name-as-directory (getenv "TMPDIR")))
+ ((file-exists-p "c:/temp") (file-name-as-directory "c:/temp"))
+ (t (message (concat "Neither `temporary-file-directory' nor "
+ "`temp-directory' is defined -- using /tmp."))
+ (file-name-as-directory "/tmp"))))
+
+(defun tramp-read-passwd (prompt)
+ "Read a password from user (compat function).
+Invokes `read-passwd' if that is defined, else `ange-ftp-read-passwd'."
+ (apply
+ (if (fboundp 'read-passwd) #'read-passwd #'ange-ftp-read-passwd)
+ (list prompt)))
+
+(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."
+ ;; Pacify byte-compiler with `symbol-function'.
+ (cond ((fboundp 'subtract-time)
+ (cadr (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))))
+ (t
+ ;; snarfed from Emacs 21 time-date.el
+ (cadr (let ((borrow (< (cadr t1) (cadr t2))))
+ (list (- (car t1) (car t2) (if borrow 1 0))
+ (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))))))
+
+(defun tramp-coding-system-change-eol-conversion (coding-system eol-type)
+ "Return a coding system like CODING-SYSTEM but with given EOL-TYPE.
+EOL-TYPE can be one of `dos', `unix', or `mac'."
+ (cond ((fboundp 'coding-system-change-eol-conversion)
+ (apply #'coding-system-change-eol-conversion
+ (list coding-system eol-type)))
+ ((fboundp 'subsidiary-coding-system)
+ (apply
+ #'subsidiary-coding-system
+ (list coding-system
+ (cond ((eq eol-type 'dos) 'crlf)
+ ((eq eol-type 'unix) 'lf)
+ ((eq eol-type 'mac) 'cr)
+ (t
+ (error "Unknown EOL-TYPE `%s', must be %s"
+ eol-type
+ "`dos', `unix', or `mac'"))))))
+ (t (error "Can't change EOL conversion -- is MULE missing?"))))
+
+(defun tramp-split-string (string pattern)
+ "Like `split-string' but omit empty strings.
+In Emacs, (split-string \"/foo/bar\" \"/\") returns (\"foo\" \"bar\").
+This is, the first, empty, element is omitted. In XEmacs, the first
+element is not omitted.
+
+Note: this function has been written for `tramp-handle-file-truename'.
+If you want to use it for something else, you'll have to check whether
+it does the right thing."
+ (delete "" (split-string string pattern)))
+
+;; ------------------------------------------------------------
+;; -- Kludges section --
+;; ------------------------------------------------------------
+
+;; Currently (as of Emacs 20.5), the function `shell-quote-argument'
+;; does not deal well with newline characters. Newline is replaced by
+;; backslash newline. But if, say, the string `a backslash newline b'
+;; is passed to a shell, the shell will expand this into "ab",
+;; completely omitting the newline. This is not what was intended.
+;; It does not appear to be possible to make the function
+;; `shell-quote-argument' work with newlines without making it
+;; dependent on the shell used. But within this package, we know that
+;; we will always use a Bourne-like shell, so we use an approach which
+;; groks newlines.
+;;
+;; The approach is simple: we call `shell-quote-argument', then
+;; massage the newline part of the result.
+;;
+;; This function should produce a string which is grokked by a Unix
+;; shell, even if the Emacs is running on Windows. Since this is the
+;; kludges section, we bind `system-type' in such a way that
+;; `shell-quote-arguments' behaves as if on Unix.
+;;
+;; Thanks to Mario DeWeerd for the hint that it is sufficient for this
+;; function to work with Bourne-like shells.
+;;
+;; CCC: This function should be rewritten so that
+;; `shell-quote-argument' is not used. This way, we are safe from
+;; changes in `shell-quote-argument'.
+(defun tramp-shell-quote-argument (s)
+ "Similar to `shell-quote-argument', but groks newlines.
+Only works for Bourne-like shells."
+ (let ((system-type 'not-windows))
+ (save-match-data
+ (let ((result (shell-quote-argument s))
+ (nl (regexp-quote (format "\\%s" tramp-rsh-end-of-line))))
+ (when (and (>= (length result) 2)
+ (string= (substring result 0 2) "\\~"))
+ (setq result (substring result 1)))
+ (while (string-match nl result)
+ (setq result (replace-match (format "'%s'" tramp-rsh-end-of-line)
+ t t result)))
+ result))))
+
+;; ;; EFS hooks itself into the file name handling stuff in more places
+;; ;; than just `file-name-handler-alist'. The following tells EFS to stay
+;; ;; away from tramp.el paths.
+;; ;;
+;; ;; This is needed because EFS installs (efs-dired-before-readin) into
+;; ;; 'dired-before-readin-hook'. This prevents EFS from opening an FTP
+;; ;; connection to help it's dired process. Not that I have any real
+;; ;; idea *why* this is helpful to dired.
+;; ;;
+;; ;; Anyway, this advice fixes the problem (with a sledgehammer :)
+;; ;;
+;; ;; Daniel Pittman <daniel@danann.net>
+;; ;;
+;; ;; CCC: when the other defadvice calls have disappeared, make sure
+;; ;; not to call defadvice unless it's necessary. How do we find out whether
+;; ;; it is necessary? (featurep 'efs) is surely the wrong way --
+;; ;; EFS might nicht be loaded yet.
+;; (defadvice efs-ftp-path (around dont-match-tramp-path activate protect)
+;; "Cause efs-ftp-path to fail when the path is a TRAMP path."
+;; (if (tramp-tramp-file-p (ad-get-arg 0))
+;; nil
+;; ad-do-it))
+
+;; We currently use "[" and "]" in the filename format. In Emacs
+;; 20.x, this means that Emacs wants to expand wildcards if
+;; `find-file-wildcards' is non-nil, and then barfs because no
+;; expansion could be found. We detect this situation and do
+;; something really awful: we have `file-expand-wildcards' return the
+;; original filename if it can't expand anything. Let's just hope
+;; that this doesn't break anything else.
+;;
+;; Another problem is that the check is done by Emacs version, which
+;; is really not what we want to do. Oh, well.
+
+;;(when (and (not (featurep 'xemacs))
+;; (= emacs-major-version 20))
+;; It seems that this advice is needed in Emacs 21, too.
+(defadvice file-expand-wildcards (around tramp-fix activate)
+ (let ((name (ad-get-arg 0)))
+ (if (tramp-tramp-file-p name)
+ ;; If it's a Tramp file, dissect it and look if wildcards
+ ;; need to be expanded at all.
+ (let ((v (tramp-dissect-file-name name)))
+ (if (string-match "[[*?]" (tramp-file-name-path v))
+ (let ((res ad-do-it))
+ (setq ad-return-value (or res (list name))))
+ (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)))))))
+;; )
+
+;; Make the `reporter` functionality available for making bug reports about
+;; the package. A most useful piece of code.
+
+(unless (fboundp 'reporter-submit-bug-report)
+ (autoload 'reporter-submit-bug-report "reporter"))
+
+(defun tramp-bug ()
+ "Submit a bug report to the TRAMP developers."
+ (interactive)
+ (require 'reporter)
+ (let ((reporter-prompt-for-summary-p t))
+ (reporter-submit-bug-report
+ tramp-bug-report-address ; to-address
+ (format "tramp (%s)" tramp-version) ; package name and version
+ `(;; Current state
+ tramp-ls-command
+ tramp-test-groks-nt
+ tramp-file-exists-command
+ tramp-current-multi-method
+ tramp-current-method
+ tramp-current-user
+ tramp-current-host
+
+ ;; System defaults
+ tramp-auto-save-directory ; vars to dump
+ tramp-default-method
+ tramp-rsh-end-of-line
+ tramp-remote-path
+ tramp-login-prompt-regexp
+ tramp-password-prompt-regexp
+ tramp-wrong-passwd-regexp
+ tramp-temp-name-prefix
+ tramp-file-name-structure
+ tramp-file-name-regexp
+ tramp-multi-file-name-structure
+ tramp-multi-file-name-hop-structure
+ tramp-multi-methods
+ tramp-multi-connection-function-alist
+ tramp-make-tramp-file-format
+ tramp-end-of-output
+
+ ;; Non-tramp variables of interest
+ shell-prompt-pattern
+ backup-by-copying
+ backup-by-copying-when-linked
+ backup-by-copying-when-mismatch
+ ,(when (boundp 'backup-by-copying-when-privileged-mismatch)
+ 'backup-by-copying-when-privileged-mismatch)
+ file-name-handler-alist)
+ nil ; pre-hook
+ nil ; post-hook
+ "\
+Enter your bug report in this message, including as much detail as you
+possibly can about the problem, what you did to cause it and what the
+local and remote machines are.
+
+If you can give a simple set of instructions to make this bug happen
+reliably, please include those. Thank you for helping kill bugs in
+TRAMP.
+--bug report follows this line--")))
+
+(defalias 'tramp-submit-bug 'tramp-bug)
+
+(provide 'tramp)
+
+;; Make sure that we get integration with the VC package.
+;; When it is loaded, we need to pull in the integration module.
+;; This must come after (provide 'tramp) because tramp-vc.el
+;; requires tramp.
+(eval-after-load "vc"
+ '(require 'tramp-vc))
+
+;;; TODO:
+
+;; * Cooperate with PCL-CVS. It uses start-process, which doesn't
+;; work for remote files.
+;; * Allow /[method/user@host:port] syntax for the ssh "-p" argument.
+;; * Rewrite `tramp-shell-quote-argument' to abstain from using
+;; `shell-quote-argument'.
+;; * Completion gets confused when you leave out the method name.
+;; * Support `dired-compress-file' filename handler.
+;; * In Emacs 21, `insert-directory' shows total number of bytes used
+;; by the files in that directory. Add this here.
+;; * Avoid screen blanking when hitting `g' in dired. (Eli Tziperman)
+;; * Make ffap.el grok Tramp filenames. (Eli Tziperman)
+;; * When logging in, keep looking for questions according to an alist
+;; and then invoke the right function.
+;; * Case-insensitive filename completion. (Norbert Goevert.)
+;; * Running CVS remotely doesn't appear to work right. It thinks
+;; files are locked by somebody else even if I'm the locking user.
+;; Sometimes, one gets `No CVSROOT specified' errors from CVS.
+;; (Skip Montanaro)
+;; * Don't use globbing for directories with many files, as this is
+;; likely to produce long command lines, and some shells choke on
+;; long command lines.
+;; * Implement `load' operation.
+;; * Find out about the new auto-save mechanism in Emacs 21 and
+;; do the right thing.
+;; * `vc-directory' does not work. It never displays any files, even
+;; if it does show files when run locally.
+;; * Allow correction of passwords, if the remote end allows this.
+;; (Mark Hershberger)
+;; * Make sure permissions of tmp file are good.
+;; (Nelson Minar <nelson@media.mit.edu>)
+;; * Grok passwd prompts with scp? (David Winter
+;; <winter@nevis1.nevis.columbia.edu>). Maybe just do `ssh -l user
+;; host', then wait a while for the passwd or passphrase prompt. If
+;; there is one, remember the passwd/phrase.
+;; * How to deal with MULE in `insert-file-contents' and `write-region'?
+;; * Do asynchronous `shell-command's.
+;; * Grok `append' parameter for `write-region'.
+;; * Test remote ksh or bash for tilde expansion in `tramp-find-shell'?
+;; * abbreviate-file-name
+;; * grok ~ in tramp-remote-path (Henrik Holm <henrikh@tele.ntnu.no>)
+;; * `C' in dired gives error `not tramp file name'.
+;; * Also allow to omit user names when doing multi-hop. Not sure yet
+;; what the user names should default to, though.
+;; * better error checking. At least whenever we see something
+;; 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)
+;; -- <daniel@danann.net>
+;; * Provide a local cache of old versions of remote files for the rsync
+;; transfer method to use. (Greg Stark)
+;; * Remove unneeded parameters from methods.
+;; * Invoke rsync once for copying a whole directory hierarchy.
+;; (Francesco Potortì)
+;; * Should we set PATH ourselves or should we rely on the remote end
+;; to do it?
+;; * Do the autoconf thing.
+;; * Make it work for XEmacs 20, which is missing `with-timeout'.
+;; * Allow non-Unix remote systems. (More a long-term thing.)
+;; * Make it work for different encodings, and for different file name
+;; encodings, too. (Daniel Pittman)
+;; * Change applicable functions to pass a struct tramp-file-name rather
+;; than the individual items MULTI-METHOD, METHOD, USER, HOST, PATH.
+;; * Implement asynchronous shell commands.
+;; * Clean up unused *tramp/foo* buffers after a while. (Pete Forman)
+;; * Progress reports while copying files. (Michael Kifer)
+;; * `Smart' connection method that uses inline for small and out of
+;; band for large files. (Michael Kifer)
+;; * Don't search for perl5 and perl. Instead, only search for perl and
+;; then look if it's the right version (with `perl -v').
+;; * When editing a remote CVS controlled file as a different user, VC
+;; gets confused about the file locking status. Try to find out why
+;; the workaround doesn't work.
+;; * When user is running ssh-agent, it would be useful to add the
+;; passwords typed by the user to that agent. This way, the next time
+;; round, the users don't have to type all this in again.
+;; This would be especially useful for start-process, I think.
+;; An easy way to implement start-process is to open a second shell
+;; connection which is inconvenient if the user has to reenter
+;; passwords.
+;; * Change `copy-file' to grok the case where the filename handler
+;; for the source and the target file are different. Right now,
+;; it looks at the source file and then calls that handler, if
+;; there is one. But since ange-ftp, for instance, does not know
+;; about Tramp, it does not do the right thing if the target file
+;; name is a Tramp name.
+
+;; Functions for file-name-handler-alist:
+;; diff-latest-backup-file -- in diff.el
+;; dired-compress-file
+;; dired-uncache -- this will be needed when we do insert-directory caching
+;; file-name-as-directory -- use primitive?
+;; file-name-directory -- use primitive?
+;; file-name-nondirectory -- use primitive?
+;; file-name-sans-versions -- use primitive?
+;; file-newer-than-file-p
+;; find-backup-file-name
+;; get-file-buffer -- use primitive
+;; load
+;; unhandled-file-name-directory
+;; vc-registered
+
+;;; tramp.el ends here
--- /dev/null
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename tramp.info
+@settitle TRAMP User Manual
+@setchapternewpage odd
+@c %**end of header
+
+@c This is *so* much nicer :)
+@footnotestyle end
+
+@c Version values, for easy modification
+@c NOTE: The 'UPDATED' value is updated by the 'time-stamp' function.
+@c If you change it by hand, the modifications will not stay.
+@set VERSION $Revision: 2.20 $
+@set UPDATED Friday, 14 June, 2002
+
+
+@c Entries for @command{install-info} to use
+@direntry
+* TRAMP: (tramp). Transparent Remote Access, Multiple Protocol
+ Emacs remote file access via rsh and rcp.
+@end direntry
+
+@c Macro to make formatting of the tramp program name consistent.
+@macro tramp
+@sc{tramp}
+@end macro
+
+@c Copying permissions, et al
+@ifinfo
+This file documents @tramp{}, a remote file editing package for Emacs and
+XEmacs.
+
+Copyright @copyright{} 1999, 2000 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries a copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+sections entitled ``Copying'' and ``GNU General Public License'' are
+included exactly as in the original, and provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation
+approved by the Free Software Foundation.
+@end ifinfo
+
+@tex
+
+@titlepage
+@title @tramp{} User Manual
+@subtitle Last updated @value{UPDATED}
+
+@author by Daniel Pittman
+@author based on documentation by Kai Gro@ss{}johann
+@page
+
+@vskip 0pt plus 1filll
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided also that the
+sections entitled ``Copying'' and ``GNU General Public License'' are
+included exactly as in the original, and provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation
+approved by the Free Software Foundation.
+
+@end titlepage
+@page
+
+@end tex
+
+@ifnottex
+@node Top, Copying, (dir), (dir)
+@top @tramp{} User Manual
+
+@tramp{} stands for `Transparent Remote (file) Access, Multiple
+Protocol'. This package provides remote file editing, similar to
+@cite{ange-ftp} and @cite{EFS}.
+
+The difference is that ange-ftp uses FTP to transfer files between the
+local and the remote host, whereas @tramp{} uses a combination of
+@command{rsh} and @command{rcp} or other work-alike programs, such as
+@command{ssh}/@command{scp}.
+
+This is version @value{VERSION} of the @tramp{} manual, last updated on
+@value{UPDATED}.
+
+You can find the latest version of this document on the web at
+@uref{http://www.freesoftware.fsf.org/tramp/}.
+
+@ifhtml
+This manual is also available as a @uref{tramp_ja.html, Japanese
+translation}.
+
+The latest release of @tramp{} is available for
+@uref{http://savannah.gnu.org/download/tramp/,
+download}, or you may see @ref{Obtaining @tramp{}} for more details,
+including the CVS server details.
+
+@tramp{} also has a @uref{https://savannah.gnu.org/projects/tramp/,
+Savannah Project Page}.
+@end ifhtml
+
+There is a mailing list for @tramp{}, available at
+@email{tramp-devel@@mail.freesoftware.fsf.org}, and archived at
+@uref{http://www.mail-archive.com/emacs-rcp@@ls6.cs.uni-dortmund.de/} as
+well as the usual Savannah archives.
+
+@end ifnottex
+
+@menu
+* Copying:: @tramp{} Copying conditions.
+* Overview:: What @tramp{} can and cannot do.
+
+For the end user:
+* Obtaining @tramp{}:: How to obtain @tramp{}.
+* History:: History of @tramp{}
+* Installation:: Installing @tramp{} with your (X)Emacs.
+* Configuration:: Configuring @tramp{} for use.
+* Usage:: An overview of the operation of @tramp{}.
+* Bug Reports:: Reporting Bugs and Problems
+* Frequently Asked Questions:: Questions and answers from the mailing list.
+
+For the developer:
+* Version Control:: The inner workings of remote version control.
+* Files directories and paths:: How file names, directories and paths are mangled and managed.
+* Issues::
+
+@detailmenu
+ --- The Detailed Node Listing ---
+
+Configuring @tramp{} for use
+
+* Connection types:: Types of connections made to remote machines.
+* Inline methods:: Inline methods.
+* External transfer methods:: External transfer methods.
+* Multi-hop Methods:: Connecting to a remote host using multiple hops.
+* Default Method:: Selecting a default method.
+* Customizing Methods:: Using Non-Standard Methods.
+* Remote Programs:: How @tramp{} finds and uses programs on the remote machine.
+* Remote shell setup::
+
+Using @tramp
+
+* Filename Syntax:: @tramp{} filename conventions.
+* Multi-hop filename syntax:: Multi-hop filename conventions
+* Dired:: Dired and filename completion.
+
+The inner workings of remote version control
+
+* Version Controlled Files:: Determining if a file is under version control.
+* Remote Commands:: Executing the version control commands on the remote machine.
+* Changed workfiles:: Detecting if the working file has changed.
+* Checking out files:: Bringing the workfile out of the repository.
+* Miscellaneous Version Control:: Things related to Version Control that don't fit elsewhere
+
+Things related to Version Control that don't fit elsewhere
+
+* Remote File Ownership:: How VC determines who owns a workfile.
+* Back-end Versions:: How VC determines what release your RCS is.
+
+How file names, directories and paths are mangled and managed.
+
+* Path deconstruction:: Breaking a path into its components.
+
+@end detailmenu
+@end menu
+
+@node Copying
+@chapter @tramp{} Copying conditions
+
+Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+
+tramp.el is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+tramp.el is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with GNU Emacs; see the file COPYING. If not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA.
+
+
+@node Overview
+@chapter An overview of @tramp
+
+After the installation of @tramp{} into your Emacs, you will be able to
+access files on remote machines as though they were local. Access to the
+remote file system for editing files, version control, and
+@command{dired} are transparently enabled.
+
+Your access to the remote machine can be with the @command{rsh},
+@command{rlogin}, @command{telnet} programs or with any similar
+connection method. This connection must pass ASCII successfully to be
+usable but need not be 8-bit clean.
+
+The package provides support for @command{ssh} connections out of the
+box, one of the more common uses of the package. This allows relatively
+secure access to machines, especially if @command{ftp} access is
+disabled.
+
+The majority of activity carried out by @tramp{} requires only that the
+remote login is possible and is carried out at the terminal. In order to
+access remote files @tramp{} needs to transfer their content to the local
+machine temporarily.
+
+@tramp{} can transfer files between the machines in a variety of ways. The
+details are easy to select, depending on your needs and the machines in
+question.
+
+The fastest transfer methods rely on a remote file transfer package such
+as @command{rcp}, @command{scp} or @command{rsync}. The use of these
+methods is only possible if the file copy command does not ask for a
+password for the remote machine.
+
+If the remote copy methods are not suitable for you, @tramp{} also
+supports the use of encoded transfers directly through the shell. This
+requires that the @command{mimencode} or @command{uuencode} tools are
+available on the remote machine.
+
+Within these limitations, @tramp{} is quite powerful. It is worth noting
+that, as of the time of writing, it is far from a polished end-user
+product. For a while yet you should expect to run into rough edges and
+problems with the code now and then.
+
+It is finished enough that the developers use it for day to day work but
+the installation and setup can be a little difficult to master, as can
+the terminology.
+
+@tramp{} is still under active development and any problems you encounter,
+trivial or major, should be reported to the @tramp{} developers.
+@xref{Bug Reports}.
+
+
+@subsubheading Behind the scenes
+
+This section tries to explain what goes on behind the scenes when you
+access a remote file through @tramp{}.
+
+Suppose you type @kbd{C-x C-f} and enter part of an @tramp{} file name,
+then hit @kbd{@key{TAB}} for completion. Suppose further that this is
+the first time that @tramp{} is invoked for the host in question. Here's
+what happens:
+
+@itemize
+@item
+@tramp{} discovers that it needs a connection to the host. So it invokes
+@command{telnet HOST} or @command{rsh HOST -l USER} or a similar tool to
+connect to the remote host. Communication with this process happens
+through an Emacs buffer, that is, the output from the remote end goes
+into a buffer.
+
+@item
+The remote host may prompt for a login name (for @command{telnet}). The
+login name is given in the file name, so @tramp{} sends the login name and
+a newline.
+
+@item
+The remote host may prompt for a password or pass phrase (for
+@command{rsh} or for @command{telnet} after sending the login name).
+@tramp{} displays the prompt in the minibuffer, asking you for the
+password or pass phrase.
+
+You enter the password or pass phrase. @tramp{} sends it to the remote
+host, followed by a newline.
+
+@item
+@tramp{} now waits for the shell prompt or for a message that the login
+failed.
+
+If @tramp{} sees neither of them after a certain period of time (a minute,
+say), then it issues an error message saying that it couldn't find the
+remote shell prompt and shows you what the remote host has sent.
+
+If @tramp{} sees a `login failed' message, it tells you so, aborts the
+login attempt and allows you to try again.
+
+@item
+Suppose that the login was successful and @tramp{} sees the shell prompt
+from the remote host. Now @tramp{} invokes @command{/bin/sh} because
+Bourne shells and C shells have different command
+syntaxes.@footnote{Invoking @command{/bin/sh} will fail if your login
+shell doesn't recognize @command{exec /bin/sh} as a valid command.
+Maybe you use the Scheme shell @command{scsh}@dots{}}
+
+After the Bourne shell has come up, @tramp{} sends a few commands to
+ensure a good working environment. It turns off echoing, it sets the
+shell prompt, and a few other things.
+
+@item
+Now the remote shell is up and it good working order. Remember, what
+was supposed to happen is that @tramp{} tries to find out what files exist
+on the remote host so that it can do filename completion.
+
+So, @tramp{} basically issues @command{cd} and @command{ls} commands and
+also sometimes @command{echo} with globbing. Another command that is
+often used is @command{test} to find out whether a file is writable or a
+directory or the like. The output of each command is parsed for the
+necessary operation.
+
+@item
+Suppose you are finished with filename completion, have entered @kbd{C-x
+C-f}, a full file name and hit @kbd{@key{RET}}. Now comes the time to
+transfer the file contents from the remote host to the local host so
+that you can edit them.
+
+See above for an explanation of how @tramp{} transfers the file contents.
+
+For inline transfers, @tramp{} issues a command like @command{mimencode -b
+/path/to/remote/file}, waits until the output has accumulated in the
+buffer that's used for communication, then decodes that output to
+produce the file contents.
+
+For out-of-band transfers, @tramp{} issues a command like @command{rcp
+user@@host:/path/to/remote/file /tmp/tramp.4711} and then reads the local
+temporary file @file{/tmp/tramp.4711} into a buffer and deletes the
+temporary file.
+
+@item
+You now edit the buffer contents, blithely unaware of what has happened
+behind the scenes. (Unless you have read this section, that is.) When
+you are finished, you type @kbd{C-x C-s} to save the buffer.
+
+@item
+Again, @tramp{} transfers the file contents to the remote host either
+inline or out-of-band. This is the reverse of what happens when reading
+the file.
+
+@end itemize
+
+I hope this has provided you with a basic overview of what happens
+behind the scenes when you open a file with @tramp{}.
+
+
+@c For the end user
+@node Obtaining @tramp{}
+@chapter Obtaining @tramp{}.
+
+@tramp{} is freely available on the Internet and the latest release may be
+downloaded from
+@uref{ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz}. This
+release includes the full documentation and code for @tramp{}, suitable
+for installation.
+
+For the especially brave, @tramp{} is available from CVS. The CVS version
+is the latest version of the code and may contain incomplete features or
+new issues. Use these versions at your own risk.
+
+Instructions for obtaining the latest development version of @tramp{}
+from CVS can be found by going to the Savannah project page at
+@uref{http://savannah.gnu.org/projects/tramp/} and then clicking on the
+CVS link in the navigation bar at the top. Or follow the example
+session below:
+
+@example
+] @strong{cd ~/lisp}
+] @strong{cvs -d:pserver:anoncvs@@subversions.gnu.org:/cvsroot/tramp login}
+
+(Logging in to anoncvs@@subversions.gnu.org)
+CVS password: @strong{(just hit RET here)}
+@dots{}
+
+] @strong{cvs -z3 -d:pserver:anoncvs@@subversions.gnu.org:/cvsroot/tramp co tramp}
+@end example
+
+You should now have a directory @file{~/lisp/tramp} containing the latest
+version of @tramp{}. You can fetch the latest updates from the repository
+by issuing the command:
+
+@example
+] @strong{cd ~/lisp/tramp}
+] @strong{cvs update -d}
+@end example
+
+
+@node History
+@chapter History of @tramp{}
+
+Development was started end of November 1998. The package was called
+`rssh.el', back then. It only provided one method to access a file,
+using @command{ssh} to log in to a remote host and using @command{scp}
+to transfer the file contents. After a while, the name was changed to
+`rcp.el', and now it's @tramp{}. Along the way, many more methods for
+getting a remote shell and for transferring the file contents were
+added. Support for VC was added.
+
+The most recent addition of a major feature was the multi-hop methods
+added in April 2000.
+
+
+@node Installation
+@chapter Installing @tramp{} into Emacs or XEmacs
+
+Installing @tramp{} into your Emacs or XEmacs is a relatively easy
+process, at least compared to rebuilding your machine from scratch. ;)
+
+Seriously though, the installation should be a fairly simple matter.
+
+The easiest way to proceed is as follows:
+
+@itemize
+@item
+Choose a directory, say @file{~/emacs/}. Change into that directory and
+unpack the tarball. This will give you a directory
+@file{~/emacs/tramp/} which contains subdirectories @file{lisp} for the
+Lisp code and @file{texi} for the documentation.
+
+@item
+Optionally byte-compile all files in the Lisp directory,
+@file{~/emacs/tramp/lisp/}, by issuing a command like the following from
+the top level directory @file{~/emacs/tramp/}:
+@example
+make EMACS=emacs all # for Emacs users
+make EMACS=xemacs all # for XEmacs users
+@end example
+
+@item
+NOTE:
+@example
+If you run into problems running the example @command{make}
+commands, don't dispare. You can still byte compile the
+@file{*.el} files by opening emacs in @command{dired}
+(@command{C-x d}) mode, at @file{~/tramp/lisp}. Mark the lisp
+files with @command{m}, then press @command{B} to byte compile
+your selections.
+
+Something similar can be done to create the info manual.
+Just cd to @file{~/emacs/tramp/texi} and load the @file{tramp.texi}
+file in emacs. Then press @command{M-x makeinfo-buffer <RET>}
+to generate @file{tramp.info}.
+@end example
+
+@item
+Tell Emacs about the new Lisp directory and the @tramp{} package
+with the following lines in @file{~/.emacs}:
+@lisp
+(add-to-list 'load-path "~/emacs/tramp/lisp/")
+(require 'tramp)
+@end lisp
+
+@item
+To be able to read the Info documentation, create a file
+@file{~/emacs/tramp/texi/dir} using for example the
+@command{install-info} command, and add the directory to the search
+path for Info.
+
+@item
+NOTE:
+@example
+On systems using `gnu' @command{install-info}, the
+@command{install-info} syntax is very direct and simple. One can
+cd to @file{~/emacs/tramp/texi} and type:
+ @command{install-info tramp.info dir}
+and a @file{dir} file will be created with the @tramp{}
+entry. The info reader will know how to interpret it, but must
+be told where to find it (see below). If you want anything fancier
+you'll need to look through @command{man install-info}.
+
+Debian gnu/linux doesn't default to `gnu' @command{install-info} and
+uses its own version. This version does not create a @file{dir} file
+for you from scratch. You must provide a skeleton dir file it
+recognizes. One can be found in a default install at
+@file{/usr/info/dir}. Copy the top of this file down to the first
+occurrence of `* Menu' including that line plus one more blank line,
+to your working directory @file{texi/dir}, or use the sample provided
+in the @file{texi} directroy of this distribution. See
+@file{texi/dir_sample}
+
+Once a @file{dir} file is in place, this command will make the entry.
+ install-info --infodir=. tramp.info
+If you want it in a specific category
+ (see @command{man install-info} for further details)
+@end example
+
+If the environment variable @env{INFOPATH} is set, add the directory
+@file{~/emacs/tramp/texi/} to it. Else, add the directory to
+@code{Info-default-directory-list}, as follows:
+@lisp
+(add-to-list 'Info-default-directory-list "~/emacs/tramp/texi/")
+@end lisp
+XEmacs 21 users should use @code{Info-directory-list} rather than
+@code{Info-default-directory-list}.
+
+@end itemize
+
+
+For XEmacs users, the package @command{fsf-compat} must be installed.
+For details on package installation, see @ref{Packages, , ,xemacs}.
+@ifhtml
+(If the previous link doesn't work, try the XEmacs documentation at
+@uref{http://www.xemacs.org/Documentation/packageGuide.html,the XEmacs
+site}.)
+@end ifhtml
+
+@node Configuration
+@chapter Configuring @tramp{} for use
+
+@tramp{} is (normally) fully functional when it is initially
+installed. It is initially configured to use the @command{rsh} and
+@command{rcp} programs to connect to the remote host.
+
+On some hosts, there are problems with opening a connection. These are
+related to the behavior of the remote shell. See @xref{Remote shell
+setup}, for details on this.
+
+If you do not wish to use these commands to connect to the remote host,
+you should change the default connection and transfer method that @tramp
+uses. There are several different methods that @tramp{} can use to
+connect to remote machines and transfer files (@pxref{Connection types}).
+
+
+@menu
+* Connection types:: Types of connections made to remote machines.
+* Inline methods:: Inline methods.
+* External transfer methods:: External transfer methods.
+* Multi-hop Methods:: Connecting to a remote host using multiple hops.
+* Default Method:: Selecting a default method.
+* Customizing Methods:: Using Non-Standard Methods.
+* Remote Programs:: How @tramp{} finds and uses programs on the remote machine.
+* Remote shell setup:: Remote shell setup hints.
+* Windows setup hints:: Issues with Cygwin ssh.
+@end menu
+
+
+@node Connection types
+@section Types of connections made to remote machines.
+
+There are two basic types of transfer methods, each with its own
+advantages and limitations. Both types of connection make use of a
+remote shell access program such as @command{rsh}, @command{ssh} or
+@command{telnet} to connect to the remote machine.
+
+This connection is used to perform many of the operations that @tramp
+requires to make the remote file system transparently accessible from
+the local machine. It is only when visiting files that the methods
+differ.
+
+Loading or saving a remote file requires that the content of the file be
+transfered between the two machines. The content of the file can be
+transfered over the same connection used to log in to the remote machine
+or the file can be transfered through another connection using a remote
+copy program such as @command{rcp}, @command{scp} or @command{rsync}.
+The former are called @dfn{inline methods}, the latter are called
+@dfn{external transfer methods}.
+
+The performance of the external transfer methods is generally better
+than that of the inline methods. This is caused by the need to encode
+and decode the data when transferring inline.
+
+The one exception to this rule are the @command{scp} based transfer
+methods. While these methods do see better performance when actually
+transferring files, the overhead of the cryptographic negotiation at
+startup may drown out the improvement in file transfer times.
+
+External transfer methods do require that the remote copy command is not
+interactive --- that is, the command does not prompt you for a password.
+If you cannot perform remote copies without a password, you will need to
+use an inline transfer method to work with @tramp{}.
+
+A variant of the inline methods are the @dfn{multi-hop methods}.
+These methods allow you to connect a remote host using a number `hops',
+each of which connects to a different host. This is useful if you are
+in a secured network where you need to go through a bastion host to
+connect to the outside world.
+
+
+@node Inline methods
+@section Inline methods
+
+The inline methods in @tramp{} are quite powerful and can work in
+situations where you cannot use an external transfer program to connect.
+Inline methods are the only methods that work when connecting to the
+remote machine via telnet. (There are also strange inline methods which
+allow you to transfer files between @emph{user identities} rather than
+hosts, see below.)
+
+These methods depend on the existence of a suitable encoding and
+decoding command on remote machine. Locally, @tramp{} may be able to use
+features of Emacs to decode and encode the files or it may require
+access to external commands to perform that task.
+
+@tramp{} supports the use of @command{uuencode} to transfer files. This is
+@emph{not} recommended. The @command{uuencode} and @command{uudecode}
+commands are not well standardized and may not function correctly or at
+all on some machines, notably AIX and IRIX. These systems do not work
+with @command{uuencode} at all. (But do see the note about AIX in the
+documentation for @var{tramp-methods}.)
+
+In summary, if possible use the @command{mimencode} methods to transfer
+the data base64 encoded. This has the advantage of using a built-in
+command in every modern Emacs, improving performance.
+
+@itemize
+@item @option{rm} --- @command{rsh} with @command{mimencode}
+
+Connect to the remote host with @command{rsh} and use base64 encoding to
+transfer files between the machines.
+
+This requires the @command{mimencode} command that is part of the
+@command{metamail} packages. This may not be installed on all remote
+machines.
+
+
+@item @option{sm} --- @command{ssh} with @command{mimencode}
+
+Connect to the remote host with @command{ssh} and use base64 encoding to
+transfer files between the machines.
+
+This is identical to the previous option except that the @command{ssh}
+package is used, making the connection more secure.
+
+There are also two variants, @option{sm1} and @option{sm2} that use the
+@command{ssh1} and @command{ssh2} commands explicitly. If you don't know
+what these are, you do not need these options.
+
+
+@item @option{tm} --- @command{telnet} with @command{mimencode}
+
+Connect to the remote host with @command{telnet} and use base64 encoding
+to transfer files between the machines.
+
+This requires the @command{mimencode} command that is part of the
+@command{metamail} packages.
+
+
+@item @option{ru} --- @command{rsh} with @command{uuencode}
+
+Connect to the remote host with @command{rsh} and use the
+@command{uuencode} and @command{uudecode} commands to transfer files
+between the machines.
+
+
+@item @option{su} --- @command{ssh} with @command{uuencode}
+
+Connect to the remote host with @command{ssh} and use the
+@command{uuencode} and @command{uudecode} commands to transfer files
+between the machines.
+
+As with the @command{ssh} and base64 option above, this provides the
+@option{su1} and @option{su2} methods to explicitly select an ssh
+version.
+
+Note that this method does not invoke the @command{su} program, see
+below for methods which use that.
+
+
+@item @option{tu} --- @command{telnet} with @command{uuencode}
+
+Connect to the remote host with @command{telnet} and use the
+@command{uuencode} and @command{uudecode} commands to transfer files
+between the machines.
+
+
+@item @option{sum} --- @command{su} with @command{mimencode}
+
+This method does not connect to a remote host at all, rather it uses the
+@command{su} program to allow you to edit files as another user. Uses
+base64 encoding to transfer the file contents.
+
+
+@item @option{suu} --- @command{su} with @command{uuencode}
+
+Like @option{sum}, this uses the @command{su} program to allow you to
+edit files on the local host as another user. Uses @command{uuencode}
+and @command{uudecode} to transfer the file contents.
+
+
+@item @option{sudm} --- @command{sudo} with @command{mimencode}
+
+This is similar to the @option{sum} method, but it uses @command{sudo}
+rather than @command{su} to become a different user.
+
+Note that @command{sudo} must be configured to allow you to start a
+shell as the user. It would be nice if it was sufficient if
+@command{ls} and @command{mimencode} were allowed, but that is not easy
+to implement, so I haven't got around to it, yet.
+
+
+@item @option{sudu} --- @command{sudo} with @command{uuencode}
+
+This is similar to the @option{suu} method, but it uses @command{sudo}
+rather than @command{su} to become a different user.
+
+
+@item @option{smx} --- @command{ssh} with @command{mimencode}
+
+As you expect, this is similar to @option{sm}, only a little
+different. Whereas @option{sm} opens a normal interactive shell on
+the remote host, this option uses @command{ssh -t -t HOST -l USER
+/bin/sh} tp open a connection. This is useful for users where the
+normal login shell is set up to ask them a number of questions when
+logging in. This procedure avoids these questions, and just gives
+@tramp{} a more-or-less `standard' login shell to work with.
+
+This is also useful for Windows users where @command{ssh}, when
+invoked from an Emacs buffer, tells them that it is not allocating a
+pseudo tty. When this happens, the login shell is wont to not print
+any shell prompt, which confuses @tramp{} mightily.
+
+
+@item @option{km} --- @command{krlogin} with @command{mimencode}
+
+This method is also similar to @option{sm}. It only uses the
+@command{krlogin -x} command to log in to the remote host.
+
+
+@item @option{plinku} --- @command{plink} with @command{uuencode}
+
+This method is mostly interesting for Windows users using the PuTTY
+implementation of SSH. It uses @command{plink -ssh} to log in to the
+remote host.
+
+CCC: Do we have to connect to the remote host once from the command
+line to accept the SSH key? Maybe this can be made automatic?
+
+@item @option{plinkm} --- @command{plink} with @command{mimencode}
+
+Like @option{plinku}, but uses base64 encoding instead of uu encoding.
+
+@end itemize
+
+
+
+@node External transfer methods
+@section External transfer methods
+
+The external transfer methods operate through multiple channels, using
+the remote shell connection for many actions while delegating file
+transfers to an external transfer utility.
+
+This saves the overhead of encoding and decoding that multiplexing the
+transfer through the one connection has with the inline methods.
+
+If you want to use an external transfer method you @emph{must} be able
+to execute the transfer utility to copy files to and from the remote
+machine without any interaction.
+
+This means that you will need to use @command{ssh-agent} if you use the
+@command{scp} program for transfers, or maybe your version of
+@command{scp} accepts a password on the command line.@footnote{PuTTY's
+@command{pscp} allows you to specify the password on the command line.}
+If you use @command{rsync} via @command{ssh} then the same rule must
+apply to that connection.
+
+If you cannot get @command{scp} to run without asking for a password but
+would still like to use @command{ssh} to secure your connection, have a
+look at the @command{ssh} based inline methods.
+
+
+@itemize
+@item @option{rcp} --- @command{rsh} and @command{rcp}
+
+This method uses the @command{rsh} and @command{rcp} commands to connect
+to the remote machine and transfer files. This is probably the fastest
+connection method available.
+
+
+@item @option{scp} --- @command{ssh} and @command{scp}
+
+Using @command{ssh} to connect to the remote host and @command{scp} to
+transfer files between the machines is the best method for securely
+connecting to a remote machine and accessing files.
+
+The performance of this option is also quite good. It may be slower than
+the inline methods when you often open and close small files however.
+The cost of the cryptographic handshake at the start of an @command{scp}
+session can begin to absorb the advantage that the lack of encoding and
+decoding presents.
+
+
+@item @option{rsync} --- @command{ssh} and @command{rsync}
+
+Using the @command{ssh} command to connect securely to the remote
+machine and the @command{rsync} command to transfer files is almost
+identical to the @option{scp} method.
+
+While @command{rsync} performs much better than @command{scp} when
+transferring files that exist on both hosts, this advantage is lost if
+the file exists only on one side of the connection.
+
+The @command{rsync} based method may be considerably faster than the
+@command{rcp} based methods when writing to the remote system. Reading
+files to the local machine is no faster than with a direct copy.
+
+
+@item @option{scpx} --- @command{ssh} and @command{scp}
+
+As you expect, this is similar to @option{scp}, only a little
+different. Whereas @option{scp} opens a normal interactive shell on the
+remote host, this option uses @command{ssh -t -t HOST -l USER /bin/sh} to
+open a connection. This is useful for users where the normal login
+shell is set up to ask them a number of questions when logging in. This
+procedure avoids these questions, and just gives @tramp{} a more-or-less
+`standard' login shell to work with.
+
+This is also useful for Windows users where @command{ssh}, when
+invoked from an Emacs buffer, tells them that it is not allocating a
+pseudo tty. When this happens, the login shell is wont to not print
+any shell prompt, which confuses @tramp{} mightily.
+
+
+@item @option{pscp} --- @command{plink} and @command{pscp}
+
+This method is similar to @option{scp}, but it uses the
+@command{plink} command to connect to the remote host, and it uses
+@command{pscp} for transferring the files. These programs are part
+of PuTTY, an SSH implementation for Windows.
+
+
+@item @option{fcp} --- @command{fsh} and @command{fcp}
+
+This method is similar to @option{scp}, but it uses the @command{fsh}
+command to connect to the remote host, and it uses @command{fcp} for
+transferring the files. @command{fsh/fcp} are a front-end for
+@command{ssh} which allow for reusing the same @command{ssh} session
+for submitting several commands. This avoids the startup overhead of
+@command{scp} (which has to establish a secure connection whenever it
+is called). Note, however, that you can also use one of the inline
+methods to achieve a similar effect.
+
+This method uses the command @command{fsh HOST -l USER /bin/sh -i} to
+establish the connection, it does not work to just say @command{fsh
+HOST -l USER}.
+
+@end itemize
+
+@node Multi-hop Methods
+@section Connecting to a remote host using multiple hops
+
+Sometimes, the methods described before are not sufficient. Sometimes,
+it is not possible to connect to a remote host using a simple command.
+For example, if you are in a secured network, you might have to log in
+to a `bastion host' first before you can connect to the outside world.
+Of course, the target host may also require a bastion host. The format
+of multi-hop filenames is slightly different than the format of normal
+@tramp{} methods.
+
+A multi-hop file name specifies a method, a number of hops, and a path
+name on the remote system. The method specifies how the file is
+transferred through the inline connection. The following two multi-hop
+methods are available:
+
+@itemize
+@item @option{multi} --- base64 encoding with @command{mimencode}
+
+The file is transferred through the connection in base64 encoding. Uses
+the @command{mimencode} program for doing encoding and decoding, but
+uses an Emacs internal implementation on the local host if available.
+
+@item @option{multiu} --- use commands @command{uuencode} and @command{uudecode}
+
+The file is transferred through the connection in `uu' encoding. Uses
+the @command{uuencode} and @command{uudecode} programs for encoding and
+decoding, but uses a Lisp implementation for decoding on the local host
+if available.
+
+@end itemize
+
+Each hop consists of a @dfn{hop method} specification, a user name and a
+host name. The following hop methods are (currently) available:
+
+@itemize
+@item @option{telnet}
+
+Uses the well-known @command{telnet} program to connect to the host.
+Whereas user name and host name are supplied in the file name, the
+user is queried for the password.
+
+@item @option{rsh}
+
+This uses @command{rsh} to connect to the host. You do not need to
+enter a password unless @command{rsh} explicitly asks for it.
+
+@item @option{ssh}
+
+This uses @command{ssh} to connect to the host. You might have to enter
+a password or a pass phrase.
+
+@item @option{su}
+
+This method does not actually contact a different host, but it allows
+you to become a different user on the host you're currently on. This
+might be useful if you want to edit files as root, but the remote host
+does not allow remote root logins. In this case you can use
+@option{telnet}, @option{rsh} or @option{ssh} to connect to the
+remote host as a non-root user, then use an @option{su} hop to become
+root. But @option{su} need not be the last hop in a sequence, you could
+also use it somewhere in the middle, if the need arises.
+
+Even though you @emph{must} specify both user and host with a
+@option{su} hop, the host name is ignored and only the user name is
+used.
+
+@item @option{sudo}
+
+This is similar to the @option{su} hop, except that it uses
+@command{sudo} rather than @command{su} to become a different user.
+
+@end itemize
+
+Some people might wish to use port forwarding with @code{ssh} or maybe
+they have to use a nonstandard port. This can be accomplished by
+putting a stanza in @file{~/.ssh/config} for the account which specifies
+a different port number for a certain host name. But it can also be
+accomplished within Tramp, by adding a multi-hop method. For example:
+
+@lisp
+(add-to-list 'tramp-multi-connection-function-alist
+ '("sshf" tramp-multi-connect-rlogin "ssh %h -l %u -p 4400%n"))
+@end lisp
+
+Now you can use a @code{sshf} hop which connects to port 4400 instead of
+the standard port.
+
+
+@node Default Method
+@section Selecting a default method
+
+When you select an appropriate transfer method for your typical usage
+you should set the variable @var{tramp-default-method} to reflect that
+choice. This variable controls which method will be used when a method
+is not specified in the @tramp{} file path. For example:
+
+@lisp
+(setq tramp-default-method "scp")
+@end lisp
+
+External transfer methods are normally preferable to inline transfer
+methods, giving better performance. They may not be useful if you use
+many remote machines where you cannot log in without a password.
+
+@xref{Inline methods}.
+@xref{External transfer methods}.
+@xref{Multi-hop Methods}.
+
+Another consideration with the selection of transfer methods is the
+environment you will use them in and, especially when used over the
+Internet, the security implications of your preferred method.
+
+The @command{rsh} and @command{telnet} methods send your password as
+plain text as you log in to the remote machine, as well as transferring
+the files in such a way that the content can easily be read from other
+machines.
+
+If you need to connect to remote systems that are accessible from the
+Internet, you should give serious thought to using @command{ssh} based
+methods to connect. These provide a much higher level of security,
+making it a non-trivial exercise for someone to obtain your password or
+read the content of the files you are editing.
+
+@node Customizing Methods
+@section Using Non-Standard Methods
+
+There is a variable @code{tramp-methods} which you can change if the
+predefined methods don't seem right.
+
+For the time being, I'll refer you to the Lisp documentation of that
+variable, accessible with @kbd{C-h v tramp-methods @key{RET}}.
+
+
+@node Remote Programs
+@section How @tramp{} finds and uses programs on the remote machine.
+
+@tramp{} depends on a number of programs on the remote host in order to
+function, including @command{ls}, @command{test}, @command{find} and
+@command{cat}.
+
+In addition to these required tools, there are various tools that may be
+required based on the connection method. See @ref{Inline methods} and
+@ref{External transfer methods} for details on these.
+
+Certain other tools, such as @command{perl} (or @command{perl5}) and
+@command{grep} will be used if they can be found. When they are
+available, they are used to improve the performance and accuracy of
+remote file access.
+
+When @tramp{} connects to the remote machine, it searches for the
+programs that it can use. The variable @var{tramp-remote-path} controls
+the directories searched on the remote machine.
+
+By default, this is set to a reasonable set of defaults for most
+machines. It is possible, however, that your local (or remote ;) system
+administrator has put the tools you want in some obscure local
+directory.
+
+In this case, you can still use them with @tramp{}. You simply need to
+add code to your @file{.emacs} to add the directory to the remote path.
+This will then be searched by @tramp{} when you connect and the software
+found.
+
+To add a directory to the remote search path, you could use code such
+as:
+
+@example
+(require 'tramp) @i{; @tramp{} must be loaded before this}
+ @i{; happens.}
+
+@i{; We have @command{perl} in "/usr/local/perl"}
+(add-to-list 'tramp-remote-path "/usr/local/perl")
+@end example
+
+@node Remote shell setup
+@comment node-name, next, previous, up
+@section Remote shell setup hints
+
+As explained in the @ref{Overview} section, @tramp{} connects to the
+remote host and talks to the shell it finds there. Of course, when you
+log in, the shell executes its init files. Suppose your init file
+requires you to enter the birthdate of your mother; clearly @tramp{}
+does not know this and hence fails to log you in to that host.
+
+There are different possible strategies for pursuing this problem. One
+strategy is to enable @tramp{} to deal with all possible situations.
+This is a losing battle, since it is not possible to deal with
+@emph{all} situations. The other strategy is to require you to set up
+the remote host such that it behaves like @tramp{} expect. This might
+be inconvenient because you have to invest a lot of effort into shell
+setup before you can begin to use @tramp{}.
+
+The package, therefore, pursues a combined approach. It tries to figure
+out some of the more common setups, and only requires you to avoid
+really exotic stuff. For example, it looks through a list of
+directories to find some programs on the remote host. And also, it
+knows that it is not obvious how to check whether a file exist, and
+therefore it tries different possibilities. (On some hosts and shells,
+the command @code{test -e} does the trick, on some hosts the shell
+builtin doesn't work but the program @code{/usr/bin/test -e} or
+@code{/bin/test -e} works. And on still other hosts, @code{ls -d} is
+the right way to do this.)
+
+Below you find a discussion of a few things that @tramp{} does not deal
+with, and that you therefore have to set up correctly.
+
+@itemize
+@item @code{shell-prompt-pattern}
+
+@vindex shell-prompt-pattern
+After logging in to the remote host, @tramp{} has to wait for the remote
+shell startup to finish before it can send commands to the remote
+shell. The strategy here is to wait for the shell prompt. In order to
+recognize the shell prompt, the variable @code{shell-prompt-pattern} has
+to be set correctly to recognize the shell prompt on the remote host.
+
+@item @code{tset} and other questions
+
+Some people invoke the @code{tset} program from their shell startup
+scripts which asks the user about the terminal type of the shell. Maybe
+some shells ask other questions when they are started. @tramp{} does
+not know how to answer these questions. (A facility for enabling
+@tramp{} to answer these questions is planned for some future version,
+but don't hold your breath.)
+
+Therefore, you should take care that the shell does not ask any
+questions when invoked from @tramp{}. You can do this by checking the
+@code{TERM} environment variable, it will be set to @code{dumb} when
+connecting.
+
+@vindex tramp-terminal-type
+The variable @code{tramp-terminal-type} can be used to change this value
+@code{dumb}.
+
+@end itemize
+
+
+@node Windows setup hints
+@section Issues with Cygwin ssh
+
+This section needs a lot of work! Please help.
+
+If you use the Cygwin installation of ssh (you have to explicitly select
+it in the installer), then it should work out of the box to just select
+@code{smx} as the connection method. You can find information about
+setting up Cygwin in their FAQ at @uref{http://cygwin.com/faq/}.
+
+
+@node Usage
+@chapter Using @tramp
+
+Once you have installed @tramp{} it will operate fairly transparently. You
+will be able to access files on any remote machine that you can log in
+to as though they were local.
+
+Files are specified to @tramp{} using a formalized syntax specifying the
+details of the system to connect to. This is similar to the syntax used
+by the @command{EFS} and @command{ange-ftp} packages.
+
+
+@menu
+* Filename Syntax:: @tramp{} filename conventions.
+* Multi-hop filename syntax:: Multi-hop filename conventions
+* Dired:: Dired and filename completion.
+@end menu
+
+
+@node Filename Syntax
+@section @tramp{} filename conventions
+
+To access the file <path> on the remote machine <machine> you would
+specify the filename @file{/[<machine>]<path>}. (The square brackets
+are part of the file name.) This will connect to <machine> and transfer
+the file using the default method. @xref{Default Method}.
+
+Some examples of @tramp{} filenames are:
+
+@table @file
+@item /[melancholia].emacs
+Edit the file @file{.emacs} in your home directory on the machine
+@code{melancholia}.
+
+@item /[melancholia.danann.net].emacs
+This edits the same file, using the fully qualified domain name of
+the machine.
+
+@item /[melancholia]~/.emacs
+This also edits the same file --- the @file{~} is expanded to your
+home directory on the remote machine, just like it is locally.
+
+@item /[melancholia]~daniel/.emacs
+This edits the file @file{.emacs} in the home directory of the user
+@code{daniel} on the machine @code{melancholia}. The @file{~<user>}
+construct is expanded to the home directory of that user on the remote
+machine.
+
+@item /[melancholia]/etc/squid.conf
+This edits the file @file{/etc/squid.conf} on the machine
+@code{melancholia}.
+
+@end table
+
+
+Unless you specify a different name to use, @tramp{} will use the current
+local user name as the remote user name to log in with. If you need to
+log in as a different user, you can specify the user name as part of the
+filename.
+
+To log in to the remote machine as a specific user, you use the syntax
+@file{/[<user>@@<machine>]/path/to.file}. That means that connecting to
+@code{melancholia} as @code{daniel} and editing @file{.emacs} in your
+home directory you would specify @file{/[daniel@@melancholia].emacs}.
+
+
+It is also possible to specify other file transfer methods
+(@pxref{Default Method}) as part of the filename. This is done by
+replacing the initial @file{/[} with @file{/[<method>/}. (Note the
+trailing slash!) The user, machine and file specification remain the
+same.
+
+So, to connect to the machine @code{melancholia} as @code{daniel}, using
+the @option{su} method to transfer files, and edit @file{.emacs} in my
+home directory I would specify the filename
+@file{/[su/daniel@@melancholia].emacs}.
+
+
+@node Multi-hop filename syntax
+@section Multi-hop filename conventions
+
+The syntax of multi-hop file names is necessarily slightly different
+than the syntax of other @tramp{} file names. Here's an example multi-hop
+file name:
+
+@file{/[multi/rsh:out@@gate/telnet:kai@@real.host]/path/to.file}
+
+This is quite a mouthful. So let's go through it step by step. The
+file name consists of three parts, separated by slashes and square
+brackets. The first part is @file{/[multi}, the method specification.
+The second part is @file{rsh:out@@gate/telnet:kai@@real.host} and
+specifies the hops. (Yes, the second part may contain even more
+slashes, so that's why this file name has more than two colons in it.)
+The final part is @file{/path/to.file} and specifies the file name on
+the remote host.
+
+The first part and the final part should be clear. @ref{Multi-hop
+Methods}, for a list of alternatives for the method specification.
+
+The second part can be subdivided again into components, so-called hops.
+In the above file name, there are two hops, @file{rsh:out@@gate} and
+@file{telnet:kai@@real.host}.
+
+Each hop can @emph{again} be subdivided into (three) components, the
+@dfn{hop method}, the @dfn{user name} and the @dfn{host name}. The
+meaning of the second and third component should be clear, and the hop
+method says what program to use to perform that hop.
+
+The first hop, @file{rsh:out@@gate}, says to use @command{rsh} to log in
+as user @code{out} to the host @code{gate}. Starting at that host, the
+second hop, @file{telnet:kai@@real.host}, says to use @command{telnet}
+to log in as user @code{kai} to host @code{real.host}.
+
+@xref{Multi-hop Methods}, for a list of possible hop method values. The
+variable @var{tramp-multi-connection-function-alist} contains the list of
+possible hop methods and information on how to execute them, should you
+want to add your own.
+
+
+@node Dired
+@section Dired and filename completion
+
+@tramp{} works transparently with dired, enabling you to use this powerful
+file management tool to manage files on any machine you have access to
+over the Internet.
+
+Filename completion also works with @tramp{} for files on remote machines
+although there is no completion for user names or machine names at this
+stage.
+
+As filename completion needs to fetch the listing of files from the
+remote machine, this feature is sometimes fairly slow. As @tramp{} does not
+yet cache the results of directory listing, there is no gain in
+performance the second time you complete filenames.
+
+If you need to browse a directory tree, Dired is a better choice, at
+present, than filename completion. Dired has its own cache mechanism
+and will only fetch the directory listing once.
+
+
+@node Bug Reports
+@chapter Reporting Bugs and Problems
+
+Bugs and problems with @tramp{} are actively worked on by the development
+team. Feature requests and suggestions are also more than welcome.
+
+The @tramp{} mailing list is a great place to get information on working
+with @tramp{}, solving problems and general discussion and advice on topics
+relating to the package.
+
+The mailing list is at @email{tramp-devel@@mail.freesoftware.fsf.org}.
+Messages sent to this address go to all the subscribers. This is
+@emph{not} the address to send subscription requests to.
+
+For help on subscribing to the list, send mail to the administrative
+address, @email{tramp-devel-request@@mail.freesoftware.fsf.org}, with the
+subject @samp{help}.
+
+To report a bug in @tramp{}, you should execute @kbd{M-x tramp-bug}. This
+will automatically generate a buffer with the details of your system and
+@tramp{} version.
+
+When submitting a bug report, please try to describe in excruciating
+detail the steps required to reproduce the problem, the setup of the
+remote machine and any special conditions that exist.
+
+If you can identify a minimal test case that reproduces the problem,
+include that with your bug report. This will make it much easier for the
+development team to analyze and correct the problem.
+
+@node Frequently Asked Questions
+@chapter Frequently Asked Questions
+
+@itemize @bullet
+@item Where can I get the latest @tramp{}?
+
+@tramp{} is available at
+@uref{ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz}.
+There is also a Savannah project page, at
+@uref{https://savannah.gnu.org/projects/tramp/}.
+
+
+@item Which systems does it work on?
+
+The package has been used successfully on Emacs 20 and Emacs 21, as well
+as XEmacs 21. XEmacs 20 is more problematic, see the notes in
+@file{tramp.el}. I don't think anybody has really tried it on Emacs 19.
+
+The package was intended to work on Unix, and it really expects a
+Unix-like system on the remote end, but some people seemed to have some
+success getting it to work on NT Emacs.
+
+There are some informations on Tramp on NT at the following URL; many
+thanks to Joe Stoy for providing the information:
+@uref{ftp://ftp.comlab.ox.ac.uk/tmp/Joe.Stoy/}
+
+The above mostly contains patches to old ssh versions; Tom Roche has a
+Web page with instructions:
+@uref{http://www4.ncsu.edu/~tlroche/plinkTramp.html}
+
+??? Is the XEmacs info correct?
+
+??? Can somebody provide some information for getting it to work on NT
+Emacs? I think there was some issue with @command{ssh}?
+
+
+@item I can't stop EFS starting with XEmacs
+
+Not all the older versions of @tramp{} supported XEmacs correctly. The
+first thing to do is to make sure that you have the latest version of
+@tramp{} installed.
+
+If you do, please try and find out exactly the conditions required for
+the @code{EFS} handlers to fire. If you can, putting a breakpoint on
+@code{efs-ftp-path} and sending in the stack trace along with your bug
+report would make it easier for the developers to work out what is going
+wrong.
+
+
+@item File name completion does not work with @tramp{}
+
+When you log in to the remote machine, do you see the output of
+@command{ls} in color? If so, this may be the cause of your problems.
+
+@command{ls} outputs @acronym{ANSI} escape sequences that your terminal
+emulator interprets to set the colors. These escape sequences will
+confuse @tramp{} however.
+
+In your @file{.bashrc}, @file{.profile} or equivalent on the remote
+machine you probably have an alias configured that adds the option
+@option{--color=yes} or @option{--color=auto}.
+
+You should remove that alias and ensure that a new login @emph{does not}
+display the output of @command{ls} in color. If you still cannot use
+filename completion, report a bug to the @tramp{} developers.
+
+
+@item File name completion does not work in large directories
+
+@tramp{} uses globbing for some operations. (Globbing means to use the
+shell to expand wildcards such as `*.c'.) This might create long
+command lines, especially in directories with many files. Some shell
+choke on long command lines, or don't cope well with the globbing
+itself.
+
+If you have a large directory on the remote end, you may wish to execute
+a command like @command{ls -d * ..?* > /dev/null} and see if it hangs.
+Note that you must first start the right shell, which might be
+@command{/bin/sh}, @command{ksh} or @command{bash}, depending on which
+of those supports tilde expansion.
+
+
+@item What kinds of systems does @tramp{} work on
+
+@tramp{} really expects the remote system to be a Unix-like system. The
+local system should preferably be Unix-like, as well, but @tramp{} might
+work on NT with some tweaking.
+
+
+@item How can I get notified when @tramp{} file transfers are complete?
+
+The following snippet can be put in your @file{~/.emacs} file. It makes
+Emacs beep after reading from or writing to the remote host.
+
+@lisp
+(defadvice tramp-handle-write-region
+ (after tramp-write-beep-advice activate)
+ " make tramp beep after writing a file."
+ (interactive)
+ (beep))
+(defadvice tramp-handle-do-copy-or-rename-file
+ (after tramp-copy-beep-advice activate)
+ " make tramp beep after copying a file."
+ (interactive)
+ (beep))
+(defadvice tramp-handle-insert-file-contents
+ (after tramp-copy-beep-advice activate)
+ " make tramp beep after copying a file."
+ (interactive)
+ (beep))
+@end lisp
+
+
+@item There's this @file{~/.sh_history} file on the remote host which
+ keeps growing and growing. What's that?
+
+Sometimes, @tramp{} starts @code{ksh} on the remote host for tilde
+expansion. Maybe @code{ksh} saves the history by default. @tramp{}
+tries to turn off saving the history, but maybe you have to help. For
+example, you could put this in your @file{.kshrc}:
+
+@example
+if [ -f $HOME/.sh_history ] ; then
+ /bin/rm $HOME/.sh_history
+fi
+if [ "$@{HISTFILE-unset@}" != "unset" ] ; then
+ unset HISTFILE
+fi
+if [ "$@{HISTSIZE-unset@}" != "unset" ] ; then
+ unset HISTSIZE
+fi
+@end example
+
+@end itemize
+
+
+@c For the developer
+@node Version Control
+@chapter The inner workings of remote version control
+
+Unlike EFS and ange-ftp, @tramp{} has full shell access to the remote
+machine. This makes it possible to provide version control for files
+accessed under @tramp{}.
+
+The actual version control binaries must be installed on the remote
+machine, accessible in the directories specified in
+@var{tramp-remote-path}.
+
+This transparent integration with the version control systems is one of
+the most valuable features provided by @tramp{}, but it is far from perfect.
+Work is ongoing to improve the transparency of the system.
+
+@menu
+* Version Controlled Files:: Determining if a file is under version control.
+* Remote Commands:: Executing the version control commands on the remote machine.
+* Changed workfiles:: Detecting if the working file has changed.
+* Checking out files:: Bringing the workfile out of the repository.
+* Miscellaneous Version Control:: Things related to Version Control that don't fit elsewhere
+@end menu
+
+
+@node Version Controlled Files
+@section Determining if a file is under version control
+
+The VC package uses the existence of on-disk revision control master
+files to determine if a given file is under revision control. These file
+tests happen on the remote machine through the standard @tramp{} mechanisms.
+
+
+@node Remote Commands
+@section Executing the version control commands on the remote machine
+
+There are no hooks provided by VC to allow intercepting of the version
+control command execution. The calls occur through the
+@code{call-process} mechanism, a function that is somewhat more
+efficient than the @code{shell-command} function but that does not
+provide hooks for remote execution of commands.
+
+To work around this, the functions @code{vc-do-command} and
+@code{vc-simple-command} have been advised to intercept requests for
+operations on files accessed via @tramp{}.
+
+In the case of a remote file, the @code{shell-command} interface is
+used, with some wrapper code, to provide the same functionality on the
+remote machine as would be seen on the local machine.
+
+
+@node Changed workfiles
+@section Detecting if the working file has changed
+
+As there is currently no way to get access to the mtime of a file on a
+remote machine in a portable way, the @code{vc-workfile-unchanged-p}
+function is advised to call an @tramp{} specific function for remote files.
+
+The @code{tramp-vc-workfile-unchanged-p} function uses the functioning VC
+diff functionality to determine if any changes have occurred between the
+workfile and the version control master.
+
+This requires that a shell command be executed remotely, a process that
+is notably heavier-weight than the mtime comparison used for local
+files. Unfortunately, unless a portable solution to the issue is found,
+this will remain the cost of remote version control.
+
+
+@node Checking out files
+@section Bringing the workfile out of the repository
+
+VC will, by default, check for remote files and refuse to act on them
+when checking out files from the repository. To work around this
+problem, the function @code{vc-checkout} knows about @tramp{} files and
+allows version control to occur.
+
+
+@node Miscellaneous Version Control
+@section Things related to Version Control that don't fit elsewhere
+
+Minor implementation details, &c.
+
+@menu
+* Remote File Ownership:: How VC determines who owns a workfile.
+* Back-end Versions:: How VC determines what release your RCS is.
+@end menu
+
+
+@node Remote File Ownership
+@subsection How VC determines who owns a workfile
+
+Emacs provides the @code{user-full-name} function to return the login name
+of the current user as well as mapping from arbitrary user id values
+back to login names. The VC code uses this functionality to map from the
+uid of the owner of a workfile to the login name in some circumstances.
+
+This will not, for obvious reasons, work if the remote system has a
+different set of logins. As such, it is necessary to delegate to the
+remote machine the job of determining the login name associated with a
+uid.
+
+Unfortunately, with the profusion of distributed management systems such
+as @code{NIS}, @code{NIS+} and @code{NetInfo}, there is no simple,
+reliable and portable method for performing this mapping.
+
+Thankfully, the only place in the VC code that depends on the mapping of
+a uid to a login name is the @code{vc-file-owner} function. This returns
+the login of the owner of the file as a string.
+
+This function has been advised to use the output of @command{ls} on the
+remote machine to determine the login name, delegating the problem of
+mapping the uid to the login to the remote system which should know more
+about it than I do.
+
+
+@node Back-end Versions
+@subsection How VC determines what release your RCS is
+
+VC needs to know what release your revision control binaries you are
+running as not all features VC supports are available with older
+versions of @command{rcs(1)}, @command{cvs(1)} or @command{sccs(1)}.
+
+The default implementation of VC determines this value the first time it
+is needed and then stores the value globally to avoid the overhead of
+executing a process and parsing its output each time the information is
+needed.
+
+Unfortunately, life is not quite so easy when remote version control
+comes into the picture. Each remote machine may have a different version
+of the version control tools and, while this is painful, we need to
+ensure that unavailable features are not used remotely.
+
+To resolve this issue, @tramp{} currently takes the sledgehammer
+approach of making the release values of the revision control tools
+local to each @tramp{} buffer, forcing VC to determine these values
+again each time a new file is visited.
+
+This has, quite obviously, some performance implications. Thankfully,
+most of the common operations performed by VC do not actually require
+that the remote version be known. This makes the problem far less
+apparent.
+
+Eventually these values will be captured by @tramp{} on a system by
+system basis and the results cached to improve performance.
+
+
+@node Files directories and paths
+@chapter How file names, directories and paths are mangled and managed.
+
+@menu
+* Path deconstruction:: Breaking a path into its components.
+@end menu
+
+
+@node Path deconstruction
+@section Breaking a path into its components.
+
+@tramp{} filenames are somewhat different, obviously, to ordinary path
+names. As such, the lisp functions @code{file-name-directory} and
+@code{file-name-nondirectory} are overridden within the @tramp{} package.
+
+Their replacements are reasonably simplistic in their approach. They
+dissect the filename, call the original handler on the remote path and
+then rebuild the @tramp{} path with the result.
+
+This allows the platform specific hacks in the original handlers to take
+effect while preserving the @tramp{} path information.
+
+
+@node Issues
+@chapter Debatable Issues and What Was Decided
+
+@itemize @bullet
+@item The uuencode method does not always work.
+
+Due to the design of @tramp{}, the encoding and decoding programs need to
+read from stdin and write to stdout. On some systems, @code{uudecode -o
+-} will read stdin and write the decoded file to stdout, on other
+systems @code{uudecode -p} does the same thing. But some systems have
+uudecode implementations which cannot do this at all---it is not
+possible to call these uudecode implementations with suitable parameters
+so that they write to stdout.
+
+Of course, this could be circumvented: the @code{begin foo 644} line
+could be rewritten to put in some temporary file name, then
+@code{uudecode} could be called, then the temp file could be printed and
+deleted.
+
+But I have decided that this is too fragile to reliably work, so on some
+systems you'll have to do without the uuencode methods.
+
+@item @tramp{} does not work on XEmacs 20.
+
+This is because it requires the macro @code{with-timeout} which does not
+appear to exist in XEmacs 20. I'm somewhat reluctant to add an
+emulation macro to @tramp{}, but if somebody who uses XEmacs 20 steps
+forward and wishes to implement and test it, please contact me or the
+mailing list.
+
+@end itemize
+
+
+@c End of tramp.texi - the TRAMP User Manual
+@bye
+
+@c TODO
+@c
+@c * Say something about the .login and .profile files of the remote
+@c shells.
+@c * Explain how tramp.el works in principle: open a shell on a remote
+@c host and then send commands to it.
+
+@c Local Variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c time-stamp-start: "@set UPDATED "
+@c time-stamp-format: "%:a, %:d %:b, %:y"
+@c time-stamp-end: "$"
+@c time-stamp-line-limit: 50
+@c End: