* net/tramp.el: Version 2.0.15 released.
[emacs.git] / lisp / net / tramp.el
blobfb94491ae0c2add8fad037c92266fa02aebafcf5
1 ;;; tramp.el --- Transparent Remote Access, Multiple Protocol -*- coding: iso-8859-1; -*-
3 ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
5 ;; Author: Kai.Grossjohann@CS.Uni-Dortmund.DE
6 ;; Keywords: comm, processes
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
25 ;;; Commentary:
27 ;; This package provides remote file editing, similar to ange-ftp.
28 ;; The difference is that ange-ftp uses FTP to transfer files between
29 ;; the local and the remote host, whereas tramp.el uses a combination
30 ;; of rsh and rcp or other work-alike programs, such as ssh/scp.
32 ;; For more detailed instructions, please see the info file, which is
33 ;; included in the file `tramp.tar.gz' mentioned below.
35 ;; Notes:
36 ;; -----
37 ;;
38 ;; This package only works for Emacs 20 and higher, and for XEmacs 21
39 ;; and higher. (XEmacs 20 is missing the `with-timeout' macro. Emacs
40 ;; 19 is reported to have other problems. For XEmacs 21, you need the
41 ;; package `fsf-compat' for the `with-timeout' macro.)
43 ;; This version might not work with pre-Emacs 21 VC unless VC is
44 ;; loaded before tramp.el. Could you please test this and tell me about
45 ;; the result? Thanks.
47 ;; Also see the todo list at the bottom of this file.
49 ;; The current version of tramp.el can be retrieved from the following
50 ;; URL: ftp://ls6-ftp.cs.uni-dortmund.de/pub/src/emacs/tramp.tar.gz
51 ;; For your convenience, the *.el file is available separately from
52 ;; the same directory.
54 ;; There's a mailing list for this, as well. Its name is:
55 ;; tramp-devel@mail.freesoftware.fsf.org
56 ;; Send a mail with `help' in the subject (!) to the administration
57 ;; address for instructions on joining the list. The administration
58 ;; address is:
59 ;; tramp-devel-request@mail.freesoftware.fsf.org
60 ;; You can also use the Web to subscribe, under the following URL:
61 ;; http://mail.freesoftware.fsf.org/mailman/listinfo/tramp-devel
63 ;; For the adventurous, the current development sources are available
64 ;; via CVS. You can find instructions about this at the following URL:
65 ;; http://savannah.gnu.org/projects/tramp/
66 ;; Click on "CVS" in the navigation bar near the top.
68 ;; Don't forget to put on your asbestos longjohns, first!
70 ;;; Code:
72 ;; In the Tramp CVS repository, the version numer is auto-frobbed from
73 ;; the Makefile, so you should edit the top-level Makefile to change
74 ;; the version number.
75 (defconst tramp-version "2.0.15"
76 "This version of tramp.")
78 (defconst tramp-bug-report-address "tramp-devel@mail.freesoftware.fsf.org"
79 "Email address to send bug reports to.")
81 (require 'timer)
82 (require 'format-spec) ;from Gnus 5.8, also in tar ball
83 ;; The explicit check is not necessary in Emacs, which provides the
84 ;; feature even if implemented in C, but it appears to be necessary
85 ;; in XEmacs.
86 (unless (and (fboundp 'base64-encode-region)
87 (fboundp 'base64-decode-region))
88 (require 'base64)) ;for the mimencode methods
89 (require 'shell)
90 (require 'advice)
92 ;; ;; It does not work to load EFS after loading TRAMP.
93 ;; (when (fboundp 'efs-file-handler-function)
94 ;; (require 'efs))
96 (eval-when-compile
97 (require 'cl)
98 (require 'custom)
99 ;; Emacs 19.34 compatibility hack -- is this needed?
100 (or (>= emacs-major-version 20)
101 (load "cl-seq")))
103 (unless (boundp 'custom-print-functions)
104 (defvar custom-print-functions nil)) ; not autoloaded before Emacs 20.4
106 ;;; User Customizable Internal Variables:
108 (defgroup tramp nil
109 "Edit remote files with a combination of rsh and rcp or similar programs."
110 :group 'files)
112 (defcustom tramp-verbose 9
113 "*Verbosity level for tramp.el. 0 means be silent, 10 is most verbose."
114 :group 'tramp
115 :type 'integer)
117 (defcustom tramp-debug-buffer nil
118 "*Whether to send all commands and responses to a debug buffer."
119 :group 'tramp
120 :type 'boolean)
122 (defcustom tramp-auto-save-directory nil
123 "*Put auto-save files in this directory, if set.
124 The idea is to use a local directory so that auto-saving is faster."
125 :group 'tramp
126 :type '(choice (const nil)
127 string))
129 (defcustom tramp-sh-program "/bin/sh"
130 "*Use this program for shell commands on the local host.
131 This MUST be a Bourne-like shell. This shell is used to execute
132 the encoding and decoding command on the local host, so if you
133 want to use `~' in those commands, you should choose a shell here
134 which groks tilde expansion. `/bin/sh' normally does not
135 understand tilde expansion.
137 Note that this variable is not used for remote commands. There are
138 mechanisms in tramp.el which automatically determine the right shell to
139 use for the remote host."
140 :group 'tramp
141 :type '(file :must-match t))
143 (defcustom tramp-multi-sh-program
144 (if (memq system-type '(windows-nt))
145 "cmd.exe"
146 tramp-sh-program)
147 "*Use this program for bootstrapping multi-hop connections.
148 This variable is similar to `tramp-sh-program', but it is only used
149 when initializing a multi-hop connection. Therefore, the set of
150 commands sent to this shell is quite restricted, and if you are
151 careful it works to use CMD.EXE under Windows (instead of a Bourne-ish
152 shell which does not normally exist on Windows anyway).
154 To use multi-hop methods from Windows, you also need suitable entries
155 in `tramp-multi-connection-function-alist' for the first hop.
157 This variable defaults to CMD.EXE on Windows NT, and to the value of
158 `tramp-sh-program' on other systems."
159 :group 'tramp
160 :type '(file :must-match t))
162 ;; CCC I have changed all occurrences of comint-quote-filename with
163 ;; tramp-shell-quote-argument, except in tramp-handle-expand-many-files.
164 ;; There, comint-quote-filename was removed altogether. If it turns
165 ;; out to be necessary there, something will need to be done.
166 ;;-(defcustom tramp-file-name-quote-list
167 ;;- '(?] ?[ ?\| ?& ?< ?> ?\( ?\) ?\; ?\ ?\* ?\? ?\! ?\" ?\' ?\` ?# ?\@ ?\+ )
168 ;;- "*Protect these characters from the remote shell.
169 ;;-Any character in this list is quoted (preceded with a backslash)
170 ;;-because it means something special to the shell. This takes effect
171 ;;-when sending file and directory names to the remote shell.
173 ;;-See `comint-file-name-quote-list' for details."
174 ;;- :group 'tramp
175 ;;- :type '(repeat character))
177 (defcustom tramp-methods
178 '( ("rcp" (tramp-connection-function tramp-open-connection-rsh)
179 (tramp-rsh-program "rsh")
180 (tramp-rcp-program "rcp")
181 (tramp-remote-sh "/bin/sh")
182 (tramp-rsh-args nil)
183 (tramp-rcp-args nil)
184 (tramp-rcp-keep-date-arg "-p")
185 (tramp-su-program nil)
186 (tramp-su-args nil)
187 (tramp-telnet-program nil)
188 (tramp-telnet-args nil))
189 ("scp" (tramp-connection-function tramp-open-connection-rsh)
190 (tramp-rsh-program "ssh")
191 (tramp-rcp-program "scp")
192 (tramp-remote-sh "/bin/sh")
193 (tramp-rsh-args ("-e" "none"))
194 (tramp-rcp-args nil)
195 (tramp-rcp-keep-date-arg "-p")
196 (tramp-su-program nil)
197 (tramp-su-args nil)
198 (tramp-telnet-program nil)
199 (tramp-telnet-args nil))
200 ("scp1" (tramp-connection-function tramp-open-connection-rsh)
201 (tramp-rsh-program "ssh")
202 (tramp-rcp-program "scp")
203 (tramp-remote-sh "/bin/sh")
204 (tramp-rsh-args ("-1" "-e" "none"))
205 (tramp-rcp-args ("-1"))
206 (tramp-rcp-keep-date-arg "-p")
207 (tramp-su-program nil)
208 (tramp-su-args nil)
209 (tramp-telnet-program nil)
210 (tramp-telnet-args nil))
211 ("scp2" (tramp-connection-function tramp-open-connection-rsh)
212 (tramp-rsh-program "ssh")
213 (tramp-rcp-program "scp")
214 (tramp-remote-sh "/bin/sh")
215 (tramp-rsh-args ("-2" "-e" "none"))
216 (tramp-rcp-args ("-2"))
217 (tramp-rcp-keep-date-arg "-p")
218 (tramp-su-program nil)
219 (tramp-su-args nil)
220 (tramp-telnet-program nil)
221 (tramp-telnet-args nil))
222 ("scp1-old"
223 (tramp-connection-function tramp-open-connection-rsh)
224 (tramp-rsh-program "ssh1")
225 (tramp-rcp-program "scp1")
226 (tramp-remote-sh "/bin/sh")
227 (tramp-rsh-args ("-e" "none"))
228 (tramp-rcp-args nil)
229 (tramp-rcp-keep-date-arg "-p")
230 (tramp-su-program nil)
231 (tramp-su-args nil)
232 (tramp-telnet-program nil)
233 (tramp-telnet-args nil))
234 ("scp2-old"
235 (tramp-connection-function tramp-open-connection-rsh)
236 (tramp-rsh-program "ssh2")
237 (tramp-rcp-program "scp2")
238 (tramp-remote-sh "/bin/sh")
239 (tramp-rsh-args ("-e" "none"))
240 (tramp-rcp-args nil)
241 (tramp-rcp-keep-date-arg "-p")
242 (tramp-su-program nil)
243 (tramp-su-args nil)
244 (tramp-telnet-program nil)
245 (tramp-telnet-args nil))
246 ("rsync" (tramp-connection-function tramp-open-connection-rsh)
247 (tramp-rsh-program "ssh")
248 (tramp-rcp-program "rsync")
249 (tramp-remote-sh "/bin/sh")
250 (tramp-rsh-args ("-e" "none"))
251 (tramp-rcp-args ("-e" "ssh"))
252 (tramp-rcp-keep-date-arg "-t")
253 (tramp-su-program nil)
254 (tramp-su-args nil)
255 (tramp-telnet-program nil)
256 (tramp-telnet-args nil))
257 ("rsh" (tramp-connection-function tramp-open-connection-rsh)
258 (tramp-rsh-program "rsh")
259 (tramp-rcp-program nil)
260 (tramp-remote-sh "/bin/sh")
261 (tramp-rsh-args nil)
262 (tramp-rcp-args nil)
263 (tramp-rcp-keep-date-arg nil)
264 (tramp-su-program nil)
265 (tramp-su-args nil)
266 (tramp-telnet-program nil)
267 (tramp-telnet-args nil))
268 ("ssh" (tramp-connection-function tramp-open-connection-rsh)
269 (tramp-rsh-program "ssh")
270 (tramp-rcp-program nil)
271 (tramp-remote-sh "/bin/sh")
272 (tramp-rsh-args ("-e" "none"))
273 (tramp-rcp-args nil)
274 (tramp-rcp-keep-date-arg nil)
275 (tramp-su-program nil)
276 (tramp-su-args nil)
277 (tramp-telnet-program nil)
278 (tramp-telnet-args nil))
279 ("ssh1" (tramp-connection-function tramp-open-connection-rsh)
280 (tramp-rsh-program "ssh")
281 (tramp-rcp-program nil)
282 (tramp-remote-sh "/bin/sh")
283 (tramp-rsh-args ("-1" "-e" "none"))
284 (tramp-rcp-args ("-1"))
285 (tramp-rcp-keep-date-arg nil)
286 (tramp-su-program nil)
287 (tramp-su-args nil)
288 (tramp-telnet-program nil)
289 (tramp-telnet-args nil))
290 ("ssh2" (tramp-connection-function tramp-open-connection-rsh)
291 (tramp-rsh-program "ssh")
292 (tramp-rcp-program nil)
293 (tramp-remote-sh "/bin/sh")
294 (tramp-rsh-args ("-2" "-e" "none"))
295 (tramp-rcp-args ("-2"))
296 (tramp-rcp-keep-date-arg nil)
297 (tramp-su-program nil)
298 (tramp-su-args nil)
299 (tramp-telnet-program nil)
300 (tramp-telnet-args nil))
301 ("ssh1-old"
302 (tramp-connection-function tramp-open-connection-rsh)
303 (tramp-rsh-program "ssh1")
304 (tramp-rcp-program nil)
305 (tramp-remote-sh "/bin/sh")
306 (tramp-rsh-args ("-e" "none"))
307 (tramp-rcp-args nil)
308 (tramp-rcp-keep-date-arg nil)
309 (tramp-su-program nil)
310 (tramp-su-args nil)
311 (tramp-telnet-program nil)
312 (tramp-telnet-args nil))
313 ("ssh2-old"
314 (tramp-connection-function tramp-open-connection-rsh)
315 (tramp-rsh-program "ssh2")
316 (tramp-rcp-program nil)
317 (tramp-remote-sh "/bin/sh")
318 (tramp-rsh-args ("-e" "none"))
319 (tramp-rcp-args nil)
320 (tramp-rcp-keep-date-arg nil)
321 (tramp-su-program nil)
322 (tramp-su-args nil)
323 (tramp-telnet-program nil)
324 (tramp-telnet-args nil))
325 ("telnet"
326 (tramp-connection-function tramp-open-connection-telnet)
327 (tramp-rsh-program nil)
328 (tramp-rcp-program nil)
329 (tramp-remote-sh "/bin/sh")
330 (tramp-rsh-args nil)
331 (tramp-rcp-args nil)
332 (tramp-rcp-keep-date-arg nil)
333 (tramp-su-program nil)
334 (tramp-su-args nil)
335 (tramp-telnet-program "telnet")
336 (tramp-telnet-args nil))
337 ("su" (tramp-connection-function tramp-open-connection-su)
338 (tramp-rsh-program nil)
339 (tramp-rcp-program nil)
340 (tramp-remote-sh "/bin/sh")
341 (tramp-rsh-args nil)
342 (tramp-rcp-args nil)
343 (tramp-rcp-keep-date-arg nil)
344 (tramp-su-program "su")
345 (tramp-su-args ("-" "%u"))
346 (tramp-telnet-program nil)
347 (tramp-telnet-args nil))
348 ("sudo" (tramp-connection-function tramp-open-connection-su)
349 (tramp-rsh-program nil)
350 (tramp-rcp-program nil)
351 (tramp-remote-sh "/bin/sh")
352 (tramp-rsh-args nil)
353 (tramp-rcp-args nil)
354 (tramp-rcp-keep-date-arg nil)
355 (tramp-su-program "sudo")
356 (tramp-su-args ("-u" "%u" "-s"))
357 (tramp-telnet-program nil)
358 (tramp-telnet-args nil))
359 ("multi" (tramp-connection-function tramp-open-connection-multi)
360 (tramp-rsh-program nil)
361 (tramp-rcp-program nil)
362 (tramp-remote-sh "/bin/sh")
363 (tramp-rsh-args nil)
364 (tramp-rcp-args nil)
365 (tramp-rcp-keep-date-arg nil)
366 (tramp-su-program nil)
367 (tramp-su-args nil)
368 (tramp-telnet-program nil)
369 (tramp-telnet-args nil))
370 ("scpx" (tramp-connection-function tramp-open-connection-rsh)
371 (tramp-rsh-program "ssh")
372 (tramp-rcp-program "scp")
373 (tramp-remote-sh "/bin/sh")
374 (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh"))
375 (tramp-rcp-args nil)
376 (tramp-rcp-keep-date-arg "-p")
377 (tramp-telnet-program nil)
378 (tramp-telnet-args nil))
379 ("sshx" (tramp-connection-function tramp-open-connection-rsh)
380 (tramp-rsh-program "ssh")
381 (tramp-rcp-program nil)
382 (tramp-remote-sh "/bin/sh")
383 (tramp-rsh-args ("-e" "none" "-t" "-t" "/bin/sh"))
384 (tramp-rcp-args nil)
385 (tramp-rcp-keep-date-arg nil)
386 (tramp-su-program nil)
387 (tramp-su-args nil)
388 (tramp-telnet-program nil)
389 (tramp-telnet-args nil))
390 ("krlogin"
391 (tramp-connection-function tramp-open-connection-rsh)
392 (tramp-rsh-program "krlogin")
393 (tramp-rcp-program nil)
394 (tramp-remote-sh "/bin/sh")
395 (tramp-rsh-args ("-x"))
396 (tramp-rcp-args nil)
397 (tramp-rcp-keep-date-arg nil)
398 (tramp-su-program nil)
399 (tramp-su-args nil)
400 (tramp-telnet-program nil)
401 (tramp-telnet-args nil))
402 ("plink"
403 (tramp-connection-function tramp-open-connection-rsh)
404 (tramp-rsh-program "plink")
405 (tramp-rcp-program nil)
406 (tramp-remote-sh "/bin/sh")
407 (tramp-rsh-args ("-ssh")) ;optionally add "-v"
408 (tramp-rcp-args nil)
409 (tramp-rcp-keep-date-arg nil)
410 (tramp-su-program nil)
411 (tramp-su-args nil)
412 (tramp-telnet-program nil)
413 (tramp-telnet-args nil))
414 ("pscp"
415 (tramp-connection-function tramp-open-connection-rsh)
416 (tramp-rsh-program "plink")
417 (tramp-rcp-program "pscp")
418 (tramp-remote-sh "/bin/sh")
419 (tramp-rsh-args ("-ssh"))
420 (tramp-rcp-args nil)
421 (tramp-rcp-keep-date-arg "-p")
422 (tramp-su-program nil)
423 (tramp-su-args nil)
424 (tramp-telnet-program nil)
425 (tramp-telnet-args nil))
426 ("fcp"
427 (tramp-connection-function tramp-open-connection-rsh)
428 (tramp-rsh-program "fsh")
429 (tramp-rcp-program "fcp")
430 (tramp-remote-sh "/bin/sh -i")
431 (tramp-rsh-args ("sh" "-i"))
432 (tramp-rcp-args nil)
433 (tramp-rcp-keep-date-arg "-p")
434 (tramp-su-program nil)
435 (tramp-su-args nil)
436 (tramp-telnet-program nil)
437 (tramp-telnet-args nil))
439 "*Alist of methods for remote files.
440 This is a list of entries of the form (NAME PARAM1 PARAM2 ...).
441 Each NAME stands for a remote access method. Each PARAM is a
442 pair of the form (KEY VALUE). The following KEYs are defined:
443 * `tramp-connection-function'
444 This specifies the function to use to connect to the remote host.
445 Currently, `tramp-open-connection-rsh', `tramp-open-connection-telnet'
446 and `tramp-open-connection-su' are defined. See the documentation
447 of these functions for more details.
448 * `tramp-remote-sh'
449 This specifies the Bourne shell to use on the remote host. This
450 MUST be a Bourne-like shell. It is normally not necessary to set
451 this to any value other than \"/bin/sh\": tramp wants to use a shell
452 which groks tilde expansion, but it can search for it. Also note
453 that \"/bin/sh\" exists on all Unixen, this might not be true for
454 the value that you decide to use. You Have Been Warned.
455 * `tramp-rsh-program'
456 This specifies the name of the program to use for rsh; this might be
457 the full path to rsh or the name of a workalike program.
458 * `tramp-rsh-args'
459 This specifies the list of arguments to pass to the above
460 mentioned program. Please note that this is a list of arguments,
461 that is, normally you don't want to put \"-a -b\" or \"-f foo\"
462 here. Instead, you want two list elements, one for \"-a\" and one
463 for \"-b\", or one for \"-f\" and one for \"foo\".
464 * `tramp-rcp-program'
465 This specifies the name of the program to use for rcp; this might be
466 the full path to rcp or the name of a workalike program.
467 * `tramp-rcp-args'
468 This specifies the list of parameters to pass to the above mentioned
469 program, the hints for `tramp-rsh-args' also apply here.
470 * `tramp-rcp-keep-date-arg'
471 This specifies the parameter to use for `rcp' when the timestamp
472 of the original file should be kept. For `rcp', use `-p', for
473 `rsync', use `-t'.
474 * `tramp-su-program'
475 This specifies the name of the program to use for `su'.
476 * `tramp-su-args'
477 This specifies the list of arguments to pass to `su'.
478 \"%u\" is replaced by the user name, use \"%%\" for a literal
479 percent character.
480 * `tramp-encoding-command'
481 This specifies a command to use to encode the file contents for
482 transfer. The command should read the raw file contents from
483 standard input and write the encoded file contents to standard
484 output. In this string, the percent escape \"%f\" should be used
485 to indicate the file to convert. Use \"%%\" if you need a literal
486 percent character in your command.
487 * `tramp-decoding-command'
488 This specifies a command to use to decode file contents encoded
489 with `tramp-encoding-command'. The command should read from standard
490 input and write to standard output.
491 * `tramp-encoding-function'
492 This specifies a function to be called to encode the file contents
493 on the local side. This function should accept two arguments
494 START and END, the beginning and end of the region to encode. The
495 region should be replaced with the encoded contents.
496 * `tramp-decoding-function'
497 Same for decoding on the local side.
498 * `tramp-telnet-program'
499 Specifies the telnet program to use when using
500 `tramp-open-connection-telnet' to log in.
501 * `tramp-telnet-args'
502 Specifies list of arguments to pass to `telnet'. The hints for
503 `tramp-rsh-args' also apply here.
505 What does all this mean? Well, you should specify `tramp-rsh-program',
506 `tramp-telnet-program' or `tramp-su-program' for all methods; this program
507 is used to log in to the remote site. Then, there are two ways to
508 actually transfer the files between the local and the remote side.
509 One way is using an additional rcp-like program. If you want to do
510 this, set `tramp-rcp-program' in the method.
512 Another possibility for file transfer is inline transfer, i.e. the
513 file is passed through the same buffer used by `tramp-rsh-program'. In
514 this case, the file contents need to be protected since the
515 `tramp-rsh-program' might use escape codes or the connection might not
516 be eight-bit clean. Therefore, file contents are encoded for transit.
518 Two possibilities for encoding are uuencode/uudecode and mimencode.
519 For uuencode/uudecode you want to set `tramp-encoding-command' to
520 something like \"uuencode\" and `tramp-decoding-command' to \"uudecode
521 -p\". For mimencode you want to set `tramp-encoding-command' to
522 something like \"mimencode -b\" and `tramp-decoding-command' to
523 \"mimencode -b -u\".
525 When using inline transfer, you can use a program or a Lisp function
526 on the local side to encode or decode the file contents. Set the
527 `tramp-encoding-function' and `tramp-decoding-function' parameters to nil
528 in order to use the commands or to the function to use. It is
529 possible to specify one function and the other parameter as nil.
531 So, to summarize: if the method is an inline method, you must specify
532 `tramp-encoding-command' and `tramp-decoding-command', and
533 `tramp-rcp-program' must be nil. If the method is out of band, then
534 you must specify `tramp-rcp-program' and `tramp-rcp-args' and
535 `tramp-encoding-command' and `tramp-decoding-command' must be nil.
536 Every method, inline or out of band, must specify
537 `tramp-connection-function' plus the associated arguments (for
538 example, the telnet program if you chose
539 `tramp-open-connection-telnet').
541 Notes:
543 When using `tramp-open-connection-su' the phrase `open connection to a
544 remote host' sounds strange, but it is used nevertheless, for
545 consistency. No connection is opened to a remote host, but `su' is
546 started on the local host. You are not allowed to specify a remote
547 host other than `localhost' or the name of the local host.
549 Using a uuencode/uudecode inline method is discouraged, please use one
550 of the base64 methods instead since base64 encoding is much more
551 reliable and the commands are more standardized between the different
552 Unix versions. But if you can't use base64 for some reason, please
553 note that the default uudecode command does not work well for some
554 Unices, in particular AIX and Irix. For AIX, you might want to use
555 the following command for uudecode:
557 sed '/^begin/d;/^[` ]$/d;/^end/d' | iconv -f uucode -t ISO8859-1
559 For Irix, no solution is known yet."
560 :group 'tramp
561 :type '(repeat
562 (cons string
563 (set (list (const tramp-connection-function) function)
564 (list (const tramp-rsh-program)
565 (choice (const nil) string))
566 (list (const tramp-rcp-program)
567 (choice (const nil) string))
568 (list (const tramp-remote-sh)
569 (choice (const nil) string))
570 (list (const tramp-rsh-args) (repeat string))
571 (list (const tramp-rcp-args) (repeat string))
572 (list (const tramp-rcp-keep-date-arg)
573 (choice (const nil) string))
574 (list (const tramp-su-program)
575 (choice (const nil) string))
576 (list (const tramp-su-args) (repeat string))
577 (list (const tramp-encoding-command)
578 (choice (const nil) string))
579 (list (const tramp-decoding-command)
580 (choice (const nil) string))
581 (list (const tramp-encoding-function)
582 (choice (const nil) function))
583 (list (const tramp-decoding-function)
584 (choice (const nil) function))
585 (list (const tramp-telnet-program)
586 (choice (const nil) string))
587 (list (const tramp-telnet-args) (repeat string))))))
589 (defcustom tramp-multi-methods '("multi" "multiu")
590 "*List of multi-hop methods.
591 Each entry in this list should be a method name as mentioned in the
592 variable `tramp-methods'."
593 :group 'tramp
594 :type '(repeat string))
596 (defcustom tramp-multi-connection-function-alist
597 '(("telnet" tramp-multi-connect-telnet "telnet %h%n")
598 ("rsh" tramp-multi-connect-rlogin "rsh %h -l %u%n")
599 ("ssh" tramp-multi-connect-rlogin "ssh %h -l %u%n")
600 ("su" tramp-multi-connect-su "su - %u%n")
601 ("sudo" tramp-multi-connect-su "sudo -u %u -s%n"))
602 "*List of connection functions for multi-hop methods.
603 Each list item is a list of three items (METHOD FUNCTION COMMAND),
604 where METHOD is the name as used in the file name, FUNCTION is the
605 function to be executed, and COMMAND is the shell command used for
606 connecting.
608 COMMAND may contain percent escapes. `%u' will be replaced with the
609 user name, `%h' will be replaced with the host name, and `%n' will be
610 replaced with an end-of-line character, as specified in the variable
611 `tramp-rsh-end-of-line'. Use `%%' for a literal percent character.
612 Note that the interpretation of the percent escapes also depends on
613 the FUNCTION. For example, the `%u' escape is forbidden with the
614 function `tramp-multi-connect-telnet'. See the documentation of the
615 various functions for details."
616 :group 'tramp
617 :type '(repeat (list string function string)))
619 (defcustom tramp-default-method "ssh"
620 "*Default method to use for transferring files.
621 See `tramp-methods' for possibilities.
622 Also see `tramp-default-method-alist'.
624 Emacs uses a unified filename syntax for Tramp and Ange-FTP.
625 For backward compatibility, the default value of this variable
626 is \"ftp\" on Emacs. But XEmacs uses a separate filename syntax
627 for Tramp and EFS, so there the default method is \"sm\"."
628 :group 'tramp
629 :type 'string)
631 (defcustom tramp-default-method-alist
632 (if (featurep 'xemacs)
634 '(("\\`ftp\\." "" "ftp")
635 ("" "\\`\\(anonymous\\|ftp\\)\\'" "ftp")))
636 "*Default method to use for specific user/host pairs.
637 This is an alist of items (HOST USER METHOD). The first matching item
638 specifies the method to use for a file name which does not specify a
639 method. HOST and USER are regular expressions or nil, which is
640 interpreted as a regular expression which always matches. If no entry
641 matches, the variable `tramp-default-method' takes effect.
643 If the file name does not specify the user, lookup is done using the
644 empty string for the user name.
646 See `tramp-methods' for a list of possibilities for METHOD."
647 :group 'tramp
648 :type '(repeat (list (regexp :tag "Host regexp")
649 (regexp :tag "User regexp")
650 (string :tag "Method"))))
652 (defcustom tramp-ftp-method "ftp"
653 "*When this method name is used, forward all calls to Ange-FTP."
654 :group 'tramp
655 :type 'string)
657 (defcustom tramp-rsh-end-of-line "\n"
658 "*String used for end of line in rsh connections.
659 I don't think this ever needs to be changed, so please tell me about it
660 if you need to change this."
661 :group 'tramp
662 :type 'string)
664 (defcustom tramp-remote-path
665 '("/bin" "/usr/bin" "/usr/sbin" "/usr/local/bin" "/usr/ccs/bin"
666 "/local/bin" "/local/freeware/bin" "/local/gnu/bin"
667 "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin")
668 "*List of directories to search for executables on remote host.
669 Please notify me about other semi-standard directories to include here.
671 You can use `~' in this list, but when searching for a shell which groks
672 tilde expansion, all directory names starting with `~' will be ignored."
673 :group 'tramp
674 :type '(repeat string))
676 (defcustom tramp-login-prompt-regexp
677 ".*ogin: *"
678 "*Regexp matching login-like prompts.
679 The regexp should match at end of buffer."
680 :group 'tramp
681 :type 'regexp)
683 (defcustom tramp-shell-prompt-pattern
684 "^[^#$%>\n]*[#$%>] *"
685 "Regexp to match prompts from remote shell.
686 Normally, Tramp expects you to configure `shell-prompt-pattern'
687 correctly, but sometimes it happens that you are connecting to a
688 remote host which sends a different kind of shell prompt. Therefore,
689 Tramp recognizes things matched by `shell-prompt-pattern' as prompt,
690 and also things matched by this variable. The default value of this
691 variable is the same as the default value of `shell-prompt-pattern',
692 which should work well in many cases."
693 :group 'tramp
694 :type 'regexp)
696 (defcustom tramp-password-prompt-regexp
697 "^.*\\([pP]assword\\|passphrase.*\\):\^@? *"
698 "*Regexp matching password-like prompts.
699 The regexp should match at end of buffer.
701 The `sudo' program appears to insert a `^@' character into the prompt."
702 :group 'tramp
703 :type 'regexp)
705 (defcustom tramp-wrong-passwd-regexp
706 (concat "^.*"
707 ;; These strings should be on the last line
708 (regexp-opt '("Permission denied."
709 "Login incorrect"
710 "Login Incorrect"
711 "Connection refused"
712 "Connection closed"
713 "Sorry, try again."
714 "Name or service not known"
715 "Host key verification failed.") t)
716 ".*"
717 "\\|"
718 "^.*\\("
719 ;; Here comes a list of regexes, separated by \\|
720 "Received signal [0-9]+"
721 "\\).*")
722 "*Regexp matching a `login failed' message.
723 The regexp should match at end of buffer."
724 :group 'tramp
725 :type 'regexp)
727 (defcustom tramp-yesno-prompt-regexp
728 (concat
729 (regexp-opt '("Are you sure you want to continue connecting (yes/no)?") t)
730 "\\s-*")
731 "Regular expression matching all yes/no queries which need to be confirmed.
732 The confirmation should be done with yes or no.
733 The regexp should match at end of buffer.
734 See also `tramp-yn-prompt-regexp'."
735 :group 'tramp
736 :type 'regexp)
738 (defcustom tramp-yn-prompt-regexp
739 (concat (regexp-opt '("Store key in cache? (y/n)") t)
740 "\\s-*")
741 "Regular expression matching all y/n queries which need to be confirmed.
742 The confirmation should be done with y or n.
743 The regexp should match at end of buffer.
744 See also `tramp-yesno-prompt-regexp'."
745 :group 'tramp
746 :type 'regexp)
749 (defcustom tramp-temp-name-prefix "tramp."
750 "*Prefix to use for temporary files.
751 If this is a relative file name (such as \"tramp.\"), it is considered
752 relative to the directory name returned by the function
753 `tramp-temporary-file-directory' (which see). It may also be an
754 absolute file name; don't forget to include a prefix for the filename
755 part, though."
756 :group 'tramp
757 :type 'string)
759 (defcustom tramp-discard-garbage nil
760 "*If non-nil, try to discard garbage sent by remote shell.
761 Some shells send such garbage upon connection setup."
762 :group 'tramp
763 :type 'boolean)
765 (defcustom tramp-sh-extra-args '(("/bash\\'" . "--norc"))
766 "*Alist specifying extra arguments to pass to the remote shell.
767 Entries are (REGEXP . ARGS) where REGEXP is a regular expression
768 matching the shell file name and ARGS is a string specifying the
769 arguments.
771 This variable is only used when Tramp needs to start up another shell
772 for tilde expansion. The extra arguments should typically prevent the
773 shell from reading its init file."
774 :group 'tramp
775 :type '(alist :key-type string :value-type string))
777 ;; File name format.
779 (defconst tramp-file-name-structure-unified
780 (list (concat "\\`/\\(\\([a-zA-Z0-9-]+\\):\\)?" ;method
781 "\\(\\([^:@/]+\\)@\\)?" ;user
782 "\\([^:/]+\\):" ;host
783 "\\(.*\\)\\'") ;path
784 2 4 5 6)
785 "Default value for `tramp-file-name-structure' for unified remoting.
786 On Emacs (not XEmacs), the Tramp and Ange-FTP packages use a unified
787 filename space. This value is used for this unified namespace.")
789 (defconst tramp-file-name-structure-separate
790 (list (concat "\\`/\\[\\(\\([a-zA-Z0-9-]+\\)/\\)?" ;method
791 "\\(\\([-a-zA-Z0-9_#/:]+\\)@\\)?" ;user
792 "\\([-a-zA-Z0-9_#/:@.]+\\)\\]" ;host
793 "\\(.*\\)\\'") ;path
794 2 4 5 6)
795 "Default value for `tramp-file-name-structure' for separate remoting.
796 On XEmacs, the Tramp and EFS packages use a separate namespace for
797 remote filenames. This value is used in that case. It is designed
798 not to clash with the EFS filename syntax.")
800 (defcustom tramp-file-name-structure
801 (if (featurep 'xemacs)
802 tramp-file-name-structure-separate
803 tramp-file-name-structure-unified)
804 "*List of five elements (REGEXP METHOD USER HOST FILE), detailing \
805 the tramp file name structure.
807 The first element REGEXP is a regular expression matching a tramp file
808 name. The regex should contain parentheses around the method name,
809 the user name, the host name, and the file name parts.
811 The second element METHOD is a number, saying which pair of
812 parentheses matches the method name. The third element USER is
813 similar, but for the user name. The fourth element HOST is similar,
814 but for the host name. The fifth element FILE is for the file name.
815 These numbers are passed directly to `match-string', which see. That
816 means the opening parentheses are counted to identify the pair.
818 See also `tramp-file-name-regexp' and `tramp-make-tramp-file-format'."
819 :group 'tramp
820 :type '(list (regexp :tag "File name regexp")
821 (integer :tag "Paren pair for method name")
822 (integer :tag "Paren pair for user name ")
823 (integer :tag "Paren pair for host name ")
824 (integer :tag "Paren pair for file name ")))
826 ;;;###autoload
827 (defconst tramp-file-name-regexp-unified
828 "\\`/[^/:]+:"
829 "Value for `tramp-file-name-regexp' for unified remoting.
830 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and
831 Tramp. See `tramp-file-name-structure-unified' for more explanations.")
833 ;;;###autoload
834 (defconst tramp-file-name-regexp-separate
835 "\\`/\\[.*\\]"
836 "Value for `tramp-file-name-regexp' for separate remoting.
837 XEmacs uses a separate filename syntax for Tramp and EFS.
838 See `tramp-file-name-structure-separate' for more explanations.")
840 ;;;###autoload
841 (defcustom tramp-file-name-regexp
842 (if (featurep 'xemacs)
843 tramp-file-name-regexp-separate
844 tramp-file-name-regexp-unified)
845 "*Regular expression matching file names handled by tramp.
846 This regexp should match tramp file names but no other file names.
847 \(When tramp.el is loaded, this regular expression is prepended to
848 `file-name-handler-alist', and that is searched sequentially. Thus,
849 if the tramp entry appears rather early in the `file-name-handler-alist'
850 and is a bit too general, then some files might be considered tramp
851 files which are not really tramp files.
853 Please note that the entry in `file-name-handler-alist' is made when
854 this file (tramp.el) is loaded. This means that this variable must be set
855 before loading tramp.el. Alternatively, `file-name-handler-alist' can be
856 updated after changing this variable.
858 Also see `tramp-file-name-structure' and `tramp-make-tramp-file-format'."
859 :group 'tramp
860 :type 'regexp)
862 (defconst tramp-make-tramp-file-format-unified
863 "/%m:%u@%h:%p"
864 "Value for `tramp-make-tramp-file-format' for unified remoting.
865 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and Tramp.
866 See `tramp-file-name-structure-unified' for more details.")
868 (defconst tramp-make-tramp-file-format-separate
869 "/[%m/%u@%h]%p"
870 "Value for `tramp-make-tramp-file-format' for separate remoting.
871 XEmacs uses a separate filename syntax for EFS and Tramp.
872 See `tramp-file-name-structure-separate' for more details.")
874 (defcustom tramp-make-tramp-file-format
875 (if (featurep 'xemacs)
876 tramp-make-tramp-file-format-separate
877 tramp-make-tramp-file-format-unified)
878 "*Format string saying how to construct tramp file name.
879 `%m' is replaced by the method name.
880 `%u' is replaced by the user name.
881 `%h' is replaced by the host name.
882 `%p' is replaced by the file name.
883 `%%' is replaced by %.
885 Also see `tramp-file-name-structure' and `tramp-file-name-regexp'."
886 :group 'tramp
887 :type 'string)
889 (defconst tramp-make-tramp-file-user-nil-format-unified
890 "/%m:%h:%p"
891 "Value of `tramp-make-tramp-file-user-nil-format' for unified remoting.
892 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and Tramp.
893 See `tramp-file-name-structure-unified' for details.")
895 (defconst tramp-make-tramp-file-user-nil-format-separate
896 "/[%m/%h]%p"
897 "Value of `tramp-make-tramp-file-user-nil-format' for separate remoting.
898 XEmacs uses a separate filename syntax for EFS and Tramp.
899 See `tramp-file-name-structure-separate' for details.")
901 (defcustom tramp-make-tramp-file-user-nil-format
902 (if (featurep 'xemacs)
903 tramp-make-tramp-file-user-nil-format-separate
904 tramp-make-tramp-file-user-nil-format-unified)
905 "*Format string saying how to construct tramp file name when the user name is not known.
906 `%m' is replaced by the method name.
907 `%h' is replaced by the host name.
908 `%p' is replaced by the file name.
909 `%%' is replaced by %.
911 Also see `tramp-make-tramp-file-format', `tramp-file-name-structure', and `tramp-file-name-regexp'."
912 :group 'tramp
913 :type 'string)
915 (defconst tramp-multi-file-name-structure-unified
916 (list (concat "\\`/\\(\\([a-zA-Z0-9]+\\)?:\\)" ;method
917 "\\(\\(%s\\)+\\)" ;hops
918 "\\(.*\\)\\'") ;path
919 2 3 -1)
920 "Value for `tramp-multi-file-name-structure' for unified remoting.
921 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and Tramp.
922 See `tramp-file-name-structure-unified' for details.")
924 (defconst tramp-multi-file-name-structure-separate
925 (list (concat
926 ;; prefix
927 "\\`/\\[\\(\\([a-z0-9]+\\)?\\)"
928 ;; regexp specifying the hops
929 "\\(\\(%s\\)+\\)"
930 ;; path name
931 "\\]\\(.*\\)\\'")
932 2 ;number of pair to match method
933 3 ;number of pair to match hops
934 -1) ;number of pair to match path
935 "Value of `tramp-multi-file-name-structure' for separate remoting.
936 XEmacs uses a separate filename syntax for EFS and Tramp.
937 See `tramp-file-name-structure-separate' for details.")
939 (defcustom tramp-multi-file-name-structure
940 (if (featurep 'xemacs)
941 tramp-multi-file-name-structure-separate
942 tramp-multi-file-name-structure-unified)
943 "*Describes the file name structure of `multi' files.
944 Multi files allow you to contact a remote host in several hops.
945 This is a list of four elements (REGEXP METHOD HOP PATH).
947 The first element, REGEXP, gives a regular expression to match against
948 the file name. In this regular expression, `%s' is replaced with the
949 value of `tramp-multi-file-name-hop-structure'. (Note: in order to
950 allow multiple hops, you normally want to use something like
951 \"\\\\(\\\\(%s\\\\)+\\\\)\" in the regular expression. The outer pair
952 of parentheses is used for the HOP element, see below.)
954 All remaining elements are numbers. METHOD gives the number of the
955 paren pair which matches the method name. HOP gives the number of the
956 paren pair which matches the hop sequence. PATH gives the number of
957 the paren pair which matches the path name on the remote host.
959 PATH can also be negative, which means to count from the end. Ie, a
960 value of -1 means the last paren pair.
962 I think it would be good if the regexp matches the whole of the
963 string, but I haven't actually tried what happens if it doesn't..."
964 :group 'tramp
965 :type '(list (regexp :tag "File name regexp")
966 (integer :tag "Paren pair for method name")
967 (integer :tag "Paren pair for hops")
968 (integer :tag "Paren pair to match path")))
970 (defconst tramp-multi-file-name-hop-structure-unified
971 (list (concat "\\([a-zA-z0-9_]+\\):" ;hop method
972 "\\([^@:/]+\\)@" ;user
973 "\\([^:/]+\\):") ;host
974 1 2 3)
975 "Value of `tramp-multi-file-name-hop-structure' for unified remoting.
976 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and Tramp.
977 See `tramp-file-name-structure-unified' for details.")
979 (defconst tramp-multi-file-name-hop-structure-separate
980 (list (concat "/\\([a-z0-9_]+\\):" ;hop method
981 "\\([a-z0-9_]+\\)@" ;user
982 "\\([a-z0-9.-]+\\)") ;host
983 1 2 3)
984 "Value of `tramp-multi-file-name-hop-structure' for separate remoting.
985 XEmacs uses a separate filename syntax for EFS and Tramp.
986 See `tramp-file-name-structure-separate' for details.")
988 (defcustom tramp-multi-file-name-hop-structure
989 (if (featurep 'xemacs)
990 tramp-multi-file-name-hop-structure-separate
991 tramp-multi-file-name-hop-structure-unified)
992 "*Describes the structure of a hop in multi files.
993 This is a list of four elements (REGEXP METHOD USER HOST). First
994 element REGEXP is used to match against the hop. Pair number METHOD
995 matches the method of one hop, pair number USER matches the user of
996 one hop, pair number HOST matches the host of one hop.
998 This regular expression should match exactly all of one hop."
999 :group 'tramp
1000 :type '(list (regexp :tag "Hop regexp")
1001 (integer :tag "Paren pair for method name")
1002 (integer :tag "Paren pair for user name")
1003 (integer :tag "Paren pair for host name")))
1005 (defconst tramp-make-multi-tramp-file-format-unified
1006 (list "/%m" ":%m:%u@%h" ":%p")
1007 "Value of `tramp-make-multi-tramp-file-format' for unified remoting.
1008 Emacs (not XEmacs) uses a unified filename syntax for Ange-FTP and Tramp.
1009 See `tramp-file-name-structure-unified' for details.")
1011 (defconst tramp-make-multi-tramp-file-format-separate
1012 (list "/[%m" "/%m:%u@%h" "]%p")
1013 "Value of `tramp-make-multi-tramp-file-format' for separate remoting.
1014 XEmacs uses a separate filename syntax for EFS and Tramp.
1015 See `tramp-file-name-structure-separate' for details.")
1017 (defcustom tramp-make-multi-tramp-file-format
1018 (if (featurep 'xemacs)
1019 tramp-make-multi-tramp-file-format-separate
1020 tramp-make-multi-tramp-file-format-unified)
1021 "*Describes how to construct a `multi' file name.
1022 This is a list of three elements PREFIX, HOP and PATH.
1024 The first element PREFIX says how to construct the prefix, the second
1025 element HOP specifies what each hop looks like, and the final element
1026 PATH says how to construct the path name.
1028 In PREFIX, `%%' means `%' and `%m' means the method name.
1030 In HOP, `%%' means `%' and `%m', `%u', `%h' mean the hop method, hop
1031 user and hop host, respectively.
1033 In PATH, `%%' means `%' and `%p' means the path name.
1035 The resulting file name always contains one copy of PREFIX and one
1036 copy of PATH, but there is one copy of HOP for each hop in the file
1037 name.
1039 Note: the current implementation requires the prefix to contain the
1040 method name, followed by all the hops, and the path name must come
1041 last."
1042 :group 'tramp
1043 :type '(list string string string))
1045 (defcustom tramp-terminal-type "dumb"
1046 "*Value of TERM environment variable for logging in to remote host.
1047 Because Tramp wants to parse the output of the remote shell, it is easily
1048 confused by ANSI color escape sequences and suchlike. Often, shell init
1049 files conditionalize this setup based on the TERM environment variable."
1050 :group 'tramp
1051 :type 'string)
1053 (defcustom tramp-completion-without-shell-p nil
1054 "*If nil, use shell wildcards for completion, else rely on Lisp only.
1055 Using shell wildcards for completions has the advantage that it can be
1056 fast even in large directories, but completion is always
1057 case-sensitive. Relying on Lisp only means that case-insensitive
1058 completion is possible (subject to the variable `completion-ignore-case'),
1059 but it might be slow on large directories."
1060 :group 'tramp
1061 :type 'boolean)
1063 (defcustom tramp-actions-before-shell
1064 '((tramp-password-prompt-regexp tramp-action-password)
1065 (tramp-login-prompt-regexp tramp-action-login)
1066 (shell-prompt-pattern tramp-action-succeed)
1067 (tramp-shell-prompt-pattern tramp-action-succeed)
1068 (tramp-wrong-passwd-regexp tramp-action-permission-denied)
1069 (tramp-yesno-prompt-regexp tramp-action-yesno)
1070 (tramp-yn-prompt-regexp tramp-action-yn))
1071 "List of pattern/action pairs.
1072 Whenever a pattern matches, the corresponding action is performed.
1073 Each item looks like (PATTERN ACTION).
1075 The PATTERN should be a symbol, a variable. The value of this
1076 variable gives the regular expression to search for. Note that the
1077 regexp must match at the end of the buffer, \"\\'\" is implicitly
1078 appended to it.
1080 The ACTION should also be a symbol, but a function. When the
1081 corresponding PATTERN matches, the ACTION function is called."
1082 :group 'tramp
1083 :type '(repeat (list variable function)))
1085 (defcustom tramp-multi-actions
1086 '((tramp-password-prompt-regexp tramp-multi-action-password)
1087 (tramp-login-prompt-regexp tramp-multi-action-login)
1088 (shell-prompt-pattern tramp-multi-action-succeed)
1089 (tramp-shell-prompt-pattern tramp-multi-action-succeed)
1090 (tramp-wrong-passwd-regexp tramp-multi-action-permission-denied))
1091 "List of pattern/action pairs.
1092 This list is used for each hop in multi-hop connections.
1093 See `tramp-actions-before-shell' for more info."
1094 :group 'tramp
1095 :type '(repeat (list variable function)))
1097 ;;; Internal Variables:
1099 (defvar tramp-buffer-file-attributes nil
1100 "Holds the `ls -ild' output for the current buffer.
1101 This variable is local to each buffer. It is not used if the remote
1102 machine groks Perl. If it is used, it's used as an emulation for
1103 the visited file modtime.")
1104 (make-variable-buffer-local 'tramp-buffer-file-attributes)
1106 (defvar tramp-end-of-output "/////"
1107 "String used to recognize end of output.")
1109 (defvar tramp-connection-function nil
1110 "This internal variable holds a parameter for `tramp-methods'.
1111 In the connection buffer, this variable has the value of the like-named
1112 method parameter, as specified in `tramp-methods' (which see).")
1114 (defvar tramp-remote-sh nil
1115 "This internal variable holds a parameter for `tramp-methods'.
1116 In the connection buffer, this variable has the value of the like-named
1117 method parameter, as specified in `tramp-methods' (which see).")
1119 (defvar tramp-rsh-program nil
1120 "This internal variable holds a parameter for `tramp-methods'.
1121 In the connection buffer, this variable has the value of the like-named
1122 method parameter, as specified in `tramp-methods' (which see).")
1124 (defvar tramp-rsh-args nil
1125 "This internal variable holds a parameter for `tramp-methods'.
1126 In the connection buffer, this variable has the value of the like-named
1127 method parameter, as specified in `tramp-methods' (which see).")
1129 (defvar tramp-rcp-program nil
1130 "This internal variable holds a parameter for `tramp-methods'.
1131 In the connection buffer, this variable has the value of the like-named
1132 method parameter, as specified in `tramp-methods' (which see).")
1134 (defvar tramp-rcp-args nil
1135 "This internal variable holds a parameter for `tramp-methods'.
1136 In the connection buffer, this variable has the value of the like-named
1137 method parameter, as specified in `tramp-methods' (which see).")
1139 (defvar tramp-rcp-keep-date-arg nil
1140 "This internal variable holds a parameter for `tramp-methods'.
1141 In the connection buffer, this variable has the value of the like-named
1142 method parameter, as specified in `tramp-methods' (which see).")
1144 (defvar tramp-encoding-command nil
1145 "This internal variable holds a parameter for `tramp-methods'.
1146 In the connection buffer, this variable has the value of the like-named
1147 method parameter, as specified in `tramp-methods' (which see).")
1149 (defvar tramp-decoding-command nil
1150 "This internal variable holds a parameter for `tramp-methods'.
1151 In the connection buffer, this variable has the value of the like-named
1152 method parameter, as specified in `tramp-methods' (which see).")
1154 (defvar tramp-encoding-function nil
1155 "This internal variable holds a parameter for `tramp-methods'.
1156 In the connection buffer, this variable has the value of the like-named
1157 method parameter, as specified in `tramp-methods' (which see).")
1159 (defvar tramp-decoding-function nil
1160 "This internal variable holds a parameter for `tramp-methods'.
1161 In the connection buffer, this variable has the value of the like-named
1162 method parameter, as specified in `tramp-methods' (which see).")
1164 (defvar tramp-telnet-program nil
1165 "This internal variable holds a parameter for `tramp-methods'.
1166 In the connection buffer, this variable has the value of the like-named
1167 method parameter, as specified in `tramp-methods' (which see).")
1169 (defvar tramp-telnet-args nil
1170 "This internal variable holds a parameter for `tramp-methods'.
1171 In the connection buffer, this variable has the value of the like-named
1172 method parameter, as specified in `tramp-methods' (which see).")
1174 (defvar tramp-su-program nil
1175 "This internal variable holds a parameter for `tramp-methods'.
1176 In the connection buffer, this variable has the value of the like-named
1177 method parameter, as specified in `tramp-methods' (which see).")
1179 ;; CCC `local in each buffer'?
1180 (defvar tramp-ls-command nil
1181 "This command is used to get a long listing with numeric user and group ids.
1182 This variable is automatically made buffer-local to each rsh process buffer
1183 upon opening the connection.")
1185 (defvar tramp-current-multi-method nil
1186 "Name of `multi' connection method for this *tramp* buffer, or nil if not multi.
1187 This variable is automatically made buffer-local to each rsh process buffer
1188 upon opening the connection.")
1190 (defvar tramp-current-method nil
1191 "Connection method for this *tramp* buffer.
1192 This variable is automatically made buffer-local to each rsh process buffer
1193 upon opening the connection.")
1195 (defvar tramp-current-user nil
1196 "Remote login name for this *tramp* buffer.
1197 This variable is automatically made buffer-local to each rsh process buffer
1198 upon opening the connection.")
1200 (defvar tramp-current-host nil
1201 "Remote host for this *tramp* buffer.
1202 This variable is automatically made buffer-local to each rsh process buffer
1203 upon opening the connection.")
1205 (defvar tramp-test-groks-nt nil
1206 "Whether the `test' command groks the `-nt' switch.
1207 \(`test A -nt B' tests if file A is newer than file B.)
1208 This variable is automatically made buffer-local to each rsh process buffer
1209 upon opening the connection.")
1211 (defvar tramp-file-exists-command nil
1212 "Command to use for checking if a file exists.
1213 This variable is automatically made buffer-local to each rsh process buffer
1214 upon opening the connection.")
1216 (defconst tramp-uudecode "\
1217 tramp_uudecode () {
1218 \(echo begin 600 /tmp/tramp.$$; tail +2) | uudecode
1219 cat /tmp/tramp.$$
1220 rm -f /tmp/tramp.$$
1222 "Shell function to implement `uudecode' to standard output.
1223 Many systems support `uudecode -o -' for this or `uudecode -p', but
1224 some systems don't, and for them we have this shell function.")
1226 ;; Perl script to implement `file-attributes' in a Lisp `read'able
1227 ;; output. If you are hacking on this, note that you get *no* output
1228 ;; unless this spits out a complete line, including the '\n' at the
1229 ;; end.
1230 (defconst tramp-perl-file-attributes "\
1231 $f = $ARGV[0];
1232 @s = lstat($f);
1233 if (($s[2] & 0170000) == 0120000) { $l = readlink($f); $l = \"\\\"$l\\\"\"; }
1234 elsif (($s[2] & 0170000) == 040000) { $l = \"t\"; }
1235 else { $l = \"nil\" };
1236 printf(\"(%s %u %d %d (%u %u) (%u %u) (%u %u) %u %u t (%u . %u) (%u %u))\\n\",
1237 $l, $s[3], $s[4], $s[5], $s[8] >> 16 & 0xffff, $s[8] & 0xffff,
1238 $s[9] >> 16 & 0xffff, $s[9] & 0xffff, $s[10] >> 16 & 0xffff, $s[10] & 0xffff,
1239 $s[7], $s[2], $s[1] >> 16 & 0xffff, $s[1] & 0xffff, $s[0] >> 16 & 0xffff, $s[0] & 0xffff);"
1240 "Perl script to produce output suitable for use with `file-attributes'
1241 on the remote file system.")
1243 ;; ;; These two use uu encoding.
1244 ;; (defvar tramp-perl-encode "%s -e'\
1245 ;; print qq(begin 644 xxx\n);
1246 ;; my $s = q();
1247 ;; my $res = q();
1248 ;; while (read(STDIN, $s, 45)) {
1249 ;; print pack(q(u), $s);
1250 ;; }
1251 ;; print qq(`\n);
1252 ;; print qq(end\n);
1253 ;; '"
1254 ;; "Perl program to use for encoding a file.
1255 ;; Escape sequence %s is replaced with name of Perl binary.")
1257 ;; (defvar tramp-perl-decode "%s -ne '
1258 ;; print unpack q(u), $_;
1259 ;; '"
1260 ;; "Perl program to use for decoding a file.
1261 ;; Escape sequence %s is replaced with name of Perl binary.")
1263 ;; These two use base64 encoding.
1264 (defvar tramp-perl-encode-with-module
1265 "perl -MMIME::Base64 -0777 -ne 'print encode_base64($_)'"
1266 "Perl program to use for encoding a file.
1267 Escape sequence %s is replaced with name of Perl binary.
1268 This string is passed to `format', so percent characters need to be doubled.
1269 This implementation requires the MIME::Base64 Perl module to be installed
1270 on the remote host.")
1272 (defvar tramp-perl-decode-with-module
1273 "perl -MMIME::Base64 -0777 -ne 'print decode_base64($_)'"
1274 "Perl program to use for decoding a file.
1275 Escape sequence %s is replaced with name of Perl binary.
1276 This string is passed to `format', so percent characters need to be doubled.
1277 This implementation requires the MIME::Base64 Perl module to be installed
1278 on the remote host.")
1280 (defvar tramp-perl-encode
1281 "%s -e '
1282 # This script contributed by Juanma Barranquero <lektu@terra.es>.
1283 # Copyright (C) 2002 Free Software Foundation, Inc.
1284 use strict;
1286 my %%trans = do {
1287 my $i = 0;
1288 map {(substr(unpack(q(B8), chr $i++), 2, 6), $_)}
1289 split //, q(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/);
1292 binmode(\\*STDIN);
1294 # We read in chunks of 54 bytes, to generate output lines
1295 # of 72 chars (plus end of line)
1296 $/ = \\54;
1298 while (my $data = <STDIN>) {
1299 my $pad = q();
1301 # Only for the last chunk, and only if did not fill the last three-byte packet
1302 if (eof) {
1303 my $mod = length($data) %% 3;
1304 $pad = q(=) x (3 - $mod) if $mod;
1307 # Not the fastest method, but it is simple: unpack to binary string, split
1308 # by groups of 6 bits and convert back from binary to byte; then map into
1309 # the translation table
1310 print
1311 join q(),
1312 map($trans{$_},
1313 (substr(unpack(q(B*), $data) . q(00000), 0, 432) =~ /....../g)),
1314 $pad,
1315 qq(\\n);
1318 "Perl program to use for encoding a file.
1319 Escape sequence %s is replaced with name of Perl binary.
1320 This string is passed to `format', so percent characters need to be doubled.")
1322 (defvar tramp-perl-decode
1323 "%s -e '
1324 # This script contributed by Juanma Barranquero <lektu@terra.es>.
1325 # Copyright (C) 2002 Free Software Foundation, Inc.
1326 use strict;
1328 my %%trans = do {
1329 my $i = 0;
1330 map {($_, sprintf(q(%%06b), $i++))}
1331 split //, q(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)
1334 my %%bytes = map {(unpack(q(B8), chr $_), chr $_)} 0 .. 255;
1336 binmode(\\*STDOUT);
1338 # We are going to accumulate into $pending to accept any line length
1339 # (we do not check they are <= 76 chars as the RFC says)
1340 my $pending = q();
1342 while (my $data = <STDIN>) {
1343 chomp $data;
1345 # If we find one or two =, we have reached the end and
1346 # any following data is to be discarded
1347 my $finished = $data =~ s/(==?).*/$1/;
1348 $pending .= $data;
1350 my $len = length($pending);
1351 my $chunk = substr($pending, 0, $len & ~3, q());
1353 # Easy method: translate from chars to (pregenerated) six-bit packets, join,
1354 # split in 8-bit chunks and convert back to char.
1355 print join q(),
1356 map $bytes{$_},
1357 ((join q(), map {$trans{$_} || q()} split //, $chunk) =~ /......../g);
1359 last if $finished;
1362 "Perl program to use for decoding a file.
1363 Escape sequence %s is replaced with name of Perl binary.
1364 This string is passed to `format', so percent characters need to be doubled.")
1366 ; These values conform to `file-attributes' from XEmacs 21.2.
1367 ; GNU Emacs and other tools not checked.
1368 (defconst tramp-file-mode-type-map '((0 . "-") ; Normal file (SVID-v2 and XPG2)
1369 (1 . "p") ; fifo
1370 (2 . "c") ; character device
1371 (3 . "m") ; multiplexed character device (v7)
1372 (4 . "d") ; directory
1373 (5 . "?") ; Named special file (XENIX)
1374 (6 . "b") ; block device
1375 (7 . "?") ; multiplexed block device (v7)
1376 (8 . "-") ; regular file
1377 (9 . "n") ; network special file (HP-UX)
1378 (10 . "l") ; symlink
1379 (11 . "?") ; ACL shadow inode (Solaris, not userspace)
1380 (12 . "s") ; socket
1381 (13 . "D") ; door special (Solaris)
1382 (14 . "w")) ; whiteout (BSD)
1383 "A list of file types returned from the `stat' system call.
1384 This is used to map a mode number to a permission string.")
1386 (defvar tramp-dos-coding-system
1387 (if (and (fboundp 'coding-system-p)
1388 (funcall 'coding-system-p '(dos)))
1389 'dos
1390 'undecided-dos)
1391 "Some Emacsen know the `dos' coding system, others need `undecided-dos'.")
1393 (defvar tramp-last-cmd-time nil
1394 "Internal Tramp variable recording the time when the last cmd was sent.
1395 This variable is buffer-local in every buffer.")
1396 (make-variable-buffer-local 'tramp-last-cmd-time)
1398 ;; This variable does not have the right value in XEmacs. What should
1399 ;; I use instead of find-operation-coding-system in XEmacs?
1400 (defvar tramp-feature-write-region-fix
1401 (unless (featurep 'xemacs)
1402 (let ((file-coding-system-alist '(("test" emacs-mule))))
1403 (find-operation-coding-system 'write-region 0 0 "" nil "test")))
1404 "Internal variable to say if `write-region' chooses the right coding.
1405 Older versions of Emacs chose the coding system for `write-region' based
1406 on the FILENAME argument, even if VISIT was a string.")
1408 ;; New handlers should be added here. The following operations can be
1409 ;; handled using the normal primitives: file-name-as-directory,
1410 ;; file-name-directory, file-name-nondirectory,
1411 ;; file-name-sans-versions, get-file-buffer.
1412 (defconst tramp-file-name-handler-alist
1414 (load . tramp-handle-load)
1415 (make-symbolic-link . tramp-handle-make-symbolic-link)
1416 (file-name-directory . tramp-handle-file-name-directory)
1417 (file-name-nondirectory . tramp-handle-file-name-nondirectory)
1418 (file-truename . tramp-handle-file-truename)
1419 (file-exists-p . tramp-handle-file-exists-p)
1420 (file-directory-p . tramp-handle-file-directory-p)
1421 (file-executable-p . tramp-handle-file-executable-p)
1422 (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
1423 (file-readable-p . tramp-handle-file-readable-p)
1424 (file-regular-p . tramp-handle-file-regular-p)
1425 (file-symlink-p . tramp-handle-file-symlink-p)
1426 (file-writable-p . tramp-handle-file-writable-p)
1427 (file-ownership-preserved-p . tramp-handle-file-ownership-preserved-p)
1428 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
1429 (file-attributes . tramp-handle-file-attributes)
1430 (file-modes . tramp-handle-file-modes)
1431 (file-directory-files . tramp-handle-file-directory-files)
1432 (directory-files . tramp-handle-directory-files)
1433 (file-name-all-completions . tramp-handle-file-name-all-completions)
1434 (file-name-completion . tramp-handle-file-name-completion)
1435 (add-name-to-file . tramp-handle-add-name-to-file)
1436 (copy-file . tramp-handle-copy-file)
1437 (rename-file . tramp-handle-rename-file)
1438 (set-file-modes . tramp-handle-set-file-modes)
1439 (make-directory . tramp-handle-make-directory)
1440 (delete-directory . tramp-handle-delete-directory)
1441 (delete-file . tramp-handle-delete-file)
1442 (directory-file-name . tramp-handle-directory-file-name)
1443 (shell-command . tramp-handle-shell-command)
1444 (insert-directory . tramp-handle-insert-directory)
1445 (expand-file-name . tramp-handle-expand-file-name)
1446 (file-local-copy . tramp-handle-file-local-copy)
1447 (insert-file-contents . tramp-handle-insert-file-contents)
1448 (write-region . tramp-handle-write-region)
1449 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
1450 (dired-call-process . tramp-handle-dired-call-process)
1451 (dired-recursive-delete-directory
1452 . tramp-handle-dired-recursive-delete-directory)
1453 (set-visited-file-modtime . tramp-handle-set-visited-file-modtime)
1454 (verify-visited-file-modtime . tramp-handle-verify-visited-file-modtime))
1455 "Alist of handler functions.
1456 Operations not mentioned here will be handled by the normal Emacs functions.")
1458 ;;; Internal functions which must come first.
1460 (defsubst tramp-message (level fmt-string &rest args)
1461 "Emit a message depending on verbosity level.
1462 First arg LEVEL says to be quiet if `tramp-verbose' is less than LEVEL. The
1463 message is emitted only if `tramp-verbose' is greater than or equal to LEVEL.
1464 Calls function `message' with FMT-STRING as control string and the remaining
1465 ARGS to actually emit the message (if applicable).
1467 This function expects to be called from the tramp buffer only!"
1468 (when (<= level tramp-verbose)
1469 (apply #'message (concat "tramp: " fmt-string) args)
1470 (when tramp-debug-buffer
1471 (save-excursion
1472 (set-buffer
1473 (tramp-get-debug-buffer
1474 tramp-current-multi-method tramp-current-method
1475 tramp-current-user tramp-current-host))
1476 (goto-char (point-max))
1477 (tramp-insert-with-face
1478 'italic
1479 (concat "# " (apply #'format fmt-string args) "\n"))))))
1481 (defun tramp-message-for-buffer
1482 (multi-method method user host level fmt-string &rest args)
1483 "Like `tramp-message' but temporarily switches to the tramp buffer.
1484 First three args METHOD, USER, and HOST identify the tramp buffer to use,
1485 remaining args passed to `tramp-message'."
1486 (save-excursion
1487 (set-buffer (tramp-get-buffer multi-method method user host))
1488 (apply 'tramp-message level fmt-string args)))
1490 (defsubst tramp-line-end-position nil
1491 "Return point at end of line.
1492 Calls `line-end-position' or `point-at-eol' if defined, else
1493 own implementation."
1494 (cond
1495 ((fboundp 'line-end-position) (funcall 'line-end-position))
1496 ((fboundp 'point-at-eol) (funcall 'point-at-eol))
1497 (t (save-excursion (end-of-line) (point)))))
1499 (defmacro with-parsed-tramp-file-name (filename var &rest body)
1500 "Parse a Tramp filename and make components available in the body.
1502 First arg FILENAME is evaluated and dissected into its components.
1503 Second arg VAR is a symbol. It is used as a variable name to hold
1504 the filename structure. It is also used as a prefix for the variables
1505 holding the components. For example, if VAR is the symbol `foo', then
1506 `foo' will be bound to the whole structure, `foo-multi-method' will
1507 be bound to the multi-method component, and so on for `foo-method',
1508 `foo-user', `foo-host', `foo-path'.
1510 Remaining args are Lisp expressions to be evaluated (inside an implicit
1511 `progn').
1513 If VAR is nil, then we bind `v' to the structure and `multi-method',
1514 `method', `user', `host', `path' to the components."
1515 `(let* ((,(or var 'v) (tramp-dissect-file-name ,filename))
1516 (,(if var (intern (concat (symbol-name var) "-multi-method")) 'multi-method)
1517 (tramp-file-name-multi-method ,(or var 'v)))
1518 (,(if var (intern (concat (symbol-name var) "-method")) 'method)
1519 (tramp-file-name-method ,(or var 'v)))
1520 (,(if var (intern (concat (symbol-name var) "-user")) 'user)
1521 (tramp-file-name-user ,(or var 'v)))
1522 (,(if var (intern (concat (symbol-name var) "-host")) 'host)
1523 (tramp-file-name-host ,(or var 'v)))
1524 (,(if var (intern (concat (symbol-name var) "-path")) 'path)
1525 (tramp-file-name-path ,(or var 'v))))
1526 ,@body))
1528 (put 'with-parsed-tramp-file-name 'lisp-indent-function 2)
1530 ;;; File Name Handler Functions:
1532 (defun tramp-handle-make-symbolic-link
1533 (filename linkname &optional ok-if-already-exists)
1534 "Like `make-symbolic-link' for tramp files.
1535 If LINKNAME is a non-Tramp file, it is used verbatim as the target of
1536 the symlink. If LINKNAME is a Tramp file, only the path component is
1537 used as the target of the symlink.
1539 If LINKNAME is a Tramp file and the path component is relative, then
1540 it is expanded first, before the path component is taken. Note that
1541 this can give surprising results if the user/host for the source and
1542 target of the symlink differ."
1543 (with-parsed-tramp-file-name linkname l
1544 (when (tramp-ange-ftp-file-name-p l-multi-method l-method)
1545 (tramp-invoke-ange-ftp 'make-symbolic-link
1546 filename linkname ok-if-already-exists))
1547 (let ((ln (tramp-get-remote-ln l-multi-method l-method l-user l-host))
1548 (cwd (file-name-directory l-path)))
1549 (unless ln
1550 (signal 'file-error
1551 (list "Making a symbolic link."
1552 "ln(1) does not exist on the remote host.")))
1554 ;; Do the 'confirm if exists' thing.
1555 (when (file-exists-p linkname)
1556 ;; What to do?
1557 (if (or (null ok-if-already-exists) ; not allowed to exist
1558 (and (numberp ok-if-already-exists)
1559 (not (yes-or-no-p
1560 (format
1561 "File %s already exists; make it a link anyway? "
1562 l-path)))))
1563 (signal 'file-already-exists (list "File already exists" l-path))
1564 (delete-file linkname)))
1566 ;; If FILENAME is a Tramp name, use just the path component.
1567 (when (tramp-tramp-file-p filename)
1568 (setq filename (tramp-file-name-path
1569 (tramp-dissect-file-name
1570 (expand-file-name filename)))))
1572 ;; Right, they are on the same host, regardless of user, method, etc.
1573 ;; We now make the link on the remote machine. This will occur as the user
1574 ;; that FILENAME belongs to.
1575 (zerop
1576 (tramp-send-command-and-check
1577 l-multi-method l-method l-user l-host
1578 (format "cd %s && %s -sf %s %s"
1579 cwd ln
1580 filename
1581 l-path)
1582 t)))))
1585 (defun tramp-handle-load (file &optional noerror nomessage nosuffix must-suffix)
1586 "Like `load' for tramp files. Not implemented!"
1587 (unless (file-name-absolute-p file)
1588 (error "Tramp cannot `load' files without absolute path name"))
1589 (with-parsed-tramp-file-name file nil
1590 (when (tramp-ange-ftp-file-name-p multi-method method)
1591 (tramp-invoke-ange-ftp 'load
1592 file noerror nomessage nosuffix must-suffix))
1593 (unless nosuffix
1594 (cond ((file-exists-p (concat file ".elc"))
1595 (setq file (concat file ".elc")))
1596 ((file-exists-p (concat file ".el"))
1597 (setq file (concat file ".el")))))
1598 (when must-suffix
1599 ;; The first condition is always true for absolute file names.
1600 ;; Included for safety's sake.
1601 (unless (or (file-name-directory file)
1602 (string-match "\\.elc?\\'" file))
1603 (error "File `%s' does not include a `.el' or `.elc' suffix"
1604 file)))
1605 (unless noerror
1606 (when (not (file-exists-p file))
1607 (error "Cannot load nonexistant file `%s'" file)))
1608 (if (not (file-exists-p file))
1610 (unless nomessage
1611 (message "Loading %s..." file))
1612 (let ((local-copy (file-local-copy file)))
1613 ;; MUST-SUFFIX doesn't exist on XEmacs, so let it default to nil.
1614 (load local-copy noerror t t)
1615 (delete-file local-copy))
1616 (unless nomessage
1617 (message "Loading %s...done" file))
1618 t)))
1620 ;; Path manipulation functions that grok TRAMP paths...
1621 (defun tramp-handle-file-name-directory (file)
1622 "Like `file-name-directory' but aware of TRAMP files."
1623 ;; everything except the last filename thing is the directory
1624 (with-parsed-tramp-file-name file nil
1625 (when (tramp-ange-ftp-file-name-p multi-method method)
1626 (tramp-invoke-ange-ftp 'file-name-directory file))
1627 ;; For the following condition, two possibilities should be tried:
1628 ;; (1) (string= path "")
1629 ;; (2) (or (string= path "") (string= path "/"))
1630 ;; The second variant fails when completing a "/" directory on
1631 ;; the remote host, that is a filename which looks like
1632 ;; "/user@host:/". But maybe wildcards fail with the first variant.
1633 ;; We should do some investigation.
1634 (if (string= path "")
1635 ;; For a filename like "/[foo]", we return "/". The `else'
1636 ;; case would return "/[foo]" unchanged. But if we do that,
1637 ;; then `file-expand-wildcards' ceases to work. It's not
1638 ;; quite clear to me what's the intuition that tells that this
1639 ;; behavior is the right behavior, but oh, well.
1641 ;; run the command on the path portion only
1642 ;; CCC: This should take into account the remote machine type, no?
1643 ;; --daniel <daniel@danann.net>
1644 (tramp-make-tramp-file-name multi-method method user host
1645 ;; This will not recurse...
1646 (or (file-name-directory path) "")))))
1648 (defun tramp-handle-file-name-nondirectory (file)
1649 "Like `file-name-nondirectory' but aware of TRAMP files."
1650 (with-parsed-tramp-file-name file nil
1651 (when (tramp-ange-ftp-file-name-p multi-method method)
1652 (tramp-invoke-ange-ftp 'file-name-nondirectory file))
1653 (file-name-nondirectory path)))
1655 (defun tramp-handle-file-truename (filename &optional counter prev-dirs)
1656 "Like `file-truename' for tramp files."
1657 (with-parsed-tramp-file-name filename nil
1658 ;; Ange-FTP does not support truename processing, but for
1659 ;; convenience we pretend it did and forward the call to Ange-FTP
1660 ;; anyway. Ange-FTP then just invokes `identity'.
1661 (when (tramp-ange-ftp-file-name-p multi-method method)
1662 (tramp-invoke-ange-ftp 'file-truename filename))
1663 (let* ((steps (tramp-split-string path "/"))
1664 (pathdir (let ((directory-sep-char ?/))
1665 (file-name-as-directory path)))
1666 (is-dir (string= path pathdir))
1667 (thisstep nil)
1668 (numchase 0)
1669 ;; Don't make the following value larger than necessary.
1670 ;; People expect an error message in a timely fashion when
1671 ;; something is wrong; otherwise they might think that Emacs
1672 ;; is hung. Of course, correctness has to come first.
1673 (numchase-limit 20)
1674 (result nil) ;result steps in reverse order
1675 (curstri "")
1676 symlink-target)
1677 (tramp-message-for-buffer
1678 multi-method method user host
1679 10 "Finding true name for `%s'" filename)
1680 (while (and steps (< numchase numchase-limit))
1681 (setq thisstep (pop steps))
1682 (tramp-message-for-buffer
1683 multi-method method user host
1684 10 "Check %s"
1685 (mapconcat 'identity
1686 (append '("") (reverse result) (list thisstep))
1687 "/"))
1688 (setq symlink-target
1689 (nth 0 (tramp-handle-file-attributes
1690 (tramp-make-tramp-file-name
1691 multi-method method user host
1692 (mapconcat 'identity
1693 (append '("")
1694 (reverse result)
1695 (list thisstep))
1696 "/")))))
1697 (cond ((string= "." thisstep)
1698 (tramp-message-for-buffer multi-method method user host
1699 10 "Ignoring step `.'"))
1700 ((string= ".." thisstep)
1701 (tramp-message-for-buffer multi-method method user host
1702 10 "Processing step `..'")
1703 (pop result))
1704 ((stringp symlink-target)
1705 ;; It's a symlink, follow it.
1706 (tramp-message-for-buffer
1707 multi-method method user host
1708 10 "Follow symlink to %s" symlink-target)
1709 (setq numchase (1+ numchase))
1710 (when (file-name-absolute-p symlink-target)
1711 (setq result nil))
1712 (setq steps
1713 (append (tramp-split-string symlink-target "/") steps)))
1715 ;; It's a file.
1716 (setq result (cons thisstep result)))))
1717 (when (>= numchase numchase-limit)
1718 (error "Maximum number (%d) of symlinks exceeded" numchase-limit))
1719 (setq result (reverse result))
1720 (tramp-message-for-buffer
1721 multi-method method user host
1722 10 "True name of `%s' is `%s'"
1723 filename (mapconcat 'identity (cons "" result) "/"))
1724 (tramp-make-tramp-file-name
1725 multi-method method user host
1726 (concat (mapconcat 'identity (cons "" result) "/")
1727 (if is-dir "/" ""))))))
1729 ;; Basic functions.
1731 (defun tramp-handle-file-exists-p (filename)
1732 "Like `file-exists-p' for tramp files."
1733 (with-parsed-tramp-file-name filename nil
1734 (when (tramp-ange-ftp-file-name-p multi-method method)
1735 (tramp-invoke-ange-ftp 'file-exists-p filename))
1736 (save-excursion
1737 (zerop (tramp-send-command-and-check
1738 multi-method method user host
1739 (format
1740 (tramp-get-file-exists-command multi-method method user host)
1741 (tramp-shell-quote-argument path)))))))
1743 ;; CCC: This should check for an error condition and signal failure
1744 ;; when something goes wrong.
1745 ;; Daniel Pittman <daniel@danann.net>
1746 (defun tramp-handle-file-attributes (filename &optional nonnumeric)
1747 "Like `file-attributes' for tramp files.
1748 Optional argument NONNUMERIC means return user and group name
1749 rather than as numbers."
1750 (let (result)
1751 (with-parsed-tramp-file-name filename nil
1752 (when (tramp-ange-ftp-file-name-p multi-method method)
1753 (tramp-invoke-ange-ftp 'file-attributes filename))
1754 (when (tramp-handle-file-exists-p filename)
1755 ;; file exists, find out stuff
1756 (save-excursion
1757 (if (tramp-get-remote-perl multi-method method user host)
1758 (setq result
1759 (tramp-handle-file-attributes-with-perl
1760 multi-method method user host path nonnumeric))
1761 (setq result
1762 (tramp-handle-file-attributes-with-ls
1763 multi-method method user host path nonnumeric))))))
1764 result))
1767 (defun tramp-handle-file-attributes-with-ls
1768 (multi-method method user host path &optional nonnumeric)
1769 "Implement `file-attributes' for tramp files using the ls(1) command."
1770 (let (symlinkp dirp
1771 res-inode res-filemodes res-numlinks
1772 res-uid res-gid res-size res-symlink-target)
1773 (tramp-message-for-buffer multi-method method user host 10
1774 "file attributes with ls: %s"
1775 (tramp-make-tramp-file-name
1776 multi-method method user host path))
1777 (tramp-send-command
1778 multi-method method user host
1779 (format "%s %s %s"
1780 (tramp-get-ls-command multi-method method user host)
1781 (if nonnumeric "-ild" "-ildn")
1782 (tramp-shell-quote-argument path)))
1783 (tramp-wait-for-output)
1784 ;; parse `ls -l' output ...
1785 ;; ... inode
1786 (setq res-inode
1787 (condition-case err
1788 (read (current-buffer))
1789 (invalid-read-syntax
1790 (when (and (equal (cadr err)
1791 "Integer constant overflow in reader")
1792 (string-match
1793 "^[0-9]+\\([0-9][0-9][0-9][0-9][0-9]\\)\\'"
1794 (caddr err)))
1795 (let* ((big (read (substring (caddr err) 0
1796 (match-beginning 1))))
1797 (small (read (match-string 1 (caddr err))))
1798 (twiddle (/ small 65536)))
1799 (cons (+ big twiddle)
1800 (- small (* twiddle 65536))))))))
1801 ;; ... file mode flags
1802 (setq res-filemodes (symbol-name (read (current-buffer))))
1803 ;; ... number links
1804 (setq res-numlinks (read (current-buffer)))
1805 ;; ... uid and gid
1806 (setq res-uid (read (current-buffer)))
1807 (setq res-gid (read (current-buffer)))
1808 (unless nonnumeric
1809 (unless (numberp res-uid) (setq res-uid -1))
1810 (unless (numberp res-gid) (setq res-gid -1)))
1811 ;; ... size
1812 (setq res-size (read (current-buffer)))
1813 ;; From the file modes, figure out other stuff.
1814 (setq symlinkp (eq ?l (aref res-filemodes 0)))
1815 (setq dirp (eq ?d (aref res-filemodes 0)))
1816 ;; if symlink, find out file name pointed to
1817 (when symlinkp
1818 (search-forward "-> ")
1819 (setq res-symlink-target
1820 (buffer-substring (point)
1821 (tramp-line-end-position))))
1822 ;; return data gathered
1823 (list
1824 ;; 0. t for directory, string (name linked to) for symbolic
1825 ;; link, or nil.
1826 (or dirp res-symlink-target nil)
1827 ;; 1. Number of links to file.
1828 res-numlinks
1829 ;; 2. File uid.
1830 res-uid
1831 ;; 3. File gid.
1832 res-gid
1833 ;; 4. Last access time, as a list of two integers. First
1834 ;; integer has high-order 16 bits of time, second has low 16
1835 ;; bits.
1836 ;; 5. Last modification time, likewise.
1837 ;; 6. Last status change time, likewise.
1838 '(0 0) '(0 0) '(0 0) ;CCC how to find out?
1839 ;; 7. Size in bytes (-1, if number is out of range).
1840 res-size
1841 ;; 8. File modes, as a string of ten letters or dashes as in ls -l.
1842 res-filemodes
1843 ;; 9. t iff file's gid would change if file were deleted and
1844 ;; recreated.
1845 nil ;hm?
1846 ;; 10. inode number.
1847 res-inode
1848 ;; 11. Device number.
1849 -1 ;hm?
1852 (defun tramp-handle-file-attributes-with-perl
1853 (multi-method method user host path &optional nonnumeric)
1854 "Implement `file-attributes' for tramp files using a Perl script.
1856 The Perl command is sent to the remote machine when the connection
1857 is initially created and is kept cached by the remote shell."
1858 (tramp-message-for-buffer multi-method method user host 10
1859 "file attributes with perl: %s"
1860 (tramp-make-tramp-file-name
1861 multi-method method user host path))
1862 (tramp-send-command
1863 multi-method method user host
1864 (format "tramp_file_attributes %s"
1865 (tramp-shell-quote-argument path)))
1866 (tramp-wait-for-output)
1867 (let ((result (read (current-buffer))))
1868 (setcar (nthcdr 8 result)
1869 (tramp-file-mode-from-int (nth 8 result)))
1870 result))
1872 (defun tramp-handle-set-visited-file-modtime (&optional time-list)
1873 "Like `set-visited-file-modtime' for tramp files."
1874 (unless (buffer-file-name)
1875 (error "Can't set-visited-file-modtime: buffer `%s' not visiting a file"
1876 (buffer-name)))
1877 (when time-list
1878 (tramp-run-real-handler 'set-visited-file-modtime (list time-list)))
1879 (let ((f (buffer-file-name))
1880 (coding-system-used nil))
1881 (with-parsed-tramp-file-name f nil
1882 ;; This operation is not handled by Ange-FTP! Compare this
1883 ;; behavior with `file-truename' which Ange-FTP does not really
1884 ;; handle, either, but at least it pretends to. I wonder if
1885 ;; Ange-FTP should also pretend to grok
1886 ;; `set-visited-file-modtime', for consistency?
1887 (when (tramp-ange-ftp-file-name-p multi-method method)
1888 (throw 'tramp-forward-to-ange-ftp
1889 (tramp-run-real-handler 'set-visited-file-modtime
1890 (list time-list))))
1891 (let* ((attr (file-attributes f))
1892 (modtime (nth 5 attr)))
1893 ;; We use '(0 0) as a don't-know value. See also
1894 ;; `tramp-handle-file-attributes-with-ls'.
1895 (when (boundp 'last-coding-system-used)
1896 (setq coding-system-used last-coding-system-used))
1897 (if (not (equal modtime '(0 0)))
1898 (tramp-run-real-handler 'set-visited-file-modtime (list modtime))
1899 (save-excursion
1900 (tramp-send-command
1901 multi-method method user host
1902 (format "%s -ild %s"
1903 (tramp-get-ls-command multi-method method user host)
1904 (tramp-shell-quote-argument path)))
1905 (tramp-wait-for-output)
1906 (setq attr (buffer-substring (point)
1907 (progn (end-of-line) (point)))))
1908 (setq tramp-buffer-file-attributes attr))
1909 (when (boundp 'last-coding-system-used)
1910 (setq last-coding-system-used coding-system-used))
1911 nil))))
1913 ;; CCC continue here
1915 ;; This function makes the same assumption as
1916 ;; `tramp-handle-set-visited-file-modtime'.
1917 (defun tramp-handle-verify-visited-file-modtime (buf)
1918 "Like `verify-visited-file-modtime' for tramp files."
1919 (with-current-buffer buf
1920 (let ((f (buffer-file-name)))
1921 (with-parsed-tramp-file-name f nil
1922 (when (tramp-ange-ftp-file-name-p multi-method method)
1923 ;; This one requires a hack since the file name is not passed
1924 ;; on the arg list.
1925 (let ((buffer-file-name (tramp-make-ange-ftp-file-name
1926 user host path)))
1927 (tramp-invoke-ange-ftp 'verify-visited-file-modtime buf)))
1928 (let* ((attr (file-attributes f))
1929 (modtime (nth 5 attr)))
1930 (cond ((and attr (not (equal modtime '(0 0))))
1931 ;; Why does `file-attributes' return a list (HIGH
1932 ;; LOW), but `visited-file-modtime' returns a cons
1933 ;; (HIGH . LOW)?
1934 (let ((mt (visited-file-modtime)))
1935 (< (abs (tramp-time-diff
1936 modtime (list (car mt) (cdr mt)))) 2)))
1937 (attr
1938 (save-excursion
1939 (tramp-send-command
1940 multi-method method user host
1941 (format "%s -ild %s"
1942 (tramp-get-ls-command multi-method method
1943 user host)
1944 (tramp-shell-quote-argument path)))
1945 (tramp-wait-for-output)
1946 (setq attr (buffer-substring
1947 (point) (progn (end-of-line) (point)))))
1948 (equal tramp-buffer-file-attributes attr))
1949 ;; If file does not exist, say it is not modified.
1950 (t nil)))))))
1952 (defadvice clear-visited-file-modtime (after tramp activate)
1953 "Set `tramp-buffer-file-attributes' back to nil.
1954 Tramp uses this variable as an emulation for the actual modtime of the file,
1955 if the remote host can't provide the modtime."
1956 (setq tramp-buffer-file-attributes nil))
1958 (defun tramp-handle-set-file-modes (filename mode)
1959 "Like `set-file-modes' for tramp files."
1960 (with-parsed-tramp-file-name filename nil
1961 (when (tramp-ange-ftp-file-name-p multi-method method)
1962 (tramp-invoke-ange-ftp 'set-file-modes mode filename))
1963 (save-excursion
1964 (unless (zerop (tramp-send-command-and-check
1965 multi-method method user host
1966 (format "chmod %s %s"
1967 (tramp-decimal-to-octal mode)
1968 (tramp-shell-quote-argument path))))
1969 (signal 'file-error
1970 (list "Doing chmod"
1971 ;; FIXME: extract the proper text from chmod's stderr.
1972 "error while changing file's mode"
1973 filename))))))
1975 ;; Simple functions using the `test' command.
1977 (defun tramp-handle-file-executable-p (filename)
1978 "Like `file-executable-p' for tramp files."
1979 (with-parsed-tramp-file-name filename nil
1980 (when (tramp-ange-ftp-file-name-p multi-method method)
1981 (tramp-invoke-ange-ftp 'file-executable-p filename))
1982 (zerop (tramp-run-test "-x" filename))))
1984 (defun tramp-handle-file-readable-p (filename)
1985 "Like `file-readable-p' for tramp files."
1986 (with-parsed-tramp-file-name filename nil
1987 (when (tramp-ange-ftp-file-name-p multi-method method)
1988 (tramp-invoke-ange-ftp 'file-readable-p filename))
1989 (zerop (tramp-run-test "-r" filename))))
1991 (defun tramp-handle-file-accessible-directory-p (filename)
1992 "Like `file-accessible-directory-p' for tramp files."
1993 (with-parsed-tramp-file-name filename nil
1994 (when (tramp-ange-ftp-file-name-p multi-method method)
1995 (tramp-invoke-ange-ftp 'file-accessible-directory-p filename))
1996 (and (zerop (tramp-run-test "-d" filename))
1997 (zerop (tramp-run-test "-r" filename))
1998 (zerop (tramp-run-test "-x" filename)))))
2000 ;; When the remote shell is started, it looks for a shell which groks
2001 ;; tilde expansion. Here, we assume that all shells which grok tilde
2002 ;; expansion will also provide a `test' command which groks `-nt' (for
2003 ;; newer than). If this breaks, tell me about it and I'll try to do
2004 ;; something smarter about it.
2005 (defun tramp-handle-file-newer-than-file-p (file1 file2)
2006 "Like `file-newer-than-file-p' for tramp files."
2007 (cond ((not (file-exists-p file1))
2008 nil)
2009 ((not (file-exists-p file2))
2011 ;; We are sure both files exist at this point. We assume that
2012 ;; both files are Tramp files, otherwise we issue an error
2013 ;; message. Todo: make a better error message.
2015 (save-excursion
2016 (with-parsed-tramp-file-name file1 v1
2017 (with-parsed-tramp-file-name file2 v2
2018 (when (and (tramp-ange-ftp-file-name-p v1-multi-method v1-method)
2019 (tramp-ange-ftp-file-name-p v2-multi-method v2-method))
2020 (tramp-invoke-ange-ftp 'file-newer-than-file-p
2021 file1 file2))
2022 (unless (and (equal v1-multi-method v2-multi-method)
2023 (equal v1-method v2-method)
2024 (equal v1-user v2-user)
2025 (equal v1-host v2-host))
2026 (signal 'file-error
2027 (list "Files must have same method, user, host"
2028 file1 file2)))
2029 (unless (and (tramp-tramp-file-p file1)
2030 (tramp-tramp-file-p file2))
2031 (signal 'file-error
2032 (list "Files must be tramp files on same host"
2033 file1 file2)))
2034 (if (tramp-get-test-groks-nt
2035 v1-multi-method v1-method v1-user v1-host)
2036 (zerop (tramp-run-test2 "test" file1 file2 "-nt"))
2037 (zerop (tramp-run-test2 "tramp_test_nt" file1 file2)))))))))
2039 ;; Functions implemented using the basic functions above.
2041 (defun tramp-handle-file-modes (filename)
2042 "Like `file-modes' for tramp files."
2043 (with-parsed-tramp-file-name filename nil
2044 (when (tramp-ange-ftp-file-name-p multi-method method)
2045 (tramp-invoke-ange-ftp 'file-modes filename))
2046 (when (file-exists-p filename)
2047 (tramp-mode-string-to-int
2048 (nth 8 (tramp-handle-file-attributes filename))))))
2050 (defun tramp-handle-file-directory-p (filename)
2051 "Like `file-directory-p' for tramp files."
2052 ;; Care must be taken that this function returns `t' for symlinks
2053 ;; pointing to directories. Surely the most obvious implementation
2054 ;; would be `test -d', but that returns false for such symlinks.
2055 ;; CCC: Stefan Monnier says that `test -d' follows symlinks. And
2056 ;; I now think he's right. So we could be using `test -d', couldn't
2057 ;; we?
2059 ;; Alternatives: `cd %s', `test -d %s'
2060 (with-parsed-tramp-file-name filename nil
2061 (when (tramp-ange-ftp-file-name-p multi-method method)
2062 (tramp-invoke-ange-ftp 'file-directory-p filename))
2063 (save-excursion
2064 (zerop
2065 (tramp-send-command-and-check
2066 multi-method method user host
2067 (format "test -d %s"
2068 (tramp-shell-quote-argument path))
2069 t))))) ;run command in subshell
2071 (defun tramp-handle-file-regular-p (filename)
2072 "Like `file-regular-p' for tramp files."
2073 (with-parsed-tramp-file-name filename nil
2074 (when (tramp-ange-ftp-file-name-p multi-method method)
2075 (tramp-invoke-ange-ftp 'file-regular-p filename))
2076 (and (tramp-handle-file-exists-p filename)
2077 (eq ?- (aref (nth 8 (tramp-handle-file-attributes filename)) 0)))))
2079 (defun tramp-handle-file-symlink-p (filename)
2080 "Like `file-symlink-p' for tramp files."
2081 (with-parsed-tramp-file-name filename nil
2082 (when (tramp-ange-ftp-file-name-p multi-method method)
2083 (tramp-invoke-ange-ftp 'file-symlink-p filename))
2084 (let ((x (car (tramp-handle-file-attributes filename))))
2085 (when (stringp x) x))))
2087 (defun tramp-handle-file-writable-p (filename)
2088 "Like `file-writable-p' for tramp files."
2089 (with-parsed-tramp-file-name filename nil
2090 (when (tramp-ange-ftp-file-name-p multi-method method)
2091 (tramp-invoke-ange-ftp 'file-writable-p filename))
2092 (if (tramp-handle-file-exists-p filename)
2093 ;; Existing files must be writable.
2094 (zerop (tramp-run-test "-w" filename))
2095 ;; If file doesn't exist, check if directory is writable.
2096 (and (zerop (tramp-run-test
2097 "-d" (tramp-handle-file-name-directory filename)))
2098 (zerop (tramp-run-test
2099 "-w" (tramp-handle-file-name-directory filename)))))))
2101 (defun tramp-handle-file-ownership-preserved-p (filename)
2102 "Like `file-ownership-preserved-p' for tramp files."
2103 (with-parsed-tramp-file-name filename nil
2104 (when (tramp-ange-ftp-file-name-p multi-method method)
2105 (tramp-invoke-ange-ftp 'file-ownership-preserved-p filename))
2106 (or (not (tramp-handle-file-exists-p filename))
2107 ;; Existing files must be writable.
2108 (zerop (tramp-run-test "-O" filename)))))
2110 ;; Other file name ops.
2112 ;; ;; Matthias Köppe <mkoeppe@mail.math.uni-magdeburg.de>
2113 ;; (defun tramp-handle-directory-file-name (directory)
2114 ;; "Like `directory-file-name' for tramp files."
2115 ;; (if (and (eq (aref directory (- (length directory) 1)) ?/)
2116 ;; (not (eq (aref directory (- (length directory) 2)) ?:)))
2117 ;; (substring directory 0 (- (length directory) 1))
2118 ;; directory))
2120 ;; Philippe Troin <phil@fifi.org>
2121 (defun tramp-handle-directory-file-name (directory)
2122 "Like `directory-file-name' for tramp files."
2123 (with-parsed-tramp-file-name directory nil
2124 (when (tramp-ange-ftp-file-name-p multi-method method)
2125 (tramp-invoke-ange-ftp 'directory-file-name directory))
2126 (let ((directory-length-1 (1- (length directory))))
2127 (save-match-data
2128 (if (and (eq (aref directory directory-length-1) ?/)
2129 (eq (string-match tramp-file-name-regexp directory) 0)
2130 (/= (match-end 0) directory-length-1))
2131 (substring directory 0 directory-length-1)
2132 directory)))))
2134 ;; Directory listings.
2136 (defun tramp-handle-directory-files (directory
2137 &optional full match nosort files-only)
2138 "Like `directory-files' for tramp files."
2139 (with-parsed-tramp-file-name directory nil
2140 (when (tramp-ange-ftp-file-name-p multi-method method)
2141 (tramp-invoke-ange-ftp 'directory-files
2142 directory full match nosort files-only))
2143 (let (result x)
2144 (save-excursion
2145 (tramp-barf-unless-okay
2146 multi-method method user host
2147 (concat "cd " (tramp-shell-quote-argument path))
2149 'file-error
2150 "tramp-handle-directory-files: couldn't `cd %s'"
2151 (tramp-shell-quote-argument path))
2152 (tramp-send-command
2153 multi-method method user host
2154 (concat (tramp-get-ls-command multi-method method user host)
2155 " -a | cat"))
2156 (tramp-wait-for-output)
2157 (goto-char (point-max))
2158 (while (zerop (forward-line -1))
2159 (setq x (buffer-substring (point)
2160 (tramp-line-end-position)))
2161 (when (or (not match) (string-match match x))
2162 (if full
2163 (push (concat (file-name-as-directory directory)
2165 result)
2166 (push x result))))
2167 (tramp-send-command multi-method method user host "cd")
2168 (tramp-wait-for-output)
2169 ;; Remove non-files or non-directories if necessary. Using
2170 ;; the remote shell for this would probably be way faster.
2171 ;; Maybe something could be adapted from
2172 ;; tramp-handle-file-name-all-completions.
2173 (when files-only
2174 (let ((temp (nreverse result))
2175 item)
2176 (setq result nil)
2177 (if (equal files-only t)
2178 ;; files only
2179 (while temp
2180 (setq item (pop temp))
2181 (when (file-regular-p item)
2182 (push item result)))
2183 ;; directories only
2184 (while temp
2185 (setq item (pop temp))
2186 (when (file-directory-p item)
2187 (push item result)))))))
2188 result)))
2190 ;; This function should return "foo/" for directories and "bar" for
2191 ;; files. We use `ls -ad' to get a list of files (including
2192 ;; directories), and `find . -type d \! -name . -prune' to get a list
2193 ;; of directories.
2194 (defun tramp-handle-file-name-all-completions (filename directory)
2195 "Like `file-name-all-completions' for tramp files."
2196 (with-parsed-tramp-file-name directory nil
2197 (when (tramp-ange-ftp-file-name-p multi-method method)
2198 (tramp-invoke-ange-ftp 'file-name-all-completions
2199 filename directory))
2200 (unless (save-match-data (string-match "/" filename))
2201 (let* ((nowild tramp-completion-without-shell-p)
2202 result)
2203 (save-excursion
2204 (tramp-barf-unless-okay
2205 multi-method method user host
2206 (format "cd %s" (tramp-shell-quote-argument path))
2207 nil 'file-error
2208 "tramp-handle-file-name-all-completions: Couldn't `cd %s'"
2209 (tramp-shell-quote-argument path))
2211 ;; Get a list of directories and files, including reliably
2212 ;; tagging the directories with a trailing '/'. Because I
2213 ;; rock. --daniel@danann.net
2214 (tramp-send-command
2215 multi-method method user host
2216 (format (concat "%s -a %s 2>/dev/null | while read f; do "
2217 "if test -d \"$f\" 2>/dev/null; "
2218 "then echo \"$f/\"; else echo \"$f\"; fi; done")
2219 (tramp-get-ls-command multi-method method user host)
2220 (if (or nowild (zerop (length filename)))
2222 (format "-d %s*"
2223 (tramp-shell-quote-argument filename)))))
2225 ;; Now grab the output.
2226 (tramp-wait-for-output)
2227 (goto-char (point-max))
2228 (while (zerop (forward-line -1))
2229 (push (buffer-substring (point)
2230 (tramp-line-end-position))
2231 result))
2233 (tramp-send-command multi-method method user host "cd")
2234 (tramp-wait-for-output)
2236 ;; Return the list.
2237 (if nowild
2238 (all-completions filename (mapcar 'list result))
2239 result))))))
2242 ;; The following isn't needed for Emacs 20 but for 19.34?
2243 (defun tramp-handle-file-name-completion (filename directory)
2244 "Like `file-name-completion' for tramp files."
2245 (unless (tramp-tramp-file-p directory)
2246 (error
2247 "tramp-handle-file-name-completion invoked on non-tramp directory `%s'"
2248 directory))
2249 (with-parsed-tramp-file-name directory nil
2250 (when (tramp-ange-ftp-file-name-p multi-method method)
2251 (tramp-invoke-ange-ftp 'file-name-completion
2252 filename directory))
2253 (try-completion
2254 filename
2255 (mapcar (lambda (x) (cons x nil))
2256 (tramp-handle-file-name-all-completions filename directory)))))
2258 ;; cp, mv and ln
2260 (defun tramp-handle-add-name-to-file
2261 (filename newname &optional ok-if-already-exists)
2262 "Like `add-name-to-file' for tramp files."
2263 (with-parsed-tramp-file-name filename v1
2264 (with-parsed-tramp-file-name newname v2
2265 (let ((ln (when v1 (tramp-get-remote-ln
2266 v1-multi-method v1-method v1-user v1-host))))
2267 (unless (and v1-method v2-method v1-user v2-user v1-host v2-host
2268 (equal v1-multi-method v2-multi-method)
2269 (equal v1-method v2-method)
2270 (equal v1-user v2-user)
2271 (equal v1-host v2-host))
2272 (error "add-name-to-file: %s"
2273 "only implemented for same method, same user, same host"))
2274 (when (and (tramp-ange-ftp-file-name-p v1-multi-method v1-method)
2275 (tramp-ange-ftp-file-name-p v2-multi-method v2-method))
2276 (tramp-invoke-ange-ftp 'add-name-to-file
2277 filename newname ok-if-already-exists))
2278 (when (tramp-ange-ftp-file-name-p v1-multi-method v1-method)
2279 (tramp-invoke-ange-ftp 'add-name-to-file
2280 filename newname ok-if-already-exists))
2281 (when (tramp-ange-ftp-file-name-p v2-multi-method v2-method)
2282 (tramp-invoke-ange-ftp 'add-name-to-file
2283 filename newname ok-if-already-exists))
2284 (when (and (not ok-if-already-exists)
2285 (file-exists-p newname)
2286 (not (numberp ok-if-already-exists))
2287 (y-or-n-p
2288 (format
2289 "File %s already exists; make it a new name anyway? "
2290 newname)))
2291 (error "add-name-to-file: file %s already exists" newname))
2292 (tramp-barf-unless-okay
2293 v1-multi-method v1-method v1-user v1-host
2294 (format "%s %s %s" ln (tramp-shell-quote-argument v1-path)
2295 (tramp-shell-quote-argument v2-path))
2296 nil 'file-error
2297 "error with add-name-to-file, see buffer `%s' for details"
2298 (buffer-name))))))
2300 (defun tramp-handle-copy-file
2301 (filename newname &optional ok-if-already-exists keep-date)
2302 "Like `copy-file' for tramp files."
2303 ;; Check if both files are local -- invoke normal copy-file.
2304 ;; Otherwise, use tramp from local system.
2305 (setq filename (expand-file-name filename))
2306 (setq newname (expand-file-name newname))
2307 ;; At least one file a tramp file?
2308 (if (or (tramp-tramp-file-p filename)
2309 (tramp-tramp-file-p newname))
2310 (tramp-do-copy-or-rename-file
2311 'copy filename newname ok-if-already-exists keep-date)
2312 (tramp-run-real-handler
2313 'copy-file
2314 (list filename newname ok-if-already-exists keep-date))))
2316 (defun tramp-handle-rename-file
2317 (filename newname &optional ok-if-already-exists)
2318 "Like `rename-file' for tramp files."
2319 ;; Check if both files are local -- invoke normal rename-file.
2320 ;; Otherwise, use tramp from local system.
2321 (setq filename (expand-file-name filename))
2322 (setq newname (expand-file-name newname))
2323 ;; At least one file a tramp file?
2324 (if (or (tramp-tramp-file-p filename)
2325 (tramp-tramp-file-p newname))
2326 (tramp-do-copy-or-rename-file
2327 'rename filename newname ok-if-already-exists)
2328 (tramp-run-real-handler 'rename-file
2329 (list filename newname ok-if-already-exists))))
2331 (defun tramp-do-copy-or-rename-file
2332 (op filename newname &optional ok-if-already-exists keep-date)
2333 "Copy or rename a remote file.
2334 OP must be `copy' or `rename' and indicates the operation to perform.
2335 FILENAME specifies the file to copy or rename, NEWNAME is the name of
2336 the new file (for copy) or the new name of the file (for rename).
2337 OK-IF-ALREADY-EXISTS means don't barf if NEWNAME exists already.
2338 KEEP-DATE means to make sure that NEWNAME has the same timestamp
2339 as FILENAME.
2341 This function is invoked by `tramp-handle-copy-file' and
2342 `tramp-handle-rename-file'. It is an error if OP is neither of `copy'
2343 and `rename'. FILENAME and NEWNAME must be absolute file names."
2344 (unless (memq op '(copy rename))
2345 (error "Unknown operation `%s', must be `copy' or `rename'" op))
2346 (unless ok-if-already-exists
2347 (when (file-exists-p newname)
2348 (signal 'file-already-exists
2349 (list newname))))
2350 (let ((t1 (tramp-tramp-file-p filename))
2351 (t2 (tramp-tramp-file-p newname)))
2352 ;; Check which ones of source and target are Tramp files.
2353 (cond
2354 ((and t1 t2)
2355 ;; Both are Tramp files.
2356 (with-parsed-tramp-file-name filename v1
2357 (with-parsed-tramp-file-name newname v2
2358 ;; Possibly invoke Ange-FTP.
2359 (when (and (tramp-ange-ftp-file-name-p v1-multi-method v1-method)
2360 (tramp-ange-ftp-file-name-p v2-multi-method v2-method))
2361 (if (eq op 'copy)
2362 (tramp-invoke-ange-ftp
2363 'copy-file filename newname ok-if-already-exists keep-date)
2364 (tramp-invoke-ange-ftp
2365 'rename-file filename newname ok-if-already-exists)))
2366 ;; Check if we can use a shortcut.
2367 (if (and (equal v1-multi-method v2-multi-method)
2368 (equal v1-method v2-method)
2369 (equal v1-host v2-host)
2370 (equal v1-user v2-user))
2371 ;; Shortcut: if method, host, user are the same for both
2372 ;; files, we invoke `cp' or `mv' on the remote host
2373 ;; directly.
2374 (tramp-do-copy-or-rename-file-directly
2375 op v1-multi-method v1-method v1-user v1-host
2376 v1-path v2-path keep-date)
2377 ;; The shortcut was not possible. So we copy the
2378 ;; file first. If the operation was `rename', we go
2379 ;; back and delete the original file (if the copy was
2380 ;; successful). The approach is simple-minded: we
2381 ;; create a new buffer, insert the contents of the
2382 ;; source file into it, then write out the buffer to
2383 ;; the target file. The advantage is that it doesn't
2384 ;; matter which filename handlers are used for the
2385 ;; source and target file.
2387 ;; CCC: If both source and target are Tramp files,
2388 ;; and both are using the same rcp-program, then we
2389 ;; can invoke rcp directly. Note that
2390 ;; default-directory should point to a local
2391 ;; directory if we want to invoke rcp.
2392 (tramp-do-copy-or-rename-via-buffer
2393 op filename newname keep-date)))))
2394 ((or t1 t2)
2395 ;; Use the generic method via a Tramp buffer.
2396 (tramp-do-copy-or-rename-via-buffer op filename newname keep-date))
2398 ;; One of them must be a Tramp file.
2399 (error "Tramp implementation says this cannot happen")))))
2401 (defun tramp-do-copy-or-rename-via-buffer (op filename newname keep-date)
2402 "Use an Emacs buffer to copy or rename a file.
2403 First arg OP is either `copy' or `rename' and indicates the operation.
2404 FILENAME is the source file, NEWNAME the target file.
2405 KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
2406 (let ((trampbuf (get-buffer-create "*tramp output*")))
2407 (when keep-date
2408 (tramp-message
2409 1 (concat "Warning: cannot preserve file time stamp"
2410 " with inline copying across machines")))
2411 (save-excursion
2412 (set-buffer trampbuf) (erase-buffer)
2413 (insert-file-contents-literally filename)
2414 (let ((coding-system-for-write 'no-conversion))
2415 (write-region (point-min) (point-max) newname)))
2416 ;; If the operation was `rename', delete the original file.
2417 (unless (eq op 'copy)
2418 (delete-file filename))))
2420 (defun tramp-do-copy-or-rename-file-directly
2421 (op multi-method method user host path1 path2 keep-date)
2422 "Invokes `cp' or `mv' on the remote system.
2423 OP must be one of `copy' or `rename', indicating `cp' or `mv',
2424 respectively. METHOD, USER, and HOST specify the connection.
2425 PATH1 and PATH2 specify the two arguments of `cp' or `mv'.
2426 If KEEP-DATE is non-nil, preserve the time stamp when copying."
2427 ;; CCC: What happens to the timestamp when renaming?
2428 (let ((cmd (cond ((and (eq op 'copy) keep-date) "cp -f -p")
2429 ((eq op 'copy) "cp -f")
2430 ((eq op 'rename) "mv -f")
2431 (t (error
2432 "Unknown operation `%s', must be `copy' or `rename'"
2433 op)))))
2434 (save-excursion
2435 (tramp-barf-unless-okay
2436 multi-method method user host
2437 (format "%s %s %s"
2439 (tramp-shell-quote-argument path1)
2440 (tramp-shell-quote-argument path2))
2441 nil 'file-error
2442 "Copying directly failed, see buffer `%s' for details."
2443 (buffer-name)))))
2445 ;; mkdir
2446 (defun tramp-handle-make-directory (dir &optional parents)
2447 "Like `make-directory' for tramp files."
2448 (setq dir (expand-file-name dir))
2449 (with-parsed-tramp-file-name dir nil
2450 (when (tramp-ange-ftp-file-name-p multi-method method)
2451 (tramp-invoke-ange-ftp 'make-directory dir parents))
2452 (save-excursion
2453 (tramp-barf-unless-okay
2454 multi-method method user host
2455 (format " %s %s"
2456 (if parents "mkdir -p" "mkdir")
2457 (tramp-shell-quote-argument path))
2458 nil 'file-error
2459 "Couldn't make directory %s" dir))))
2461 ;; CCC error checking?
2462 (defun tramp-handle-delete-directory (directory)
2463 "Like `delete-directory' for tramp files."
2464 (setq directory (expand-file-name directory))
2465 (with-parsed-tramp-file-name directory nil
2466 (when (tramp-ange-ftp-file-name-p multi-method method)
2467 (tramp-invoke-ange-ftp 'delete-directory directory))
2468 (save-excursion
2469 (tramp-send-command
2470 multi-method method user host
2471 (format "rmdir %s ; echo ok"
2472 (tramp-shell-quote-argument path)))
2473 (tramp-wait-for-output))))
2475 (defun tramp-handle-delete-file (filename)
2476 "Like `delete-file' for tramp files."
2477 (setq filename (expand-file-name filename))
2478 (with-parsed-tramp-file-name filename nil
2479 (when (tramp-ange-ftp-file-name-p multi-method method)
2480 (tramp-invoke-ange-ftp 'delete-file filename))
2481 (save-excursion
2482 (unless (zerop (tramp-send-command-and-check
2483 multi-method method user host
2484 (format "rm -f %s"
2485 (tramp-shell-quote-argument path))))
2486 (signal 'file-error "Couldn't delete Tramp file")))))
2488 ;; Dired.
2490 ;; CCC: This does not seem to be enough. Something dies when
2491 ;; we try and delete two directories under TRAMP :/
2492 (defun tramp-handle-dired-recursive-delete-directory (filename)
2493 "Recursively delete the directory given.
2494 This is like `dired-recursive-delete-directory' for tramp files."
2495 (with-parsed-tramp-file-name filename nil
2496 (when (tramp-ange-ftp-file-name-p multi-method method)
2497 (tramp-invoke-ange-ftp 'dired-recursive-delete-directory
2498 filename))
2499 ;; run a shell command 'rm -r <path>'
2500 ;; Code shamelessly stolen for the dired implementation and, um, hacked :)
2501 (or (tramp-handle-file-exists-p filename)
2502 (signal
2503 'file-error
2504 (list "Removing old file name" "no such directory" filename)))
2505 ;; Which is better, -r or -R? (-r works for me <daniel@danann.net>)
2506 (tramp-send-command multi-method method user host
2507 (format "rm -r %s" (tramp-shell-quote-argument path)))
2508 ;; Wait for the remote system to return to us...
2509 ;; This might take a while, allow it plenty of time.
2510 (tramp-wait-for-output 120)
2511 ;; Make sure that it worked...
2512 (and (tramp-handle-file-exists-p filename)
2513 (error "Failed to recusively delete %s" filename))))
2516 (defun tramp-handle-dired-call-process (program discard &rest arguments)
2517 "Like `dired-call-process' for tramp files."
2518 (with-parsed-tramp-file-name default-directory nil
2519 (when (tramp-ange-ftp-file-name-p multi-method method)
2520 (let ((default-directory
2521 (tramp-make-ange-ftp-file-name user host path)))
2522 (tramp-invoke-ange-ftp 'dired-call-process
2523 program discard arguments)))
2524 (save-excursion
2525 (tramp-barf-unless-okay
2526 multi-method method user host
2527 (format "cd %s" (tramp-shell-quote-argument path))
2528 nil 'file-error
2529 "tramp-handle-dired-call-process: Couldn't `cd %s'"
2530 (tramp-shell-quote-argument path))
2531 (tramp-send-command
2532 multi-method method user host
2533 (mapconcat #'tramp-shell-quote-argument (cons program arguments) " "))
2534 (tramp-wait-for-output))
2535 (unless discard
2536 (insert-buffer (tramp-get-buffer multi-method method user host)))
2537 (save-excursion
2538 (prog1
2539 (tramp-send-command-and-check multi-method method user host nil)
2540 (tramp-send-command multi-method method user host "cd")
2541 (tramp-wait-for-output)))))
2543 ;; Pacify byte-compiler. The function is needed on XEmacs only. I'm
2544 ;; not sure at all that this is the right way to do it, but let's hope
2545 ;; it works for now, and wait for a guru to point out the Right Way to
2546 ;; achieve this.
2547 ;;(eval-when-compile
2548 ;; (unless (fboundp 'dired-insert-set-properties)
2549 ;; (fset 'dired-insert-set-properties 'ignore)))
2550 ;; Gerd suggests this:
2551 (eval-when-compile (require 'dired))
2552 ;; Note that dired is required at run-time, too, when it is needed.
2553 ;; It is only needed on XEmacs for the function
2554 ;; `dired-insert-set-properties'.
2556 (defun tramp-handle-insert-directory
2557 (filename switches &optional wildcard full-directory-p)
2558 "Like `insert-directory' for tramp files."
2559 (setq filename (expand-file-name filename))
2560 (with-parsed-tramp-file-name filename nil
2561 (when (tramp-ange-ftp-file-name-p multi-method method)
2562 (tramp-invoke-ange-ftp 'insert-directory
2563 filename switches wildcard full-directory-p))
2564 (tramp-message-for-buffer
2565 multi-method method user host 10
2566 "Inserting directory `ls %s %s', wildcard %s, fulldir %s"
2567 switches filename (if wildcard "yes" "no")
2568 (if full-directory-p "yes" "no"))
2569 (when wildcard
2570 (setq wildcard (file-name-nondirectory path))
2571 (setq path (file-name-directory path)))
2572 (when (listp switches)
2573 (setq switches (mapconcat 'identity switches " ")))
2574 (unless full-directory-p
2575 (setq switches (concat "-d " switches)))
2576 (when wildcard
2577 (setq switches (concat switches " " wildcard)))
2578 (save-excursion
2579 ;; If `full-directory-p', we just say `ls -l FILENAME'.
2580 ;; Else we chdir to the parent directory, then say `ls -ld BASENAME'.
2581 (if full-directory-p
2582 (tramp-send-command
2583 multi-method method user host
2584 (format "%s %s %s"
2585 (tramp-get-ls-command multi-method method user host)
2586 switches
2587 (if wildcard
2588 path
2589 (tramp-shell-quote-argument (concat path ".")))))
2590 (tramp-barf-unless-okay
2591 multi-method method user host
2592 (format "cd %s" (tramp-shell-quote-argument
2593 (file-name-directory path)))
2594 nil 'file-error
2595 "Couldn't `cd %s'"
2596 (tramp-shell-quote-argument (file-name-directory path)))
2597 (tramp-send-command
2598 multi-method method user host
2599 (format "%s %s %s"
2600 (tramp-get-ls-command multi-method method user host)
2601 switches
2602 (if full-directory-p
2603 ;; Add "/." to make sure we got complete dir
2604 ;; listing for symlinks, too.
2605 (concat (file-name-as-directory
2606 (file-name-nondirectory path)) ".")
2607 (file-name-nondirectory path)))))
2608 (sit-for 1) ;needed for rsh but not ssh?
2609 (tramp-wait-for-output))
2610 (insert-buffer (tramp-get-buffer multi-method method user host))
2611 ;; On XEmacs, we want to call (exchange-point-and-mark t), but
2612 ;; that doesn't exist on Emacs, so we use this workaround instead.
2613 ;; Since zmacs-region-stays doesn't exist in Emacs, this ought to
2614 ;; be safe. Thanks to Daniel Pittman <daniel@danann.net>.
2615 (let ((zmacs-region-stays t))
2616 (exchange-point-and-mark))
2617 (save-excursion
2618 (tramp-send-command multi-method method user host "cd")
2619 (tramp-wait-for-output))
2620 ;; Another XEmacs specialty follows. What's the right way to do
2621 ;; it?
2622 (when (and (featurep 'xemacs)
2623 (eq major-mode 'dired-mode))
2624 (save-excursion
2625 (require 'dired)
2626 (dired-insert-set-properties (point) (mark t))))))
2628 ;; Continuation of kluge to pacify byte-compiler.
2629 ;;(eval-when-compile
2630 ;; (when (eq (symbol-function 'dired-insert-set-properties) 'ignore)
2631 ;; (fmakunbound 'dired-insert-set-properties)))
2633 ;; CCC is this the right thing to do?
2634 (defun tramp-handle-unhandled-file-name-directory (filename)
2635 "Like `unhandled-file-name-directory' for tramp files."
2636 (with-parsed-tramp-file-name filename nil
2637 (when (tramp-ange-ftp-file-name-p multi-method method)
2638 (tramp-invoke-ange-ftp 'unhandled-file-name-directory
2639 filename))
2640 (expand-file-name "~/")))
2642 ;; Canonicalization of file names.
2644 (defun tramp-drop-volume-letter (name)
2645 "Cut off unnecessary drive letter from file NAME.
2646 The function `tramp-handle-expand-file-name' calls `expand-file-name'
2647 locally on a remote file name. When the local system is a W32 system
2648 but the remote system is Unix, this introduces a superfluous drive
2649 letter into the file name. This function removes it.
2651 Doesn't do anything if the NAME does not start with a drive letter."
2652 (if (and (> (length name) 1)
2653 (char-equal (aref name 1) ?:)
2654 (let ((c1 (aref name 0)))
2655 (or (and (>= c1 ?A) (<= c1 ?Z))
2656 (and (>= c1 ?a) (<= c1 ?z)))))
2657 (substring name 2)
2658 name))
2660 (defun tramp-handle-expand-file-name (name &optional dir)
2661 "Like `expand-file-name' for tramp files."
2662 ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
2663 (setq dir (or dir default-directory "/"))
2664 ;; Unless NAME is absolute, concat DIR and NAME.
2665 (unless (file-name-absolute-p name)
2666 (setq name (concat (file-name-as-directory dir) name)))
2667 ;; If NAME is not a tramp file, run the real handler
2668 (if (not (tramp-tramp-file-p name))
2669 (tramp-run-real-handler 'expand-file-name
2670 (list name nil))
2671 ;; Dissect NAME.
2672 (with-parsed-tramp-file-name name nil
2673 (when (tramp-ange-ftp-file-name-p multi-method method)
2674 (tramp-invoke-ange-ftp 'expand-file-name name nil))
2675 (unless (file-name-absolute-p path)
2676 (setq path (concat "~/" path)))
2677 (save-excursion
2678 ;; Tilde expansion if necessary. This needs a shell which
2679 ;; groks tilde expansion! The function `tramp-find-shell' is
2680 ;; supposed to find such a shell on the remote host. Please
2681 ;; tell me about it when this doesn't work on your system.
2682 (when (string-match "\\`\\(~[^/]*\\)\\(.*\\)\\'" path)
2683 (let ((uname (match-string 1 path))
2684 (fname (match-string 2 path)))
2685 ;; CCC fanatic error checking?
2686 (set-buffer (tramp-get-buffer multi-method method user host))
2687 (erase-buffer)
2688 (tramp-send-command
2689 multi-method method user host
2690 (format "cd %s; pwd" uname)
2692 (tramp-wait-for-output)
2693 (goto-char (point-min))
2694 (setq uname (buffer-substring (point) (tramp-line-end-position)))
2695 (setq path (concat uname fname))
2696 (erase-buffer)))
2697 ;; No tilde characters in file name, do normal
2698 ;; expand-file-name (this does "/./" and "/../"). We bind
2699 ;; directory-sep-char here for XEmacs on Windows, which would
2700 ;; otherwise use backslash.
2701 (let ((directory-sep-char ?/))
2702 (tramp-make-tramp-file-name
2703 multi-method method user host
2704 (tramp-drop-volume-letter
2705 (tramp-run-real-handler 'expand-file-name (list path)))))))))
2707 ;; Remote commands.
2709 (defun tramp-handle-shell-command (command &optional output-buffer error-buffer)
2710 "Like `shell-command' for tramp files.
2711 This will break if COMMAND prints a newline, followed by the value of
2712 `tramp-end-of-output', followed by another newline."
2713 (when (tramp-tramp-file-p default-directory)
2714 (with-parsed-tramp-file-name default-directory nil
2715 (when (tramp-ange-ftp-file-name-p multi-method method)
2716 (let ((default-directory (tramp-make-ange-ftp-file-name
2717 user host path)))
2718 (tramp-invoke-ange-ftp 'shell-command
2719 command output-buffer error-buffer)))
2720 (let (status)
2721 (when (string-match "&[ \t]*\\'" command)
2722 (error "Tramp doesn't grok asynchronous shell commands, yet"))
2723 (when error-buffer
2724 (error "Tramp doesn't grok optional third arg ERROR-BUFFER, yet"))
2725 (save-excursion
2726 (tramp-barf-unless-okay
2727 multi-method method user host
2728 (format "cd %s" (tramp-shell-quote-argument path))
2729 nil 'file-error
2730 "tramp-handle-shell-command: Couldn't `cd %s'"
2731 (tramp-shell-quote-argument path))
2732 (tramp-send-command multi-method method user host
2733 (concat command "; tramp_old_status=$?"))
2734 ;; This will break if the shell command prints "/////"
2735 ;; somewhere. Let's just hope for the best...
2736 (tramp-wait-for-output))
2737 (unless output-buffer
2738 (setq output-buffer (get-buffer-create "*Shell Command Output*"))
2739 (set-buffer output-buffer)
2740 (erase-buffer))
2741 (unless (bufferp output-buffer)
2742 (setq output-buffer (current-buffer)))
2743 (set-buffer output-buffer)
2744 (insert-buffer (tramp-get-buffer multi-method method user host))
2745 (save-excursion
2746 (tramp-send-command multi-method method user host "cd")
2747 (tramp-wait-for-output)
2748 (tramp-send-command
2749 multi-method method user host
2750 (concat "tramp_set_exit_status $tramp_old_status;"
2751 " echo tramp_exit_status $?"))
2752 (tramp-wait-for-output)
2753 (goto-char (point-max))
2754 (unless (search-backward "tramp_exit_status " nil t)
2755 (error "Couldn't find exit status of `%s'" command))
2756 (skip-chars-forward "^ ")
2757 (setq status (read (current-buffer))))
2758 (unless (zerop (buffer-size))
2759 (pop-to-buffer output-buffer))
2760 status)))
2761 ;; The following is only executed if something strange was
2762 ;; happening. Emit a helpful message and do it anyway.
2763 (message "tramp-handle-shell-command called with non-tramp directory: `%s'"
2764 default-directory)
2765 (tramp-run-real-handler 'shell-command
2766 (list command output-buffer error-buffer)))
2768 ;; File Editing.
2770 (defsubst tramp-make-temp-file ()
2771 (funcall (if (fboundp 'make-temp-file) 'make-temp-file 'make-temp-name)
2772 (expand-file-name tramp-temp-name-prefix
2773 (tramp-temporary-file-directory))))
2775 (defun tramp-handle-file-local-copy (filename)
2776 "Like `file-local-copy' for tramp files."
2777 (with-parsed-tramp-file-name filename nil
2778 (when (tramp-ange-ftp-file-name-p multi-method method)
2779 (tramp-invoke-ange-ftp 'file-local-copy filename))
2780 (let ((trampbuf (get-buffer-create "*tramp output*"))
2781 tmpfil)
2782 (unless (file-exists-p filename)
2783 (error "Cannot make local copy of non-existing file `%s'"
2784 filename))
2785 (setq tmpfil (tramp-make-temp-file))
2786 (cond ((tramp-get-rcp-program multi-method method)
2787 ;; Use rcp-like program for file transfer.
2788 (tramp-message-for-buffer
2789 multi-method method user host
2790 5 "Fetching %s to tmp file %s..." filename tmpfil)
2791 (save-excursion (set-buffer trampbuf) (erase-buffer))
2792 (unless (equal
2794 (apply #'call-process
2795 (tramp-get-rcp-program multi-method method)
2796 nil trampbuf nil
2797 (append (tramp-get-rcp-args multi-method method)
2798 (list
2799 (tramp-make-rcp-program-file-name
2800 user host
2801 (tramp-shell-quote-argument path))
2802 tmpfil))))
2803 (pop-to-buffer trampbuf)
2804 (error
2805 (concat "tramp-handle-file-local-copy: `%s' didn't work, "
2806 "see buffer `%s' for details")
2807 (tramp-get-rcp-program multi-method method) trampbuf))
2808 (tramp-message-for-buffer
2809 multi-method method user host
2810 5 "Fetching %s to tmp file %s...done" filename tmpfil))
2811 ((and (tramp-get-encoding-command multi-method method user host)
2812 (tramp-get-decoding-command multi-method method user host))
2813 ;; Use inline encoding for file transfer.
2814 (save-excursion
2815 ;; Following line for setting tramp-current-method,
2816 ;; tramp-current-user, tramp-current-host.
2817 (set-buffer (tramp-get-buffer multi-method method user host))
2818 (tramp-message 5 "Encoding remote file %s..." filename)
2819 (tramp-barf-unless-okay
2820 multi-method method user host
2821 (concat (tramp-get-encoding-command multi-method method user host)
2822 " < " (tramp-shell-quote-argument path))
2823 nil 'file-error
2824 "Encoding remote file failed, see buffer `%s' for details"
2825 (tramp-get-buffer multi-method method user host))
2826 ;; Remove trailing status code
2827 (goto-char (point-max))
2828 (delete-region (point) (progn (forward-line -1) (point)))
2830 (tramp-message 5 "Decoding remote file %s..." filename)
2831 (if (and (tramp-get-decoding-function multi-method method user host)
2832 (fboundp (tramp-get-decoding-function
2833 multi-method method user host)))
2834 ;; If tramp-decoding-function is defined for this
2835 ;; method, we call it.
2836 (let ((tmpbuf (get-buffer-create " *tramp tmp*")))
2837 (set-buffer tmpbuf)
2838 (erase-buffer)
2839 (insert-buffer (tramp-get-buffer multi-method method
2840 user host))
2841 (tramp-message-for-buffer
2842 multi-method method user host
2843 6 "Decoding remote file %s with function %s..."
2844 filename
2845 (tramp-get-decoding-function multi-method method user host))
2846 (set-buffer tmpbuf)
2847 (let ((coding-system-for-write 'no-conversion))
2848 (funcall (tramp-get-decoding-function
2849 multi-method method user host)
2850 (point-min)
2851 (point-max))
2852 (write-region (point-min) (point-max) tmpfil))
2853 (kill-buffer tmpbuf))
2854 ;; If tramp-decoding-function is not defined for this
2855 ;; method, we invoke tramp-decoding-command instead.
2856 (let ((tmpfil2 (tramp-make-temp-file)))
2857 (write-region (point-min) (point-max) tmpfil2)
2858 (tramp-message
2859 6 "Decoding remote file %s with command %s..."
2860 filename
2861 (tramp-get-decoding-command multi-method method user host))
2862 (call-process
2863 tramp-sh-program
2864 tmpfil2 ;input
2865 nil ;output
2866 nil ;display
2867 "-c" (concat (tramp-get-decoding-command
2868 multi-method method user host)
2869 " > " tmpfil))
2870 (delete-file tmpfil2)))
2871 (tramp-message-for-buffer
2872 multi-method method user host
2873 5 "Decoding remote file %s...done" filename)))
2875 (t (error "Wrong method specification for `%s'" method)))
2876 tmpfil)))
2879 (defun tramp-handle-insert-file-contents
2880 (filename &optional visit beg end replace)
2881 "Like `insert-file-contents' for tramp files."
2882 (barf-if-buffer-read-only)
2883 (setq filename (expand-file-name filename))
2884 (with-parsed-tramp-file-name filename nil
2885 (when (tramp-ange-ftp-file-name-p multi-method method)
2886 (tramp-invoke-ange-ftp 'insert-file-contents
2887 filename visit beg end replace))
2888 (if (not (tramp-handle-file-exists-p filename))
2889 (progn
2890 (when visit
2891 (setq buffer-file-name filename)
2892 (set-visited-file-modtime)
2893 (set-buffer-modified-p nil))
2894 (signal 'file-error
2895 (format "File `%s' not found on remote host" filename))
2896 (list (tramp-handle-expand-file-name filename) 0))
2897 (let ((local-copy (tramp-handle-file-local-copy filename))
2898 (coding-system-used nil)
2899 (result nil))
2900 (when visit
2901 (setq buffer-file-name filename)
2902 (set-visited-file-modtime)
2903 (set-buffer-modified-p nil))
2904 (tramp-message-for-buffer
2905 multi-method method user host
2906 9 "Inserting local temp file `%s'..." local-copy)
2907 (setq result
2908 (tramp-run-real-handler 'insert-file-contents
2909 (list local-copy nil beg end replace)))
2910 ;; Now `last-coding-system-used' has right value. Remember it.
2911 (when (boundp 'last-coding-system-used)
2912 (setq coding-system-used last-coding-system-used))
2913 (tramp-message 9 "Inserting local temp file `%s'...done" local-copy)
2914 (delete-file local-copy)
2915 (when (boundp 'last-coding-system-used)
2916 (setq last-coding-system-used coding-system-used))
2917 (list (expand-file-name filename)
2918 (second result))))))
2920 ;; CCC grok APPEND, LOCKNAME, CONFIRM
2921 (defun tramp-handle-write-region
2922 (start end filename &optional append visit lockname confirm)
2923 "Like `write-region' for tramp files."
2924 (unless (eq append nil)
2925 (error "Cannot append to file using tramp (`%s')" filename))
2926 (setq filename (expand-file-name filename))
2927 ;; Following part commented out because we don't know what to do about
2928 ;; file locking, and it does not appear to be a problem to ignore it.
2929 ;; Ange-ftp ignores it, too.
2930 ;; (when (and lockname (stringp lockname))
2931 ;; (setq lockname (expand-file-name lockname)))
2932 ;; (unless (or (eq lockname nil)
2933 ;; (string= lockname filename))
2934 ;; (error
2935 ;; "tramp-handle-write-region: LOCKNAME must be nil or equal FILENAME"))
2936 ;; XEmacs takes a coding system as the sevent argument, not `confirm'
2937 (when (and (not (featurep 'xemacs))
2938 confirm (file-exists-p filename))
2939 (unless (y-or-n-p (format "File %s exists; overwrite anyway? "
2940 filename))
2941 (error "File not overwritten")))
2942 (with-parsed-tramp-file-name filename nil
2943 (when (tramp-ange-ftp-file-name-p multi-method method)
2944 (tramp-invoke-ange-ftp 'write-region
2945 start end filename append visit))
2946 (let ((curbuf (current-buffer))
2947 (rcp-program (tramp-get-rcp-program multi-method method))
2948 (rcp-args (tramp-get-rcp-args multi-method method))
2949 (encoding-command
2950 (tramp-get-encoding-command multi-method method user host))
2951 (encoding-function
2952 (tramp-get-encoding-function multi-method method user host))
2953 (decoding-command
2954 (tramp-get-decoding-command multi-method method user host))
2955 (trampbuf (get-buffer-create "*tramp output*"))
2956 ;; We use this to save the value of `last-coding-system-used'
2957 ;; after writing the tmp file. At the end of the function,
2958 ;; we set `last-coding-system-used' to this saved value.
2959 ;; This way, any intermediary coding systems used while
2960 ;; talking to the remote shell or suchlike won't hose this
2961 ;; variable. This approach was snarfed from ange-ftp.el.
2962 coding-system-used
2963 tmpfil)
2964 ;; Write region into a tmp file. This isn't really needed if we
2965 ;; use an encoding function, but currently we use it always
2966 ;; because this makes the logic simpler.
2967 (setq tmpfil (tramp-make-temp-file))
2968 ;; We say `no-message' here because we don't want the visited file
2969 ;; modtime data to be clobbered from the temp file. We call
2970 ;; `set-visited-file-modtime' ourselves later on.
2971 (tramp-run-real-handler
2972 'write-region
2973 (if confirm ; don't pass this arg unless defined for backward compat.
2974 (list start end tmpfil append 'no-message lockname confirm)
2975 (list start end tmpfil append 'no-message lockname)))
2976 ;; Now, `last-coding-system-used' has the right value. Remember it.
2977 (when (boundp 'last-coding-system-used)
2978 (setq coding-system-used last-coding-system-used))
2979 ;; This is a bit lengthy due to the different methods possible for
2980 ;; file transfer. First, we check whether the method uses an rcp
2981 ;; program. If so, we call it. Otherwise, both encoding and
2982 ;; decoding command must be specified. However, if the method
2983 ;; _also_ specifies an encoding function, then that is used for
2984 ;; encoding the contents of the tmp file.
2985 (cond (rcp-program
2986 ;; use rcp-like program for file transfer
2987 (let ((argl (append rcp-args
2988 (list
2989 tmpfil
2990 (tramp-make-rcp-program-file-name
2991 user host
2992 (tramp-shell-quote-argument path))))))
2993 (tramp-message-for-buffer
2994 multi-method method user host
2995 6 "Writing tmp file using `%s'..." rcp-program)
2996 (save-excursion (set-buffer trampbuf) (erase-buffer))
2997 (when tramp-debug-buffer
2998 (save-excursion
2999 (set-buffer (tramp-get-debug-buffer multi-method
3000 method user host))
3001 (goto-char (point-max))
3002 (tramp-insert-with-face
3003 'bold (format "$ %s %s\n" rcp-program
3004 (mapconcat 'identity argl " ")))))
3005 (unless (equal 0
3006 (apply #'call-process
3007 rcp-program nil trampbuf nil argl))
3008 (pop-to-buffer trampbuf)
3009 (error
3010 "Cannot write region to file `%s', command `%s' failed"
3011 filename rcp-program))
3012 (tramp-message-for-buffer
3013 multi-method method user host
3014 6 "Transferring file using `%s'...done"
3015 rcp-program)))
3016 ((and encoding-command decoding-command)
3017 ;; Use inline file transfer
3018 (let ((tmpbuf (get-buffer-create " *tramp file transfer*")))
3019 (save-excursion
3020 ;; Encode tmpfil into tmpbuf
3021 (tramp-message-for-buffer multi-method method user host
3022 5 "Encoding region...")
3023 (set-buffer tmpbuf)
3024 (erase-buffer)
3025 ;; Use encoding function or command.
3026 (if (and encoding-function
3027 (fboundp encoding-function))
3028 (progn
3029 (tramp-message-for-buffer
3030 multi-method method user host
3031 6 "Encoding region using function...")
3032 (insert-file-contents-literally tmpfil)
3033 ;; CCC. The following `let' is a workaround for
3034 ;; the base64.el that comes with pgnus-0.84. If
3035 ;; both of the following conditions are
3036 ;; satisfied, it tries to write to a local file
3037 ;; in default-directory, but at this point,
3038 ;; default-directory is remote.
3039 ;; (CALL-PROCESS-REGION can't write to remote
3040 ;; files, it seems.) The file in question is a
3041 ;; tmp file anyway.
3042 (let ((default-directory
3043 (tramp-temporary-file-directory)))
3044 (funcall encoding-function (point-min) (point-max)))
3045 (goto-char (point-max))
3046 (unless (bolp)
3047 (newline)))
3048 (tramp-message-for-buffer
3049 multi-method method user host
3050 6 "Encoding region using command...")
3051 (unless (equal 0
3052 (call-process
3053 tramp-sh-program
3054 tmpfil ;input = local tmp file
3055 t ;output is current buffer
3056 nil ;don't redisplay
3057 "-c"
3058 encoding-command))
3059 (pop-to-buffer trampbuf)
3060 (error (concat "Cannot write to `%s', local encoding"
3061 " command `%s' failed")
3062 filename encoding-command)))
3063 ;; Send tmpbuf into remote decoding command which
3064 ;; writes to remote file. Because this happens on the
3065 ;; remote host, we cannot use the function.
3066 (tramp-message-for-buffer
3067 multi-method method user host
3068 5 "Decoding region into remote file %s..." filename)
3069 (tramp-send-command
3070 multi-method method user host
3071 (format "%s >%s <<'EOF'"
3072 decoding-command
3073 (tramp-shell-quote-argument path)))
3074 (set-buffer tmpbuf)
3075 (tramp-message-for-buffer
3076 multi-method method user host
3077 6 "Sending data to remote host...")
3078 (tramp-send-region multi-method method user host
3079 (point-min) (point-max))
3080 ;; wait for remote decoding to complete
3081 (tramp-message-for-buffer
3082 multi-method method user host
3083 6 "Sending end of data token...")
3084 (tramp-send-command
3085 multi-method method user host "EOF" nil t)
3086 (tramp-message-for-buffer
3087 multi-method method user host 6
3088 "Waiting for remote host to process data...")
3089 (set-buffer (tramp-get-buffer multi-method method user host))
3090 (tramp-wait-for-output)
3091 (tramp-barf-unless-okay
3092 multi-method method user host nil nil 'file-error
3093 (concat "Couldn't write region to `%s',"
3094 " decode using `%s' failed")
3095 filename decoding-command)
3096 (tramp-message 5 "Decoding region into remote file %s...done"
3097 filename)
3098 (kill-buffer tmpbuf))))
3100 (error
3101 (concat "Method `%s' should specify both encoding and "
3102 "decoding command or an rcp program")
3103 method)))
3104 (delete-file tmpfil)
3105 (unless (equal curbuf (current-buffer))
3106 (error "Buffer has changed from `%s' to `%s'"
3107 curbuf (current-buffer)))
3108 (when (eq visit t)
3109 (set-visited-file-modtime))
3110 ;; Make `last-coding-system-used' have the right value.
3111 (when (boundp 'last-coding-system-used)
3112 (setq last-coding-system-used coding-system-used))
3113 (when (or (eq visit t)
3114 (eq visit nil)
3115 (stringp visit))
3116 (message "Wrote %s" filename)))))
3118 ;; Call down to the real handler.
3119 ;; Because EFS does not play nicely with TRAMP (both systems match an
3120 ;; TRAMP path) it is needed to disable efs as well as tramp for the
3121 ;; operation.
3123 ;; Other than that, this is the canon file-handler code that the doco
3124 ;; says should be used here. Which is nice.
3126 ;; Under XEmacs current, EFS also hooks in as
3127 ;; efs-sifn-handler-function to handle any path with environment
3128 ;; variables. This has two implications:
3129 ;; 1) That EFS may not be completely dead (yet) for TRAMP paths
3130 ;; 2) That TRAMP might want to do the same thing.
3131 ;; Details as they come in.
3133 ;; Daniel Pittman <daniel@danann.net>
3135 ;; (defun tramp-run-real-handler (operation args)
3136 ;; "Invoke normal file name handler for OPERATION.
3137 ;; This inhibits EFS and Ange-FTP, too, because they conflict with tramp.
3138 ;; First arg specifies the OPERATION, remaining ARGS are passed to the
3139 ;; OPERATION."
3140 ;; (let ((inhibit-file-name-handlers
3141 ;; (list 'tramp-file-name-handler
3142 ;; 'efs-file-handler-function
3143 ;; 'ange-ftp-hook-function
3144 ;; (and (eq inhibit-file-name-operation operation)
3145 ;; inhibit-file-name-handlers)))
3146 ;; (inhibit-file-name-operation operation))
3147 ;; (apply operation args)))
3149 (defun tramp-run-real-handler (operation args)
3150 "Invoke normal file name handler for OPERATION.
3151 First arg specifies the OPERATION, second arg is a list of arguments to
3152 pass to the OPERATION."
3153 (let* ((op (if (eq operation 'ange-ftp-hook-function)
3154 (car args)
3155 operation))
3156 (inhibit-file-name-handlers
3157 (list 'tramp-file-name-handler
3158 (and (eq inhibit-file-name-operation op)
3159 inhibit-file-name-handlers)))
3160 (inhibit-file-name-operation op))
3161 (apply operation args)))
3163 ;; Main function.
3164 ;;;###autoload
3165 (defun tramp-file-name-handler (operation &rest args)
3166 "Invoke tramp file name handler.
3167 Falls back to normal file name handler if no tramp file name handler exists."
3168 (let ((fn (assoc operation tramp-file-name-handler-alist)))
3169 (if fn
3170 (catch 'tramp-forward-to-ange-ftp
3171 (save-match-data (apply (cdr fn) args)))
3172 (tramp-run-real-handler operation args))))
3174 ;; Register in file name handler alist
3175 ;;;###autoload
3176 (add-to-list 'file-name-handler-alist
3177 (cons tramp-file-name-regexp 'tramp-file-name-handler))
3179 ;; To handle EFS, the following functions need to be dealt with:
3181 ;; * dired-before-readin-hook contains efs-dired-before-readin
3182 ;; * file-name-handler-alist contains efs-file-handler-function
3183 ;; and efs-root-handler-function and efs-sifn-handler-function
3184 ;; * find-file-hooks contains efs-set-buffer-mode
3186 ;; But it won't happen for EFS since the XEmacs maintainers
3187 ;; don't want to use a unified filename syntax.
3188 (defun tramp-disable-ange-ftp ()
3189 "Turn Ange-FTP off.
3190 This is useful for unified remoting. See
3191 `tramp-file-name-structure-unified' and
3192 `tramp-file-name-structure-separate' for details. Requests suitable
3193 for Ange-FTP will be forwarded to Ange-FTP. Also see the variables
3194 `tramp-ftp-method', `tramp-default-method', and
3195 `tramp-default-method-alist'.
3197 This function is not needed in Emacsen which include Tramp, but is
3198 present for backward compatibility."
3199 (let ((a1 (rassq 'ange-ftp-hook-function file-name-handler-alist))
3200 (a2 (rassq 'ange-ftp-completion-hook-function file-name-handler-alist)))
3201 (setq file-name-handler-alist
3202 (delete a1 (delete a2 file-name-handler-alist)))))
3203 (tramp-disable-ange-ftp)
3205 (defun tramp-repair-jka-compr ()
3206 "If jka-compr is already loaded, move it to the front of
3207 `file-name-handler-alist'. On Emacs 21.4 or so this will not be
3208 necessary anymore."
3209 (let ((jka (rassoc 'jka-compr-handler file-name-handler-alist)))
3210 (when jka
3211 (setq file-name-handler-alist
3212 (cons jka (delete jka file-name-handler-alist))))))
3213 (tramp-repair-jka-compr)
3215 (defun tramp-invoke-ange-ftp (operation &rest args)
3216 "Invoke the Ange-FTP handler function and throw."
3217 (or (boundp 'ange-ftp-name-format)
3218 (and (require 'ange-ftp)
3219 (tramp-disable-ange-ftp)))
3220 (let ((ange-ftp-name-format
3221 (list (nth 0 tramp-file-name-structure)
3222 (nth 3 tramp-file-name-structure)
3223 (nth 2 tramp-file-name-structure)
3224 (nth 4 tramp-file-name-structure))))
3225 (throw 'tramp-forward-to-ange-ftp
3226 (tramp-run-real-handler 'ange-ftp-hook-function
3227 (cons operation args)))))
3229 (defun tramp-ange-ftp-file-name-p (multi-method method)
3230 "Check if it's a filename that should be forwarded to Ange-FTP."
3231 (and (not (featurep 'xemacs))
3232 (null multi-method)
3233 (string= method tramp-ftp-method)))
3236 ;;; Interactions with other packages:
3238 ;; -- complete.el --
3240 ;; This function contributed by Ed Sabol
3241 (defun tramp-handle-expand-many-files (name)
3242 "Like `PC-expand-many-files' for tramp files."
3243 (with-parsed-tramp-file-name name nil
3244 (when (tramp-ange-ftp-file-name-p multi-method method)
3245 (tramp-invoke-ange-ftp 'expand-many-files name))
3246 (save-match-data
3247 (if (or (string-match "\\*" name)
3248 (string-match "\\?" name)
3249 (string-match "\\[.*\\]" name))
3250 (save-excursion
3251 (let (bufstr)
3252 ;; CCC: To do it right, we should quote certain characters
3253 ;; in the file name, but since the echo command is going to
3254 ;; break anyway when there are spaces in the file names, we
3255 ;; don't bother.
3256 ;;-(let ((comint-file-name-quote-list
3257 ;;- (set-difference tramp-file-name-quote-list
3258 ;;- '(?\* ?\? ?[ ?]))))
3259 ;;- (tramp-send-command
3260 ;;- multi-method method user host
3261 ;;- (format "echo %s" (comint-quote-filename path)))
3262 ;;- (tramp-wait-for-output))
3263 (tramp-send-command multi-method method user host
3264 (format "echo %s" path))
3265 (tramp-wait-for-output)
3266 (setq bufstr (buffer-substring (point-min)
3267 (tramp-line-end-position)))
3268 (goto-char (point-min))
3269 (if (string-equal path bufstr)
3271 (insert "(\"")
3272 (while (search-forward " " nil t)
3273 (delete-backward-char 1)
3274 (insert "\" \""))
3275 (goto-char (point-max))
3276 (delete-backward-char 1)
3277 (insert "\")")
3278 (goto-char (point-min))
3279 (mapcar
3280 (function (lambda (x)
3281 (tramp-make-tramp-file-name multi-method method
3282 user host x)))
3283 (read (current-buffer))))))
3284 (list (tramp-handle-expand-file-name name))))))
3286 ;; Check for complete.el and override PC-expand-many-files if appropriate.
3287 (eval-when-compile
3288 (defun tramp-save-PC-expand-many-files (name))); avoid compiler warning
3290 (defun tramp-setup-complete ()
3291 (fset 'tramp-save-PC-expand-many-files
3292 (symbol-function 'PC-expand-many-files))
3293 (defun PC-expand-many-files (name)
3294 (if (tramp-tramp-file-p name)
3295 (tramp-handle-expand-many-files name)
3296 (tramp-save-PC-expand-many-files name))))
3298 ;; Why isn't eval-after-load sufficient?
3299 (if (fboundp 'PC-expand-many-files)
3300 (tramp-setup-complete)
3301 (eval-after-load "complete" '(tramp-setup-complete)))
3306 ;;; Internal Functions:
3308 (defun tramp-set-auto-save ()
3309 (when (and (buffer-file-name)
3310 (tramp-tramp-file-p (buffer-file-name))
3311 auto-save-default)
3312 (auto-save-mode 1)))
3313 (add-hook 'find-file-hooks 'tramp-set-auto-save t)
3315 (defun tramp-run-test (switch filename)
3316 "Run `test' on the remote system, given a SWITCH and a FILENAME.
3317 Returns the exit code of the `test' program."
3318 (let ((v (tramp-dissect-file-name filename)))
3319 (save-excursion
3320 (tramp-send-command-and-check
3321 (tramp-file-name-multi-method v) (tramp-file-name-method v)
3322 (tramp-file-name-user v) (tramp-file-name-host v)
3323 (format "test %s %s" switch
3324 (tramp-shell-quote-argument (tramp-file-name-path v)))))))
3326 (defun tramp-run-test2 (program file1 file2 &optional switch)
3327 "Run `test'-like PROGRAM on the remote system, given FILE1, FILE2.
3328 The optional SWITCH is inserted between the two files.
3329 Returns the exit code of the `test' PROGRAM. Barfs if the methods,
3330 hosts, or files, disagree."
3331 (let* ((v1 (tramp-dissect-file-name file1))
3332 (v2 (tramp-dissect-file-name file2))
3333 (mmethod1 (tramp-file-name-multi-method v1))
3334 (mmethod2 (tramp-file-name-multi-method v2))
3335 (method1 (tramp-file-name-method v1))
3336 (method2 (tramp-file-name-method v2))
3337 (user1 (tramp-file-name-user v1))
3338 (user2 (tramp-file-name-user v2))
3339 (host1 (tramp-file-name-host v1))
3340 (host2 (tramp-file-name-host v2))
3341 (path1 (tramp-file-name-path v1))
3342 (path2 (tramp-file-name-path v2)))
3343 (unless (and method1 method2 host1 host2
3344 (equal mmethod1 mmethod2)
3345 (equal method1 method2)
3346 (equal user1 user2)
3347 (equal host1 host2))
3348 (error "tramp-run-test2: %s"
3349 "only implemented for same method, same user, same host"))
3350 (save-excursion
3351 (tramp-send-command-and-check
3352 mmethod1 method1 user1 host1
3353 (format "%s %s %s %s"
3354 program
3355 (tramp-shell-quote-argument path1)
3356 (or switch "")
3357 (tramp-shell-quote-argument path2))))))
3359 (defun tramp-buffer-name (multi-method method user host)
3360 "A name for the connection buffer for USER at HOST using METHOD."
3361 (cond (multi-method
3362 (tramp-buffer-name-multi-method "tramp" multi-method method user host))
3363 (user
3364 (format "*tramp/%s %s@%s*" method user host))
3366 (format "*tramp/%s %s*" method host))))
3368 (defun tramp-buffer-name-multi-method (prefix multi-method method user host)
3369 "A name for the multi method connection buffer.
3370 MULTI-METHOD gives the multi method, METHOD the array of methods,
3371 USER the array of user names, HOST the array of host names."
3372 (unless (and (= (length method) (length user))
3373 (= (length method) (length host)))
3374 (error "Syntax error in multi method (implementation error)"))
3375 (let ((len (length method))
3376 (i 0)
3377 string-list)
3378 (while (< i len)
3379 (setq string-list
3380 (cons (if (aref user i)
3381 (format "%s#%s@%s:" (aref method i)
3382 (aref user i) (aref host i))
3383 (format "%s@%s:" (aref method i) (aref host i)))
3384 string-list))
3385 (incf i))
3386 (format "*%s/%s %s*"
3387 prefix multi-method
3388 (apply 'concat (reverse string-list)))))
3390 (defun tramp-get-buffer (multi-method method user host)
3391 "Get the connection buffer to be used for USER at HOST using METHOD."
3392 (get-buffer-create (tramp-buffer-name multi-method method user host)))
3394 (defun tramp-debug-buffer-name (multi-method method user host)
3395 "A name for the debug buffer for USER at HOST using METHOD."
3396 (cond (multi-method
3397 (tramp-buffer-name-multi-method "debug tramp"
3398 multi-method method user host))
3399 (user
3400 (format "*debug tramp/%s %s@%s*" method user host))
3402 (format "*debug tramp/%s %s*" method host))))
3404 (defun tramp-get-debug-buffer (multi-method method user host)
3405 "Get the debug buffer for USER at HOST using METHOD."
3406 (get-buffer-create (tramp-debug-buffer-name multi-method method user host)))
3408 (defun tramp-find-executable (multi-method method user host
3409 progname dirlist ignore-tilde)
3410 "Searches for PROGNAME in all directories mentioned in DIRLIST.
3411 First args METHOD, USER and HOST specify the connection, PROGNAME
3412 is the program to search for, and DIRLIST gives the list of directories
3413 to search. If IGNORE-TILDE is non-nil, directory names starting
3414 with `~' will be ignored.
3416 Returns the full path name of PROGNAME, if found, and nil otherwise.
3418 This function expects to be in the right *tramp* buffer."
3419 (let (result)
3420 (when ignore-tilde
3421 ;; Remove all ~/foo directories from dirlist. In Emacs 20,
3422 ;; `remove' is in CL, and we want to avoid CL dependencies.
3423 (let (newdl d)
3424 (while dirlist
3425 (setq d (car dirlist))
3426 (setq dirlist (cdr dirlist))
3427 (unless (char-equal ?~ (aref d 0))
3428 (setq newdl (cons d newdl))))
3429 (setq dirlist (nreverse newdl))))
3430 (tramp-send-command
3431 multi-method method user host
3432 (format (concat "while read d; "
3433 "do if test -x $d/%s -a -f $d/%s; "
3434 "then echo tramp_executable $d/%s; "
3435 "break; fi; done <<'EOF'")
3436 progname progname progname))
3437 (mapcar (lambda (d)
3438 (tramp-send-command multi-method method user host d))
3439 dirlist)
3440 (tramp-send-command multi-method method user host "EOF")
3441 (tramp-wait-for-output)
3442 (goto-char (point-max))
3443 (when (search-backward "tramp_executable " nil t)
3444 (skip-chars-forward "^ ")
3445 (skip-chars-forward " ")
3446 (buffer-substring (point) (tramp-line-end-position)))))
3448 (defun tramp-set-remote-path (multi-method method user host var dirlist)
3449 "Sets the remote environment VAR to existing directories from DIRLIST.
3450 I.e., for each directory in DIRLIST, it is tested whether it exists and if
3451 so, it is added to the environment variable VAR."
3452 (let ((existing-dirs
3453 (mapcar
3454 (lambda (x)
3455 (when (and
3456 (file-exists-p
3457 (tramp-make-tramp-file-name multi-method method user host x))
3458 (file-directory-p
3459 (tramp-make-tramp-file-name multi-method method user host x)))
3461 dirlist)))
3462 (tramp-send-command
3463 multi-method method user host
3464 (concat var "="
3465 (mapconcat 'identity (delq nil existing-dirs) ":")
3466 "; export " var))
3467 (tramp-wait-for-output)))
3469 ;; -- communication with external shell --
3471 (defun tramp-find-file-exists-command (multi-method method user host)
3472 "Find a command on the remote host for checking if a file exists.
3473 Here, we are looking for a command which has zero exit status if the
3474 file exists and nonzero exit status otherwise."
3475 (make-local-variable 'tramp-file-exists-command)
3476 (tramp-message 9 "Finding command to check if file exists")
3477 (let ((existing
3478 (tramp-make-tramp-file-name
3479 multi-method method user host
3480 "/")) ;assume this file always exists
3481 (nonexisting
3482 (tramp-make-tramp-file-name
3483 multi-method method user host
3484 "/ this file does not exist "))) ;assume this never exists
3485 ;; The algorithm is as follows: we try a list of several commands.
3486 ;; For each command, we first run `$cmd /' -- this should return
3487 ;; true, as the root directory always exists. And then we run
3488 ;; `$cmd /this\ file\ does\ not\ exist', hoping that the file indeed
3489 ;; does not exist. This should return false. We use the first
3490 ;; command we find that seems to work.
3491 ;; The list of commands to try is as follows:
3492 ;; `ls -d' This works on most systems, but NetBSD 1.4
3493 ;; has a bug: `ls' always returns zero exit
3494 ;; status, even for files which don't exist.
3495 ;; `test -e' Some Bourne shells have a `test' builtin
3496 ;; which does not know the `-e' option.
3497 ;; `/bin/test -e' For those, the `test' binary on disk normally
3498 ;; provides the option. Alas, the binary
3499 ;; is sometimes `/bin/test' and sometimes it's
3500 ;; `/usr/bin/test'.
3501 ;; `/usr/bin/test -e' In case `/bin/test' does not exist.
3502 (unless (or
3503 (and (setq tramp-file-exists-command "test -e %s")
3504 (tramp-handle-file-exists-p existing)
3505 (not (tramp-handle-file-exists-p nonexisting)))
3506 (and (setq tramp-file-exists-command "/bin/test -e %s")
3507 (tramp-handle-file-exists-p existing)
3508 (not (tramp-handle-file-exists-p nonexisting)))
3509 (and (setq tramp-file-exists-command "/usr/bin/test -e %s")
3510 (tramp-handle-file-exists-p existing)
3511 (not (tramp-handle-file-exists-p nonexisting)))
3512 (and (setq tramp-file-exists-command "ls -d %s")
3513 (tramp-handle-file-exists-p existing)
3514 (not (tramp-handle-file-exists-p nonexisting))))
3515 (error "Couldn't find command to check if file exists."))))
3518 ;; CCC test ksh or bash found for tilde expansion?
3519 (defun tramp-find-shell (multi-method method user host)
3520 "Find a shell on the remote host which groks tilde expansion."
3521 (let ((shell nil))
3522 (tramp-send-command multi-method method user host "echo ~root")
3523 (tramp-wait-for-output)
3524 (cond
3525 ((string-match "^~root$" (buffer-string))
3526 (setq shell
3527 (or (tramp-find-executable multi-method method user host
3528 "bash" tramp-remote-path t)
3529 (tramp-find-executable multi-method method user host
3530 "ksh" tramp-remote-path t)))
3531 (unless shell
3532 (error "Couldn't find a shell which groks tilde expansion"))
3533 ;; Find arguments for this shell.
3534 (let ((alist tramp-sh-extra-args)
3535 item extra-args)
3536 (while (and alist (null extra-args))
3537 (setq item (pop alist))
3538 (when (string-match (car item) shell)
3539 (setq extra-args (cdr item))))
3540 (when extra-args (setq shell (concat shell " " extra-args))))
3541 (tramp-message
3542 5 "Starting remote shell `%s' for tilde expansion..." shell)
3543 (tramp-send-command
3544 multi-method method user host
3545 (concat "PS1='$ ' exec " shell)) ;
3546 (unless (tramp-wait-for-regexp
3547 (get-buffer-process (current-buffer))
3548 60 (format "\\(\\(%s\\)\\|\\(%s\\)\\)\\'"
3549 tramp-shell-prompt-pattern shell-prompt-pattern))
3550 (pop-to-buffer (buffer-name))
3551 (error "Couldn't find remote `%s' prompt." shell))
3552 (tramp-message
3553 9 "Setting remote shell prompt...")
3554 (process-send-string nil (format "PS1='%s%s%s'; PS2=''; PS3=''%s"
3555 tramp-rsh-end-of-line
3556 tramp-end-of-output
3557 tramp-rsh-end-of-line
3558 tramp-rsh-end-of-line))
3559 (tramp-wait-for-output)
3560 (tramp-message
3561 9 "Setting remote shell prompt...done")
3562 ;; (tramp-send-command multi-method method user host "echo hello")
3563 ;; (tramp-message 5 "Waiting for remote `%s' to start up..." shell)
3564 ;; (unless (tramp-wait-for-output 5)
3565 ;; (unless (tramp-wait-for-output 5)
3566 ;; (pop-to-buffer (buffer-name))
3567 ;; (error "Couldn't start remote `%s', see buffer `%s' for details"
3568 ;; shell (buffer-name))))
3569 ;; (tramp-message 5 "Waiting for remote `%s' to start up...done" shell)
3571 (t (tramp-message 5 "Remote `%s' groks tilde expansion, good"
3572 (tramp-get-remote-sh multi-method method))))))
3574 (defun tramp-check-ls-command (multi-method method user host cmd)
3575 "Checks whether the given `ls' executable groks `-n'.
3576 METHOD, USER and HOST specify the connection, CMD (the full path name of)
3577 the `ls' executable. Returns t if CMD supports the `-n' option, nil
3578 otherwise."
3579 (tramp-message 9 "Checking remote `%s' command for `-n' option"
3580 cmd)
3581 (when (tramp-handle-file-executable-p
3582 (tramp-make-tramp-file-name multi-method method user host cmd))
3583 (let ((result nil))
3584 (tramp-message 7 "Testing remote command `%s' for -n..." cmd)
3585 (setq result
3586 (tramp-send-command-and-check
3587 multi-method method user host
3588 (format "%s -lnd / >/dev/null"
3589 cmd)))
3590 (tramp-message 7 "Testing remote command `%s' for -n...%s"
3592 (if (zerop result) "okay" "failed"))
3593 (zerop result))))
3595 (defun tramp-check-ls-commands (multi-method method user host cmd dirlist)
3596 "Checks whether the given `ls' executable in one of the dirs groks `-n'.
3597 Returns nil if none was found, else the command is returned."
3598 (let ((dl dirlist)
3599 (result nil)
3600 (directory-sep-char ?/)) ;for XEmacs
3601 ;; It would be better to use the CL function `find', but
3602 ;; we don't want run-time dependencies on CL.
3603 (while (and dl (not result))
3604 (let ((x (concat (file-name-as-directory (car dl)) cmd)))
3605 (when (tramp-check-ls-command multi-method method user host x)
3606 (setq result x)))
3607 (setq dl (cdr dl)))
3608 result))
3610 (defun tramp-find-ls-command (multi-method method user host)
3611 "Finds an `ls' command which groks the `-n' option, returning nil if failed.
3612 \(This option prints numeric user and group ids in a long listing.)"
3613 (tramp-message 9 "Finding a suitable `ls' command")
3615 (tramp-check-ls-commands multi-method method user host "ls" tramp-remote-path)
3616 (tramp-check-ls-commands multi-method method user host "gnuls" tramp-remote-path)
3617 (tramp-check-ls-commands multi-method method user host "gls" tramp-remote-path)))
3619 ;; ------------------------------------------------------------
3620 ;; -- Functions for establishing connection --
3621 ;; ------------------------------------------------------------
3623 ;; The following functions are actions to be taken when seeing certain
3624 ;; prompts from the remote host. See the variable
3625 ;; `tramp-actions-before-shell' for usage of these functions.
3627 (defun tramp-action-login (p multi-method method user host)
3628 "Send the login name."
3629 (tramp-message 9 "Sending login name `%s'"
3630 (or user (user-login-name)))
3631 (erase-buffer)
3632 (process-send-string nil (concat (or user (user-login-name))
3633 tramp-rsh-end-of-line)))
3635 (defun tramp-action-password (p multi-method method user host)
3636 "Query the user for a password."
3637 (when (tramp-method-out-of-band-p multi-method method)
3638 (kill-process (get-buffer-process (current-buffer)))
3639 (error (concat "Out of band method `%s' not applicable "
3640 "for remote shell asking for a password")
3641 method))
3642 (tramp-enter-password p (match-string 0)))
3644 (defun tramp-action-succeed (p multi-method method user host)
3645 "Signal success in finding shell prompt."
3646 (tramp-message 9 "Found remote shell prompt.")
3647 (erase-buffer)
3648 (throw 'tramp-action 'ok))
3650 (defun tramp-action-permission-denied (p multi-method method user host)
3651 "Signal permission denied."
3652 (pop-to-buffer (tramp-get-buffer multi-method method user host))
3653 (tramp-message 9 "Permission denied by remote host.")
3654 (kill-process p)
3655 (throw 'tramp-action 'permission-denied))
3657 (defun tramp-action-yesno (p multi-method method user host)
3658 "Ask the user for confirmation using `yes-or-no-p'.
3659 Send \"yes\" to remote process on confirmation, abort otherwise.
3660 See also `tramp-action-yn'."
3661 (save-window-excursion
3662 (pop-to-buffer (tramp-get-buffer multi-method method user host))
3663 (unless (yes-or-no-p (match-string 0))
3664 (kill-process p)
3665 (erase-buffer)
3666 (throw 'tramp-action 'permission-denied))
3667 (process-send-string p (concat "yes" tramp-rsh-end-of-line))
3668 (erase-buffer)))
3670 (defun tramp-action-yn (p multi-method method user host)
3671 "Ask the user for confirmation using `y-or-n-p'.
3672 Send \"y\" to remote process on confirmation, abort otherwise.
3673 See also `tramp-action-yesno'."
3674 (save-window-excursion
3675 (pop-to-buffer (tramp-get-buffer multi-method method user host))
3676 (unless (y-or-n-p (match-string 0))
3677 (kill-process p)
3678 (erase-buffer)
3679 (throw 'tramp-action 'permission-denied))
3680 (process-send-string p (concat "y" tramp-rsh-end-of-line))))
3682 ;; The following functions are specifically for multi connections.
3684 (defun tramp-multi-action-login (p method user host)
3685 "Send the login name."
3686 (tramp-message 9 "Sending login name `%s'" user)
3687 (erase-buffer)
3688 (process-send-string p (concat user tramp-rsh-end-of-line)))
3690 (defun tramp-multi-action-password (p method user host)
3691 "Query the user for a password."
3692 (tramp-enter-password p (match-string 0)))
3694 (defun tramp-multi-action-succeed (p method user host)
3695 "Signal success in finding shell prompt."
3696 (tramp-message 9 "Found shell prompt on `%s'" host)
3697 (erase-buffer)
3698 (throw 'tramp-action 'ok))
3700 (defun tramp-multi-action-permission-denied (p method user host)
3701 "Signal permission denied."
3702 (tramp-message 9 "Permission denied by remote host `%s'" host)
3703 (kill-process p)
3704 (erase-buffer)
3705 (throw 'tramp-action 'permission-denied))
3707 ;; Functions for processing the actions.
3709 (defun tramp-process-one-action (p multi-method method user host actions)
3710 "Wait for output from the shell and perform one action."
3711 (let (found item pattern action todo)
3712 (erase-buffer)
3713 (tramp-message 9 "Waiting 60s for prompt from remote shell")
3714 (with-timeout (60 (throw 'tramp-action 'timeout))
3715 (while (not found)
3716 (accept-process-output p 1)
3717 (goto-char (point-min))
3718 (setq todo actions)
3719 (while todo
3720 (goto-char (point-min))
3721 (setq item (pop todo))
3722 (setq pattern (symbol-value (nth 0 item)))
3723 (setq action (nth 1 item))
3724 (tramp-message 10 "Looking for regexp \"%s\" from remote shell"
3725 pattern)
3726 (when (re-search-forward (concat pattern "\\'") nil t)
3727 (setq found (funcall action p multi-method method user host)))))
3728 found)))
3730 (defun tramp-process-actions (p multi-method method user host actions)
3731 "Perform actions until success."
3732 (let (exit)
3733 (while (not exit)
3734 (tramp-message 9 "Waiting for prompts from remote shell")
3735 (setq exit
3736 (catch 'tramp-action
3737 (tramp-process-one-action
3738 p multi-method method user host actions)
3739 nil)))
3740 (unless (eq exit 'ok)
3741 (error "Login failed"))))
3743 ;; For multi-actions.
3745 (defun tramp-process-one-multi-action (p method user host actions)
3746 "Wait for output from the shell and perform one action."
3747 (let (found item pattern action todo)
3748 (erase-buffer)
3749 (tramp-message 9 "Waiting 60s for prompt from remote shell")
3750 (with-timeout (60 (throw 'tramp-action 'timeout))
3751 (while (not found)
3752 (accept-process-output p 1)
3753 (setq todo actions)
3754 (goto-char (point-min))
3755 (while todo
3756 (goto-char (point-min))
3757 (setq item (pop todo))
3758 (setq pattern (symbol-value (nth 0 item)))
3759 (setq action (nth 1 item))
3760 (tramp-message 10 "Looking for regexp \"%s\" from remote shell"
3761 pattern)
3762 (when (re-search-forward (concat pattern "\\'") nil t)
3763 (setq found (funcall action p method user host)))))
3764 found)))
3766 (defun tramp-process-multi-actions (p method user host actions)
3767 "Perform actions until success."
3768 (let (exit)
3769 (while (not exit)
3770 (tramp-message 9 "Waiting for prompts from remote shell")
3771 (setq exit
3772 (catch 'tramp-action
3773 (tramp-process-one-multi-action p method user host actions)
3774 nil)))
3775 (unless (eq exit 'ok)
3776 (error "Login failed"))))
3778 ;; The actual functions for opening connections.
3780 (defun tramp-open-connection-telnet (multi-method method user host)
3781 "Open a connection using a telnet METHOD.
3782 This starts the command `telnet HOST ARGS'[*], then waits for a remote
3783 login prompt, then sends the user name USER, then waits for a remote
3784 password prompt. It queries the user for the password, then sends the
3785 password to the remote host.
3787 If USER is nil, uses value returned by `(user-login-name)' instead.
3789 Recognition of the remote shell prompt is based on the variables
3790 `shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be
3791 set up correctly.
3793 Please note that it is NOT possible to use this connection method
3794 together with an out-of-band transfer method! You must use an inline
3795 transfer method.
3797 Maybe the different regular expressions need to be tuned.
3799 * Actually, the telnet program as well as the args to be used can be
3800 specified in the method parameters, see the variable `tramp-methods'."
3801 (save-match-data
3802 (when (tramp-method-out-of-band-p multi-method method)
3803 (error "Cannot use out-of-band method `%s' with telnet connection method"
3804 method))
3805 (when multi-method
3806 (error "Cannot multi-connect using telnet connection method"))
3807 (tramp-pre-connection multi-method method user host)
3808 (tramp-message 7 "Opening connection for %s@%s using %s..."
3809 (or user (user-login-name)) host method)
3810 (let ((process-environment (copy-sequence process-environment)))
3811 (setenv "TERM" tramp-terminal-type)
3812 (let* ((default-directory (tramp-temporary-file-directory))
3813 (coding-system-for-read (unless (and (not (featurep 'xemacs))
3814 (> emacs-major-version 20))
3815 tramp-dos-coding-system))
3816 (p (apply 'start-process
3817 (tramp-buffer-name multi-method method user host)
3818 (tramp-get-buffer multi-method method user host)
3819 (tramp-get-telnet-program multi-method method)
3820 host
3821 (tramp-get-telnet-args multi-method method)))
3822 (found nil)
3823 (pw nil))
3824 (process-kill-without-query p)
3825 (set-buffer (tramp-get-buffer multi-method method user host))
3826 (erase-buffer)
3827 (tramp-process-actions p multi-method method user host
3828 tramp-actions-before-shell)
3829 (tramp-open-connection-setup-interactive-shell
3830 p multi-method method user host)
3831 (tramp-post-connection multi-method method user host)))))
3834 (defun tramp-open-connection-rsh (multi-method method user host)
3835 "Open a connection using an rsh METHOD.
3836 This starts the command `rsh HOST -l USER'[*], then waits for a remote
3837 password or shell prompt. If a password prompt is seen, the user is
3838 queried for a password, this function sends the password to the remote
3839 host and waits for a shell prompt.
3841 If USER is nil, start the command `rsh HOST'[*] instead
3843 Recognition of the remote shell prompt is based on the variables
3844 `shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be
3845 set up correctly.
3847 Please note that it is NOT possible to use this connection method with
3848 an out-of-band transfer method if this function asks the user for a
3849 password! You must use an inline transfer method in this case.
3850 Sadly, the transfer method cannot be switched on the fly, instead you
3851 must specify the right method in the file name.
3853 Kludgy feature: if HOST has the form \"xx#yy\", then yy is assumed to
3854 be a port number for ssh, and \"-p yy\" will be added to the list of
3855 arguments, and xx will be used as the host name to connect to.
3857 * Actually, the rsh program to be used can be specified in the
3858 method parameters, see the variable `tramp-methods'."
3859 (save-match-data
3860 (when multi-method
3861 (error "Cannot multi-connect using rsh connection method"))
3862 (tramp-pre-connection multi-method method user host)
3863 (if user
3864 (tramp-message 7 "Opening connection for %s@%s using %s..."
3865 user host method)
3866 (tramp-message 7 "Opening connection at %s using %s..." host method))
3867 (let ((process-environment (copy-sequence process-environment))
3868 (bufnam (tramp-buffer-name multi-method method user host))
3869 (buf (tramp-get-buffer multi-method method user host))
3870 (rsh-program (tramp-get-rsh-program multi-method method))
3871 (rsh-args (tramp-get-rsh-args multi-method method)))
3872 ;; The following should be changed. We need a more general
3873 ;; mechanism to parse extra host args.
3874 (when (string-match "\\([^#]*\\)#\\(.*\\)" host)
3875 (setq rsh-args (cons "-p" (cons (match-string 2 host) rsh-args)))
3876 (setq host (match-string 1 host)))
3877 (setenv "TERM" tramp-terminal-type)
3878 (let* ((default-directory (tramp-temporary-file-directory))
3879 (coding-system-for-read (unless (and (not (featurep 'xemacs))
3880 (> emacs-major-version 20))
3881 tramp-dos-coding-system))
3882 (p (if user
3883 (apply #'start-process bufnam buf rsh-program
3884 host "-l" user rsh-args)
3885 (apply #'start-process bufnam buf rsh-program
3886 host rsh-args)))
3887 (found nil))
3888 (process-kill-without-query p)
3890 (set-buffer buf)
3891 (tramp-process-actions p multi-method method user host
3892 tramp-actions-before-shell)
3893 (tramp-message 7 "Initializing remote shell")
3894 (tramp-open-connection-setup-interactive-shell
3895 p multi-method method user host)
3896 (tramp-post-connection multi-method method user host)))))
3898 (defun tramp-open-connection-su (multi-method method user host)
3899 "Open a connection using the `su' program with METHOD.
3900 This starts `su - USER', then waits for a password prompt. The HOST
3901 name must be equal to the local host name or to `localhost'.
3903 If USER is nil, uses value returned by user-login-name instead.
3905 Recognition of the remote shell prompt is based on the variables
3906 `shell-prompt-pattern' and `tramp-shell-prompt-pattern' which must be
3907 set up correctly. Note that the other user may have a different shell
3908 prompt than you do, so it is not at all unlikely that the variable
3909 `shell-prompt-pattern' is set up wrongly!"
3910 (save-match-data
3911 (when (tramp-method-out-of-band-p multi-method method)
3912 (error "Cannot use out-of-band method `%s' with `su' connection method"
3913 method))
3914 (unless (or (string-match (concat "^" (regexp-quote host))
3915 (system-name))
3916 (string= "localhost" host))
3917 (error
3918 "Cannot connect to different host `%s' with `su' connection method"
3919 host))
3920 (when (not user)
3921 (setq user "root"))
3922 (tramp-pre-connection multi-method method user host)
3923 (tramp-message 7 "Opening connection for `%s' using `%s'..." user method)
3924 (let ((process-environment (copy-sequence process-environment)))
3925 (setenv "TERM" tramp-terminal-type)
3926 (let* ((default-directory (tramp-temporary-file-directory))
3927 (coding-system-for-read (unless (and (not (featurep 'xemacs))
3928 (> emacs-major-version 20))
3929 tramp-dos-coding-system))
3930 (p (apply 'start-process
3931 (tramp-buffer-name multi-method method user host)
3932 (tramp-get-buffer multi-method method user host)
3933 (tramp-get-su-program multi-method method)
3934 (mapcar
3935 '(lambda (x)
3936 (format-spec x `((?u . ,user))))
3937 (tramp-get-su-args multi-method method))))
3938 (found nil)
3939 (pw nil))
3940 (process-kill-without-query p)
3941 (set-buffer (tramp-get-buffer multi-method method user host))
3942 (tramp-process-actions p multi-method method user host
3943 tramp-actions-before-shell)
3944 (tramp-open-connection-setup-interactive-shell
3945 p multi-method method user host)
3946 (tramp-post-connection multi-method method
3947 user host)))))
3949 ;; HHH: Not Changed. Multi method. It is not clear to me how this can
3950 ;; handle not giving a user name in the "file name".
3952 ;; This is more difficult than for the single-hop method. In the
3953 ;; multi-hop-method, the desired behaviour should be that the
3954 ;; user must specify names for the telnet hops of which the user
3955 ;; name is different than the "original" name (or different from
3956 ;; the previous hop.
3957 (defun tramp-open-connection-multi (multi-method method user host)
3958 "Open a multi-hop connection using METHOD.
3959 This uses a slightly changed file name syntax. The idea is to say
3960 [multi/telnet:u1@h1/rsh:u2@h2]/path/to/file
3961 This will use telnet to log in as u1 to h1, then use rsh from there to
3962 log in as u2 to h2."
3963 (save-match-data
3964 (unless multi-method
3965 (error "Multi-hop open connection function called on non-multi method"))
3966 (when (tramp-method-out-of-band-p multi-method method)
3967 (error "No out of band multi-hop connections"))
3968 (unless (and (arrayp method) (not (stringp method)))
3969 (error "METHOD must be an array of strings for multi methods"))
3970 (unless (and (arrayp user) (not (stringp user)))
3971 (error "USER must be an array of strings for multi methods"))
3972 (unless (and (arrayp host) (not (stringp host)))
3973 (error "HOST must be an array of strings for multi methods"))
3974 (unless (and (= (length method) (length user))
3975 (= (length method) (length host)))
3976 (error "Arrays METHOD, USER, HOST must have equal length"))
3977 (tramp-pre-connection multi-method method user host)
3978 (tramp-message 7 "Opening `%s' connection..." multi-method)
3979 (let ((process-environment (copy-sequence process-environment)))
3980 (setenv "TERM" tramp-terminal-type)
3981 (let* ((default-directory (tramp-temporary-file-directory))
3982 (coding-system-for-read (unless (and (not (featurep 'xemacs))
3983 (> emacs-major-version 20))
3984 tramp-dos-coding-system))
3985 (p (start-process (tramp-buffer-name multi-method method user host)
3986 (tramp-get-buffer multi-method method user host)
3987 tramp-multi-sh-program))
3988 (num-hops (length method))
3989 (i 0))
3990 (process-kill-without-query p)
3991 (tramp-message 9 "Waiting 60s for local shell to come up...")
3992 (unless (tramp-wait-for-regexp
3993 p 60 (format "\\(%s\\)\\'\\|\\(%s\\)\\'"
3994 shell-prompt-pattern tramp-shell-prompt-pattern))
3995 (pop-to-buffer (buffer-name))
3996 (kill-process p)
3997 (error "Couldn't find local shell prompt"))
3998 ;; Now do all the connections as specified.
3999 (while (< i num-hops)
4000 (let* ((m (aref method i))
4001 (u (aref user i))
4002 (h (aref host i))
4003 (entry (assoc m tramp-multi-connection-function-alist))
4004 (multi-func (nth 1 entry))
4005 (command (nth 2 entry)))
4006 ;; The multi-funcs don't need to do save-match-data, as that
4007 ;; is done here.
4008 (funcall multi-func p m u h command)
4009 (erase-buffer)
4010 (incf i)))
4011 (tramp-open-connection-setup-interactive-shell
4012 p multi-method method user host)
4013 (tramp-post-connection multi-method method user host)))))
4015 ;; HHH: Changed. Multi method. Don't know how to handle this in the case
4016 ;; of no user name provided. Hack to make it work as it did before:
4017 ;; changed `user' to `(or user (user-login-name))' in the places where
4018 ;; the value is actually used.
4019 (defun tramp-multi-connect-telnet (p method user host command)
4020 "Issue `telnet' command.
4021 Uses shell COMMAND to issue a `telnet' command to log in as USER to
4022 HOST. You can use percent escapes in COMMAND: `%h' is replaced with
4023 the host name, and `%n' is replaced with an end of line character, as
4024 set in `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent
4025 character.
4027 If USER is nil, uses the return value of (user-login-name) instead."
4028 (let ((cmd (format-spec command
4029 `((?h . ,host) (?n . ,tramp-rsh-end-of-line))))
4030 (cmd1 (format-spec command `((?h . ,host) (?n . ""))))
4031 found pw)
4032 (erase-buffer)
4033 (tramp-message 9 "Sending telnet command `%s'" cmd1)
4034 (process-send-string p cmd)
4035 (tramp-process-multi-actions p method user host
4036 tramp-multi-actions)))
4038 ;; HHH: Changed. Multi method. Don't know how to handle this in the case
4039 ;; of no user name provided. Hack to make it work as it did before:
4040 ;; changed `user' to `(or user (user-login-name))' in the places where
4041 ;; the value is actually used.
4042 (defun tramp-multi-connect-rlogin (p method user host command)
4043 "Issue `rlogin' command.
4044 Uses shell COMMAND to issue an `rlogin' command to log in as USER to
4045 HOST. You can use percent escapes in COMMAND. `%u' will be replaced
4046 with the user name, `%h' will be replaced with the host name, and `%n'
4047 will be replaced with the value of `tramp-rsh-end-of-line'. You can use
4048 `%%' if you want to use a literal percent character.
4050 If USER is nil, uses the return value of (user-login-name) instead."
4051 (let ((cmd (format-spec command `((?h . ,host)
4052 (?u . ,(or user (user-login-name)))
4053 (?n . ,tramp-rsh-end-of-line))))
4054 (cmd1 (format-spec command `((?h . ,host)
4055 (?u . ,(or user (user-login-name)))
4056 (?n . ""))))
4057 found)
4058 (erase-buffer)
4059 (tramp-message 9 "Sending rlogin command `%s'" cmd1)
4060 (process-send-string p cmd)
4061 (tramp-process-multi-actions p method user host
4062 tramp-multi-actions)))
4064 ;; HHH: Changed. Multi method. Don't know how to handle this in the case
4065 ;; of no user name provided. Hack to make it work as it did before:
4066 ;; changed `user' to `(or user (user-login-name))' in the places where
4067 ;; the value is actually used.
4068 (defun tramp-multi-connect-su (p method user host command)
4069 "Issue `su' command.
4070 Uses shell COMMAND to issue a `su' command to log in as USER on
4071 HOST. The HOST name is ignored, this just changes the user id on the
4072 host currently logged in to.
4074 If USER is nil, uses the return value of (user-login-name) instead.
4076 You can use percent escapes in the COMMAND. `%u' is replaced with the
4077 user name, and `%n' is replaced with the value of
4078 `tramp-rsh-end-of-line'. Use `%%' if you want a literal percent
4079 character."
4080 (let ((cmd (format-spec command `((?u . ,(or user (user-login-name)))
4081 (?n . ,tramp-rsh-end-of-line))))
4082 (cmd1 (format-spec command `((?u . ,(or user (user-login-name)))
4083 (?n . ""))))
4084 found)
4085 (erase-buffer)
4086 (tramp-message 9 "Sending su command `%s'" cmd1)
4087 (process-send-string p cmd)
4088 (tramp-process-multi-actions p method user host
4089 tramp-multi-actions)))
4091 ;; Utility functions.
4093 (defun tramp-wait-for-regexp (proc timeout regexp)
4094 "Wait for a REGEXP to appear from process PROC within TIMEOUT seconds.
4095 Expects the output of PROC to be sent to the current buffer. Returns
4096 the string that matched, or nil. Waits indefinitely if TIMEOUT is
4097 nil."
4098 (let ((found nil)
4099 (start-time (current-time)))
4100 (cond (timeout
4101 ;; Work around a bug in XEmacs 21, where the timeout
4102 ;; expires faster than it should. This degenerates
4103 ;; to polling for buggy XEmacsen, but oh, well.
4104 (while (and (not found)
4105 (< (tramp-time-diff (current-time) start-time)
4106 timeout))
4107 (with-timeout (timeout)
4108 (while (not found)
4109 (accept-process-output proc 1)
4110 (goto-char (point-min))
4111 (setq found (when (re-search-forward regexp nil t)
4112 (tramp-match-string-list)))))))
4114 (while (not found)
4115 (accept-process-output proc 1)
4116 (goto-char (point-min))
4117 (setq found (when (re-search-forward regexp nil t)
4118 (tramp-match-string-list))))))
4119 (when tramp-debug-buffer
4120 (append-to-buffer
4121 (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
4122 tramp-current-user tramp-current-host)
4123 (point-min) (point-max))
4124 (when (not found)
4125 (save-excursion
4126 (set-buffer
4127 (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
4128 tramp-current-user tramp-current-host))
4129 (goto-char (point-max))
4130 (insert "[[Regexp `" regexp "' not found"
4131 (if timeout (format " in %d secs" timeout) "")
4132 "]]"))))
4133 found))
4135 (defun tramp-enter-password (p prompt)
4136 "Prompt for a password and send it to the remote end.
4137 Uses PROMPT as a prompt and sends the password to process P."
4138 (let ((pw (tramp-read-passwd prompt)))
4139 (erase-buffer)
4140 (process-send-string p (concat pw tramp-rsh-end-of-line))))
4142 ;; HHH: Not Changed. This might handle the case where USER is not
4143 ;; given in the "File name" very poorly. Then, the local
4144 ;; variable tramp-current user will be set to nil.
4145 (defun tramp-pre-connection (multi-method method user host)
4146 "Do some setup before actually logging in.
4147 METHOD, USER and HOST specify the connection."
4148 (set-buffer (tramp-get-buffer multi-method method user host))
4149 (set (make-local-variable 'tramp-current-multi-method) multi-method)
4150 (set (make-local-variable 'tramp-current-method) method)
4151 (set (make-local-variable 'tramp-current-user) user)
4152 (set (make-local-variable 'tramp-current-host) host)
4153 (set (make-local-variable 'inhibit-eol-conversion) nil)
4154 (erase-buffer))
4156 (defun tramp-open-connection-setup-interactive-shell
4157 (p multi-method method user host)
4158 "Set up an interactive shell.
4159 Mainly sets the prompt and the echo correctly. P is the shell process
4160 to set up. METHOD, USER and HOST specify the connection."
4161 ;; Wait a bit in case the remote end feels like sending a little
4162 ;; junk first. It seems that fencepost.gnu.org does this when doing
4163 ;; a Kerberos login.
4164 (sit-for 1)
4165 (tramp-discard-garbage-erase-buffer p multi-method method user host)
4166 (process-send-string nil (format "exec %s%s"
4167 (tramp-get-remote-sh multi-method method)
4168 tramp-rsh-end-of-line))
4169 (when tramp-debug-buffer
4170 (save-excursion
4171 (set-buffer (tramp-get-debug-buffer multi-method method user host))
4172 (goto-char (point-max))
4173 (tramp-insert-with-face
4174 'bold (format "$ exec %s\n" (tramp-get-remote-sh multi-method method)))))
4175 (tramp-message 9 "Waiting 30s for remote `%s' to come up..."
4176 (tramp-get-remote-sh multi-method method))
4177 (unless (tramp-wait-for-regexp
4178 p 30 (format "\\(%s\\|%s\\)\\'"
4179 shell-prompt-pattern tramp-shell-prompt-pattern))
4180 (pop-to-buffer (buffer-name))
4181 (error "Remote `%s' didn't come up. See buffer `%s' for details"
4182 (tramp-get-remote-sh multi-method method) (buffer-name)))
4183 (tramp-message 9 "Setting up remote shell environment")
4184 (tramp-discard-garbage-erase-buffer p multi-method method user host)
4185 (process-send-string
4186 nil (format "stty -inlcr -echo kill '^U'%s" tramp-rsh-end-of-line))
4187 (unless (tramp-wait-for-regexp
4188 p 30 (format "\\(%s\\|%s\\)\\'"
4189 shell-prompt-pattern tramp-shell-prompt-pattern))
4190 (pop-to-buffer (buffer-name))
4191 (error "Couldn't `stty -echo', see buffer `%s'" (buffer-name)))
4192 (erase-buffer)
4193 (process-send-string nil (format "TERM=dumb; export TERM%s"
4194 tramp-rsh-end-of-line))
4195 (unless (tramp-wait-for-regexp
4196 p 30 (format "\\(%s\\|%s\\)\\'"
4197 shell-prompt-pattern tramp-shell-prompt-pattern))
4198 (pop-to-buffer (buffer-name))
4199 (error "Couldn't `TERM=dumb; export TERM', see buffer `%s'" (buffer-name)))
4200 ;; Try to set up the coding system correctly.
4201 ;; CCC this can't be the right way to do it. Hm.
4202 (save-excursion
4203 (erase-buffer)
4204 (tramp-message 9 "Determining coding system")
4205 (process-send-string nil (format "echo foo ; echo bar %s"
4206 tramp-rsh-end-of-line))
4207 (unless (tramp-wait-for-regexp
4208 p 30 (format "\\(%s\\|%s\\)\\'"
4209 shell-prompt-pattern tramp-shell-prompt-pattern))
4210 (pop-to-buffer (buffer-name))
4211 (error "Couldn't `echo foo; echo bar' to determine line endings'"))
4212 (goto-char (point-min))
4213 (if (featurep 'mule)
4214 ;; Use MULE to select the right EOL convention for communicating
4215 ;; with the process.
4216 (let* ((cs (or (process-coding-system p) (cons 'undecided 'undecided)))
4217 cs-decode cs-encode)
4218 (when (symbolp cs) (setq cs (cons cs cs)))
4219 (setq cs-decode (car cs))
4220 (setq cs-encode (cdr cs))
4221 (unless cs-decode (setq cs-decode 'undecided))
4222 (unless cs-encode (setq cs-encode 'undecided))
4223 (setq cs-encode (tramp-coding-system-change-eol-conversion
4224 cs-encode 'unix))
4225 (when (search-forward "\r" nil t)
4226 (setq cs-decode (tramp-coding-system-change-eol-conversion
4227 cs-decode 'dos)))
4228 (set-buffer-process-coding-system cs-decode cs-encode))
4229 ;; Look for ^M and do something useful if found.
4230 (when (search-forward "\r" nil t)
4231 ;; We have found a ^M but cannot frob the process coding system
4232 ;; because we're running on a non-MULE Emacs. Let's try
4233 ;; stty, instead.
4234 (tramp-message 9 "Trying `stty -onlcr'")
4235 (process-send-string nil (format "stty -onlcr%s" tramp-rsh-end-of-line))
4236 (unless (tramp-wait-for-regexp
4237 p 30 (format "\\(%s\\|%s\\)\\'"
4238 shell-prompt-pattern tramp-shell-prompt-pattern))
4239 (pop-to-buffer (buffer-name))
4240 (error "Couldn't `stty -onlcr', see buffer `%s'" (buffer-name))))))
4241 (erase-buffer)
4242 (tramp-message
4243 9 "Waiting 30s for `HISTFILE=$HOME/.tramp_history; HISTSIZE=1'")
4244 (process-send-string
4245 nil (format "HISTFILE=$HOME/.tramp_history; HISTSIZE=1%s"
4246 tramp-rsh-end-of-line))
4247 (unless (tramp-wait-for-regexp
4248 p 30 (format "\\(%s\\|%s\\)\\'"
4249 shell-prompt-pattern tramp-shell-prompt-pattern))
4250 (pop-to-buffer (buffer-name))
4251 (error (concat "Couldn't `HISTFILE=$HOME/.tramp_history; "
4252 "HISTSIZE=1', see buffer `%s'")
4253 (buffer-name)))
4254 (erase-buffer)
4255 (tramp-message 9 "Waiting 30s for `set +o vi +o emacs'")
4256 (process-send-string
4257 nil (format "set +o vi +o emacs%s" ;mustn't `>/dev/null' with AIX?
4258 tramp-rsh-end-of-line))
4259 (unless (tramp-wait-for-regexp
4260 p 30 (format "\\(%s\\|%s\\)\\'"
4261 shell-prompt-pattern tramp-shell-prompt-pattern))
4262 (pop-to-buffer (buffer-name))
4263 (error "Couldn't `set +o vi +o emacs', see buffer `%s'"
4264 (buffer-name)))
4265 (erase-buffer)
4266 (tramp-message 9 "Waiting 30s for `unset MAIL MAILCHECK MAILPATH'")
4267 (process-send-string
4268 nil (format "unset MAIL MAILCHECK MAILPATH 1>/dev/null 2>/dev/null%s"
4269 tramp-rsh-end-of-line))
4270 (unless (tramp-wait-for-regexp
4271 p 30 (format "\\(%s\\|%s\\)\\'"
4272 shell-prompt-pattern tramp-shell-prompt-pattern))
4273 (pop-to-buffer (buffer-name))
4274 (error "Couldn't `unset MAIL MAILCHECK MAILPATH', see buffer `%s'"
4275 (buffer-name)))
4276 (erase-buffer)
4277 (tramp-message 9 "Waiting 30s for `unset CDPATH'")
4278 (process-send-string
4279 nil (format "unset CDPATH%s" tramp-rsh-end-of-line))
4280 (unless (tramp-wait-for-regexp
4281 p 30 (format "\\(%s\\|%s\\)\\'"
4282 shell-prompt-pattern tramp-shell-prompt-pattern))
4283 (pop-to-buffer (buffer-name))
4284 (error "Couldn't `unset CDPATH', see buffer `%s'"
4285 (buffer-name)))
4286 (erase-buffer)
4287 (tramp-message 9 "Setting shell prompt")
4288 (tramp-send-command
4289 multi-method method user host
4290 (format "PS1='%s%s%s'; PS2=''; PS3=''"
4291 tramp-rsh-end-of-line
4292 tramp-end-of-output
4293 tramp-rsh-end-of-line))
4294 (tramp-wait-for-output)
4295 ;; (tramp-send-command multi-method method user host "echo hello")
4296 ;; (tramp-message 9 "Waiting for remote `%s' to come up..."
4297 ;; (tramp-get-remote-sh multi-method method))
4298 ;; (unless (tramp-wait-for-output 5)
4299 ;; (unless (tramp-wait-for-output 5)
4300 ;; (pop-to-buffer (buffer-name))
4301 ;; (error "Couldn't set remote shell prompt. See buffer `%s' for details"
4302 ;; (buffer-name))))
4303 ;; (tramp-message 7 "Waiting for remote `%s' to come up...done"
4304 ;; (tramp-get-remote-sh multi-method method))
4307 (defun tramp-post-connection (multi-method method user host)
4308 "Prepare a remote shell before being able to work on it.
4309 METHOD, USER and HOST specify the connection.
4310 Among other things, this finds a shell which groks tilde expansion,
4311 tries to find an `ls' command which groks the `-n' option, sets the
4312 locale to C and sets up the remote shell search path."
4313 ;; Search for a good shell before searching for a command which
4314 ;; checks if a file exists. This is done because Tramp wants to use
4315 ;; "test foo; echo $?" to check if various conditions hold, and
4316 ;; there are buggy /bin/sh implementations which don't execute the
4317 ;; "echo $?" part if the "test" part has an error. In particular,
4318 ;; the Solaris /bin/sh is a problem. I'm betting that all systems
4319 ;; with buggy /bin/sh implementations will have a working bash or
4320 ;; ksh. Whee...
4321 (tramp-find-shell multi-method method user host)
4322 ;; Without (sit-for 0.1) at least, my machine will almost always blow
4323 ;; up on 'not numberp /root' - a race that causes the 'echo ~root'
4324 ;; output of (tramp-find-shell) to show up along with the output of
4325 ;; (tramp-find-ls-command) testing.
4327 ;; I can't work out why this is a problem though. The (tramp-wait-for-output)
4328 ;; call in (tramp-find-shell) *should* make this not happen, I thought.
4330 ;; After much debugging I couldn't find any problem with the implementation
4331 ;; of that function though. The workaround stays for me at least. :/
4333 ;; Daniel Pittman <daniel@danann.net>
4334 (sleep-for 1)
4335 (erase-buffer)
4336 (tramp-find-file-exists-command multi-method method user host)
4337 (make-local-variable 'tramp-ls-command)
4338 (setq tramp-ls-command (tramp-find-ls-command multi-method method user host))
4339 (unless tramp-ls-command
4340 (tramp-message
4342 "Danger! Couldn't find ls which groks -n. Muddling through anyway")
4343 (setq tramp-ls-command
4344 (tramp-find-executable multi-method method user host
4345 "ls" tramp-remote-path nil)))
4346 (unless tramp-ls-command
4347 (error "Fatal error: Couldn't find remote executable `ls'"))
4348 (tramp-message 5 "Using remote command `%s' for getting directory listings"
4349 tramp-ls-command)
4350 (tramp-send-command multi-method method user host
4351 (concat "tramp_set_exit_status () {" tramp-rsh-end-of-line
4352 "return $1" tramp-rsh-end-of-line
4353 "}"))
4354 (tramp-wait-for-output)
4355 ;; Set remote PATH variable.
4356 (tramp-set-remote-path multi-method method user host "PATH" tramp-remote-path)
4357 ;; Tell remote shell to use standard time format, needed for
4358 ;; parsing `ls -l' output.
4359 (tramp-send-command multi-method method user host
4360 "LC_TIME=C; export LC_TIME; echo huhu")
4361 (tramp-wait-for-output)
4362 (tramp-send-command multi-method method user host
4363 "mesg n; echo huhu")
4364 (tramp-wait-for-output)
4365 (tramp-send-command multi-method method user host
4366 "biff n ; echo huhu")
4367 (tramp-wait-for-output)
4368 ;; Unalias ls(1) to work around issues with those silly people who make it
4369 ;; spit out ANSI escapes or whatever.
4370 (tramp-send-command multi-method method user host
4371 "unalias ls; echo huhu")
4372 (tramp-wait-for-output)
4373 ;; Does `test A -nt B' work? Use abominable `find' construct if it
4374 ;; doesn't. BSD/OS 4.0 wants the parentheses around the command,
4375 ;; for otherwise the shell crashes.
4376 (erase-buffer)
4377 (make-local-variable 'tramp-test-groks-nt)
4378 (tramp-send-command multi-method method user host
4379 "( test / -nt / )")
4380 (tramp-wait-for-output)
4381 (goto-char (point-min))
4382 (setq tramp-test-groks-nt
4383 (looking-at (format "\n%s\n" (regexp-quote tramp-end-of-output))))
4384 (unless tramp-test-groks-nt
4385 (tramp-send-command
4386 multi-method method user host
4387 (concat "tramp_test_nt () {" tramp-rsh-end-of-line
4388 "test -n \"`find $1 -prune -newer $2 -print`\"" tramp-rsh-end-of-line
4389 "}")))
4390 (tramp-wait-for-output)
4391 ;; Send the fallback `uudecode' script.
4392 (erase-buffer)
4393 (tramp-send-linewise multi-method method user host tramp-uudecode)
4394 (tramp-wait-for-output)
4395 ;; Find a `perl'.
4396 (erase-buffer)
4397 (let ((tramp-remote-perl
4398 (or (tramp-find-executable multi-method method user host
4399 "perl5" tramp-remote-path nil)
4400 (tramp-find-executable multi-method method user host
4401 "perl" tramp-remote-path nil))))
4402 (when tramp-remote-perl
4403 (tramp-set-connection-property "perl" tramp-remote-perl
4404 multi-method method user host)
4405 ;; Set up stat in Perl if we can.
4406 (when tramp-remote-perl
4407 (tramp-message 5 "Sending the Perl `file-attributes' implementation.")
4408 (tramp-send-linewise
4409 multi-method method user host
4410 (concat "tramp_file_attributes () {\n"
4411 tramp-remote-perl
4412 " -e '" tramp-perl-file-attributes "' $1 2>/dev/null\n"
4413 "}"))
4414 (tramp-wait-for-output)
4415 (tramp-message 5 "Sending the Perl `mime-encode' implementations.")
4416 (tramp-send-linewise
4417 multi-method method user host
4418 (concat "tramp_encode () {\n"
4419 (format tramp-perl-encode tramp-remote-perl)
4420 " 2>/dev/null"
4421 "\n}"))
4422 (tramp-wait-for-output)
4423 (tramp-send-linewise
4424 multi-method method user host
4425 (concat "tramp_encode_with_module () {\n"
4426 (format tramp-perl-encode-with-module tramp-remote-perl)
4427 " 2>/dev/null"
4428 "\n}"))
4429 (tramp-wait-for-output)
4430 (tramp-message 5 "Sending the Perl `mime-decode' implementations.")
4431 (tramp-send-linewise
4432 multi-method method user host
4433 (concat "tramp_decode () {\n"
4434 (format tramp-perl-decode tramp-remote-perl)
4435 " 2>/dev/null"
4436 "\n}"))
4437 (tramp-wait-for-output)
4438 (tramp-send-linewise
4439 multi-method method user host
4440 (concat "tramp_decode_with_module () {\n"
4441 (format tramp-perl-decode-with-module tramp-remote-perl)
4442 " 2>/dev/null"
4443 "\n}"))
4444 (tramp-wait-for-output))))
4445 ;; Find ln(1)
4446 (erase-buffer)
4447 (let ((ln (tramp-find-executable multi-method method user host
4448 "ln" tramp-remote-path nil)))
4449 (when ln
4450 (tramp-set-connection-property "ln" ln multi-method method user host)))
4451 (erase-buffer)
4452 ;; Find the right encoding/decoding commands to use.
4453 (unless (tramp-get-rcp-program multi-method method)
4454 (tramp-find-inline-encoding multi-method method user host))
4455 ;; If encoding/decoding command are given, test to see if they work.
4456 ;; CCC: Maybe it would be useful to run the encoder both locally and
4457 ;; remotely to see if they produce the same result.
4458 (let ((decoding (tramp-get-decoding-command multi-method method user host))
4459 (encoding (tramp-get-encoding-command multi-method method user host))
4460 (magic-string "xyzzy"))
4461 (when (and (or decoding encoding) (not (and decoding encoding)))
4462 (tramp-kill-process multi-method method user host)
4463 (error
4464 "Must give both decoding and encoding command in method definition"))
4465 (when (and decoding encoding)
4466 (tramp-message
4468 "Checking to see if encoding/decoding commands work on remote host...")
4469 (tramp-send-command
4470 multi-method method user host
4471 (format "echo %s | %s | %s"
4472 (tramp-shell-quote-argument magic-string) encoding decoding))
4473 (tramp-wait-for-output)
4474 (unless (looking-at (regexp-quote magic-string))
4475 (tramp-kill-process multi-method method user host)
4476 (error "Remote host cannot execute de/encoding commands. See buffer `%s' for details"
4477 (buffer-name)))
4478 (erase-buffer)
4479 (tramp-message
4480 5 "Checking to see if encoding/decoding commands work on remote host...done"))))
4482 ;; CCC: We should either implement a Perl version of base64 encoding
4483 ;; and decoding. Then we just use that in the last item. The other
4484 ;; alternative is to use the Perl version of UU encoding. But then
4485 ;; we need a Lisp version of uuencode.
4486 (defvar tramp-coding-commands
4487 '(("mimencode -b" "mimencode -u -b"
4488 base64-encode-region base64-decode-region)
4489 ("mmencode -b" "mmencode -u -b"
4490 base64-encode-region base64-decode-region)
4491 ("recode data..base64" "recode base64..data"
4492 base64-encode-region base64-decode-region)
4493 ("uuencode xxx" "uudecode -o -"
4494 nil uudecode-decode-region)
4495 ("uuencode xxx" "uudecode -p"
4496 nil uudecode-decode-region)
4497 ("uuencode xxx" "tramp_uudecode"
4498 nil uudecode-decode-region)
4499 ("tramp_encode_with_module" "tramp_decode_with_module"
4500 base64-encode-region base64-decode-region)
4501 ("tramp_encode" "tramp_decode"
4502 base64-encode-region base64-decode-region))
4503 "List of coding commands for inline transfer.
4504 Each item is a list (ENCODING-COMMAND DECODING-COMMAND
4505 ENCODING-FUNCTION DECODING-FUNCTION).
4507 Each item can be a string, giving a command, or a symbol, giving
4508 a function.
4510 The ENCODING-COMMAND should be a command accepting a plain file on
4511 standard input and writing the encoded file to standard output. The
4512 DECODING-COMMAND should be a command accepting an encoded file on
4513 standard input and writing the decoded file to standard output.
4515 The ENCODING-FUNCTION and DECODING-FUNCTION functions will be called
4516 with two arguments, start and end of region, and are expected to
4517 replace the region contents with the encoded or decoded results,
4518 respectively.")
4520 (defun tramp-find-inline-encoding (multi-method method user host)
4521 "Find an inline transfer encoding that works.
4522 Goes through the list `tramp-coding-commands'."
4523 (let ((commands tramp-coding-commands)
4524 item found)
4525 (while (and commands (null found))
4526 (setq item (pop commands))
4527 (catch 'wont-work
4528 (let ((ec (nth 0 item))
4529 (dc (nth 1 item))
4530 (ef (nth 2 item))
4531 (df (nth 3 item)))
4532 ;; Check if encoding and decoding commands can be called
4533 ;; remotely with null input and output. This makes sure there
4534 ;; are no syntax errors and the command is really found.
4535 (tramp-message-for-buffer
4536 multi-method method user host 9
4537 "Checking remote encoding command `%s' for sanity" ec)
4538 (unless (zerop (tramp-send-command-and-check
4539 multi-method method user host
4540 (format "%s </dev/null >/dev/null" ec) t))
4541 (throw 'wont-work nil))
4542 (tramp-message-for-buffer
4543 multi-method method user host 9
4544 "Checking remote decoding command `%s' for sanity" dc)
4545 (unless (zerop (tramp-send-command-and-check
4546 multi-method method user host
4547 (format "echo xyzzy | %s | %s >/dev/null" ec dc) t))
4548 (throw 'wont-work nil))
4549 ;; If no encoding/decoding function is given, the
4550 ;; corresponding encoding/decoding command also has to work
4551 ;; locally.
4552 (when (not (fboundp ef))
4553 (tramp-message-for-buffer
4554 multi-method method user host 9
4555 "Checking local encoding command `%s' for sanity" ec)
4556 (unless (zerop (call-process
4557 tramp-sh-program ;program
4558 nil ;input
4559 nil ;output buffer
4560 nil ;redisplay
4561 "-c"
4562 (format "%s </dev/null >/dev/null" ec)))
4563 (throw 'wont-work nil)))
4564 (when (not (fboundp df))
4565 (tramp-message-for-buffer
4566 multi-method method user host 9
4567 "Checking local decoding command `%s' for sanity" dc)
4568 (unless (zerop (call-process
4569 tramp-sh-program ;program
4570 nil ;input file
4571 nil ;output buffer
4572 nil ;redisplay
4573 "-c"
4574 (format "%s </dev/null >/dev/null" dc)))
4575 (throw 'wont-work nil)))
4576 ;; CCC: At this point, maybe we should check that the output
4577 ;; of the commands is correct. But for the moment we will
4578 ;; assume that commands working on empty input will also
4579 ;; work in practice.
4580 (setq found item))))
4581 ;; Did we find something? If not, issue error. If so,
4582 ;; set connection properties.
4583 (unless found
4584 (error "Couldn't find an inline transfer encoding"))
4585 (let ((ec (nth 0 found))
4586 (dc (nth 1 found))
4587 (ef (nth 2 found))
4588 (df (nth 3 found)))
4589 (tramp-set-encoding-command multi-method method user host ec)
4590 (tramp-set-decoding-command multi-method method user host dc)
4591 (tramp-set-encoding-function multi-method method user host ef)
4592 (tramp-set-decoding-function multi-method method user host df))))
4595 (defun tramp-maybe-open-connection (multi-method method user host)
4596 "Maybe open a connection to HOST, logging in as USER, using METHOD.
4597 Does not do anything if a connection is already open, but re-opens the
4598 connection if a previous connection has died for some reason."
4599 (let ((p (get-buffer-process (tramp-get-buffer multi-method method user host)))
4600 last-cmd-time)
4601 ;; If too much time has passed since last command was sent, look
4602 ;; whether process is still alive. If it isn't, kill it. When
4603 ;; using ssh, it can sometimes happen that the remote end has hung
4604 ;; up but the local ssh client doesn't recognize this until it
4605 ;; tries to send some data to the remote end. So that's why we
4606 ;; try to send a command from time to time, then look again
4607 ;; whether the process is really alive.
4608 (save-excursion
4609 (set-buffer (tramp-get-buffer multi-method method user host))
4610 (when (and tramp-last-cmd-time
4611 (> (tramp-time-diff (current-time) tramp-last-cmd-time) 60))
4612 (tramp-send-command
4613 multi-method method user host "echo are you awake" nil t)
4614 (unless (tramp-wait-for-output 10)
4615 (delete-process p)
4616 (setq p nil))
4617 (erase-buffer)))
4618 (unless (and p (processp p) (memq (process-status p) '(run open)))
4619 (when (and p (processp p))
4620 (delete-process p))
4621 (funcall (tramp-get-connection-function multi-method method)
4622 multi-method method user host))))
4624 (defun tramp-send-command
4625 (multi-method method user host command &optional noerase neveropen)
4626 "Send the COMMAND to USER at HOST (logged in using METHOD).
4627 Erases temporary buffer before sending the command (unless NOERASE
4628 is true).
4629 If optional seventh arg NEVEROPEN is non-nil, never try to open the
4630 connection. This is meant to be used from
4631 `tramp-maybe-open-connection' only."
4632 (or neveropen
4633 (tramp-maybe-open-connection multi-method method user host))
4634 (setq tramp-last-cmd-time (current-time))
4635 (when tramp-debug-buffer
4636 (save-excursion
4637 (set-buffer (tramp-get-debug-buffer multi-method method user host))
4638 (goto-char (point-max))
4639 (tramp-insert-with-face 'bold (format "$ %s\n" command))))
4640 (let ((proc nil))
4641 (set-buffer (tramp-get-buffer multi-method method user host))
4642 (unless noerase (erase-buffer))
4643 (setq proc (get-buffer-process (current-buffer)))
4644 (process-send-string proc
4645 (concat command tramp-rsh-end-of-line))))
4647 ;; It seems that Tru64 Unix does not like it if long strings are sent
4648 ;; to it in one go. (This happens when sending the Perl
4649 ;; `file-attributes' implementation, for instance.) Therefore, we
4650 ;; have this function which waits a bit at each line.
4651 (defun tramp-send-linewise
4652 (multi-method method user host string &optional noerase)
4653 "Send the STRING to USER at HOST linewise.
4654 Erases temporary buffer before sending the STRING (unless NOERASE
4655 is true).
4657 The STRING is expected to use Unix line-endings, but the lines sent to
4658 the remote host use line-endings as defined in the variable
4659 `tramp-rsh-end-of-line'."
4660 (tramp-maybe-open-connection multi-method method user host)
4661 (when tramp-debug-buffer
4662 (save-excursion
4663 (set-buffer (tramp-get-debug-buffer multi-method method user host))
4664 (goto-char (point-max))
4665 (tramp-insert-with-face 'bold (format "$ %s\n" string))))
4666 (let ((proc nil)
4667 (lines (split-string string "\n")))
4668 (set-buffer (tramp-get-buffer multi-method method user host))
4669 (unless noerase (erase-buffer))
4670 (setq proc (get-buffer-process (current-buffer)))
4671 (mapcar (lambda (x)
4672 (sleep-for 0.1)
4673 (process-send-string proc
4674 (concat x tramp-rsh-end-of-line)))
4675 lines)))
4677 (defun tramp-wait-for-output (&optional timeout)
4678 "Wait for output from remote rsh command."
4679 (let ((proc (get-buffer-process (current-buffer)))
4680 (found nil)
4681 (start-time (current-time))
4682 (end-of-output (concat "^"
4683 (regexp-quote tramp-end-of-output)
4684 "$")))
4685 ;; Algorithm: get waiting output. See if last line contains
4686 ;; end-of-output sentinel. If not, wait a bit and again get
4687 ;; waiting output. Repeat until timeout expires or end-of-output
4688 ;; sentinel is seen. Will hang if timeout is nil and
4689 ;; end-of-output sentinel never appears.
4690 (save-match-data
4691 (cond (timeout
4692 ;; Work around an XEmacs bug, where the timeout expires
4693 ;; faster than it should. This degenerates into polling
4694 ;; for buggy XEmacsen, but oh, well.
4695 (while (and (not found)
4696 (< (tramp-time-diff (current-time) start-time)
4697 timeout))
4698 (with-timeout (timeout)
4699 (while (not found)
4700 (accept-process-output proc 1)
4701 (goto-char (point-max))
4702 (forward-line -1)
4703 (setq found (looking-at end-of-output))))))
4705 (while (not found)
4706 (accept-process-output proc 1)
4707 (goto-char (point-max))
4708 (forward-line -1)
4709 (setq found (looking-at end-of-output))))))
4710 ;; At this point, either the timeout has expired or we have found
4711 ;; the end-of-output sentinel.
4712 (when found
4713 (goto-char (point-max))
4714 (forward-line -2)
4715 (delete-region (point) (point-max)))
4716 ;; Add output to debug buffer if appropriate.
4717 (when tramp-debug-buffer
4718 (append-to-buffer
4719 (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
4720 tramp-current-user tramp-current-host)
4721 (point-min) (point-max))
4722 (when (not found)
4723 (save-excursion
4724 (set-buffer
4725 (tramp-get-debug-buffer tramp-current-multi-method tramp-current-method
4726 tramp-current-user tramp-current-host))
4727 (goto-char (point-max))
4728 (insert "[[Remote prompt `" end-of-output "' not found"
4729 (if timeout (format " in %d secs" timeout) "")
4730 "]]"))))
4731 (goto-char (point-min))
4732 ;; Return value is whether end-of-output sentinel was found.
4733 found))
4735 (defun tramp-match-string-list (&optional string)
4736 "Returns list of all match strings.
4737 That is, (list (match-string 0) (match-string 1) ...), according to the
4738 number of matches."
4739 (let* ((nmatches (/ (length (match-data)) 2))
4740 (i (- nmatches 1))
4741 (res nil))
4742 (while (>= i 0)
4743 (setq res (cons (match-string i string) res))
4744 (setq i (- i 1)))
4745 res))
4747 (defun tramp-send-command-and-check (multi-method method user host command
4748 &optional subshell)
4749 "Run COMMAND and check its exit status.
4750 MULTI-METHOD and METHOD specify how to log in (as USER) to the remote HOST.
4751 Sends `echo $?' along with the COMMAND for checking the exit status. If
4752 COMMAND is nil, just sends `echo $?'. Returns the exit status found.
4754 If the optional argument SUBSHELL is non-nil, the command is executed in
4755 a subshell, ie surrounded by parentheses."
4756 (tramp-send-command multi-method method user host
4757 (concat (if subshell "( " "")
4758 command
4759 (if command " 2>/dev/null; " "")
4760 "echo tramp_exit_status $?"
4761 (if subshell " )" " ")))
4762 (tramp-wait-for-output)
4763 (goto-char (point-max))
4764 (unless (search-backward "tramp_exit_status " nil t)
4765 (error "Couldn't find exit status of `%s'" command))
4766 (skip-chars-forward "^ ")
4767 (read (current-buffer)))
4769 (defun tramp-barf-unless-okay (multi-method method user host command subshell
4770 signal fmt &rest args)
4771 "Run COMMAND, check exit status, throw error if exit status not okay.
4772 Similar to `tramp-send-command-and-check' but accepts two more arguments
4773 FMT and ARGS which are passed to `error'."
4774 (unless (zerop (tramp-send-command-and-check
4775 multi-method method user host command subshell))
4776 ;; CCC: really pop-to-buffer? Maybe it's appropriate to be more
4777 ;; silent.
4778 (pop-to-buffer (current-buffer))
4779 (funcall 'signal signal (apply 'format fmt args))))
4781 (defun tramp-send-region (multi-method method user host start end)
4782 "Send the region from START to END to remote command
4783 running as USER on HOST using METHOD."
4784 (let ((proc (get-buffer-process
4785 (tramp-get-buffer multi-method method user host))))
4786 (unless proc
4787 (error "Can't send region to remote host -- not logged in"))
4788 (process-send-region proc start end)
4789 (when tramp-debug-buffer
4790 (append-to-buffer
4791 (tramp-get-debug-buffer multi-method method user host)
4792 start end))))
4794 (defun tramp-send-eof (multi-method method user host)
4795 "Send EOF to the remote end.
4796 METHOD, HOST and USER specify the connection."
4797 (let ((proc (get-buffer-process
4798 (tramp-get-buffer multi-method method user host))))
4799 (unless proc
4800 (error "Can't send EOF to remote host -- not logged in"))
4801 (process-send-eof proc)))
4802 ; (process-send-string proc "\^D")))
4804 (defun tramp-kill-process (multi-method method user host)
4805 "Kill the connection process used by Tramp.
4806 MULTI-METHOD, METHOD, USER, and HOST specify the connection."
4807 (let ((proc (get-buffer-process
4808 (tramp-get-buffer multi-method method user host))))
4809 (kill-process proc)))
4811 (defun tramp-discard-garbage-erase-buffer (p multi-method method user host)
4812 "Erase buffer, then discard subsequent garbage.
4813 If `tramp-discard-garbage' is nil, just erase buffer."
4814 (if (not tramp-discard-garbage)
4815 (erase-buffer)
4816 (while (prog1 (erase-buffer) (accept-process-output p 0.25))
4817 (when tramp-debug-buffer
4818 (save-excursion
4819 (set-buffer (tramp-get-debug-buffer multi-method method user host))
4820 (goto-char (point-max))
4821 (tramp-insert-with-face
4822 'bold (format "Additional characters detected\n")))))))
4824 (defun tramp-mode-string-to-int (mode-string)
4825 "Converts a ten-letter `drwxrwxrwx'-style mode string into mode bits."
4826 (let* ((mode-chars (string-to-vector mode-string))
4827 (owner-read (aref mode-chars 1))
4828 (owner-write (aref mode-chars 2))
4829 (owner-execute-or-setid (aref mode-chars 3))
4830 (group-read (aref mode-chars 4))
4831 (group-write (aref mode-chars 5))
4832 (group-execute-or-setid (aref mode-chars 6))
4833 (other-read (aref mode-chars 7))
4834 (other-write (aref mode-chars 8))
4835 (other-execute-or-sticky (aref mode-chars 9)))
4836 (save-match-data
4837 (logior
4838 (case owner-read
4839 (?r (tramp-octal-to-decimal "00400")) (?- 0)
4840 (t (error "Second char `%c' must be one of `r-'" owner-read)))
4841 (case owner-write
4842 (?w (tramp-octal-to-decimal "00200")) (?- 0)
4843 (t (error "Third char `%c' must be one of `w-'" owner-write)))
4844 (case owner-execute-or-setid
4845 (?x (tramp-octal-to-decimal "00100"))
4846 (?S (tramp-octal-to-decimal "04000"))
4847 (?s (tramp-octal-to-decimal "04100"))
4848 (?- 0)
4849 (t (error "Fourth char `%c' must be one of `xsS-'"
4850 owner-execute-or-setid)))
4851 (case group-read
4852 (?r (tramp-octal-to-decimal "00040")) (?- 0)
4853 (t (error "Fifth char `%c' must be one of `r-'" group-read)))
4854 (case group-write
4855 (?w (tramp-octal-to-decimal "00020")) (?- 0)
4856 (t (error "Sixth char `%c' must be one of `w-'" group-write)))
4857 (case group-execute-or-setid
4858 (?x (tramp-octal-to-decimal "00010"))
4859 (?S (tramp-octal-to-decimal "02000"))
4860 (?s (tramp-octal-to-decimal "02010"))
4861 (?- 0)
4862 (t (error "Seventh char `%c' must be one of `xsS-'"
4863 group-execute-or-setid)))
4864 (case other-read
4865 (?r (tramp-octal-to-decimal "00004")) (?- 0)
4866 (t (error "Eighth char `%c' must be one of `r-'" other-read)))
4867 (case other-write
4868 (?w (tramp-octal-to-decimal "00002")) (?- 0)
4869 (t (error "Nineth char `%c' must be one of `w-'" other-write)))
4870 (case other-execute-or-sticky
4871 (?x (tramp-octal-to-decimal "00001"))
4872 (?T (tramp-octal-to-decimal "01000"))
4873 (?t (tramp-octal-to-decimal "01001"))
4874 (?- 0)
4875 (t (error "Tenth char `%c' must be one of `xtT-'"
4876 other-execute-or-sticky)))))))
4879 (defun tramp-file-mode-from-int (mode)
4880 "Turn an integer representing a file mode into an ls(1)-like string."
4881 (let ((type (cdr (assoc (logand (lsh mode -12) 15) tramp-file-mode-type-map)))
4882 (user (logand (lsh mode -6) 7))
4883 (group (logand (lsh mode -3) 7))
4884 (other (logand (lsh mode -0) 7))
4885 (suid (> (logand (lsh mode -9) 4) 0))
4886 (sgid (> (logand (lsh mode -9) 2) 0))
4887 (sticky (> (logand (lsh mode -9) 1) 0)))
4888 (setq user (tramp-file-mode-permissions user suid "s"))
4889 (setq group (tramp-file-mode-permissions group sgid "s"))
4890 (setq other (tramp-file-mode-permissions other sticky "t"))
4891 (concat type user group other)))
4894 (defun tramp-file-mode-permissions (perm suid suid-text)
4895 "Convert a permission bitset into a string.
4896 This is used internally by `tramp-file-mode-from-int'."
4897 (let ((r (> (logand perm 4) 0))
4898 (w (> (logand perm 2) 0))
4899 (x (> (logand perm 1) 0)))
4900 (concat (or (and r "r") "-")
4901 (or (and w "w") "-")
4902 (or (and suid x suid-text) ; suid, execute
4903 (and suid (upcase suid-text)) ; suid, !execute
4904 (and x "x") "-")))) ; !suid
4907 (defun tramp-decimal-to-octal (i)
4908 "Return a string consisting of the octal digits of I.
4909 Not actually used. Use `(format \"%o\" i)' instead?"
4910 (cond ((< i 0) (error "Cannot convert negative number to octal"))
4911 ((not (integerp i)) (error "Cannot convert non-integer to octal"))
4912 ((zerop i) "0")
4913 (t (concat (tramp-decimal-to-octal (/ i 8))
4914 (number-to-string (% i 8))))))
4917 ;;(defun tramp-octal-to-decimal (ostr)
4918 ;; "Given a string of octal digits, return a decimal number."
4919 ;; (cond ((null ostr) 0)
4920 ;; ((string= "" ostr) 0)
4921 ;; (t (let ((last (aref ostr (1- (length ostr))))
4922 ;; (rest (substring ostr 0 (1- (length ostr)))))
4923 ;; (unless (and (>= last ?0)
4924 ;; (<= last ?7))
4925 ;; (error "Not an octal digit: %c" last))
4926 ;; (+ (- last ?0) (* 8 (tramp-octal-to-decimal rest)))))))
4927 ;; Kudos to Gerd Moellmann for this suggestion.
4928 (defun tramp-octal-to-decimal (ostr)
4929 "Given a string of octal digits, return a decimal number."
4930 (let ((x (or ostr "")))
4931 ;; `save-match' is in `tramp-mode-string-to-int' which calls this.
4932 (unless (string-match "\\`[0-7]*\\'" x)
4933 (error "Non-octal junk in string `%s'" x))
4934 (string-to-number ostr 8)))
4936 (defun tramp-shell-case-fold (string)
4937 "Converts STRING to shell glob pattern which ignores case."
4938 (mapconcat
4939 (lambda (c)
4940 (if (equal (downcase c) (upcase c))
4941 (vector c)
4942 (format "[%c%c]" (downcase c) (upcase c))))
4943 string
4944 ""))
4947 ;; ------------------------------------------------------------
4948 ;; -- TRAMP file names --
4949 ;; ------------------------------------------------------------
4950 ;; Conversion functions between external representation and
4951 ;; internal data structure. Convenience functions for internal
4952 ;; data structure.
4954 (defstruct tramp-file-name multi-method method user host path)
4956 (defun tramp-tramp-file-p (name)
4957 "Return t iff NAME is a tramp file."
4958 (save-match-data
4959 (string-match tramp-file-name-regexp name)))
4961 ;; HHH: Changed. Used to assign the return value of (user-login-name)
4962 ;; to the `user' part of the structure if a user name was not
4963 ;; provided, now it assigns nil.
4964 (defun tramp-dissect-file-name (name)
4965 "Return an `tramp-file-name' structure.
4966 The structure consists of remote method, remote user, remote host and
4967 remote path name."
4968 (let (method)
4969 (save-match-data
4970 (unless (string-match (nth 0 tramp-file-name-structure) name)
4971 (error "Not a tramp file name: %s" name))
4972 (setq method (match-string (nth 1 tramp-file-name-structure) name))
4973 (if (and method (member method tramp-multi-methods))
4974 ;; If it's a multi method, the file name structure contains
4975 ;; arrays of method, user and host.
4976 (tramp-dissect-multi-file-name name)
4977 ;; Normal method. First, find out default method.
4978 (let ((user (match-string (nth 2 tramp-file-name-structure) name))
4979 (host (match-string (nth 3 tramp-file-name-structure) name))
4980 (path (match-string (nth 4 tramp-file-name-structure) name)))
4981 (when (not method)
4982 (setq method (tramp-find-default-method user host)))
4983 (make-tramp-file-name
4984 :multi-method nil
4985 :method method
4986 :user (or user nil)
4987 :host host
4988 :path path))))))
4990 (defun tramp-find-default-method (user host)
4991 "Look up the right method to use in `tramp-default-method-alist'."
4992 (let ((choices tramp-default-method-alist)
4993 (method tramp-default-method)
4994 item)
4995 (while choices
4996 (setq item (pop choices))
4997 (when (and (string-match (nth 0 item) host)
4998 (string-match (nth 1 item) (or user "")))
4999 (setq method (nth 2 item))
5000 (setq choices nil)))
5001 method))
5003 ;; HHH: Not Changed. Multi method. Will probably not handle the case where
5004 ;; a user name is not provided in the "file name" very well.
5005 (defun tramp-dissect-multi-file-name (name)
5006 "Not implemented yet."
5007 (let ((regexp (nth 0 tramp-multi-file-name-structure))
5008 (method-index (nth 1 tramp-multi-file-name-structure))
5009 (hops-index (nth 2 tramp-multi-file-name-structure))
5010 (path-index (nth 3 tramp-multi-file-name-structure))
5011 (hop-regexp (nth 0 tramp-multi-file-name-hop-structure))
5012 (hop-method-index (nth 1 tramp-multi-file-name-hop-structure))
5013 (hop-user-index (nth 2 tramp-multi-file-name-hop-structure))
5014 (hop-host-index (nth 3 tramp-multi-file-name-hop-structure))
5015 method hops len hop-methods hop-users hop-hosts path)
5016 (unless (string-match (format regexp hop-regexp) name)
5017 (error "Not a multi tramp file name: %s" name))
5018 (setq method (match-string method-index name))
5019 (setq hops (match-string hops-index name))
5020 (setq len (/ (length (match-data t)) 2))
5021 (when (< path-index 0) (incf path-index len))
5022 (setq path (match-string path-index name))
5023 (let ((index 0))
5024 (while (string-match hop-regexp hops index)
5025 (setq index (match-end 0))
5026 (setq hop-methods
5027 (cons (match-string hop-method-index hops) hop-methods))
5028 (setq hop-users
5029 (cons (match-string hop-user-index hops) hop-users))
5030 (setq hop-hosts
5031 (cons (match-string hop-host-index hops) hop-hosts))))
5032 (make-tramp-file-name
5033 :multi-method method
5034 :method (apply 'vector (reverse hop-methods))
5035 :user (apply 'vector (reverse hop-users))
5036 :host (apply 'vector (reverse hop-hosts))
5037 :path path)))
5039 (defun tramp-make-tramp-file-name (multi-method method user host path)
5040 "Constructs a tramp file name from METHOD, USER, HOST and PATH."
5041 (unless tramp-make-tramp-file-format
5042 (error "`tramp-make-tramp-file-format' is nil"))
5043 (if multi-method
5044 (tramp-make-tramp-multi-file-name multi-method method user host path)
5045 (if user
5046 (format-spec tramp-make-tramp-file-format
5047 `((?m . ,method) (?u . ,user) (?h . ,host) (?p . ,path)))
5048 (format-spec tramp-make-tramp-file-user-nil-format
5049 `((?m . ,method) (?h . ,host) (?p . ,path))))))
5051 ;; CCC: Henrik Holm: Not Changed. Multi Method. What should be done
5052 ;; with this when USER is nil?
5053 (defun tramp-make-tramp-multi-file-name (multi-method method user host path)
5054 "Constructs a tramp file name for a multi-hop method."
5055 (unless tramp-make-multi-tramp-file-format
5056 (error "`tramp-make-multi-tramp-file-format' is nil"))
5057 (let* ((prefix-format (nth 0 tramp-make-multi-tramp-file-format))
5058 (hop-format (nth 1 tramp-make-multi-tramp-file-format))
5059 (path-format (nth 2 tramp-make-multi-tramp-file-format))
5060 (prefix (format-spec prefix-format `((?m . ,multi-method))))
5061 (hops "")
5062 (path (format-spec path-format `((?p . ,path))))
5063 (i 0)
5064 (len (length method)))
5065 (while (< i len)
5066 (let ((m (aref method i)) (u (aref user i)) (h (aref host i)))
5067 (setq hops (concat hops (format-spec hop-format
5068 `((?m . ,m) (?u . ,u) (?h . ,h)))))
5069 (incf i)))
5070 (concat prefix hops path)))
5072 (defun tramp-make-rcp-program-file-name (user host path)
5073 "Create a file name suitable to be passed to `rcp'."
5074 (if user
5075 (format "%s@%s:%s" user host path)
5076 (format "%s:%s" host path)))
5078 (defun tramp-make-ange-ftp-file-name (user host path)
5079 "Given user, host, and path, return an Ange-FTP filename."
5080 (if user
5081 (format "/%s@%s:%s" user host path)
5082 (format "/%s:%s" host path)))
5084 (defun tramp-method-out-of-band-p (multi-method method)
5085 "Return t if this is an out-of-band method, nil otherwise.
5086 It is important to check for this condition, since it is not possible
5087 to enter a password for the `tramp-rcp-program'."
5088 (tramp-get-rcp-program multi-method method))
5090 ;; Variables local to connection.
5092 (defun tramp-get-ls-command (multi-method method user host)
5093 (save-excursion
5094 (tramp-maybe-open-connection multi-method method user host)
5095 (set-buffer (tramp-get-buffer multi-method method user host))
5096 tramp-ls-command))
5098 (defun tramp-get-test-groks-nt (multi-method method user host)
5099 (save-excursion
5100 (tramp-maybe-open-connection multi-method method user host)
5101 (set-buffer (tramp-get-buffer multi-method method user host))
5102 tramp-test-groks-nt))
5104 (defun tramp-get-file-exists-command (multi-method method user host)
5105 (save-excursion
5106 (tramp-maybe-open-connection multi-method method user host)
5107 (set-buffer (tramp-get-buffer multi-method method user host))
5108 tramp-file-exists-command))
5110 (defun tramp-get-remote-perl (multi-method method user host)
5111 (tramp-get-connection-property "perl" nil multi-method method user host))
5113 (defun tramp-get-remote-ln (multi-method method user host)
5114 (tramp-get-connection-property "ln" nil multi-method method user host))
5116 ;; Get a property of a TRAMP connection.
5117 (defun tramp-get-connection-property
5118 (property default multi-method method user host)
5119 "Get the named property for the connection.
5120 If the value is not set for the connection, return `default'"
5121 (tramp-maybe-open-connection multi-method method user host)
5122 (with-current-buffer (tramp-get-buffer multi-method method user host)
5123 (let (error)
5124 (condition-case nil
5125 (symbol-value (intern (concat "tramp-connection-property-" property)))
5126 (error default)))))
5128 ;; Set a property of a TRAMP connection.
5129 (defun tramp-set-connection-property
5130 (property value multi-method method user host)
5131 "Set the named property of a TRAMP connection."
5132 (tramp-maybe-open-connection multi-method method user host)
5133 (with-current-buffer (tramp-get-buffer multi-method method user host)
5134 (set (make-local-variable
5135 (intern (concat "tramp-connection-property-" property)))
5136 value)))
5138 ;; Some predefined connection properties.
5139 (defun tramp-get-encoding-command (multi-method method user host)
5140 (tramp-get-connection-property "encoding-command" nil
5141 multi-method method user host))
5142 (defun tramp-set-encoding-command (multi-method method user host command)
5143 (tramp-set-connection-property "encoding-command" command
5144 multi-method method user host))
5145 (defun tramp-get-decoding-command (multi-method method user host)
5146 (tramp-get-connection-property "decoding-command" nil
5147 multi-method method user host))
5148 (defun tramp-set-decoding-command (multi-method method user host command)
5149 (tramp-set-connection-property "decoding-command" command
5150 multi-method method user host))
5151 (defun tramp-get-encoding-function (multi-method method user host)
5152 (tramp-get-connection-property "encoding-function" nil
5153 multi-method method user host))
5154 (defun tramp-set-encoding-function (multi-method method user host func)
5155 (tramp-set-connection-property "encoding-function" func
5156 multi-method method user host))
5157 (defun tramp-get-decoding-function (multi-method method user host)
5158 (tramp-get-connection-property "decoding-function" nil
5159 multi-method method user host))
5160 (defun tramp-set-decoding-function (multi-method method user host func)
5161 (tramp-set-connection-property "decoding-function" func
5162 multi-method method user host))
5165 (defun tramp-get-connection-function (multi-method method)
5166 (second (or (assoc 'tramp-connection-function
5167 (assoc (or multi-method method tramp-default-method)
5168 tramp-methods))
5169 (error "Method `%s' didn't specify a connection function"
5170 (or multi-method method)))))
5172 (defun tramp-get-remote-sh (multi-method method)
5173 (second (or (assoc 'tramp-remote-sh
5174 (assoc (or multi-method method tramp-default-method)
5175 tramp-methods))
5176 (error "Method `%s' didn't specify a remote shell"
5177 (or multi-method method)))))
5179 (defun tramp-get-rsh-program (multi-method method)
5180 (second (or (assoc 'tramp-rsh-program
5181 (assoc (or multi-method method tramp-default-method)
5182 tramp-methods))
5183 (error "Method `%s' didn't specify an rsh program"
5184 (or multi-method method)))))
5186 (defun tramp-get-rsh-args (multi-method method)
5187 (second (or (assoc 'tramp-rsh-args
5188 (assoc (or multi-method method tramp-default-method)
5189 tramp-methods))
5190 (error "Method `%s' didn't specify rsh args"
5191 (or multi-method method)))))
5193 (defun tramp-get-rcp-program (multi-method method)
5194 (second (or (assoc 'tramp-rcp-program
5195 (assoc (or multi-method method tramp-default-method)
5196 tramp-methods))
5197 (error "Method `%s' didn't specify an rcp program"
5198 (or multi-method method)))))
5200 (defun tramp-get-rcp-args (multi-method method)
5201 (second (or (assoc 'tramp-rcp-args
5202 (assoc (or multi-method method tramp-default-method)
5203 tramp-methods))
5204 (error "Method `%s' didn't specify rcp args"
5205 (or multi-method method)))))
5207 (defun tramp-get-rcp-keep-date-arg (multi-method method)
5208 (second (or (assoc 'tramp-rcp-keep-date-arg
5209 (assoc (or multi-method method tramp-default-method)
5210 tramp-methods))
5211 (error "Method `%s' didn't specify `keep-date' arg for tramp"
5212 (or multi-method method)))))
5214 (defun tramp-get-su-program (multi-method method)
5215 (second (or (assoc 'tramp-su-program
5216 (assoc (or multi-method method tramp-default-method)
5217 tramp-methods))
5218 (error "Method `%s' didn't specify a su program"
5219 (or multi-method method)))))
5221 (defun tramp-get-su-args (multi-method method)
5222 (second (or (assoc 'tramp-su-args
5223 (assoc (or multi-method method tramp-default-method)
5224 tramp-methods))
5225 (error "Method `%s' didn't specify su args"
5226 (or multi-method method)))))
5228 (defun tramp-get-telnet-program (multi-method method)
5229 (second (or (assoc 'tramp-telnet-program
5230 (assoc (or multi-method method tramp-default-method)
5231 tramp-methods))
5232 (error "Method `%s' didn't specify a telnet program"
5233 (or multi-method method)))))
5235 (defun tramp-get-telnet-args (multi-method method)
5236 (second (or (assoc 'tramp-telnet-args
5237 (assoc (or multi-method method tramp-default-method)
5238 tramp-methods))
5239 (error "Method `%s' didn't specify telnet args"
5240 (or multi-method method)))))
5242 ;; (defun tramp-get-encoding-command (multi-method method)
5243 ;; (second (or (assoc 'tramp-encoding-command
5244 ;; (assoc (or multi-method method tramp-default-method)
5245 ;; tramp-methods))
5246 ;; (error "Method `%s' didn't specify an encoding command"
5247 ;; (or multi-method method)))))
5249 ;; (defun tramp-get-decoding-command (multi-method method)
5250 ;; (second (or (assoc 'tramp-decoding-command
5251 ;; (assoc (or multi-method method tramp-default-method)
5252 ;; tramp-methods))
5253 ;; (error "Method `%s' didn't specify a decoding command"
5254 ;; (or multi-method method)))))
5256 ;; (defun tramp-get-encoding-function (multi-method method)
5257 ;; (second (or (assoc 'tramp-encoding-function
5258 ;; (assoc (or multi-method method tramp-default-method)
5259 ;; tramp-methods))
5260 ;; (error "Method `%s' didn't specify an encoding function"
5261 ;; (or multi-method method)))))
5263 ;; (defun tramp-get-decoding-function (multi-method method)
5264 ;; (second (or (assoc 'tramp-decoding-function
5265 ;; (assoc (or multi-method method tramp-default-method)
5266 ;; tramp-methods))
5267 ;; (error "Method `%s' didn't specify a decoding function"
5268 ;; (or multi-method method)))))
5270 ;; Auto saving to a special directory.
5272 (defun tramp-make-auto-save-file-name (fn)
5273 "Returns a file name in `tramp-auto-save-directory' for autosaving this file."
5274 (when tramp-auto-save-directory
5275 (unless (file-exists-p tramp-auto-save-directory)
5276 (make-directory tramp-auto-save-directory t)))
5277 ;; jka-compr doesn't like auto-saving, so by appending "~" to the
5278 ;; file name we make sure that jka-compr isn't used for the
5279 ;; auto-save file.
5280 (let ((buffer-file-name (expand-file-name
5281 (tramp-subst-strs-in-string '(("_" . "|")
5282 ("/" . "_a")
5283 (":" . "_b")
5284 ("|" . "__")
5285 ("[" . "_l")
5286 ("]" . "_r"))
5288 tramp-auto-save-directory)))
5289 (make-auto-save-file-name)))
5291 (defadvice make-auto-save-file-name
5292 (around tramp-advice-make-auto-save-file-name () activate)
5293 "Invoke `tramp-make-auto-save-file-name' for tramp files."
5294 (if (and (buffer-file-name) (tramp-tramp-file-p (buffer-file-name))
5295 tramp-auto-save-directory)
5296 (setq ad-return-value
5297 (tramp-make-auto-save-file-name (buffer-file-name)))
5298 ad-do-it))
5300 (defun tramp-subst-strs-in-string (alist string)
5301 "Replace all occurrences of the string FROM with TO in STRING.
5302 ALIST is of the form ((FROM . TO) ...)."
5303 (save-match-data
5304 (while alist
5305 (let* ((pr (car alist))
5306 (from (car pr))
5307 (to (cdr pr)))
5308 (while (string-match (regexp-quote from) string)
5309 (setq string (replace-match to t t string)))
5310 (setq alist (cdr alist))))
5311 string))
5313 (defun tramp-insert-with-face (face string)
5314 "Insert text with a specific face."
5315 (let ((start (point)))
5316 (insert string)
5317 (add-text-properties start (point) (list 'face face))))
5319 ;; ------------------------------------------------------------
5320 ;; -- Compatibility functions section --
5321 ;; ------------------------------------------------------------
5323 (defun tramp-temporary-file-directory ()
5324 "Return name of directory for temporary files (compat function).
5325 For Emacs, this is the variable `temporary-file-directory', for XEmacs
5326 this is the function `temp-directory'."
5327 (cond ((boundp 'temporary-file-directory)
5328 (symbol-value 'temporary-file-directory))
5329 ((fboundp 'temp-directory)
5330 (funcall (symbol-function 'temp-directory))) ;pacify byte-compiler
5331 ((let ((d (getenv "TEMP"))) (and d (file-directory-p d)))
5332 (file-name-as-directory (getenv "TEMP")))
5333 ((let ((d (getenv "TMP"))) (and d (file-directory-p d)))
5334 (file-name-as-directory (getenv "TMP")))
5335 ((let ((d (getenv "TMPDIR"))) (and d (file-directory-p d)))
5336 (file-name-as-directory (getenv "TMPDIR")))
5337 ((file-exists-p "c:/temp") (file-name-as-directory "c:/temp"))
5338 (t (message (concat "Neither `temporary-file-directory' nor "
5339 "`temp-directory' is defined -- using /tmp."))
5340 (file-name-as-directory "/tmp"))))
5342 (defun tramp-read-passwd (prompt)
5343 "Read a password from user (compat function).
5344 Invokes `read-passwd' if that is defined, else `ange-ftp-read-passwd'."
5345 (apply
5346 (if (fboundp 'read-passwd) #'read-passwd #'ange-ftp-read-passwd)
5347 (list prompt)))
5349 (defun tramp-time-diff (t1 t2)
5350 "Return the difference between the two times, in seconds.
5351 T1 and T2 are time values (as returned by `current-time' for example).
5353 NOTE: This function will fail if the time difference is too large to
5354 fit in an integer."
5355 ;; Pacify byte-compiler with `symbol-function'.
5356 (cond ((fboundp 'subtract-time)
5357 (cadr (funcall (symbol-function 'subtract-time) t1 t2)))
5358 ((fboundp 'itimer-time-difference)
5359 (floor (funcall
5360 (symbol-function 'itimer-time-difference)
5361 (if (< (length t1) 3) (append t1 '(0)) t1)
5362 (if (< (length t2) 3) (append t2 '(0)) t2))))
5364 ;; snarfed from Emacs 21 time-date.el
5365 (cadr (let ((borrow (< (cadr t1) (cadr t2))))
5366 (list (- (car t1) (car t2) (if borrow 1 0))
5367 (- (+ (if borrow 65536 0) (cadr t1)) (cadr t2))))))))
5369 (defun tramp-coding-system-change-eol-conversion (coding-system eol-type)
5370 "Return a coding system like CODING-SYSTEM but with given EOL-TYPE.
5371 EOL-TYPE can be one of `dos', `unix', or `mac'."
5372 (cond ((fboundp 'coding-system-change-eol-conversion)
5373 (apply #'coding-system-change-eol-conversion
5374 (list coding-system eol-type)))
5375 ((fboundp 'subsidiary-coding-system)
5376 (apply
5377 #'subsidiary-coding-system
5378 (list coding-system
5379 (cond ((eq eol-type 'dos) 'crlf)
5380 ((eq eol-type 'unix) 'lf)
5381 ((eq eol-type 'mac) 'cr)
5383 (error "Unknown EOL-TYPE `%s', must be %s"
5384 eol-type
5385 "`dos', `unix', or `mac'"))))))
5386 (t (error "Can't change EOL conversion -- is MULE missing?"))))
5388 (defun tramp-split-string (string pattern)
5389 "Like `split-string' but omit empty strings.
5390 In Emacs, (split-string \"/foo/bar\" \"/\") returns (\"foo\" \"bar\").
5391 This is, the first, empty, element is omitted. In XEmacs, the first
5392 element is not omitted.
5394 Note: this function has been written for `tramp-handle-file-truename'.
5395 If you want to use it for something else, you'll have to check whether
5396 it does the right thing."
5397 (delete "" (split-string string pattern)))
5399 ;; ------------------------------------------------------------
5400 ;; -- Kludges section --
5401 ;; ------------------------------------------------------------
5403 ;; Currently (as of Emacs 20.5), the function `shell-quote-argument'
5404 ;; does not deal well with newline characters. Newline is replaced by
5405 ;; backslash newline. But if, say, the string `a backslash newline b'
5406 ;; is passed to a shell, the shell will expand this into "ab",
5407 ;; completely omitting the newline. This is not what was intended.
5408 ;; It does not appear to be possible to make the function
5409 ;; `shell-quote-argument' work with newlines without making it
5410 ;; dependent on the shell used. But within this package, we know that
5411 ;; we will always use a Bourne-like shell, so we use an approach which
5412 ;; groks newlines.
5414 ;; The approach is simple: we call `shell-quote-argument', then
5415 ;; massage the newline part of the result.
5417 ;; This function should produce a string which is grokked by a Unix
5418 ;; shell, even if the Emacs is running on Windows. Since this is the
5419 ;; kludges section, we bind `system-type' in such a way that
5420 ;; `shell-quote-arguments' behaves as if on Unix.
5422 ;; Thanks to Mario DeWeerd for the hint that it is sufficient for this
5423 ;; function to work with Bourne-like shells.
5425 ;; CCC: This function should be rewritten so that
5426 ;; `shell-quote-argument' is not used. This way, we are safe from
5427 ;; changes in `shell-quote-argument'.
5428 (defun tramp-shell-quote-argument (s)
5429 "Similar to `shell-quote-argument', but groks newlines.
5430 Only works for Bourne-like shells."
5431 (let ((system-type 'not-windows))
5432 (save-match-data
5433 (let ((result (shell-quote-argument s))
5434 (nl (regexp-quote (format "\\%s" tramp-rsh-end-of-line))))
5435 (when (and (>= (length result) 2)
5436 (string= (substring result 0 2) "\\~"))
5437 (setq result (substring result 1)))
5438 (while (string-match nl result)
5439 (setq result (replace-match (format "'%s'" tramp-rsh-end-of-line)
5440 t t result)))
5441 result))))
5443 ;; ;; EFS hooks itself into the file name handling stuff in more places
5444 ;; ;; than just `file-name-handler-alist'. The following tells EFS to stay
5445 ;; ;; away from tramp.el paths.
5446 ;; ;;
5447 ;; ;; This is needed because EFS installs (efs-dired-before-readin) into
5448 ;; ;; 'dired-before-readin-hook'. This prevents EFS from opening an FTP
5449 ;; ;; connection to help it's dired process. Not that I have any real
5450 ;; ;; idea *why* this is helpful to dired.
5451 ;; ;;
5452 ;; ;; Anyway, this advice fixes the problem (with a sledgehammer :)
5453 ;; ;;
5454 ;; ;; Daniel Pittman <daniel@danann.net>
5455 ;; ;;
5456 ;; ;; CCC: when the other defadvice calls have disappeared, make sure
5457 ;; ;; not to call defadvice unless it's necessary. How do we find out whether
5458 ;; ;; it is necessary? (featurep 'efs) is surely the wrong way --
5459 ;; ;; EFS might nicht be loaded yet.
5460 ;; (defadvice efs-ftp-path (around dont-match-tramp-path activate protect)
5461 ;; "Cause efs-ftp-path to fail when the path is a TRAMP path."
5462 ;; (if (tramp-tramp-file-p (ad-get-arg 0))
5463 ;; nil
5464 ;; ad-do-it))
5466 ;; We currently use "[" and "]" in the filename format. In Emacs
5467 ;; 20.x, this means that Emacs wants to expand wildcards if
5468 ;; `find-file-wildcards' is non-nil, and then barfs because no
5469 ;; expansion could be found. We detect this situation and do
5470 ;; something really awful: we have `file-expand-wildcards' return the
5471 ;; original filename if it can't expand anything. Let's just hope
5472 ;; that this doesn't break anything else.
5474 ;; Another problem is that the check is done by Emacs version, which
5475 ;; is really not what we want to do. Oh, well.
5477 ;;(when (and (not (featurep 'xemacs))
5478 ;; (= emacs-major-version 20))
5479 ;; It seems that this advice is needed in Emacs 21, too.
5480 (defadvice file-expand-wildcards (around tramp-fix activate)
5481 (let ((name (ad-get-arg 0)))
5482 (if (tramp-tramp-file-p name)
5483 ;; If it's a Tramp file, dissect it and look if wildcards
5484 ;; need to be expanded at all.
5485 (let ((v (tramp-dissect-file-name name)))
5486 (if (string-match "[[*?]" (tramp-file-name-path v))
5487 (let ((res ad-do-it))
5488 (setq ad-return-value (or res (list name))))
5489 (setq ad-return-value (list name))))
5490 ;; If it is not a Tramp file, just run the original function.
5491 (let ((res ad-do-it))
5492 (setq ad-return-value (or res (list name)))))))
5493 ;; )
5495 ;; Tramp version is useful in a number of situations.
5497 (defun tramp-version (arg)
5498 "Print version number of tramp.el in minibuffer or current buffer."
5499 (interactive "P")
5500 (if arg (insert tramp-version) (message tramp-version)))
5502 ;; Make the `reporter` functionality available for making bug reports about
5503 ;; the package. A most useful piece of code.
5505 (unless (fboundp 'reporter-submit-bug-report)
5506 (autoload 'reporter-submit-bug-report "reporter"))
5508 (defun tramp-bug ()
5509 "Submit a bug report to the TRAMP developers."
5510 (interactive)
5511 (require 'reporter)
5512 (let ((reporter-prompt-for-summary-p t))
5513 (reporter-submit-bug-report
5514 tramp-bug-report-address ; to-address
5515 (format "tramp (%s)" tramp-version) ; package name and version
5516 `(;; Current state
5517 tramp-ls-command
5518 tramp-test-groks-nt
5519 tramp-file-exists-command
5520 tramp-current-multi-method
5521 tramp-current-method
5522 tramp-current-user
5523 tramp-current-host
5525 ;; System defaults
5526 tramp-auto-save-directory ; vars to dump
5527 tramp-default-method
5528 tramp-rsh-end-of-line
5529 tramp-remote-path
5530 tramp-login-prompt-regexp
5531 tramp-password-prompt-regexp
5532 tramp-wrong-passwd-regexp
5533 tramp-yesno-prompt-regexp
5534 tramp-yn-prompt-regexp
5535 tramp-temp-name-prefix
5536 tramp-file-name-structure
5537 tramp-file-name-regexp
5538 tramp-multi-file-name-structure
5539 tramp-multi-file-name-hop-structure
5540 tramp-multi-methods
5541 tramp-multi-connection-function-alist
5542 tramp-make-tramp-file-format
5543 tramp-end-of-output
5544 tramp-coding-commands
5545 tramp-actions-before-shell
5546 tramp-multi-actions
5547 tramp-terminal-type
5548 tramp-shell-prompt-pattern
5550 ;; Non-tramp variables of interest
5551 shell-prompt-pattern
5552 backup-by-copying
5553 backup-by-copying-when-linked
5554 backup-by-copying-when-mismatch
5555 ,(when (boundp 'backup-by-copying-when-privileged-mismatch)
5556 'backup-by-copying-when-privileged-mismatch)
5557 file-name-handler-alist)
5558 nil ; pre-hook
5559 nil ; post-hook
5561 Enter your bug report in this message, including as much detail as you
5562 possibly can about the problem, what you did to cause it and what the
5563 local and remote machines are.
5565 If you can give a simple set of instructions to make this bug happen
5566 reliably, please include those. Thank you for helping kill bugs in
5567 TRAMP.
5569 Another useful thing to do is to put (setq tramp-debug-buffer t) in
5570 the ~/.emacs file and to repeat the bug. Then, include the contents
5571 of the *tramp/foo* buffer and the *debug tramp/foo* buffer in your bug
5572 report.
5574 --bug report follows this line--
5575 ")))
5577 (defalias 'tramp-submit-bug 'tramp-bug)
5579 (provide 'tramp)
5581 ;; Make sure that we get integration with the VC package.
5582 ;; When it is loaded, we need to pull in the integration module.
5583 ;; This must come after (provide 'tramp) because tramp-vc.el
5584 ;; requires tramp.
5585 (eval-after-load "vc"
5586 '(require 'tramp-vc))
5588 ;;; TODO:
5590 ;; * Add fallback for inline encodings. This should be used
5591 ;; if the remote end doesn't support mimencode or a similar program.
5592 ;; For reading files from the remote host, we can just parse the output
5593 ;; of `od -b'. For writing files to the remote host, we construct
5594 ;; a shell program which contains only "safe" ascii characters
5595 ;; and which writes the right bytes to the file. We can use printf(1)
5596 ;; or "echo -e" or the printf function in awk and use octal escapes
5597 ;; for the "dangerous" characters. The null byte might be a problem.
5598 ;; On some systems, the octal escape doesn't work. So we try the following
5599 ;; two commands to write a null byte:
5600 ;; dd if=/dev/zero bs=1 count=1
5601 ;; echo | tr '\n' '\000'
5602 ;; * Cooperate with PCL-CVS. It uses start-process, which doesn't
5603 ;; work for remote files.
5604 ;; * Rewrite `tramp-shell-quote-argument' to abstain from using
5605 ;; `shell-quote-argument'.
5606 ;; * Completion gets confused when you leave out the method name.
5607 ;; * Support `dired-compress-file' filename handler.
5608 ;; * In Emacs 21, `insert-directory' shows total number of bytes used
5609 ;; by the files in that directory. Add this here.
5610 ;; * Avoid screen blanking when hitting `g' in dired. (Eli Tziperman)
5611 ;; * Make ffap.el grok Tramp filenames. (Eli Tziperman)
5612 ;; * When logging in, keep looking for questions according to an alist
5613 ;; and then invoke the right function.
5614 ;; * Case-insensitive filename completion. (Norbert Goevert.)
5615 ;; * Running CVS remotely doesn't appear to work right. It thinks
5616 ;; files are locked by somebody else even if I'm the locking user.
5617 ;; Sometimes, one gets `No CVSROOT specified' errors from CVS.
5618 ;; (Skip Montanaro)
5619 ;; * Don't use globbing for directories with many files, as this is
5620 ;; likely to produce long command lines, and some shells choke on
5621 ;; long command lines.
5622 ;; * Find out about the new auto-save mechanism in Emacs 21 and
5623 ;; do the right thing.
5624 ;; * `vc-directory' does not work. It never displays any files, even
5625 ;; if it does show files when run locally.
5626 ;; * Allow correction of passwords, if the remote end allows this.
5627 ;; (Mark Hershberger)
5628 ;; * Make sure permissions of tmp file are good.
5629 ;; (Nelson Minar <nelson@media.mit.edu>)
5630 ;; * Grok passwd prompts with scp? (David Winter
5631 ;; <winter@nevis1.nevis.columbia.edu>). Maybe just do `ssh -l user
5632 ;; host', then wait a while for the passwd or passphrase prompt. If
5633 ;; there is one, remember the passwd/phrase.
5634 ;; * How to deal with MULE in `insert-file-contents' and `write-region'?
5635 ;; * Do asynchronous `shell-command's.
5636 ;; * Grok `append' parameter for `write-region'.
5637 ;; * Test remote ksh or bash for tilde expansion in `tramp-find-shell'?
5638 ;; * abbreviate-file-name
5639 ;; * grok ~ in tramp-remote-path (Henrik Holm <henrikh@tele.ntnu.no>)
5640 ;; * `C' in dired gives error `not tramp file name'.
5641 ;; * Also allow to omit user names when doing multi-hop. Not sure yet
5642 ;; what the user names should default to, though.
5643 ;; * better error checking. At least whenever we see something
5644 ;; strange when doing zerop, we should kill the process and start
5645 ;; again. (Greg Stark)
5646 ;; * Add caching for filename completion. (Greg Stark)
5647 ;; Of course, this has issues with usability (stale cache bites)
5648 ;; -- <daniel@danann.net>
5649 ;; * Provide a local cache of old versions of remote files for the rsync
5650 ;; transfer method to use. (Greg Stark)
5651 ;; * Remove unneeded parameters from methods.
5652 ;; * Invoke rsync once for copying a whole directory hierarchy.
5653 ;; (Francesco Potortì)
5654 ;; * Should we set PATH ourselves or should we rely on the remote end
5655 ;; to do it?
5656 ;; * Do the autoconf thing.
5657 ;; * Make it work for XEmacs 20, which is missing `with-timeout'.
5658 ;; * Allow non-Unix remote systems. (More a long-term thing.)
5659 ;; * Make it work for different encodings, and for different file name
5660 ;; encodings, too. (Daniel Pittman)
5661 ;; * Change applicable functions to pass a struct tramp-file-name rather
5662 ;; than the individual items MULTI-METHOD, METHOD, USER, HOST, PATH.
5663 ;; * Implement asynchronous shell commands.
5664 ;; * Clean up unused *tramp/foo* buffers after a while. (Pete Forman)
5665 ;; * Progress reports while copying files. (Michael Kifer)
5666 ;; * `Smart' connection method that uses inline for small and out of
5667 ;; band for large files. (Michael Kifer)
5668 ;; * Don't search for perl5 and perl. Instead, only search for perl and
5669 ;; then look if it's the right version (with `perl -v').
5670 ;; * When editing a remote CVS controlled file as a different user, VC
5671 ;; gets confused about the file locking status. Try to find out why
5672 ;; the workaround doesn't work.
5673 ;; * When user is running ssh-agent, it would be useful to add the
5674 ;; passwords typed by the user to that agent. This way, the next time
5675 ;; round, the users don't have to type all this in again.
5676 ;; This would be especially useful for start-process, I think.
5677 ;; An easy way to implement start-process is to open a second shell
5678 ;; connection which is inconvenient if the user has to reenter
5679 ;; passwords.
5680 ;; * Change `copy-file' to grok the case where the filename handler
5681 ;; for the source and the target file are different. Right now,
5682 ;; it looks at the source file and then calls that handler, if
5683 ;; there is one. But since ange-ftp, for instance, does not know
5684 ;; about Tramp, it does not do the right thing if the target file
5685 ;; name is a Tramp name.
5686 ;; * Username and hostname completion.
5688 ;; Functions for file-name-handler-alist:
5689 ;; diff-latest-backup-file -- in diff.el
5690 ;; dired-compress-file
5691 ;; dired-uncache -- this will be needed when we do insert-directory caching
5692 ;; file-name-as-directory -- use primitive?
5693 ;; file-name-directory -- use primitive?
5694 ;; file-name-nondirectory -- use primitive?
5695 ;; file-name-sans-versions -- use primitive?
5696 ;; file-newer-than-file-p
5697 ;; find-backup-file-name
5698 ;; get-file-buffer -- use primitive
5699 ;; load
5700 ;; unhandled-file-name-directory
5701 ;; vc-registered
5703 ;;; tramp.el ends here