Mercurial > hg > xemacs-beta
comparison lisp/efs/efs.el @ 98:0d2f883870bc r20-1b1
Import from CVS: tag r20-1b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 09:13:56 +0200 |
parents | |
children | 4be1180a9e89 |
comparison
equal
deleted
inserted
replaced
97:498bf5da1c90 | 98:0d2f883870bc |
---|---|
1 ;; -*-Emacs-Lisp-*- | |
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
3 ;; | |
4 ;; File: efs.el | |
5 ;; Release: $efs release: 1.15 $ | |
6 ;; Version: $Revision: 1.2 $ | |
7 ;; RCS: | |
8 ;; Description: Transparent FTP support for the original GNU Emacs | |
9 ;; from FSF and Lucid Emacs | |
10 ;; Authors: Andy Norman <ange@hplb.hpl.hp.com>, | |
11 ;; Sandy Rutherford <sandy@ibm550.sissa.it> | |
12 ;; Created: Thu Oct 12 14:00:05 1989 (as ange-ftp) | |
13 ;; | |
14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
15 | |
16 ;;; The following restrictions apply to all of the files in the efs | |
17 ;;; distribution. | |
18 ;;; | |
19 ;;; Copyright (C) 1993 Andy Norman / Sandy Rutherford | |
20 ;;; | |
21 ;;; Authors: | |
22 ;;; Andy Norman (ange@hplb.hpl.hp.com) | |
23 ;;; Sandy Rutherford (sandy@ibm550.sissa.it) | |
24 ;;; | |
25 ;;; The authors of some of the sub-files of efs are different | |
26 ;;; from the above. We are very grateful to people who have | |
27 ;;; contributed code to efs. | |
28 ;;; | |
29 ;;; This program is free software; you can redistribute it and/or modify | |
30 ;;; it under the terms of the GNU General Public License as published by | |
31 ;;; the Free Software Foundation; either version 1, or (at your option) | |
32 ;;; any later version. | |
33 ;;; | |
34 ;;; This program is distributed in the hope that it will be useful, | |
35 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
36 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
37 ;;; GNU General Public License for more details. | |
38 ;;; | |
39 ;;; A copy of the GNU General Public License can be obtained from this | |
40 ;;; program's authors (send electronic mail to ange@hplb.hpl.hp.com) or | |
41 ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, | |
42 ;;; MA 02139, USA. | |
43 | |
44 ;;; Description: | |
45 ;;; | |
46 ;;; This package attempts to make accessing files and directories on | |
47 ;;; remote computers from within GNU Emacs as simple and transparent | |
48 ;;; as possible. Currently all remote files are accessed using FTP. | |
49 ;;; The goal is to make the entire internet accessible as a virtual | |
50 ;;; file system. | |
51 | |
52 ;;; Acknowledgements: << please add to this list >> | |
53 ;;; | |
54 ;;; Corny de Souza for writing efs-mpe.el. | |
55 ;;; Jamie Zawinski for writing efs-ti-twenex.el and efs-ti-explorer.el | |
56 ;;; Joe Wells for writing the first pass at vms support for ange-ftp.el. | |
57 ;;; Sebastian Kremer for helping with dired support. | |
58 ;;; Ishikawa Ichiro for MULE support. | |
59 ;;; | |
60 ;;; Many other people have contributed code, advice, and beta testing | |
61 ;;; (sometimes without even realizing it) to both ange-ftp and efs: | |
62 ;;; | |
63 ;;; Rob Austein, Doug Bagley, Andy Caiger, Jim Franklin, Noah | |
64 ;;; Friedman, Aksnes Knut-Havard, Elmar Heeb, John Interrante, Roland | |
65 ;;; McGrath, Jeff Morgenthaler, Mike Northam, Jens Petersen, Jack | |
66 ;;; Repenning, Joerg-Martin Schwarz, Michael Sperber, Svein Tjemsland, | |
67 ;;; Andy Whitcroft, Raymond A. Wiker | |
68 ;;; | |
69 ;;; Also, thank you to all the people on the efs-testers mailing list. | |
70 ;;; | |
71 | |
72 ;;; -------------------------------------------------------------- | |
73 ;;; Documentation: | |
74 ;;; -------------------------------------------------------------- | |
75 ;;; | |
76 ;;; Currently efs does not have a tex info file, and what you are | |
77 ;;; reading represents the only efs documentation. Please report any | |
78 ;;; errors or omissions in this documentation to the "bugs" address | |
79 ;;; below. Eventually, a tex info file should be written. If you have | |
80 ;;; any problems with efs, please read this section *before* | |
81 ;;; submitting a bug report. | |
82 | |
83 ;;; Installation: | |
84 ;;; | |
85 ;;; For byte compiling the efs package, a Makefile is provided. | |
86 ;;; You should follow the instructions at the top of the Makefile. | |
87 ;;; If you have any problems, please let us know so that we can fix | |
88 ;;; them for other users. Don't even consider using efs without | |
89 ;;; byte compiling it. It will be far too slow. | |
90 ;;; | |
91 ;;; If you decide to byte compile efs by hand, it is important that | |
92 ;;; the file efs-defun.el be byte compiled first, followed by efs.el. | |
93 ;;; The other files may be byte compiled in any order. | |
94 ;;; | |
95 ;;; To use efs, simply put the byte compiled files in your load path | |
96 ;;; and add | |
97 ;;; | |
98 ;;; (require 'efs) | |
99 ;;; | |
100 ;;; in your .emacs file. | |
101 ;;; | |
102 ;;; If you would like efs to be autoloaded when you attempt to access | |
103 ;;; a remote file, put | |
104 ;;; | |
105 ;;; (require 'efs-auto) | |
106 ;;; | |
107 ;;; in your .emacs file. Note that there are some limitations associated | |
108 ;;; with autoloading efs. A discussion of them is given at the top of | |
109 ;;; efs-auto.el. | |
110 | |
111 ;;; Configuration variables: | |
112 ;;; | |
113 ;;; It is important that you read through the section on user customization | |
114 ;;; variables (search forward for the string ">>>"). If your local network | |
115 ;;; is not fully connected to the internet, but accesses the internet only | |
116 ;;; via a gateway, then it is vital to set the appropriate variables to | |
117 ;;; inform efs about the geometry of your local network. Also, see the | |
118 ;;; paragraph on gateways below. | |
119 | |
120 ;;; Usage: | |
121 ;;; | |
122 ;;; Once installed, efs operates largely transparently. All files | |
123 ;;; normally accessible to you on the internet, become part of a large | |
124 ;;; virtual file system. These files are accessed using an extended | |
125 ;;; file name syntax. To access file <path> on remote host <host> by | |
126 ;;; logging in as user <user>, you simply specify the full path of the | |
127 ;;; file as /<user>@<host>:<path>. Nearly all GNU Emacs file handling | |
128 ;;; functions work for remote files. It is not possible to access | |
129 ;;; remote files using shell commands in an emacs *shell* buffer, as such | |
130 ;;; commands are passed directly to the shell, and not handled by emacs. | |
131 ;;; FTP is the underlying utility that efs uses to operate on remote files. | |
132 ;;; | |
133 ;;; For example, if find-file is given a filename of: | |
134 ;;; | |
135 ;;; /ange@anorman:/tmp/notes | |
136 ;;; | |
137 ;;; then efs will spawn an FTP process, connect to the host 'anorman' as | |
138 ;;; user 'ange', get the file '/tmp/notes' and pop up a buffer containing the | |
139 ;;; contents of that file as if it were on the local file system. If efs | |
140 ;;; needed a password to connect then it would prompt the user in the | |
141 ;;; minibuffer. For further discussion of the efs path syntax, see the | |
142 ;;; paragraph on extended file name syntax below. | |
143 | |
144 ;;; Ports: | |
145 ;;; | |
146 ;;; efs supports the use of nonstandard ports on remote hosts. | |
147 ;;; To specify that port <port> should be used, give the host name as | |
148 ;;; host#<port>. Host names may be given in this form anywhere that efs | |
149 ;;; normally expects a host name. This includes in the .netrc file. | |
150 ;;; Logically, efs treats different ports to correspond to different | |
151 ;;; remote hosts. | |
152 | |
153 ;;; Extended filename syntax: | |
154 ;;; | |
155 ;;; The default full efs path syntax is | |
156 ;;; | |
157 ;;; /<user>@<host>#<port>:<path> | |
158 ;;; | |
159 ;;; Both the `#<port>' and `<user>@' may be omitted. | |
160 ;;; | |
161 ;;; If the `#<port>' is omitted, then the default port is taken to be 21, | |
162 ;;; the usual FTP port. For most users, the port syntax will only | |
163 ;;; very rarely be necessary. | |
164 ;;; | |
165 ;;; If the `<user>@' is omitted, then efs will use a default user. If a | |
166 ;;; login token is specified in your .netrc file, then this will be used as | |
167 ;;; the default user for <host>. Otherwise, it is determined based on the | |
168 ;;; value of the variable efs-default-user. | |
169 ;;; | |
170 ;;; This efs path syntax can be customised to a certain extent by | |
171 ;;; changing a number of variables in the subsection Internal Variables. | |
172 ;;; To undertake such a customization requires some knowledge about the | |
173 ;;; internal workings of efs. | |
174 | |
175 ;;; Passwords: | |
176 ;;; | |
177 ;;; A password is required for each host / user pair. This will be | |
178 ;;; prompted for when needed, unless already set by calling | |
179 ;;; efs-set-passwd, or specified in a *valid* ~/.netrc file. | |
180 ;;; | |
181 ;;; When efs prompts for a password, it provides defaults from its | |
182 ;;; cache of currently known passwords. The defaults are ordered such | |
183 ;;; that passwords for accounts which have the same user name as the | |
184 ;;; login which is currently underway have priority. You can cycle | |
185 ;;; through your list of defaults with C-n to cycle forwards and C-p | |
186 ;;; to cycle backwards. The list is circular. | |
187 | |
188 ;;; Passwords for user "anonymous": | |
189 ;;; | |
190 ;;; Passwords for the user "anonymous" (or "ftp") are handled | |
191 ;;; specially. The variable efs-generate-anonymous-password controls | |
192 ;;; what happens. If the value of this variable is a string, then this | |
193 ;;; is used as the password; if non-nil, then a password is created | |
194 ;;; from the name of the user and the hostname of the machine on which | |
195 ;;; GNU Emacs is running; if nil (the default) then the user is | |
196 ;;; prompted for a password as normal. | |
197 | |
198 ;;; "Dumb" UNIX hosts: | |
199 ;;; | |
200 ;;; The FTP servers on some UNIX machines have problems if the "ls" | |
201 ;;; command is used. efs will try to correct for this automatically, | |
202 ;;; and send the "dir" command instead. If it fails, you can call the | |
203 ;;; function efs-add-host, and give the host type as dumb-unix. Note | |
204 ;;; that this change will take effect for the current GNU Emacs | |
205 ;;; session only. To make this specification for future emacs | |
206 ;;; sessions, put | |
207 ;;; | |
208 ;;; (efs-add-host 'dumb-unix "hostname") | |
209 ;;; | |
210 ;;; in your .emacs file. Also, please report any failure to automatically | |
211 ;;; recognize dumb unix to the "bugs" address given below, so that we can | |
212 ;;; fix the auto recognition code. | |
213 | |
214 ;;; File name completion: | |
215 ;;; | |
216 ;;; Full file-name completion is supported on every type of remote | |
217 ;;; host. To do filename completion, efs needs a listing from the | |
218 ;;; remote host. Therefore, for very slow connections, it might not | |
219 ;;; save any time. However, the listing is cached, so subsequent uses | |
220 ;;; of file-name completion will be just as fast as for local file | |
221 ;;; names. | |
222 | |
223 ;;; FTP processes: | |
224 ;;; | |
225 ;;; When efs starts up an FTP process, it leaves it running for speed | |
226 ;;; purposes. Some FTP servers will close the connection after a period of | |
227 ;;; time, but efs should be able to quietly reconnect the next time that | |
228 ;;; the process is needed. | |
229 ;;; | |
230 ;;; The FTP process will be killed should the associated "*ftp user@host*" | |
231 ;;; buffer be deleted. This should not cause efs any grief. | |
232 | |
233 ;;; Showing background FTP activity on the mode-line: | |
234 ;;; | |
235 ;;; After efs is loaded, the command efs-display-ftp-activity will cause | |
236 ;;; background FTP activity to be displayed on the mode line. The variable | |
237 ;;; efs-mode-line-format is used to determine how this data is displayed. | |
238 ;;; efs does not continuously track the number of active sessions, as this | |
239 ;;; would cause the display to change too rapidly. Rather, it uses a heuristic | |
240 ;;; algorithm to determine when there is a significant change in FTP activity. | |
241 | |
242 ;;; File types: | |
243 ;;; | |
244 ;;; By default efs will assume that all files are ASCII. If a file | |
245 ;;; being transferred matches the value of efs-binary-file-name-regexp | |
246 ;;; then the file will be assumed to be a binary file, and efs will | |
247 ;;; transfer it using "type image". ASCII files will be transferred | |
248 ;;; using a transfer type which efs computes to be correct according | |
249 ;;; to its knowledge of the file system of the remote host. The | |
250 ;;; command `efs-prompt-for-transfer-type' toggles the variable | |
251 ;;; `efs-prompt-for-transfer-type'. When this variable is non-nil, efs | |
252 ;;; will prompt the user for the transfer type to use for every FTP | |
253 ;;; transfer. Having this set all the time is annoying, but it is | |
254 ;;; useful to give special treatment to a small set of files. | |
255 ;;; There is also variable efs-text-file-name-regexp. This is tested before | |
256 ;;; efs-binary-file-name-regexp, so if you set efs-text-file-name-regexp | |
257 ;;; to a non-trivial regular expression, and efs-binary-file-name-regexp | |
258 ;;; to ".*", the result will to make image the default tranfer type. | |
259 ;;; | |
260 ;;; Also, if you set efs-treat-crlf-as-nl, then efs will use type image | |
261 ;;; to transfer files between hosts whose file system differ only in that | |
262 ;;; one specifies end of line as CR-LF, and the other as NL. This is useful | |
263 ;;; if you are transferring files between UNIX and DOS machines, and have a | |
264 ;;; package such as dos-mode.el, that handles the extra ^M's. | |
265 | |
266 ;;; Account passwords: | |
267 ;;; | |
268 ;;; Some FTP servers require an additional password which is sent by | |
269 ;;; the ACCOUNT command. efs will detect this and prompt the user for | |
270 ;;; an account password if the server expects one. Also, an account | |
271 ;;; password can be set by calling efs-set-account, or by specifying | |
272 ;;; an account token in the .netrc file. | |
273 ;;; | |
274 ;;; Some operating systems, such as CMS, require that ACCOUNT be used to | |
275 ;;; give a write access password for minidisks. efs-set-account can be used | |
276 ;;; to set a write password for a specific minidisk. Also, tokens of the form | |
277 ;;; minidisk <minidisk name> <password> | |
278 ;;; may be added to host lines in your .netrc file. Minidisk tokens must be | |
279 ;;; at the end of the host line, however there may be an arbitrary number of | |
280 ;;; them for any given host. | |
281 | |
282 ;;; Preloading: | |
283 ;;; | |
284 ;;; efs can be preloaded, but must be put in the site-init.el file and | |
285 ;;; not the site-load.el file in order for the documentation strings for the | |
286 ;;; functions being overloaded to be available. | |
287 | |
288 ;;; Status reports: | |
289 ;;; | |
290 ;;; Most efs commands that talk to the FTP process output a status | |
291 ;;; message on what they are doing. In addition, efs can take advantage | |
292 ;;; of the FTP client's HASH command to display the status of transferring | |
293 ;;; files and listing directories. See the documentation for the variables | |
294 ;;; efs-hash-mark-size, efs-send-hash and efs-verbose for more details. | |
295 | |
296 ;;; Caching of directory information: | |
297 ;;; | |
298 ;;; efs keeps an internal cache of file listings from remote hosts. | |
299 ;;; If this cache gets out of synch, it can be renewed by reverting a | |
300 ;;; dired buffer for the appropriate directory (dired-revert is usually | |
301 ;;; bound to "g"). | |
302 ;;; | |
303 ;;; Alternatively, you can add the following two lines to your .emacs file | |
304 ;;; if you want C-r to refresh efs's cache whilst doing filename | |
305 ;;; completion. | |
306 ;;; (define-key minibuffer-local-completion-map "\C-r" 'efs-re-read-dir) | |
307 ;;; (define-key minibuffer-local-must-match-map "\C-r" 'efs-re-read-dir) | |
308 | |
309 ;;; Gateways: | |
310 ;;; | |
311 ;;; Sometimes it is neccessary for the FTP process to be run on a different | |
312 ;;; machine than the machine running GNU Emacs. This can happen when the | |
313 ;;; local machine has restrictions on what hosts it can access. | |
314 ;;; | |
315 ;;; efs has support for running the ftp process on a different (gateway) | |
316 ;;; machine. The way it works is as follows: | |
317 ;;; | |
318 ;;; 1) Set the variable 'efs-gateway-host' to the name of a machine | |
319 ;;; that doesn't have the access restrictions. If you need to use | |
320 ;;; a nonstandard port to access this host for gateway use, then | |
321 ;;; specify efs-gateway-host as "<hostname>#<port>". | |
322 ;;; | |
323 ;;; 2) Set the variable 'efs-ftp-local-host-regexp' to a regular expression | |
324 ;;; that matches hosts that can be contacted from running a local ftp | |
325 ;;; process, but fails to match hosts that can't be accessed locally. For | |
326 ;;; example: | |
327 ;;; | |
328 ;;; "\\.hp\\.com$\\|^[^.]*$" | |
329 ;;; | |
330 ;;; will match all hosts that are in the .hp.com domain, or don't have an | |
331 ;;; explicit domain in their name, but will fail to match hosts with | |
332 ;;; explicit domains or that are specified by their ip address. | |
333 ;;; | |
334 ;;; 3) Set the variable `efs-local-host-regexp' to machines that you have | |
335 ;;; direct TCP/IP access. In other words, you must be able to ping these | |
336 ;;; hosts. Usually, efs-ftp-local-host-regexp and efs-local-host-regexp | |
337 ;;; will be the same. However, they will differ for so-called transparent | |
338 ;;; gateways. See #7 below for more details. | |
339 ;;; | |
340 ;;; 4) Set the variable 'efs-gateway-tmp-name-template' to the name of | |
341 ;;; a directory plus an identifying filename prefix for making temporary | |
342 ;;; files on the gateway. For example: "/tmp/hplose/ange/efs" | |
343 ;;; | |
344 ;;; 5) If the gateway and the local host share cross-mounted directories, | |
345 ;;; set the value of `efs-gateway-mounted-dirs-alist' accordingly. It | |
346 ;;; is particularly useful, but not mandatory, that the directory | |
347 ;;; of `efs-gateway-tmp-name-template' be cross-mounted. | |
348 ;;; | |
349 ;;; 6) Set the variable `efs-gateway-type' to the type gateway that you have. | |
350 ;;; This variable is a list, the first element of which is a symbol | |
351 ;;; denoting the type of gateway. Following elements give further | |
352 ;;; data on the gateway. | |
353 ;;; | |
354 ;;; Supported gateway types: | |
355 ;;; | |
356 ;;; a) local: | |
357 ;;; This means that your local host is itself the gateway. However, | |
358 ;;; it is necessary to use a different FTP client to gain access to | |
359 ;;; the outside world. If the name of the FTP client were xftp, you might | |
360 ;;; set efs-gateway-type to | |
361 ;;; | |
362 ;;; (list 'local "xftp" efs-ftp-program-args) | |
363 ;;; | |
364 ;;; If xftp required special arguments, then give them in place of | |
365 ;;; efs-ftp-program-args. See the documentation for efs-ftp-program-args | |
366 ;;; for the syntax. | |
367 ;;; | |
368 ;;; b) proxy: | |
369 ;;; This indicates that your gateway works by first FTP'ing to it, and | |
370 ;;; then issuing a USER command of the form | |
371 ;;; | |
372 ;;; USER <username>@<host> | |
373 ;;; | |
374 ;;; In this case, you might set efs-gateway-type to | |
375 ;;; | |
376 ;;; (list 'proxy "ftp" efs-ftp-program-args) | |
377 ;;; | |
378 ;;; If you need to use a nonstandard client, such as iftp, give this | |
379 ;;; instead of "ftp". If this client needs to take special arguments, | |
380 ;;; give them instead of efs-ftp-program-args. | |
381 ;;; | |
382 ;;; c) remsh: | |
383 ;;; For this type of gateway, you need to start a remote shell on | |
384 ;;; your gateway, using either remsh or rsh. You should set | |
385 ;;; efs-gateway-type to something like | |
386 ;;; | |
387 ;;; (list 'remsh "remsh" nil "ftp" efs-ftp-program-args) | |
388 ;;; | |
389 ;;; If you use rsh instead of remsh, change the second element from | |
390 ;;; "remsh" to "rsh". Note that the symbol indicating the gateway | |
391 ;;; type should still be 'remsh. If you want to pass arguments | |
392 ;;; to the remsh program, give them as the third element. For example, | |
393 ;;; if you need to specify a user, make this (list "-l" "sandy"). | |
394 ;;; If you need to use a nonstandard FTP client, specify that as the fourth | |
395 ;;; element. If your FTP client needs to be given special arguments, | |
396 ;;; give them instead of efs-ftp-program-args. | |
397 ;;; | |
398 ;;; d) interactive: | |
399 ;;; This indicates that you need to establish a login on the gateway, | |
400 ;;; using either telnet or rlogin. | |
401 ;;; You should set efs-gateway-type to something like | |
402 ;;; | |
403 ;;; (list 'interactive "rlogin" nil "exec ftp" efs-ftp-program-args) | |
404 ;;; | |
405 ;;; If you need to use telnet, then give "telnet" in place of the second | |
406 ;;; element "rlogin". If your login program needs to be given arguments, | |
407 ;;; then they should be given in the third slot. The fourth element | |
408 ;;; is for the name of the FTP client program. Giving this as "exec ftp", | |
409 ;;; instead of "ftp", ensures that you are logged out if the FTP client | |
410 ;;; dies. If the FTP client takes special arguments, give these instead | |
411 ;;; of efs-ftp-program-args. Furthermore, you should see the documentation | |
412 ;;; at the top of efs-gwp.el. You may need to set the variables | |
413 ;;; efs-gwp-setup-term-command, and efs-gwp-prompt-pattern. | |
414 ;;; | |
415 ;;; e) raptor: | |
416 ;;; This is a type of gateway where efs is expected to specify a gateway | |
417 ;;; user, and send a password for this user using the ACCOUNT command. | |
418 ;;; For example, to log in to foobar.edu as sandy, while using the account | |
419 ;;; ange on the gateway, the following commands would be sent: | |
420 ;;; | |
421 ;;; open raptorgate.com | |
422 ;;; quote USER sandy@foobar.edu ange | |
423 ;;; quote pass <sandy's password on foobar> | |
424 ;;; quote account <ange's password on raptorgate> | |
425 ;;; | |
426 ;;; For such a gateway, you would set efs-gateway-type to | |
427 ;;; | |
428 ;;; (list 'raptor efs-ftp-program efs-ftp-program-args <GATEWAY USER>) | |
429 ;;; | |
430 ;;; where <GATEWAY USER> is the name of your account on the gateway. In | |
431 ;;; the above example, this would be "ange". You can set your gateway | |
432 ;;; password by simply setting an account password for the gateway host. | |
433 ;;; This can be done with either efs-set-account, or within your .netrc | |
434 ;;; file. If no password is set, you will be prompted for one. | |
435 ;;; | |
436 ;;; f) interlock: | |
437 ;;; This is a type of gateway where you are expected to send a PASS | |
438 ;;; command after opening the connection to the gateway. | |
439 ;;; The precise login sequence is | |
440 ;;; | |
441 ;;; open interlockgate | |
442 ;;; quote PASS <sandy's password on interlockgate> | |
443 ;;; quote USER sandy@foobar.edu | |
444 ;;; quote PASS <sandy's password on foobar.edu> | |
445 ;;; | |
446 ;;; For such a gateway, you should set efs-gateway-type to | |
447 ;;; | |
448 ;;; (list 'interlock efs-ftp-program efs-ftp-program-args) | |
449 ;;; | |
450 ;;; If you need to use a nonstandard name for your FTP client, | |
451 ;;; then replace efs-ftp-program with this name. If your FTP client | |
452 ;;; needs to take nonstandard arguments, then replace efs-ftp-program-args | |
453 ;;; with these arguments. See efs-ftp-program-args <V> for the required | |
454 ;;; syntax. | |
455 ;;; | |
456 ;;; If your gateway returns both a 220 code and a 331 code to the | |
457 ;;; "open interlockgate" command, then you should add a regular | |
458 ;;; expression to efs-skip-msgs <V> that matches the 220 response. | |
459 ;;; Returning two response codes to a single FTP command is not permitted | |
460 ;;; in RFC 959. It is not possible for efs to ignore the 220 by default, | |
461 ;;; because than it would hang for interlock installations which do not | |
462 ;;; require a password. | |
463 ;;; | |
464 ;;; g) kerberos: | |
465 ;;; With this gateway, you need to authenticate yourself by getting a | |
466 ;;; kerberos "ticket" first. Usually, this is done with the kinit program. | |
467 ;;; Once authenticated, you connect to foobar.com as user sandy with the | |
468 ;;; sequence: (Note that the "-n" argument inhibits automatic login. | |
469 ;;; Although, in manual use you probably don't use it, efs always uses it.) | |
470 ;;; | |
471 ;;; iftp -n | |
472 ;;; open foobar.com | |
473 ;;; user sandy@foobar.com | |
474 ;;; | |
475 ;;; You should set efs-gateway-type to something like | |
476 ;;; | |
477 ;;; (list 'kerberos "iftp" efs-ftp-program-args "kinit" <KINIT-ARGS>) | |
478 ;;; | |
479 ;;; If you use an FTP client other than iftp, insert its name instead | |
480 ;;; of "iftp" above. If your FTP client needs special arguments, give | |
481 ;;; them as a list of strings in place of efs-ftp-program-args. If | |
482 ;;; the program that you use to collect a ticket in not called "kinit", | |
483 ;;; then give its name in place of "kinit" above. <KINIT-ARGS> should be | |
484 ;;; any arguments that you need to pass to your kinit program, given as a | |
485 ;;; list of strings. Most likely, you will give this as nil. | |
486 ;;; | |
487 ;;; See the file efs-kerberos.el for more configuration variables. If you | |
488 ;;; need to adjust any of these variables, please report this to us so that | |
489 ;;; we can fix them for other users. | |
490 ;;; | |
491 ;;; If efs detects that you are not authenticated to use the gateway, it | |
492 ;;; will run the kinit program automatically, prompting you for a password. | |
493 ;;; If you give a password in your .netrc file for login the value of | |
494 ;;; efs-gateway-host <V> and user kerberos, then efs will use this to | |
495 ;;; obtain gateway authentication. | |
496 ;;; | |
497 ;;; 7) Transparent gateways: | |
498 ;;; | |
499 ;;; If your gateway is completely transparent (for example it uses | |
500 ;;; socks), then you should set efs-gateway-type to nil. Also, | |
501 ;;; set efs-ftp-local-host-regexp to ".*". However, efs-local-host-regexp, | |
502 ;;; must still be set to a regular expression matching hosts in your local | |
503 ;;; domain. efs uses this to determine which machines that it can | |
504 ;;; open-network-stream to. Furthermore, you should still set | |
505 ;;; efs-gateway-host to the name of your gateway machine. That way efs | |
506 ;;; will know that this is a special machine having direct TCP/IP access | |
507 ;;; to both hosts in the outside world, and hosts in your local domain. | |
508 ;;; | |
509 ;;; 8) Common Problems with Gateways: | |
510 ;;; | |
511 ;;; a) Spurious 220 responses: | |
512 ;;; Some proxy-style gateways (eg gateway type 'proxy or 'raptor), | |
513 ;;; return two 3-digit FTP reply codes to the USER command. | |
514 ;;; For example: | |
515 ;;; | |
516 ;;; open gateway.weird | |
517 ;;; 220 Connected to gateway.weird | |
518 ;;; quote USER sandy@foobar | |
519 ;;; 220 Connected to foobar | |
520 ;;; 331 Password required for sandy | |
521 ;;; | |
522 ;;; This is wrong, according to the FT Protocol. Each command must return | |
523 ;;; exactly one 3-digit reply code. It may be preceded by continuation | |
524 ;;; lines. What should really be returned is: | |
525 ;;; | |
526 ;;; quote USER sandy@foobar | |
527 ;;; 331-Connected to foobar. | |
528 ;;; 331 Password required for sandy. | |
529 ;;; | |
530 ;;; or even | |
531 ;;; | |
532 ;;; quote USER sandy@foobar | |
533 ;;; 331-220 Connected to foobar. | |
534 ;;; 331 Password required for sandy. | |
535 ;;; | |
536 ;;; Even though the "331-220" looks strange, it is correct protocol, and | |
537 ;;; efs will parse it properly. | |
538 ;;; | |
539 ;;; If your gateway is returning a spurious 220 to USER, a work-around | |
540 ;;; is to add a regular expression to `efs-skip-msgs' that matches | |
541 ;;; this line. It must not match the 220 line returned to the open | |
542 ;;; command. This work-around may not work, as some system FTP clients | |
543 ;;; also get confused by the spurious 220. In this case, the only | |
544 ;;; solution is to patch the gateway server. In either case, please | |
545 ;;; send a bug report to the author of your gateway software. | |
546 ;;; | |
547 ;;; b) Case-sensitive parsing of FTP commands: | |
548 ;;; Some gateway servers seem to treat FTP commands case-sensitively. | |
549 ;;; This is incorrect, as RFC 959 clearly states that FTP commands | |
550 ;;; are always to be case-insensitive. If this is a problem with your | |
551 ;;; gateway server, you should send a bug report to its author. | |
552 ;;; If efs is using a case for FTP commands that does not suit your server, | |
553 ;;; a possible work-around is to edit the efs source so that the required | |
554 ;;; case is used. However, we will not be making any changes to the | |
555 ;;; standard efs distribution to support this type of server behaviour. | |
556 ;;; If you need help changing the efs source, you should enquire with the | |
557 ;;; efs-help mailing list. | |
558 ;;; | |
559 | |
560 ;;; --------------------------------------------------------------- | |
561 ;;; Tips for using efs: | |
562 ;;; --------------------------------------------------------------- | |
563 | |
564 ;;; 1) Beware of compressing files on non-UNIX hosts. efs will do it by | |
565 ;;; copying the file to the local machine, compressing it there, and then | |
566 ;;; sending it back. Binary file transfers between machines of different | |
567 ;;; architectures can be a risky business. Test things out first on some | |
568 ;;; test files. See "Bugs" below. Also, note that efs sometimes | |
569 ;;; copies files by moving them through the local machine. Again, | |
570 ;;; be careful when doing this with binary files on non-Unix | |
571 ;;; machines. | |
572 ;;; | |
573 ;;; 2) Beware that dired over ftp will use your setting of dired-no-confirm | |
574 ;;; (list of dired commands for which confirmation is not asked). | |
575 ;;; You might want to reconsider your setting of this variable, | |
576 ;;; because you might want confirmation for more commands on remote | |
577 ;;; direds than on local direds. For example, I strongly recommend | |
578 ;;; that you not include compress in this list. If there is enough | |
579 ;;; demand it might be a good idea to have an alist | |
580 ;;; efs-dired-no-confirm of pairs ( TYPE . LIST ), where TYPE is an | |
581 ;;; operating system type and LIST is a list of commands for which | |
582 ;;; confirmation would be suppressed. Then remote dired listings | |
583 ;;; would take their (buffer-local) value of dired-no-confirm from | |
584 ;;; this alist. Who votes for this? | |
585 ;;; | |
586 ;;; 3) Some combinations of FTP clients and servers break and get out of sync | |
587 ;;; when asked to list a non-existent directory. Some of the ai.mit.edu | |
588 ;;; machines cause this problem for some FTP clients. Using | |
589 ;;; efs-kill-ftp-process can be used to restart the ftp process, which | |
590 ;;; should get things back in synch. | |
591 ;;; | |
592 ;;; 4) Some ftp servers impose a length limit on the password that can | |
593 ;;; be sent. If this limit is exceeded they may bomb in an | |
594 ;;; incomprehensible way. This sort of behaviour is common with | |
595 ;;; MVS servers. Therefore, you should beware of this possibility | |
596 ;;; if you are generating a long password (like an email address) | |
597 ;;; with efs-generate-anonymous-password. | |
598 ;;; | |
599 ;;; 5) Some antiquated FTP servers hang when asked for an RNFR command. | |
600 ;;; efs sometimes uses this to test whether its local cache is stale. | |
601 ;;; If your server for HOST hangs when asked for this command, put | |
602 ;;; (efs-set-host-property HOST 'rnfr-failed t) | |
603 ;;; in your efs-ftp-startup-function-alist entry for HOST. | |
604 ;;; | |
605 | |
606 ;;; ----------------------------------------------------------------------- | |
607 ;;; Where to get the latest version of efs: | |
608 ;;; ----------------------------------------------------------------------- | |
609 ;;; | |
610 ;;; The authors are grateful to anyone or any organization which | |
611 ;;; provides anonymous FTP distribution for efs. | |
612 ;;; | |
613 ;;; | |
614 ;;; Europe: | |
615 ;;; | |
616 ;;; Switzerland | |
617 ;;; /anonymous@itp.ethz.ch:/sandy/efs/ | |
618 ;;; | |
619 ;;; North America: | |
620 ;;; | |
621 ;;; Massachusetts, USA | |
622 ;;; /anonymous@alpha.gnu.ai.mit.edu:/efs/ | |
623 ;;; | |
624 ;;; California, USA | |
625 ;;; /anonymous@ftp.hmc.edu:/pub/emacs/packages/efs/ | |
626 ;;; | |
627 ;;; Australia and New Zealand: | |
628 ;;; | |
629 ;;; ???????????? | |
630 ;;; | |
631 ;;; Japan: | |
632 ;;; | |
633 ;;; ???????????? | |
634 | |
635 ;;; --------------------------------------------------------------------- | |
636 ;;; Non-UNIX support: | |
637 ;;; --------------------------------------------------------------------- | |
638 | |
639 ;;; efs has full support, incuding file name completion and tree dired | |
640 ;;; for: | |
641 ;;; | |
642 ;;; VMS, CMS, MTS, MVS, ti-twenex, ti-explorer (the last two are lisp | |
643 ;;; machines), TOPS-20, DOS (running the Distinct, Novell, FTP | |
644 ;;; software, NCSA, Microsoft in both unix and DOS mode, Super TCP, and | |
645 ;;; Hellsoft FTP servers), unix descriptive listings (dl), KA9Q, OS/2, | |
646 ;;; VOS, NOS/VE, CMS running the KNET server, Tandem's Guardian OS, COKE | |
647 ;;; | |
648 ;;; efs should be able to automatically recognize any of the operating | |
649 ;;; systems and FTP servers that it supports. Please report any | |
650 ;;; failure to do so to the "bugs" address below. You can specify a | |
651 ;;; certain host as being of a given host type with the command | |
652 ;;; | |
653 ;;; (efs-add-host <host-type> <host>) | |
654 ;;; | |
655 ;;; <host-type> is a symbol, <host> is a string. If this command is | |
656 ;;; used interactively, then <host-type> is prompted for with | |
657 ;;; completion. Some host types have regexps that can be used to | |
658 ;;; specify a class of host names as being of a certain type. Note | |
659 ;;; that if you specify a host as being of a certain type, efs does | |
660 ;;; not verify that that is really the type of the host. This calls | |
661 ;;; for caution when using regexps to specify host types, as an | |
662 ;;; inadvertent match to a regexp might have unpleasant consequences. | |
663 ;;; | |
664 ;;; See the respective efs-TYPE.el files for more information. | |
665 ;;; When or if we get a tex info file, it should contain some more | |
666 ;;; details on the non-unix support. | |
667 | |
668 ;;; ------------------------------------------------------------------ | |
669 ;;; Bugs and other things that go clunk in the night: | |
670 ;;; ------------------------------------------------------------------ | |
671 | |
672 ;;; How to report a bug: | |
673 ;;; -------------------- | |
674 ;;; | |
675 ;;; Type M-x efs-report-bug | |
676 ;;; or | |
677 ;;; send mail to efs-bugs@cuckoo.hpl.hp.com. | |
678 ;;; | |
679 ;;; efs is a "free" program. This means that you didn't (or shouldn't | |
680 ;;; have) paid anything for it. It also means that nobody is paid to | |
681 ;;; maintain it, and the authors weren't paid for writing it. | |
682 ;;; Therefore, please try to write your bug report in a clear and | |
683 ;;; complete fashion. It will greatly enhance the probability that | |
684 ;;; something will be done about your problem. | |
685 ;;; | |
686 ;;; Note that efs relies heavily in cached information, so the bug may | |
687 ;;; depend in a complicated fashion on commands that were performed on | |
688 ;;; remote files from the beginning of your emacs session. Trying to | |
689 ;;; reproduce your bug starting from a fresh emacs session is usually | |
690 ;;; a good idea. | |
691 ;;; | |
692 | |
693 ;;; Fan/hate mail: | |
694 ;;; -------------- | |
695 ;;; | |
696 ;;; efs has its own mailing list called efs-help. All users of efs | |
697 ;;; are welcome to subscribe (see below) and to discuss aspects of | |
698 ;;; efs. New versions of efs are posted periodically to the mailing | |
699 ;;; list. | |
700 ;;; | |
701 ;;; To [un]subscribe to efs-help, or to report mailer problems with the | |
702 ;;; list, please mail one of the following addresses: | |
703 ;;; | |
704 ;;; efs-help-request@cuckoo.hpl.hp.com | |
705 ;;; or | |
706 ;;; efs-help-request%cuckoo.hpl.hp.com@hplb.hpl.hp.com | |
707 ;;; | |
708 ;;; Please don't forget the -request part. | |
709 ;;; | |
710 ;;; For mail to be posted directly to efs-help, send to one of the | |
711 ;;; following addresses: | |
712 ;;; | |
713 ;;; efs-help@cuckoo.hpl.hp.com | |
714 ;;; or | |
715 ;;; efs-help%cuckoo.hpl.hp.com@hplb.hpl.hp.com | |
716 ;;; | |
717 ;;; Alternatively, there is a mailing list that only gets | |
718 ;;; announcements of new efs releases. This is called efs-announce, | |
719 ;;; and can be subscribed to by e-mailing to the -request address as | |
720 ;;; above. Please make it clear in the request which mailing list you | |
721 ;;; wish to join. | |
722 ;;; | |
723 | |
724 ;;; Known bugs: | |
725 ;;; ----------- | |
726 ;;; | |
727 ;;; If you hit a bug in this list, please report it anyway. Most of | |
728 ;;; the bugs here remain unfixed because they are considered too | |
729 ;;; esoteric to be a high priority. If one of them gets reported | |
730 ;;; enough, we will likely change our view on that. | |
731 ;;; | |
732 ;;; 1) efs does not check to make sure that when creating a new file, | |
733 ;;; you provide a valid filename for the remote operating system. | |
734 ;;; If you do not, then the remote FTP server will most likely | |
735 ;;; translate your filename in some way. This may cause efs to | |
736 ;;; get confused about what exactly is the name of the file. | |
737 ;;; | |
738 ;;; 2) For CMS support, we send too many cd's. Since cd's are cheap, I haven't | |
739 ;;; worried about this too much. Eventually, we should have some caching | |
740 ;;; of the current minidisk. This is complicated by the fact that some | |
741 ;;; CMS servers lie about the current minidisk, so sending redundant | |
742 ;;; cd's helps us recover in this case. | |
743 ;;; | |
744 ;;; 3) The code to do compression of files over ftp is not as careful as it | |
745 ;;; should be. It deletes the old remote version of the file, before | |
746 ;;; actually checking if the local to remote transfer of the compressed | |
747 ;;; file succeeds. Of course to delete the original version of the file | |
748 ;;; after transferring the compressed version back is also dangerous, | |
749 ;;; because some OS's have severe restrictions on the length of filenames, | |
750 ;;; and when the compressed version is copied back the "-Z" or ".Z" may be | |
751 ;;; truncated. Then, efs would delete the only remaining version of | |
752 ;;; the file. Maybe efs should make backups when it compresses files | |
753 ;;; (of course, the backup "~" could also be truncated off, sigh...). | |
754 ;;; Suggestions? | |
755 ;;; | |
756 ;;; 4) If a dir listing is attempted for an empty directory on (at least | |
757 ;;; some) VMS hosts, an ftp error is given. This is really an ftp bug, and | |
758 ;;; I don't know how to get efs work to around it. | |
759 ;;; | |
760 ;;; 5) efs gets confused by directories containing file names with | |
761 ;;; embedded newlines. A temporary solution is to add "q" to your | |
762 ;;; dired listing switches. As long as your dired listing switches | |
763 ;;; also contain "l" and either "a" or "A", efs will use these | |
764 ;;; switches to get listings for its internal cache. The "q" switch | |
765 ;;; should force listings to be exactly one file per line. You | |
766 ;;; still will not be able to access a file with embedded newlines, | |
767 ;;; but at least it won't mess up the parsing of the rest of the files. | |
768 ;;; | |
769 ;;; 6) efs cannot parse symlinks which have an embedded " -> " | |
770 ;;; in their name. It's alright to have an embedded " -> " in the name | |
771 ;;; of any other type of file. A fix is possible, but probably not worth | |
772 ;;; the trouble. If you disagree, send us a bug report. | |
773 ;;; | |
774 ;;; 7) efs doesn't handle context-dep. files in H-switch listings on | |
775 ;;; HP's. It wouldn't be such a big roaring deal to fix this. I'm | |
776 ;;; waiting until I get an actual bug report though. | |
777 ;;; | |
778 ;;; 8) If a hard link is added or deleted, efs will not update its | |
779 ;;; internal cache of the link count for other names of the file. | |
780 ;;; This may cause file-nlinks to return incorrectly. Reverting | |
781 ;;; any dired buffer containing other names for the file will | |
782 ;;; cause the file data to be updated, including the link counts. | |
783 ;;; A fix for this problem is known and will be eventually | |
784 ;;; implemented. How it is implemented will depend on how we decide | |
785 ;;; to handle inodes. See below. | |
786 ;;; | |
787 ;;; 9) efs is unable to parse R-switch listings from remote unix hosts. | |
788 ;;; This is inefficient, because efs will insist on doing individual | |
789 ;;; listings of the subdirectories to get its file information. | |
790 ;;; This may be fixed if there is enough demand. | |
791 ;;; | |
792 ;;; 10) In file-attributes, efs returns a fake inode number. Of course | |
793 ;;; this is necessary, but this inode number is not even necessarily | |
794 ;;; unique. It is simply the sum of the characters (treated as | |
795 ;;; integers) in the host name, user name, and file name. Possible | |
796 ;;; ways to get a unique inode number are: | |
797 ;;; a) Simply keep a count of all remote file in the cache, and | |
798 ;;; return the file's position in this count as a negative number. | |
799 ;;; b) For unix systems, we could actually get at the real inode | |
800 ;;; number on the remote host, by adding an "i" to the ls switches. | |
801 ;;; The inode numbers would then be removed from the listing | |
802 ;;; returned by efs-ls, if the caller hadn't requested the "i" | |
803 ;;; switch. We could then make a unique number out of the host name | |
804 ;;; and the real inode number. | |
805 ;;; | |
806 ;;; 11) efs tries to determine if a file is readable or writable by comparing | |
807 ;;; the file modes, file owner, and user name under which it is logged | |
808 ;;; into the remote host. This does not take into account groups. | |
809 ;;; We simply assume that the user belongs to all groups. As a result | |
810 ;;; we may assume that a file is writable, when in fact it is not. | |
811 ;;; Groups are tough to handle correctly over FTP. Suggestions? | |
812 ;;; (For new FTP servers, can do a "QUOTE SITE EXEC groups" to | |
813 ;;; handle this.) | |
814 | |
815 ;;; ----------------------------------------------------------- | |
816 ;;; Technical information on this package: | |
817 ;;; ----------------------------------------------------------- | |
818 | |
819 ;;; efs hooks onto the following functions using the | |
820 ;;; file-name-handler-alist. Depending on which version of emacs you | |
821 ;;; are using, not all of these functions may access this alist. In | |
822 ;;; this case, efs overloads the definitions of these functions with | |
823 ;;; versions that do access the file-name-handler-alist. These | |
824 ;;; overloads are done in efs's version-specific files. | |
825 ;;; | |
826 ;;; abbreviate-file-name | |
827 ;;; backup-buffer | |
828 ;;; copy-file | |
829 ;;; create-file-buffer | |
830 ;;; delete-directory | |
831 ;;; delete-file | |
832 ;;; directory-file-name | |
833 ;;; directory-files | |
834 ;;; file-attributes | |
835 ;;; file-directory-p | |
836 ;;; file-exists-p | |
837 ;;; file-local-copy | |
838 ;;; file-modes | |
839 ;;; file-name-all-completions | |
840 ;;; file-name-as-directory | |
841 ;;; file-name-completion | |
842 ;;; file-name-directory | |
843 ;;; file-name-nondirectory | |
844 ;;; file-name-sans-versions | |
845 ;;; file-newer-than-file-p | |
846 ;;; file-readable-p | |
847 ;;; file-executable-p | |
848 ;;; file-accessible-directory-p | |
849 ;;; file-symlink-p | |
850 ;;; file-writable-p | |
851 ;;; get-file-buffer | |
852 ;;; insert-directory | |
853 ;;; insert-file-contents | |
854 ;;; list-directory | |
855 ;;; make-directory-internal | |
856 ;;; rename-file | |
857 ;;; set-file-modes | |
858 ;;; set-visited-file-modtime | |
859 ;;; substitute-in-file-name | |
860 ;;; verify-visited-file-modtime | |
861 ;;; write-region | |
862 ;;; | |
863 ;;; The following functions are overloaded in efs.el, because they cannot | |
864 ;;; be handled via the file-name-handler-alist. | |
865 ;;; | |
866 ;;; expand-file-name | |
867 ;;; load | |
868 ;;; read-file-name-internal (Emacs 18, only) | |
869 ;;; require | |
870 ;;; | |
871 ;;; The following dired functions are handled by hooking them into the | |
872 ;;; the file-name-handler-alist. This is done in efs-dired.el. | |
873 ;;; | |
874 ;;; efs-dired-compress-file | |
875 ;;; eds-dired-print-file | |
876 ;;; efs-dired-make-compressed-filename | |
877 ;;; efs-compress-file | |
878 ;;; efs-dired-print-file | |
879 ;;; efs-dired-create-directory | |
880 ;;; efs-dired-recursive-delete-directory | |
881 ;;; efs-dired-uncache | |
882 ;;; efs-dired-call-process | |
883 ;;; | |
884 ;;; In efs-dired.el, the following dired finctions are overloaded. | |
885 ;;; | |
886 ;;; dired-collect-file-versions | |
887 ;;; dired-find-file | |
888 ;;; dired-flag-backup-files | |
889 ;;; dired-get-filename | |
890 ;;; dired-insert-headerline | |
891 ;;; dired-move-to-end-of-filename | |
892 ;;; dired-move-to-filename | |
893 ;;; dired-run-shell-command | |
894 ;;; | |
895 ;;; efs makes use of the following hooks | |
896 ;;; | |
897 ;;; diff-load-hook | |
898 ;;; dired-before-readin-hook | |
899 ;;; find-file-hooks | |
900 ;;; dired-grep-load-hook | |
901 | |
902 ;;; LISPDIR ENTRY for the Elisp Archive: | |
903 ;;; | |
904 ;;; LCD Archive Entry: | |
905 ;;; efs|Andy Norman and Sandy Rutherford | |
906 ;;; |ange@hplb.hpl.hp.com and sandy@ibm550.sissa.it | |
907 ;;; |transparent FTP Support for GNU Emacs | |
908 ;;; |$Date: 1997/02/15 22:20:36 $|$efs release: 1.15 beta $| | |
909 | |
910 ;;; Host and listing type notation: | |
911 ;;; | |
912 ;;; The functions efs-host-type and efs-listing-type, and the | |
913 ;;; variable efs-dired-host-type follow the following conventions | |
914 ;;; for remote host types. | |
915 ;;; | |
916 ;;; nil = local host type, whatever that is (probably unix). | |
917 ;;; Think nil as in "not a remote host". This value is used by | |
918 ;;; efs-dired-host-type for local buffers. | |
919 ;;; (efs-host-type nil) => nil | |
920 ;;; | |
921 ;;; 'type = a remote host of TYPE type. | |
922 ;;; | |
923 ;;; 'type:list = a remote host using listing type 'type:list. | |
924 ;;; This is currently used for Unix dl (descriptive | |
925 ;;; listings), when efs-dired-host-type is set to | |
926 ;;; 'unix:dl, and to support the myriad of DOS FTP | |
927 ;;; servers. | |
928 | |
929 ;;; Supported host and listing types: | |
930 ;;; | |
931 ;;; unknown, unix, dumb-unix, bsd-unix, sysV-unix, next-unix, | |
932 ;;; super-dumb-unix, dumb-apollo-unix, | |
933 ;;; apollo-unix, unix:dl, dos-distinct, ka9q, dos, dos:ftp, dos:novell, | |
934 ;;; dos:ncsa, dos:winsock, vos, hell, dos:microsoft, super-dumb-unix | |
935 ;;; vms, cms, mts, mvs, mvs:tcp mvs:nih tops-20, mpe, ti-twenex, | |
936 ;;; ti-explorer, os2, vos, | |
937 ;;; vms:full, guardian, ms-unix (This is the Microsoft NT Windows server | |
938 ;;; in unix mode.), plan9, unix:unknown, nos-ve (actually NOS/VE). | |
939 | |
940 ;;; Host and listing type hierarchy: | |
941 ;;; | |
942 ;;; unknown: unix, dumb-unix, sysV-unix, bsd-unix, next-unix, apollo-unix, | |
943 ;;; ka9q, dos-distinct, unix:dl, hell, | |
944 ;;; super-dumb-unix, dumb-apollo-unix | |
945 ;;; unix: sysV-unix, bsd-unix, next-unix, apollo-unix, unix:dl | |
946 ;;; dos: dos:ftp, dos:novell, dos:ncsa, dos:microsoft, dos:winsock | |
947 ;;; dumb-unix: | |
948 ;;; bsd-unix: | |
949 ;;; sysV-unix: | |
950 ;;; next-unix: | |
951 ;;; apollo-unix: | |
952 ;;; dumb-apollo-unix: | |
953 ;;; unix:dl: | |
954 ;;; unix:unknown: unix:dl, unix | |
955 ;;; super-dumb-unix: | |
956 ;;; dos-distinct: | |
957 ;;; dos:ftp: | |
958 ;;; dos:novell: | |
959 ;;; dos:microsoft | |
960 ;;; ka9q: | |
961 ;;; vms: vms:full | |
962 ;;; cms: | |
963 ;;; mts: | |
964 ;;; mvs: mvs:tcp, mvs:nih | |
965 ;;; mvs:tcp: | |
966 ;;; mvs:nih: | |
967 ;;; tops-20: | |
968 ;;; ti-twenex: | |
969 ;;; ti-explorer: | |
970 ;;; os2: | |
971 ;;; vos: | |
972 ;;; vms:full: | |
973 ;;; dos:ncsa: | |
974 ;;; dos:winsock: | |
975 ;;; vos: | |
976 ;;; hell: | |
977 ;;; guardian: | |
978 ;;; ms-unix: | |
979 ;;; plan9: | |
980 ;;; nos-ve: | |
981 ;;; coke: | |
982 ;;; | |
983 | |
984 | |
985 ;;;; ================================================================ | |
986 ;;;; >0 | |
987 ;;;; Table of Contents for efs.el | |
988 ;;;; ================================================================ | |
989 ;; | |
990 ;; Each section of efs.el is labelled by >#, where # is the number of | |
991 ;; the section. | |
992 ;; | |
993 ;; 1. Provisions, requirements, and autoloads. | |
994 ;; 2. Variable definitions. | |
995 ;; 3. Utilities. | |
996 ;; 4. Hosts, users, accounts, and passwords. | |
997 ;; 5. FTP client process and server responses. | |
998 ;; 6. Sending commands to the FTP server. | |
999 ;; 7. Parsing and storing remote file system data. | |
1000 ;; 8. Redefinitions of standard GNU Emacs functions. | |
1001 ;; 9. Multiple host type support. | |
1002 ;; 10. Attaching onto the appropriate emacs version. | |
1003 | |
1004 | |
1005 ;;;; ================================================================ | |
1006 ;;;; >1 | |
1007 ;;;; General provisions, requirements, and autoloads. | |
1008 ;;;; Host type, and local emacs type dependent loads, and autoloads | |
1009 ;;;; are in the last two sections of this file. | |
1010 ;;;; ================================================================ | |
1011 | |
1012 ;;;; ---------------------------------------------------------------- | |
1013 ;;;; Provide the package (Do this now to avoid an infinite loop) | |
1014 ;;;; ---------------------------------------------------------------- | |
1015 | |
1016 (provide 'efs) | |
1017 | |
1018 ;;;; ---------------------------------------------------------------- | |
1019 ;;;; Our requirements. | |
1020 ;;;; ---------------------------------------------------------------- | |
1021 | |
1022 (require 'backquote) | |
1023 (require 'comint) | |
1024 (require 'efs-defun) | |
1025 (require 'efs-netrc) | |
1026 (require 'efs-cu) | |
1027 (require 'efs-ovwrt) | |
1028 ;; Do this last, as it installs efs into the file-name-handler-alist. | |
1029 (require 'efs-fnh) | |
1030 | |
1031 (autoload 'efs-report-bug "efs-report" "Submit a bug report for efs." t) | |
1032 (autoload 'efs-gwp-start "efs-gwp" ; For interactive gateways. | |
1033 "Login to the gateway machine and fire up an FTP client.") | |
1034 (autoload 'efs-kerberos-login "efs-kerberos") | |
1035 (autoload 'efs-insert-directory "efs-dired" "Insert a directory listing.") | |
1036 (autoload 'efs-set-mdtm-of "efs-cp-p") | |
1037 (autoload 'diff-latest-backup-file "diff") | |
1038 (autoload 'read-passwd "passwd" "Read a password from the minibuffer." t) | |
1039 | |
1040 | |
1041 ;;;; ============================================================ | |
1042 ;;;; >2 | |
1043 ;;;; Variable Definitions | |
1044 ;;;; **** The user configuration variables are in **** | |
1045 ;;;; **** the second subsection of this section. **** | |
1046 ;;;; ============================================================ | |
1047 | |
1048 ;;;; ------------------------------------------------------------ | |
1049 ;;;; Constant Definitions | |
1050 ;;;; ------------------------------------------------------------ | |
1051 | |
1052 (defconst efs-version | |
1053 (concat (substring "$efs release: 1.15 $" 14 -2) | |
1054 "/" | |
1055 (substring "$Revision: 1.2 $" 11 -2))) | |
1056 | |
1057 (defconst efs-time-zero 1970) ; we count time from midnight, Jan 1, 1970 GMT. | |
1058 | |
1059 (defconst efs-dumb-host-types | |
1060 '(dumb-unix super-dumb-unix vms cms mts ti-twenex ti-explorer dos mvs | |
1061 tops-20 mpe ka9q dos-distinct os2 vos hell guardian | |
1062 netware cms-knet nos-ve coke dumb-apollo-unix) | |
1063 "List of host types that can't take UNIX ls-style listing options.") | |
1064 ;; dos-distinct only ignores ls switches; it doesn't barf. | |
1065 ;; Still treat it as dumb. | |
1066 | |
1067 (defconst efs-unix-host-types | |
1068 '(unix sysV-unix bsd-unix next-unix apollo-unix dumb-unix | |
1069 dumb-apollo-unix super-dumb-unix) | |
1070 "List of unix host types.") | |
1071 | |
1072 (defconst efs-version-host-types '(vms tops-20 ti-twenex ti-explorer) | |
1073 "List of host-types which associated a version number to all files. | |
1074 This is not the same as associating version numbers to only backup files.") | |
1075 ;; Note that on these systems, | |
1076 ;; (file-name-sans-versions EXISTING-FILE) does not exist as a file. | |
1077 | |
1078 (defconst efs-single-extension-host-types | |
1079 '(vms tops-20 ti-twenex ti-explorer cms mvs dos ka9q dos-distinct hell | |
1080 netware ms-unix plan9 cms-knet nos-ve) | |
1081 "List of host types which allow at most one extension on a file name. | |
1082 Extensions are deliminated by \".\". In addition, these host-types must | |
1083 allow \"-\" in file names, because it will be used to add additional extensions | |
1084 to indicate compressed files.") | |
1085 | |
1086 (defconst efs-idle-host-types | |
1087 (append '(coke unknown) efs-unix-host-types)) | |
1088 ;; List of host types for which it is possible that the SITE IDLE command | |
1089 ;; is supported. | |
1090 | |
1091 (defconst efs-listing-types | |
1092 '(unix:dl unix:unknown | |
1093 dos:novell dos:ftp dos:ncsa dos:microsoft dos:stcp dos:winsock | |
1094 mvs:nih mvs:tcp mvs:tcp | |
1095 vms:full) | |
1096 "List of supported listing types") | |
1097 | |
1098 (defconst efs-nlist-listing-types | |
1099 '(vms:full)) | |
1100 ;; Listing types which give a long useless listing when asked for a | |
1101 ;; LIST. For these, use an NLST instead. This can only be done | |
1102 ;; when there is some way to distinguish directories from | |
1103 ;; plain files in an NLST. | |
1104 | |
1105 (defconst efs-opaque-gateways '(remsh interactive)) | |
1106 ;; List of gateway types for which we need to do explicit file handling on | |
1107 ;; the gateway machine. | |
1108 | |
1109 ;;;; ------------------------------------------------------------------ | |
1110 ;;;; User customization variables. Please read through these carefully. | |
1111 ;;;; ------------------------------------------------------------------ | |
1112 | |
1113 ;;;>>>> If you are not fully connected to the internet, <<<< | |
1114 ;;;>>>> and need to use a gateway (no matter how transparent) <<<< | |
1115 ;;;>>>> you will need to set some of the following variables. <<<< | |
1116 ;;;>>>> Read the documentation carefully. <<<< | |
1117 | |
1118 (defvar efs-local-host-regexp ".*" | |
1119 "Regexp to match names of local hosts. | |
1120 These are hosts to which it is possible to obtain a direct internet | |
1121 connection. Even if the host is accessible by a very transparent FTP gateway, | |
1122 it does not qualify as a local host. The test to determine if machine A is | |
1123 local to your machine is if it is possible to ftp from A _back_ to your | |
1124 local machine. Also, open-network-stream must be able to reach the host | |
1125 in question.") | |
1126 | |
1127 (defvar efs-ftp-local-host-regexp ".*" | |
1128 "Regexp to match the names of hosts reachable by a direct ftp connection. | |
1129 This regexp should match the names of hosts which can be reached using ftp, | |
1130 without requiring any explicit connection to a gateway. If you have a smart | |
1131 ftp client which is able to transparently go through a gateway, this will | |
1132 differ from `efs-local-host-regexp'.") | |
1133 | |
1134 (defvar efs-gateway-host nil | |
1135 "If non-nil, this must be the name of your ftp gateway machine. | |
1136 If your net world is divided into two domains according to | |
1137 `efs-local-ftp-host-regexp', set this variable to the name of the | |
1138 gateway machine.") | |
1139 | |
1140 (defvar efs-gateway-type nil | |
1141 "Specifies which type of gateway you wish efs to use. | |
1142 This should be a list, the first element of which is a symbol denoting the | |
1143 gateway type, and following elements give data on how to use the gateway. | |
1144 | |
1145 The following possibilities are supported: | |
1146 | |
1147 '(local FTP-PROGRAM FTP-PROGRAM-ARGS) | |
1148 This means that your local host is itself the gateway. However, | |
1149 you need to run a special FTP client to access outside hosts. | |
1150 FTP-PROGRAM should be the name of this FTP client, and FTP-PROGRAM-ARGS | |
1151 is a list of arguments to pass to it \(probably set this to the value of | |
1152 efs-ftp-program-args <V>\). Note that if your gateway is of this type, | |
1153 then you would set efs-gateway-host to nil. | |
1154 | |
1155 '(proxy FTP-PROGRAM FTP-PROGRAM-ARGS) | |
1156 This indicates that your gateway works by first FTP'ing to it, and | |
1157 then giving a USER command of the form \"USER <username>@<host>\". | |
1158 FTP-PROGRAM is the FTP program to use to connect to the gateway; this | |
1159 is most likely \"ftp\". FTP-PROGRAM-ARGS is a list of arguments to | |
1160 pass to it. You likely want this to be set to the value of | |
1161 efs-ftp-program-args <V>. If the connection to the gateway FTP server | |
1162 is to be on a port different from 21, set efs-gateway-host to | |
1163 \"<host>#<port>\". | |
1164 | |
1165 '(raptor FTP-PROGRAM FTP-PROGRAM-ARGS USER) | |
1166 This is for the gateway called raptor by Eagle. After connecting to the | |
1167 the gateway, the command \"user <user>@host USER\" is issued to login | |
1168 as <user> on <host>, where USER is an authentication username for the | |
1169 gateway. After issuing the password for the remote host, efs will | |
1170 send the password for USER on efs-gateway-host <V> as an account command. | |
1171 | |
1172 '(interlock FTP-PROGRAM FTP-PROGRAM-ARGS) | |
1173 This is for the interlock gateway. The exact login sequence is to | |
1174 connect to the gateway specified by efs-gateway-host <V>, send the | |
1175 gateway password with a PASS command, send the command | |
1176 \"user <user>@<host>\" to connect to remote host <host> as user <user>, | |
1177 and finally to send the password for <user> on <host> with a second | |
1178 PASS command. | |
1179 | |
1180 '(kerberos FTP-PROGRAM FTP-PROGRAM-ARGS KINIT-PROGRAM KINIT-PROGRAM-ARGS) | |
1181 This is for the kerberos gateway where you need to run a program (kinit) to | |
1182 obtain a ticket for gateway authroization first. FTP-PROGRAM should be | |
1183 the name of the FTP client that you use to connect to the gateway. This | |
1184 may likely be \"iftp\". FTP-PROGRAM-ARGS are the arguments that you need | |
1185 to pass to FTP-PROGRAM. This is probably the value of | |
1186 efs-ftp-program-args <V>. KINIT-PROGRAM is the name of the program to | |
1187 run in order to obtain a ticket. This is probably \"kinit\". | |
1188 KINIT-PROGRAM-ARGS is a list og strings indicating any arguments that you | |
1189 need to pass to KINIT-PROGRAM. Most likely this is nil. | |
1190 | |
1191 '(remsh GATEWAY-PROGRAM GATEWAY-PROGRAM-ARGS FTP-PROGRAM FTP-PROGRAM-ARGS) | |
1192 This indicates that you wish to run FTP on your gateway using a remote shell. | |
1193 GATEWAY-PROGRAM is the name of the program to use to start a remote shell. | |
1194 It is assumed that it is not necessary to provide a password to start | |
1195 this remote shell. Likely values are \"remsh\" or \"rsh\". | |
1196 GATEWAY-PROGRAM-ARGS is a list of arguments to pass to GATEWAY-PROGRAM. | |
1197 FTP-PROGRAM is the name of the FTP program on the gateway. A likely setting | |
1198 of this is \"ftp\". FTP-PROGRAM-ARGS is a list of arguments to pass to | |
1199 FTP-PROGRAM. Most likely these should be set to the value of | |
1200 efs-ftp-program-args <V>. | |
1201 | |
1202 '(interactive GATEWAY-PROGRAM GATEWAY-PROGRAM-ARGS FTP-PROGRAM | |
1203 FTP-PROGRAM-ARGS) | |
1204 This indicates that you need to start an interactive login on your gatway, | |
1205 using rlogin, telnet, or something similar. GATEWAY-PROGRAM is the name | |
1206 of the program to use to log in to the gateway, and GATEWAY-PROGRAM-ARGS | |
1207 is a list of arguments to pass to it. FTP-PROGRAM is the name of the FTP | |
1208 program on the gateway. A likely setting for this variable would be | |
1209 \"exec ftp\". FTP-PROGRAM-ARGS is a list of arguments to pass | |
1210 to FTP-PROGRAM. You probably want to set these to the same value as | |
1211 efs-ftp-program-args <V>. If you are using this option, read the | |
1212 documentation at the top of efs-gwp.el, and see | |
1213 efs-gwp-setup-term-command <V>.") | |
1214 | |
1215 (defvar efs-gateway-hash-mark-size nil | |
1216 "*Value of `efs-hash-mark-size' for FTP clients on `efs-gateway-host'. | |
1217 See the documentation of these variables for more information.") | |
1218 | |
1219 (defvar efs-gateway-incoming-binary-hm-size nil | |
1220 "*Value of `efs-incoming-binary-hm-size' for `efs-gateway-host'. | |
1221 See documentation of these variables for more information.") | |
1222 | |
1223 (defvar efs-gateway-tmp-name-template "/tmp/efs" | |
1224 "Template used to create temporary files when ftp-ing through a gateway. | |
1225 This should be the name of the file on the gateway, and not necessarily | |
1226 the name on the local host.") | |
1227 | |
1228 (defvar efs-gateway-mounted-dirs-alist nil | |
1229 "An alist of directories cross-mounted between the gateway and local host. | |
1230 Each entry is of the form \( DIR1 . DIR2 \), where DIR1 is the name of the | |
1231 directory on the local host, and DIR2 is its name on the remote host. Both | |
1232 DIR1 and DIR2 must be specified in directory syntax, i.e. end in a slash. | |
1233 Note that we will assume that subdirs of DIR1 and DIR2 are also accessible | |
1234 on both machines.") | |
1235 | |
1236 (defvar efs-gateway-ftp-prompt-regexp "^\\(ftp\\|Ftp\\|FTP\\)> *" | |
1237 "*Regular expression to match the prompt of the gateway FTP client.") | |
1238 | |
1239 ;;; End of gateway config variables. | |
1240 | |
1241 (defvar efs-tmp-name-template "/tmp/efs" | |
1242 "Template used to create temporary files. | |
1243 If you are worried about security, make this a directory in some | |
1244 bomb-proof cave somewhere. efs does clean up its temp files, but | |
1245 they do live for short periods of time.") | |
1246 | |
1247 (defvar efs-generate-anonymous-password t | |
1248 "*If t, use a password of `user@host' when logging in as the anonymous user. | |
1249 `host' is generated by the function `efs-system-fqdn'. If `system name' returns | |
1250 a fully qualified domain name, `efs-system-fqdn' will return this. Otherwise, | |
1251 it will attempt to use nslookup to obtain a fully qualified domain name. If | |
1252 this is unsuccessful, the returned value will be the same as `system-name', | |
1253 whether this is a fully qualified domain name or not. | |
1254 | |
1255 If a string then use that as the password. | |
1256 | |
1257 If nil then prompt the user for a password. | |
1258 | |
1259 Beware that some operating systems, such as MVS, restrict substantially | |
1260 the password length. The login will fail with a weird error message | |
1261 if you exceed it.") | |
1262 | |
1263 (defvar efs-high-security-hosts nil | |
1264 "*Indicates host user pairs for which passwords should not be cached. | |
1265 If non-nil, should be a regexp matching user@host constructions for which | |
1266 efs should not store passwords in its internal cache.") | |
1267 | |
1268 ;; The following regexps are tested in the following order: | |
1269 ;; efs-binary-file-host-regexp, efs-36-bit-binary-file-name-regexp, | |
1270 ;; efs-binary-file-name-regexp, efs-text-file-name-regexp. | |
1271 ;; File names which match nothing are transferred in 'image mode. | |
1272 | |
1273 ;; If we're not careful, we're going to blow the regexp stack here. | |
1274 ;; Probably should move to a list of regexps. Slower, but safer. | |
1275 ;; This is not a problem in Emacs 19. | |
1276 (defvar efs-binary-file-name-regexp | |
1277 (concat "\\." ; the dot | |
1278 ;; extensions | |
1279 "\\([zZ]\\|t?gz\\|lzh\\|arc\\|zip\\|zoo\\|ta[rz]\\|dvi\\|sit\\|" | |
1280 "ps\\|elc\\|gif\\|Z-part-..\\|tpz\\|exe\\|[jm]pg\\|TZ[a-z]?\\|lib\\)" | |
1281 "\\(~\\|~[0-9]+~\\)?$" ; backups | |
1282 "\\|" | |
1283 ;; UPPER CASE LAND | |
1284 "\\." | |
1285 "\\(ARC\\|ELC\\|TAGS\\|EXE\\|ZIP\\|DVI\|ZOO\\|GIF\\|T?GZ\\|" | |
1286 "[JM]PG\\)" | |
1287 "\\([.#;][0-9]+\\)?$" ; versions | |
1288 ) | |
1289 "*Files whose names match this regexp will be considered to be binary. | |
1290 By binary here, we mean 8-bit binary files (the usual unix binary files). | |
1291 If nil, no files will be considered to be binary.") | |
1292 | |
1293 (defvar efs-binary-file-host-regexp nil | |
1294 "*All files on hosts matching this regexp are treated as 8-bit binary. | |
1295 Setting this to nil, inhibits this feature.") | |
1296 | |
1297 (defvar efs-36-bit-binary-file-name-regexp nil | |
1298 "*Files whose names match this regexp will be considered to PDP 10 binaries. | |
1299 These are 36-bit word-aligned binary files. This is really only relevant for | |
1300 files on PDP 10's, and similar machines. If nil, no files will be considered | |
1301 to be PDP 10 binaries.") | |
1302 | |
1303 (defvar efs-text-file-name-regexp ".*" | |
1304 "*Files whose names match this regexp will be considered to be text files.") | |
1305 | |
1306 (defvar efs-prompt-for-transfer-type nil | |
1307 "*If non-nil, efs will prompt for the transfer type for each file transfer. | |
1308 The command efs-prompt-for-transfer-type can be used to toggle its value.") | |
1309 | |
1310 (defvar efs-treat-crlf-as-nl nil | |
1311 "*Controls how file systems using CRLF as end of line are treated. | |
1312 If non-nil, such file systems will be considered equivalent to those which use | |
1313 LF as end of line. This is particularly relevant to transfers between DOS | |
1314 systems and UNIX. Setting this to be non-nil will cause all file transfers | |
1315 between DOS and UNIX systems to use be image or binary transfers.") | |
1316 | |
1317 (defvar efs-send-hash t | |
1318 "*If non-nil, send the HASH command to the FTP client.") | |
1319 | |
1320 (defvar efs-hash-mark-size nil | |
1321 "*Default size, in bytes, between hash-marks when transferring a file. | |
1322 If this is nil then efs will attempt to assign a value based on the | |
1323 output of the HASH command. Also, if this variable is incorrectly set, | |
1324 then efs will try to correct it based on the size of the last file | |
1325 transferred, and the number hashes outputed by the client during the | |
1326 transfer. | |
1327 | |
1328 The variable `efs-gateway-hash-mark-size' defines the corresponding value | |
1329 for the FTP client on the gateway, if you are using a gateway. | |
1330 | |
1331 Some client-server combinations do not correctly compute the number of hash | |
1332 marks for incoming binary transfers. In this case, a separate variable | |
1333 `efs-incoming-binary-hm-size' can be used to set a default value of the | |
1334 hash mark size for incoming binary transfers.") | |
1335 | |
1336 (defvar efs-incoming-binary-hm-size nil | |
1337 "*Default hash mark size for incoming binary transfers. | |
1338 If this is nil, incoming binary transfers will use `efs-hash-mark-size' as | |
1339 the default. See the documentation of this variable for more details.") | |
1340 | |
1341 (defvar efs-verbose t | |
1342 "*If non-NIL then be chatty about interaction with the FTP process. | |
1343 If 0 do not give % transferred reports for asynchronous commands and status | |
1344 reports for commands verifying file modtimes, but report on everything else.") | |
1345 | |
1346 (defvar efs-message-interval 0 | |
1347 "*Defines the minimum time in seconds between status messages. | |
1348 A new status message is not displayed, if one has already been given | |
1349 within this period of time.") | |
1350 | |
1351 (defvar efs-max-ftp-buffer-size 3000 | |
1352 "*Maximum size in characters of FTP process buffer, before it is trimmed. | |
1353 The buffer is trimmed to approximately half this size. Setting this to nil | |
1354 inhibits trimming of FTP process buffers.") | |
1355 | |
1356 (defvar efs-ls-cache-max 5 | |
1357 "*Maximum number of directory listings to be cached in efs-ls-cache.") | |
1358 | |
1359 (defvar efs-mode-line-format " ftp(%d)" | |
1360 "Format string used to determine how FTP activity is shown on the mode line. | |
1361 It is passed to format, with second argument the number of active FTP | |
1362 sessions as an integer.") | |
1363 | |
1364 (defvar efs-show-host-type-in-dired t | |
1365 "If non-nil, show the system type on the mode line of remote dired buffers.") | |
1366 | |
1367 (defvar efs-ftp-activity-function nil | |
1368 "Function called to indicate FTP activity. | |
1369 It must have exactly one argument, the number of active FTP sessions as an | |
1370 integer.") | |
1371 | |
1372 (defvar efs-ftp-program-name "ftp" | |
1373 "Name of FTP program to run.") | |
1374 | |
1375 (defvar efs-ftp-program-args '("-i" "-n" "-g" "-v") | |
1376 "*A list of arguments passed to the FTP program when started.") | |
1377 | |
1378 (defvar efs-ftp-prompt-regexp "^\\(ftp\\|Ftp\\|FTP\\)> *" | |
1379 "*Regular expression to match the prompt of your FTP client.") | |
1380 | |
1381 (defvar efs-nslookup-program "nslookup" | |
1382 "*If non-NIL then a string naming nslookup program." ) | |
1383 | |
1384 (defvar efs-nslookup-on-connect nil | |
1385 "*If non-NIL then use nslookup to resolve the host name before connecting.") | |
1386 | |
1387 (defvar efs-nslookup-threshold 1000 | |
1388 "How many iterations efs waits on the nslookup program. | |
1389 Applies when nslookup is used to compute a fully qualified domain name | |
1390 for the local host, in the case when `system-name' does not return one. | |
1391 If you set this to nil, efs will wait an arbitrary amount of time to get | |
1392 output.") | |
1393 | |
1394 (defvar efs-make-backup-files efs-unix-host-types | |
1395 "*A list of operating systems for which efs will make Emacs backup files. | |
1396 The backup files are made on the remote host. | |
1397 | |
1398 For example: | |
1399 '\(unix sysV-unix bsd-unix apollo-unix dumb-unix\) makes sense, but | |
1400 '\(unix vms\) would be silly, since vms makes its own backups.") | |
1401 | |
1402 ;; Is this variable really useful? We should try to figure a way to | |
1403 ;; do local copies on a remote machine that doesn't take forever. | |
1404 (defvar efs-backup-by-copying nil | |
1405 "*Version of `backup by copying' for remote files. | |
1406 If non-nil, remote files will be backed up by copying, instead of by renaming. | |
1407 Note the copying will be done by moving the file through the local host -- a | |
1408 very time consuming operation.") | |
1409 | |
1410 ;;; Auto-save variables. Relevant for auto-save.el | |
1411 | |
1412 (defvar efs-auto-save 0 | |
1413 "*If 1, allows efs files to be auto-saved. | |
1414 If 0, suppresses auto-saving of efs files. | |
1415 Don't use any other value.") | |
1416 | |
1417 (defvar efs-auto-save-remotely nil | |
1418 "*Determines where remote files are auto-saved. | |
1419 | |
1420 If nil, auto-saves for remote files will be written in `auto-save-directory' | |
1421 or `auto-save-directory-fallback' if this isn't defined. | |
1422 | |
1423 If non-nil, causes the auto-save file for an efs file to be written in | |
1424 the remote directory containing the file, rather than in a local directory. | |
1425 For remote files, this overrides a non-nil `auto-save-directory'. Local files | |
1426 are unaffected. If you want to use this feature, you probably only want to | |
1427 set this true in a few buffers, rather than globally. You might want to give | |
1428 each buffer its own value using `make-variable-buffer-local'. It is usually | |
1429 a good idea to auto-save remote files locally, because it is not only faster, | |
1430 but provides protection against a connection going down. | |
1431 | |
1432 See also variable `efs-auto-save'.") | |
1433 | |
1434 (defvar efs-short-circuit-to-remote-root nil | |
1435 "*Defines whether \"//\" short-circuits to the remote or local root.") | |
1436 | |
1437 ;; Can we somehow grok this from system type? No. | |
1438 (defvar efs-local-apollo-unix | |
1439 (eq 0 (string-match "//" (or (getenv "HOME") (getenv "SHELL") ""))) | |
1440 "*Defines whether the local machine is an apollo running Domain. | |
1441 This variable has nothing to do with efs, and should be basic to all | |
1442 of emacs.") | |
1443 | |
1444 (defvar efs-root-umask nil | |
1445 "*umask to use for root logins.") | |
1446 | |
1447 (defvar efs-anonymous-umask nil | |
1448 "*umask to use for anonymous logins.") | |
1449 | |
1450 (defvar efs-umask nil | |
1451 "*umask to use for efs sessions. | |
1452 If this is nil, then the setting of umask on the local host is used.") | |
1453 | |
1454 ;; Eliminate these variables when Sun gets around to getting its FTP server | |
1455 ;; out of the stone age. | |
1456 (defvar efs-ding-on-umask-failure t | |
1457 "*Ring the bell if the umask command fails on a unix host. Many servers don't | |
1458 support this command, so if you get a lot of annoying failures, set this | |
1459 to nil.") | |
1460 | |
1461 (defvar efs-ding-on-chmod-failure t | |
1462 "*Ring the bell if the chmod command fails on a unix host. Some servers don't | |
1463 support this command, so if you get a lot of annoying failures, set this | |
1464 to nil.") | |
1465 | |
1466 ;; Please let us know if you can contribute more entries to this guessing game. | |
1467 (defvar efs-nlist-cmd | |
1468 (cond | |
1469 ;; Covers Ultrix, SunOS, and NeXT. | |
1470 ((eq system-type 'berkeley-unix) | |
1471 "ls") | |
1472 ((memq system-type '(hpux aix-v3 silicon-graphics-unix)) | |
1473 "nlist") | |
1474 ;; Blind guess | |
1475 ("ls")) | |
1476 "*FTP client command for getting a brief listing (NLST) from the FTP server. | |
1477 We try to guess this based on the local system-type, but obviously if you | |
1478 are using a gateway, you'll have to set it yourself.") | |
1479 | |
1480 (defvar efs-compute-remote-buffer-file-truename nil | |
1481 "*If non-nil, `buffer-file-truename' will be computed for remote buffers. | |
1482 In emacs 19, each buffer has a local variable, `buffer-file-truename', | |
1483 which is used to ensure that symbolic links will not confuse emacs into | |
1484 visiting the same file with two buffers. This variable is computed by | |
1485 chasing all symbolic links in `buffer-file-name', both at the level of the | |
1486 file and at the level of all parent directories. Since this operation can be | |
1487 very time-consuming over FTP, this variable can be used to inhibit it.") | |
1488 | |
1489 (defvar efs-buffer-name-case nil | |
1490 "*Selects the case used for buffer names of case-insensitive file names. | |
1491 Case-insensitive file names are files on hosts whose host type is in | |
1492 `efs-case-insensitive-host-types'. | |
1493 | |
1494 If this is 'up upper case is used, if it is 'down lower case is used. | |
1495 If this has any other value, the case is inherited from the name used | |
1496 to access the file.") | |
1497 | |
1498 (defvar efs-fancy-buffer-names "%s@%s" | |
1499 "Format used to compute names of buffers attached to remote files. | |
1500 | |
1501 If this is nil, buffer names are computed in the usual way. | |
1502 | |
1503 If it is a string, then the it is passed to format with second and third | |
1504 arguments the host name and file name. | |
1505 | |
1506 Otherwise, it is assumed to be function taking three arguments, the host name, | |
1507 the user name, and the truncated file name. It should returns the name to | |
1508 be used for the buffer.") | |
1509 | |
1510 (defvar efs-verify-anonymous-modtime nil | |
1511 "*Determines if efs checks modtimes for remote files on anonymous logins. | |
1512 If non-nil, efs runs `verify-visited-file-modtime' for remote files on | |
1513 anonymous ftp logins. Since verify-visited-file-modtime slows things down, | |
1514 and most people aren't editing files on anonymous ftp logins, this is nil | |
1515 by default.") | |
1516 | |
1517 (defvar efs-verify-modtime-host-regexp ".*" | |
1518 "*Regexp to match host names for which efs checks file modtimes. | |
1519 If non-nil, efs will run `verify-visited-file-modtime' for remote | |
1520 files on hosts matching this regexp. If nil, verify-visited-file-modtime | |
1521 is supressed for all remote hosts. This is tested before | |
1522 `efs-verify-anonymous-modtime'.") | |
1523 | |
1524 (defvar efs-maximize-idle nil | |
1525 "*If non-nil, efs will attempt to maximize the idle time out period. | |
1526 At some idle moment in the connection after login, efs will attempt to | |
1527 set the idle time out period to the maximum amount allowed by the server. | |
1528 It applies only to non-anonymous logins on unix hosts.") | |
1529 | |
1530 (defvar efs-expire-ftp-buffers t | |
1531 "*If non-nil ftp buffers will be expired. | |
1532 The buffers will be killed either after `efs-ftp-buffer-expire-time' has | |
1533 elapsed with no activity, or the remote FTP server has timed out.") | |
1534 | |
1535 (defvar efs-ftp-buffer-expire-time nil | |
1536 "*If non-nil, the time after which ftp buffers will be expired. | |
1537 If nil, ftp buffers will be expired only when the remote server has timed out. | |
1538 If an integer, ftp buffers will be expired either when the remote server | |
1539 has timed out, or when this many seconds on inactivity has elapsed.") | |
1540 | |
1541 ;; If you need to increase this variable much, it is likely that | |
1542 ;; the true problem is timing errors between the efs process filter | |
1543 ;; and the FTP server. This could either be caused by the server | |
1544 ;; not following RFC959 response codes, or a bug in efs. In either | |
1545 ;; case please report the problem to us. If it's a bug, we'll fix it. | |
1546 ;; If the server is at fault we may try to do something. Our rule | |
1547 ;; of thumb is that we will support non-RFC959 behaviour, as long as | |
1548 ;; it doesn't risk breaking efs for servers which behave properly. | |
1549 | |
1550 (defvar efs-retry-time 5 | |
1551 "*Number of seconds to wait before retrying if data doesn't arrive. | |
1552 The FTP command isn't retried, rather efs just takes a second look | |
1553 for the data file. This might need to be increased for very slow FTP | |
1554 clients.") | |
1555 | |
1556 (defvar efs-pty-check-threshold 1000 | |
1557 "*How long efs waits before deciding that it doesn't have a pty. | |
1558 Specifically it is the number of iterations through `accept-process-output' | |
1559 that `efs-pty-p' waits before deciding that the pty is really a pipe. | |
1560 Set this to nil to inhibit checking for pty's. If efs seems to be | |
1561 mistaking some pty's for pipes, try increasing this number.") | |
1562 | |
1563 (defvar efs-pty-check-retry-time 5 | |
1564 "*Number of seconds that efs waits before retrying a pty check. | |
1565 This can be lengthened, if your FTP client is slow to start.") | |
1566 | |
1567 (defvar efs-suppress-abort-recursive-edit-and-then nil | |
1568 "*If non-nil, `efs-abort-recursive-edit-and-then' will not run its function. | |
1569 This means that when a recursive edit is in progress, automatic popping of the | |
1570 FTP process buffer, and automatic popping of the bug report buffer will not | |
1571 work. `efs-abort-recursive-edit-and-then' works by forking a \"sleep 0\" | |
1572 process. On some unix implementations the forked process might be of the same | |
1573 size as the original GNU Emacs process. Forking such a large process just to | |
1574 do a \"sleep 0\" is probably not good.") | |
1575 | |
1576 (defvar efs-ftp-buffer-format "*ftp %s@%s*" | |
1577 "Format to construct the name of FTP process buffers. | |
1578 This string is fed to `format' with second and third arguments the user | |
1579 name and host name.") | |
1580 ;; This does not affect the process name of the FTP client process. | |
1581 ;; That is always *ftp USER@HOST* | |
1582 | |
1583 (defvar efs-debug-ftp-connection nil | |
1584 "*If non-nil, the user will be permitted to debug the FTP connection. | |
1585 This means that typing a C-g to the FTP process filter will give the user | |
1586 the option to type commands at the FTP connection. Normally, the connection | |
1587 is killed first. Note that doing this may result in the FTP process filter | |
1588 getting out of synch with the FTP client, so using this feature routinely | |
1589 isn't recommended.") | |
1590 | |
1591 ;;; Hooks and crooks. | |
1592 | |
1593 (defvar efs-ftp-startup-hook nil | |
1594 "Hook to run immediately after starting the FTP client. | |
1595 This hook is run before the FTP OPEN command is sent.") | |
1596 | |
1597 (defvar efs-ftp-startup-function-alist nil | |
1598 "Association list of functions to running after FTP login. | |
1599 This should be an alist of the form '\(\(REGEXP . FUNCTION\) ...\), where | |
1600 REGEXP is a regular expression matched against the name of the remote host, | |
1601 and FUNCTION is a function of two arguments, HOST and USER. REGEXP is | |
1602 compared to the host name with `case-fold-search' bound to t. Only the first | |
1603 match in the alist is run.") | |
1604 | |
1605 (defvar efs-load-hook nil | |
1606 "Hook to run immediately after loading efs.el. | |
1607 You can use it to alter definitions in efs.el, but why would you want | |
1608 to do such a thing?") | |
1609 | |
1610 ;;;; ----------------------------------------------------------- | |
1611 ;;;; Regexps for parsing FTP server responses. | |
1612 ;;;; ----------------------------------------------------------- | |
1613 ;;; | |
1614 ;;; If you have to tune these variables, please let us know, so that | |
1615 ;;; we can get them right in the next release. | |
1616 | |
1617 (defvar efs-multi-msgs | |
1618 ;; RFC959 compliant codes | |
1619 "^[1-5][0-5][0-7]-") | |
1620 ;; Regexp to match the start of an FTP server multiline reply. | |
1621 | |
1622 (defvar efs-skip-msgs | |
1623 ;; RFC959 compliant codes | |
1624 (concat | |
1625 "^110 \\|" ; Restart marker reply. | |
1626 "^125 \\|" ; Data connection already open; transfer starting. | |
1627 "^150 ")) ; File status OK; about to open connection. | |
1628 ;; Regexp to match an FTP server response which we wish to ignore. | |
1629 | |
1630 (defvar efs-cmd-ok-msgs | |
1631 ;; RFC959 compliant | |
1632 "^200 \\|^227 ") | |
1633 ;; Regexp to match the server command OK response. | |
1634 ;; Because PORT commands return this we usually ignore it. However, it is | |
1635 ;; a valid response for TYPE, SITE, and a few other commands (cf. RFC 959). | |
1636 ;; If we are explicitly sending a PORT, or one of these other commands, | |
1637 ;; then we don't want to ignore this response code. Also use this to match | |
1638 ;; the return code for PASV, as some clients burp these things out at odd | |
1639 ;; times. | |
1640 | |
1641 (defvar efs-pending-msgs | |
1642 ;; RFC959 compliant | |
1643 "^350 ") ; Requested file action, pending further information. | |
1644 ;; Regexp to match the \"requested file action, pending further information\" | |
1645 ;; message. These are usually ignored, except if we are using RNFR to test for | |
1646 ;; file existence. | |
1647 | |
1648 (defvar efs-cmd-ok-cmds | |
1649 (concat | |
1650 "^quote port \\|^type \\|^quote site \\|^chmod \\|^quote noop\\|" | |
1651 "^quote pasv")) | |
1652 ;; Regexp to match commands for which efs-cmd-ok-msgs is a valid server | |
1653 ;; response for success. | |
1654 | |
1655 (defvar efs-passwd-cmds | |
1656 "^quote pass \\|^quote acct \\|^quote site gpass ") | |
1657 ;; Regexp to match commands for sending passwords. | |
1658 ;; All text following (match-end 0) will be replaced by "Turtle Power!" | |
1659 | |
1660 (defvar efs-bytes-received-msgs | |
1661 ;; Strictly a client response | |
1662 "^[0-9]+ bytes ") | |
1663 ;; Regexp to match the reply from the FTP client that it has finished | |
1664 ;; receiving data. | |
1665 | |
1666 (defvar efs-server-confused-msgs | |
1667 ;; ka9q uses this to indicate an incorrectly set transfer mode, and | |
1668 ;; then does send a second completion code for the command. This does | |
1669 ;; *not* conform to RFC959. | |
1670 "^100 Warning: type is ") | |
1671 ;; Regexp to match non-standard response from the FTP server. This can | |
1672 ;; sometimes be the result of an incorrectly set transfer mode. In this case | |
1673 ;; we do not rely on the server to tell us when the data transfer is complete, | |
1674 ;; but check with the client. | |
1675 | |
1676 (defvar efs-good-msgs | |
1677 (concat | |
1678 ;; RFC959 compliant codes | |
1679 "^2[01345][0-7] \\|" ; 2yz = positive completion reply | |
1680 "^22[02-7] \\|" ; 221 = successful logout | |
1681 ; (Sometimes get this with a timeout, | |
1682 ; so treat as fatal.) | |
1683 "^3[0-5][0-7] \\|" ; 3yz = positive intermediate reply | |
1684 ;; client codes | |
1685 "^[Hh]ash mark ")) | |
1686 ;; Response to indicate that the requested action was successfully completed. | |
1687 | |
1688 (defvar efs-failed-msgs | |
1689 (concat | |
1690 ;; RFC959 compliant codes | |
1691 "^120 \\|" ; Service ready in nnn minutes. | |
1692 "^450 \\|" ; File action not taken; file is unavailable, or busy. | |
1693 "^452 \\|" ; Insufficient storage space on system. | |
1694 "^5[0-5][0-7] \\|" ; Permanent negative reply codes. | |
1695 ;; When clients tell us that a file doesn't exist, or can't access. | |
1696 "^\\(local: +\\)?/[^ ]* +" | |
1697 "\\([Nn]o such file or directory\\|[Nn]ot a plain file\\|" | |
1698 "The file access permissions do not allow \\|Is a directory\\b\\)")) | |
1699 ;; Regexp to match responses for failed commands. However, the ftp connection | |
1700 ;; is assumed to be good. | |
1701 | |
1702 (defvar efs-fatal-msgs | |
1703 (concat | |
1704 ;; RFC959 codes | |
1705 "^221 \\|" ; Service closing control connection. | |
1706 "^421 \\|" ; Service not available. | |
1707 "^425 \\|" ; Can't open data connection. | |
1708 "^426 \\|" ; Connection closed, transfer aborted. | |
1709 "^451 \\|" ; Requested action aborted, local error in processing. | |
1710 ;; RFC959 non-compliant codes | |
1711 "^552 Maximum Idle Time Exceded\\.$\\|" ; Hellsoft server uses this to | |
1712 ; indicate a timeout. 552 is | |
1713 ; supposed to be used for exceeded | |
1714 ; storage allocation. Note that | |
1715 ; they also misspelled the error | |
1716 ; message. | |
1717 ;; client problems | |
1718 "^ftp: \\|^Not connected\\|^rcmd: \\|^No control connection\\|" | |
1719 "^unknown host\\|: unknown host$\\|^lost connection\\|" | |
1720 "^[Ss]egmentation fault\\|" | |
1721 ;; Make sure that the "local: " isn't just a message about a file. | |
1722 "^local: [^/]\\|" | |
1723 ;; Gateways | |
1724 "^iftp: cannot authenticate to server\\b" | |
1725 )) | |
1726 ;; Regexp to match responses that something has gone drastically wrong with | |
1727 ;; either the client, server, or connection. We kill the ftp process, and start | |
1728 ;; anew. | |
1729 | |
1730 (defvar efs-unknown-response-msgs | |
1731 "^[0-9][0-9][0-9] ") | |
1732 ;; Regexp to match server response codes that we don't understand. This | |
1733 ;; is tested after all the other regexp, so it can match everything. | |
1734 | |
1735 (defvar efs-pasv-msgs | |
1736 ;; According to RFC959. | |
1737 "^227 .*(\\([0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+\\))$") | |
1738 ;; Matches the output of a PASV. (match-beginning 1) and (match-end 1) | |
1739 ;; must bracket the IP address and port. | |
1740 | |
1741 (defvar efs-syst-msgs "^215 \\|^210 ") | |
1742 ;; 215 is RFC959. Plan 9 FTP server returns a 210. 210 is not assigned in | |
1743 ;; RFC 959. | |
1744 ;; The plan 9 people tell me that they fixed this. -- sr 18/4/94 | |
1745 ;; Matches the output of a SYST. | |
1746 | |
1747 (defvar efs-mdtm-msgs | |
1748 (concat | |
1749 "^213 [0-9][0-9][0-9][0-9][0-9][0-9][0-9]" | |
1750 "[0-9][0-9][0-9][0-9][0-9][0-9][0-9]$")) | |
1751 ;; Regexp to match the output of a quote mdtm command. | |
1752 | |
1753 (defvar efs-idle-msgs | |
1754 "^200 [^0-9]+ \\([0-9]+\\)[^0-9]* max \\([0-9]+\\)") | |
1755 ;; Regexp to match the output of a SITE IDLE command. | |
1756 ;; Match 1 should refer to the current idle time, and match 2 the maximum | |
1757 ;; idle time. | |
1758 | |
1759 (defvar efs-write-protect-msgs "^532 ") ; RFC959 | |
1760 ;; Regexp to match a server ressponse to indicate that a STOR failed | |
1761 ;; because of insufficient write privileges. | |
1762 | |
1763 (defvar efs-hash-mark-msgs | |
1764 "[hH]ash mark [^0-9]*\\([0-9]+\\)") | |
1765 ;; Regexp matching the FTP client's output upon doing a HASH command. | |
1766 | |
1767 (defvar efs-xfer-size-msgs | |
1768 (concat | |
1769 ;; UN*X | |
1770 "^150 .* connection for .* (\\([0-9]+\\) bytes)\\|" | |
1771 ;; Wollongong VMS server. | |
1772 "^125 .* transfer started for .* (\\([0-9]+\\) bytes)\\|" | |
1773 ;; TOPS-20 server | |
1774 "^150 .* retrieve of .* ([0-9]+ pages?, \\([0-9]+\\) 7-bit bytes)")) | |
1775 ;; Regular expression used to determine the number of bytes | |
1776 ;; in a FTP transfer. The first (match-beginning #) which is non-nil is assumed | |
1777 ;; to give the size. | |
1778 | |
1779 (defvar efs-expand-dir-msgs "^550 \\([^: ]+\\):") | |
1780 ;; Regexp to match the error response from a "get ~sandy". | |
1781 ;; By parsing the error, we can get a quick expansion of ~sandy | |
1782 ;; According to RFC 959, should be a 550. | |
1783 | |
1784 (defvar efs-gateway-fatal-msgs | |
1785 "No route to host\\|Connection closed\\|No such host\\|Login incorrect") | |
1786 ;; Regular expression matching messages from the rlogin / telnet process that | |
1787 ;; indicates that logging in to the gateway machine has gone wrong. | |
1788 | |
1789 (defvar efs-too-many-users-msgs | |
1790 ;; The test for "two many" is because some people can't spell. | |
1791 ;; I allow for up to two adjectives before "users". | |
1792 (concat | |
1793 "\\b[Tt][wo]o many\\( +[^ \n]+\\)?\\( +[^ \n]+\\)? +users\\b\\|" | |
1794 "\\btry back later\\b")) | |
1795 ;; Regular expresion to match what servers output when there are too many | |
1796 ;; anonymous logins. It is assumed that this is part of a 530 or 530- response | |
1797 ;; to USER or PASS. | |
1798 | |
1799 ;;;; ------------------------------------------------------------- | |
1800 ;;;; Buffer local FTP process variables | |
1801 ;;;; ------------------------------------------------------------- | |
1802 | |
1803 ;;; Variables buffer local to the process buffers are | |
1804 ;;; named with the prefix efs-process- | |
1805 | |
1806 (defvar efs-process-q nil) | |
1807 ;; List of functions to be performed asynch. | |
1808 (make-variable-buffer-local 'efs-process-q) | |
1809 | |
1810 (defvar efs-process-cmd-waiting nil) | |
1811 ;; Set to t if a process has a synchronous cmd waiting to execute. | |
1812 ;; In this case, it will allow the synch. cmd to run before returning to | |
1813 ;; the cmd queue. | |
1814 (make-variable-buffer-local 'efs-process-cmd-waiting) | |
1815 | |
1816 (defvar efs-process-server-confused nil) | |
1817 (make-variable-buffer-local 'efs-process-server-confused) | |
1818 | |
1819 (defvar efs-process-cmd nil) | |
1820 ;; The command currently being executed, as a string. | |
1821 (make-variable-buffer-local 'efs-process-cmd) | |
1822 | |
1823 (defvar efs-process-xfer-size 0) | |
1824 (make-variable-buffer-local 'efs-process-xfer-size) | |
1825 | |
1826 (defvar efs-process-umask nil) | |
1827 ;; nil if the umask hash not been set | |
1828 ;; an integer (the umask) if the umask has been set | |
1829 (make-variable-buffer-local 'efs-process-umask) | |
1830 | |
1831 (defvar efs-process-idle-time nil) | |
1832 ;; If non-nil, the idle time of the server in seconds. | |
1833 (make-variable-buffer-local 'efs-process-idle-time) | |
1834 | |
1835 (defvar efs-process-busy nil) | |
1836 (make-variable-buffer-local 'efs-process-busy) | |
1837 | |
1838 (defvar efs-process-result-line "") | |
1839 (make-variable-buffer-local 'efs-process-result-line) | |
1840 | |
1841 (defvar efs-process-result nil) | |
1842 (make-variable-buffer-local 'efs-process-result) | |
1843 | |
1844 (defvar efs-process-result-cont-lines "") | |
1845 (make-variable-buffer-local 'efs-process-result-cont-lines) | |
1846 | |
1847 (defvar efs-process-msg "") | |
1848 (make-variable-buffer-local 'efs-process-msg) | |
1849 | |
1850 (defvar efs-process-nowait nil) | |
1851 (make-variable-buffer-local 'efs-process-nowait) | |
1852 | |
1853 (defvar efs-process-string "") | |
1854 (make-variable-buffer-local 'efs-process-string) | |
1855 | |
1856 (defvar efs-process-continue nil) | |
1857 (make-variable-buffer-local 'efs-process-continue) | |
1858 | |
1859 (defvar efs-process-hash-mark-count 0) | |
1860 (make-variable-buffer-local 'efs-process-hash-mark-count) | |
1861 | |
1862 (defvar efs-process-hash-mark-unit nil) | |
1863 (make-variable-buffer-local 'efs-process-hash-mark-unit) | |
1864 | |
1865 (defvar efs-process-last-percent -1) | |
1866 (make-variable-buffer-local 'efs-process-last-percent) | |
1867 | |
1868 (defvar efs-process-host nil) | |
1869 (make-variable-buffer-local 'efs-process-host) | |
1870 | |
1871 (defvar efs-process-user nil) | |
1872 (make-variable-buffer-local 'efs-process-user) | |
1873 | |
1874 (defvar efs-process-host-type nil) | |
1875 ;; Holds the host-type as a string, for showing it on the mode line. | |
1876 (make-variable-buffer-local 'efs-process-host-type) | |
1877 | |
1878 (defvar efs-process-xfer-type nil) | |
1879 ;; Set to one of 'ascii, 'ebcdic, 'image, 'tenex, or nil to indicate | |
1880 ;; the current setting of the transfer type for the connection. nil means | |
1881 ;; that we don't know. | |
1882 (make-variable-buffer-local 'efs-process-xfer-type) | |
1883 | |
1884 (defvar efs-process-client-altered-xfer-type nil) | |
1885 ;; Sometimes clients alter the xfer type, such as doing | |
1886 ;; an ls it is changed to ascii. If we are using quoted commands | |
1887 ;; to do xfers the client doesn't get a chance to set it back. | |
1888 (make-variable-buffer-local 'efs-process-client-altered-xfer-type) | |
1889 | |
1890 (defvar efs-process-prompt-regexp nil) | |
1891 ;; local value of prompt of FTP client. | |
1892 (make-variable-buffer-local 'efs-process-prompt-regexp) | |
1893 | |
1894 (defvar efs-process-cmd-counter 0) | |
1895 ;; Counts FTP commands, mod 16. | |
1896 (make-variable-buffer-local 'efs-process-cmd-counter) | |
1897 | |
1898 ;;;; ------------------------------------------------------------ | |
1899 ;;;; General Internal Variables. | |
1900 ;;;; ------------------------------------------------------------ | |
1901 | |
1902 ;;; For the byte compiler | |
1903 ;; | |
1904 ;; These variables are usually unbound. We are just notifying the | |
1905 ;; byte compiler that we know what we are doing. | |
1906 | |
1907 (defvar bv-length) ; getting file versions. | |
1908 (defvar default-file-name-handler-alist) ; for file-name-handler-alist | |
1909 (defvar efs-completion-dir) ; for file name completion predicates | |
1910 (defvar dired-directory) ; for default actions in interactive specs | |
1911 (defvar dired-local-variables-file) ; for inhibiting child look ups | |
1912 (defvar dired-in-query) ; don't clobber dired queries with stat messages | |
1913 (defvar after-load-alist) ; in case we're in emacs 18. | |
1914 (defvar comint-last-input-start) | |
1915 (defvar comint-last-input-end) | |
1916 (defvar explicit-shell-file-name) | |
1917 | |
1918 ;;; fluid vars | |
1919 | |
1920 (defvar efs-allow-child-lookup t) | |
1921 ;; let-bind to nil, if want to inhibit child lookups. | |
1922 | |
1923 (defvar efs-nested-cmd nil) | |
1924 ;; let-bound to t, when a cmd is executed by a cont or pre-cont. | |
1925 ;; Such cmds will never end by looking at the next item in the queue, | |
1926 ;; if they are run synchronously, but rely on their calling function | |
1927 ;; to do this. | |
1928 | |
1929 ;;; polling ftp buffers | |
1930 | |
1931 (defvar efs-ftp-buffer-poll-time 300 | |
1932 "Period, in seconds, which efs will poll ftp buffers for activity. | |
1933 Used for expiring \(killing\) inactive ftp buffers.") | |
1934 | |
1935 (defconst efs-ftp-buffer-alist nil) | |
1936 ;; alist of ftp buffers, and the total number of seconds that they | |
1937 ;; have been idle. | |
1938 | |
1939 ;;; load extensions | |
1940 | |
1941 (defvar efs-load-lisp-extensions '(".elc" ".el" "") | |
1942 "List of extensions to try when loading lisp files.") | |
1943 | |
1944 ;;; mode-line | |
1945 | |
1946 (defvar efs-mode-line-string "") | |
1947 ;; Stores the string that efs displays on the mode line. | |
1948 | |
1949 ;;; data & temporary buffers | |
1950 | |
1951 (defvar efs-data-buffer-name " *ftp data*") | |
1952 ;; Buffer name to hold directory listing data received from ftp process. | |
1953 | |
1954 (defvar efs-data-buffer-name-2 " *ftp data-2*") | |
1955 ;; A second buffer name in which to hold directory listings. | |
1956 ;; Used for listings which are made during another directory listing. | |
1957 | |
1958 ;;; process names | |
1959 | |
1960 (defvar efs-ctime-process-name-format "*efs ctime %s*") | |
1961 ;; Passed to format with second arg the host name. | |
1962 | |
1963 ;;; For temporary files. | |
1964 | |
1965 ;; This is a list of symbols. | |
1966 (defconst efs-tmp-name-files ()) | |
1967 ;; Here is where these symbols live: | |
1968 (defconst efs-tmp-name-obarray (make-vector 7 0)) | |
1969 ;; We put our version of the emacs PID here: | |
1970 (defvar efs-pid nil) | |
1971 | |
1972 ;;; For abort-recursive-edit | |
1973 | |
1974 (defvar efs-abort-recursive-edit-data nil) | |
1975 (defvar efs-abort-recursive-edit-delay 5) | |
1976 ;; Number of seconds after which efs-abort-recursive-edit-and-then | |
1977 ;; will decide not to runs its sentinel. The assumption is that something | |
1978 ;; went wrong. | |
1979 | |
1980 ;;; hashtables (Use defconst's to clobber any user silliness.) | |
1981 | |
1982 (defconst efs-files-hashtable (efs-make-hashtable 97)) | |
1983 ;; Hash table for storing directories and their respective files. | |
1984 | |
1985 (defconst efs-expand-dir-hashtable (efs-make-hashtable)) | |
1986 ;; Hash table of tilde expansions for remote directories. | |
1987 | |
1988 (defconst efs-ls-converter-hashtable (efs-make-hashtable 37)) | |
1989 ;; Hashtable for storing functions to convert listings from one | |
1990 ;; format to another. Keys are the required switches, and the values | |
1991 ;; are alist of the form ((SWITCHES . CONVERTER)...) where is SWITCHES | |
1992 ;; are the listing switches for the original listing, and CONVERTER is a | |
1993 ;; function of one-variable, the listing-type, to do the conversion | |
1994 ;; on data in the current buffer. SWITCHES is either a string, or nil. | |
1995 ;; nil means that the listing can be converted from cache in | |
1996 ;; efs-files-hashtable, a string from cache in efs-ls-cache. For the latter, | |
1997 ;; listings with no switches (dumb listings), represent SWITCHES as a string | |
1998 ;; consisting only of the ASCII null character. | |
1999 | |
2000 ;;; cache variables (Use defconst's to clobber any user sillines.) | |
2001 | |
2002 (defconst efs-ls-cache nil | |
2003 "List of results from efs-ls. | |
2004 Each entry is a list of four elements, the file listed, the switches used | |
2005 \(nil if none\), the listing string, and whether this string has already been | |
2006 parsed.") | |
2007 | |
2008 (defvar efs-ls-uncache nil) | |
2009 ;; let-bind this to t, if you want to be sure that efs-ls will replace any | |
2010 ;; cache entries. | |
2011 | |
2012 ;; This is a cache to see if the user has changed | |
2013 ;; completion-ignored-extensions. | |
2014 (defconst efs-completion-ignored-extensions completion-ignored-extensions | |
2015 "This variable is internal to efs. Do not set. | |
2016 See completion-ignored-extensions, instead.") | |
2017 | |
2018 ;; We cache the regexp we use for completion-ignored-extensions. This | |
2019 ;; saves building a string every time we do completion. String construction | |
2020 ;; is costly in emacs. | |
2021 (defconst efs-completion-ignored-pattern | |
2022 (mapconcat (function | |
2023 (lambda (s) (if (stringp s) | |
2024 (concat (regexp-quote s) "$") | |
2025 "/"))) ; / never in filename | |
2026 efs-completion-ignored-extensions | |
2027 "\\|") | |
2028 "This variable is internal to efs. Do not set. | |
2029 See completion-ignored-extensions, instead.") | |
2030 | |
2031 (defvar efs-system-fqdn nil | |
2032 "Cached value of the local systems' fully qualified domain name.") | |
2033 | |
2034 ;;; The file-type-alist | |
2035 | |
2036 ;; efs-file-type-alist is an alist indexed by host-type | |
2037 ;; which stores data on how files are structured on the given | |
2038 ;; host-type. Each entry is a list of three elements. The first is the | |
2039 ;; definition of a `byte', the second the native character representation, | |
2040 ;; and the third, the file structure. | |
2041 ;; | |
2042 ;; Meanings of the symbols: | |
2043 ;; ------------------------ | |
2044 ;; The byte symbols: | |
2045 ;; 8-bit = bytes of 8-bits | |
2046 ;; 36-bit-wa = 36-bit word aligned. Precisely, the addressing unit is that | |
2047 ;; of a PDP-10 using the "<440700,,0> byte pointer". | |
2048 ;; | |
2049 ;; The native character set symbols: | |
2050 ;; 8-ascii = 8-bit NVT-ASCII | |
2051 ;; 7-ascii = 7-bit ascii as on a PDP-10 | |
2052 ;; ebcdic = EBCDIC as on an IBM mainframe | |
2053 ;; lispm = the native character set on a lispm (Symbolics and LMI) | |
2054 ;; mts = native character representation in the Michigan Terminal System | |
2055 ;; (which runs on IBM and Amdal mainframes), similar to ebcdic | |
2056 ;; | |
2057 ;; The file structure symbols: | |
2058 ;; | |
2059 ;; file-nl = data is stored as a contiguous sequence of data bytes | |
2060 ;; with EOL denoted by <NL>. | |
2061 ;; file-crlf = data is stored as a contiguous sequence of data bytes | |
2062 ;; with EOL denoted by <CR-LF> | |
2063 ;; record = data is stored as a sequence of records | |
2064 ;; file-lispm = data as stored on a lispm. i.e. a sequence of bits | |
2065 ;; with EOL denoted by character code 138 (?) | |
2066 ;; | |
2067 ;; If we've messed anything up here, please let us know. | |
2068 | |
2069 (defvar efs-file-type-alist | |
2070 '((unix . (8-bit 8-ascii file-nl)) | |
2071 (sysV-unix . (8-bit 8-ascii file-nl)) | |
2072 (bsd-unix . (8-bit 8-ascii file-nl)) | |
2073 (apollo-unix . (8-bit 8-ascii file-nl)) | |
2074 (dumb-apollo-unix . (8-bit 8-ascii file-nl)) | |
2075 (dumb-unix . (8-bit 8-ascii file-nl)) | |
2076 (super-dumb-unix . (8-bit 8-ascii file-nl)) | |
2077 (guardian . (8-bit ascii file-nl)) | |
2078 (plan9 . (8-bit 8-ascii file-nl)) | |
2079 (dos . (8-bit 8-ascii file-crlf)) | |
2080 (ms-unix . (8-bit 8-ascii file-crlf)) | |
2081 (netware . (8-bit 8-ascii file-crlf)) | |
2082 (os2 . (8-bit 8-ascii file-crlf)) | |
2083 (tops-20 . (36-bit-wa 7-ascii file-crlf)) | |
2084 (mpe . (8-bit 8-ascii record)) | |
2085 (mvs . (8-bit ebcdic record)) | |
2086 (cms . (8-bit ebcdic record)) | |
2087 (cms-knet . (8-bit ebcdic record)) | |
2088 (mts . (8-bit mts record)) ; mts seems to have its own char rep. | |
2089 ; Seems to be close to ebcdic, but not the same. | |
2090 (dos-distinct . (8-bit 8-ascii file-crlf)) | |
2091 (ka9q . (8-bit 8-ascii file-crlf)) | |
2092 (vms . (8-bit 8-ascii record)) ; The mysteries of VMS's RMS. | |
2093 (hell . (8-bit 8-ascii file-crlf)) | |
2094 (vos . (8-bit 8-ascii record)) | |
2095 (ti-explorer . (8-bit lispm file-lispm)) ; lispms use a file structure, but | |
2096 ; use an out of range char to | |
2097 ; indicate EOL. | |
2098 (ti-twenex . (8-bit lispm file-lispm)) | |
2099 (nos-ve . (8-bit 8-ascii record)) | |
2100 (coke . (8-bit 8-ascii file-nl)) ; only support 8-bit beverages | |
2101 (nil . (8-bit 8-ascii file-nl)))) ; the local host | |
2102 | |
2103 ;;; Status messages | |
2104 | |
2105 (defvar efs-last-message-time -86400) ; yesterday | |
2106 ;; The time of the last efs status message. c.f. efs-message-interval | |
2107 | |
2108 ;;; For handling dir listings | |
2109 | |
2110 ;; This MUST match all the way to to the start of the filename. | |
2111 ;; This version corresponds to what dired now uses (sandy, 14.1.93) | |
2112 (defvar efs-month-and-time-regexp | |
2113 (concat | |
2114 " \\([0-9]+\\) +" ; file size | |
2115 "\\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|June?\\|July?\\|Aug\\|Sep\\|Oct" | |
2116 ; June and July are for HP-UX 9.0 | |
2117 "\\|Nov\\|Dec\\) \\([ 0-3][0-9]\\)\\(" | |
2118 " [012][0-9]:[0-6][0-9] \\|" ; time | |
2119 " [12][90][0-9][0-9] \\|" ; year on IRIX, NeXT, SunOS, ULTRIX, Apollo | |
2120 ; HP-UX, A/UX | |
2121 " [12][90][0-9][0-9] \\)" ; year on AIX | |
2122 )) | |
2123 | |
2124 (defvar efs-month-alist | |
2125 '(("Jan" . 1) ("Feb". 2) ("Mar" . 3) ("Apr" . 4) ("May" . 5) ("Jun" . 6) | |
2126 ("June" . 6) ("Jul" . 7) ("July" . 7) ("Aug" . 8) ("Sep" . 9) ("Oct" . 10) | |
2127 ("Nov" . 11) ("Dec" . 12))) | |
2128 | |
2129 ;; Matches the file modes, link number, and owner string. | |
2130 ;; The +/- is for extended file access permissions. | |
2131 (defvar efs-modes-links-owner-regexp | |
2132 (concat | |
2133 "\\([^ ][-r][-w][^ ][-r][-w][^ ][-r][-w][^ ]\\)[-+]? *\\([0-9]+\\)" | |
2134 " +\\([^ ]+\\) ")) | |
2135 | |
2136 ;;;; --------------------------------------------------------------- | |
2137 ;;;; efs-dired variables | |
2138 ;;;; --------------------------------------------------------------- | |
2139 | |
2140 ;; These variables must be here, instead of in efs-dired.el, because | |
2141 ;; the efs-HOST-TYPE.el files need to add to it. | |
2142 (defvar efs-dired-re-exe-alist nil | |
2143 "Association list of regexps which match file lines of executable files.") | |
2144 | |
2145 (defvar efs-dired-re-dir-alist nil | |
2146 "Association list of regexps which match file lines of subdirectories.") | |
2147 | |
2148 (defvar efs-dired-host-type nil | |
2149 "Host type of a dired buffer. \(buffer local\)") | |
2150 (make-variable-buffer-local 'efs-dired-host-type) | |
2151 | |
2152 (defvar efs-dired-listing-type nil | |
2153 "Listing type of a dired buffer. \(buffer local\)") | |
2154 (make-variable-buffer-local 'efs-dired-listing-type) | |
2155 | |
2156 (defvar efs-dired-listing-type-string nil) | |
2157 (make-variable-buffer-local 'efs-dired-listing-type-string) | |
2158 | |
2159 ;;;; ------------------------------------------------------------- | |
2160 ;;;; New error symbols. | |
2161 ;;;; ------------------------------------------------------------- | |
2162 | |
2163 (put 'ftp-error 'error-conditions '(ftp-error file-error error)) | |
2164 ;; (put 'ftp-error 'error-message "FTP error") | |
2165 | |
2166 | |
2167 ;;;; ============================================================= | |
2168 ;;;; >3 | |
2169 ;;;; Utilities | |
2170 ;;;; ============================================================= | |
2171 | |
2172 ;;; ------------------------------------------------------------------- | |
2173 ;;; General Macros (Make sure that macros are defined before they're | |
2174 ;;; used, for the byte compiler. | |
2175 ;;; ------------------------------------------------------------------- | |
2176 | |
2177 (defmacro efs-kbd-quit-protect (proc &rest body) | |
2178 ;; When an efs function controlling an FTP connection gets a kbd-quit | |
2179 ;; this tries to make sure that everything unwinds consistently. | |
2180 (let ((temp (make-symbol "continue"))) | |
2181 (list 'let | |
2182 (list '(quit-flag nil) | |
2183 '(inhibit-quit nil) | |
2184 (list temp t)) | |
2185 (list | |
2186 'while temp | |
2187 (list 'setq temp nil) | |
2188 (list | |
2189 'condition-case nil | |
2190 (cons 'progn | |
2191 body) | |
2192 (list 'quit | |
2193 (list 'setq temp | |
2194 (list 'efs-kbd-quit-protect-cover-quit proc)))))))) | |
2195 | |
2196 (defun efs-kbd-quit-protect-cover-quit (proc) | |
2197 ;; This function exists to keep the macro expansion of the | |
2198 ;; efs-kbd-quit-protect down to a reasonable size. | |
2199 (let ((pop-up-windows t) | |
2200 (buff (get-buffer (process-buffer proc))) | |
2201 res) | |
2202 (if (save-window-excursion | |
2203 (if buff | |
2204 (progn | |
2205 (pop-to-buffer buff) | |
2206 (goto-char (point-max)) | |
2207 (recenter (- (window-height) | |
2208 2)))) | |
2209 (setq res (efs-kill-ftp-buffer-with-prompt proc buff))) | |
2210 (progn | |
2211 (if (eq res 0) | |
2212 (if (eq (selected-window) | |
2213 (minibuffer-window)) | |
2214 (efs-abort-recursive-edit-and-then | |
2215 (function | |
2216 (lambda (buff) | |
2217 (if (get-buffer buff) | |
2218 (display-buffer buff)))) | |
2219 buff) | |
2220 (if (get-buffer buff) | |
2221 (display-buffer buff)) | |
2222 (signal 'quit nil)) | |
2223 (if (eq (selected-window) (minibuffer-window)) | |
2224 (abort-recursive-edit) | |
2225 (signal (quote quit) nil))) | |
2226 nil) | |
2227 (sit-for 0) | |
2228 (message "Waiting on %s..." (or (car (efs-parse-proc-name proc)) | |
2229 "a whim")) | |
2230 t))) | |
2231 | |
2232 (put 'efs-kbd-quit-protect 'lisp-indent-hook 1) | |
2233 | |
2234 (defmacro efs-save-buffer-excursion (&rest forms) | |
2235 "Execute FORMS, restoring the current buffer afterwards. | |
2236 Unlike, save-excursion, this does not restore the point." | |
2237 (let ((temp (make-symbol "saved-buff"))) | |
2238 (list 'let | |
2239 (list (list temp '(current-buffer))) | |
2240 (list 'unwind-protect | |
2241 (cons 'progn forms) | |
2242 (list 'condition-case nil | |
2243 (list 'set-buffer temp) | |
2244 '(error nil)))))) | |
2245 | |
2246 (put 'efs-save-buffer-excursion 'lisp-indent-hook 0) | |
2247 | |
2248 (defmacro efs-unquote-dollars (string) | |
2249 ;; Unquote $$'s to $'s in STRING. | |
2250 (` (let ((string (, string)) | |
2251 (start 0) | |
2252 new) | |
2253 (while (string-match "\\$\\$" string start) | |
2254 (setq new (concat new (substring | |
2255 string start (1+ (match-beginning 0)))) | |
2256 start (match-end 0))) | |
2257 (if new | |
2258 (concat new (substring string start)) | |
2259 string)))) | |
2260 | |
2261 (defmacro efs-get-file-part (path) | |
2262 ;; Given PATH, return the file part used for looking up the file's entry | |
2263 ;; in a hashtable. | |
2264 ;; This need not be the same thing as file-name-nondirectory. | |
2265 (` (let ((file (file-name-nondirectory (, path)))) | |
2266 (if (string-equal file "") | |
2267 "." | |
2268 file)))) | |
2269 | |
2270 (defmacro efs-ftp-path-macro (path) | |
2271 ;; Just a macro version of efs-ftp-path, for speed critical | |
2272 ;; situations. Could use (inline ...) instead, but not everybody | |
2273 ;; uses the V19 byte-compiler. Also, doesn't call efs-save-match-data, | |
2274 ;; but assumes that the calling function does it. | |
2275 (` | |
2276 (let ((path (, path))) | |
2277 (or (string-equal path efs-ftp-path-arg) | |
2278 (setq efs-ftp-path-res | |
2279 (and (string-match efs-path-regexp path) | |
2280 (let ((host (substring path (match-beginning 2) | |
2281 (match-end 2))) | |
2282 (user (and (match-beginning 1) | |
2283 (substring path (match-beginning 1) | |
2284 (1- (match-end 1))))) | |
2285 (rpath (substring path (1+ (match-end 2))))) | |
2286 (list (if (string-equal host "") | |
2287 (setq host (system-name)) | |
2288 host) | |
2289 (or user (efs-get-user host)) | |
2290 rpath))) | |
2291 ;; Set this last, in case efs-get-user calls this function, | |
2292 ;; which would modify an earlier setting. | |
2293 efs-ftp-path-arg path)) | |
2294 efs-ftp-path-res))) | |
2295 | |
2296 (defmacro efs-canonize-switches (switches) | |
2297 ;; Converts a switches string, into a lexographically ordered string, | |
2298 ;; omitting - and spaces. Should we remove duplicate characters too? | |
2299 (` (if (, switches) | |
2300 (mapconcat | |
2301 'char-to-string | |
2302 (sort (delq ?- (delq ?\ (mapcar 'identity (, switches)))) '<) "") | |
2303 ;; For the purpose of interning in a hashtable, represent the nil | |
2304 ;; switches, as a string consisting of the ascii null character. | |
2305 (char-to-string 0)))) | |
2306 | |
2307 (defmacro efs-canonize-file-name (fn) | |
2308 ;; Canonizes the case of file names. | |
2309 (` (let ((parsed (efs-ftp-path (, fn)))) | |
2310 (if parsed | |
2311 (let ((host (car parsed))) | |
2312 (if (memq (efs-host-type host) efs-case-insensitive-host-types) | |
2313 (downcase (, fn)) | |
2314 (format efs-path-format-string (nth 1 parsed) (downcase host) | |
2315 (nth 2 parsed)))) | |
2316 (, fn))))) | |
2317 | |
2318 (defmacro efs-get-files-hashtable-entry (fn) | |
2319 (` (efs-get-hash-entry (efs-canonize-file-name (, fn)) efs-files-hashtable))) | |
2320 | |
2321 ;;;; ------------------------------------------------------------ | |
2322 ;;;; Utility Functions | |
2323 ;;;; ------------------------------------------------------------ | |
2324 | |
2325 (defun efs-kill-ftp-buffer-with-prompt (proc buffer) | |
2326 ;; Does a 3-way prompt to kill a ftp PROC and BUFFER. | |
2327 ;; Returns t if buffer was killed, 0 if only process, nil otherwise. | |
2328 (let ((inhibit-quit t) | |
2329 (cursor-in-echo-area t) | |
2330 char) | |
2331 (message | |
2332 (if efs-debug-ftp-connection | |
2333 "Kill ftp process and buffer (y[es], n[o], c[lose], d[ebug] ) " | |
2334 "Kill ftp process and buffer? (y or n, c to only close process) ")) | |
2335 (setq char (read-char)) | |
2336 (prog1 | |
2337 (cond | |
2338 ((memq char '(?y ?Y ?\ )) | |
2339 (set-process-sentinel proc nil) | |
2340 (condition-case nil | |
2341 (kill-buffer buffer) | |
2342 (error nil)) | |
2343 t) | |
2344 ((memq char '(?c ?C)) | |
2345 (set-process-sentinel proc nil) | |
2346 (condition-case nil | |
2347 (save-excursion | |
2348 (set-buffer buffer) | |
2349 (setq efs-process-busy nil | |
2350 efs-process-q nil) | |
2351 (delete-process proc)) | |
2352 (error nil)) | |
2353 0) | |
2354 ((memq char '(?n ?N)) | |
2355 (message "") | |
2356 nil) | |
2357 ((and efs-debug-ftp-connection | |
2358 (memq char '(?d ?D))) | |
2359 (condition-case nil | |
2360 (save-excursion | |
2361 (set-buffer buffer) | |
2362 (setq efs-process-busy nil | |
2363 efs-process-q nil)) | |
2364 (error nil)) | |
2365 0) | |
2366 (t | |
2367 (message | |
2368 (if efs-debug-ftp-connection | |
2369 "Type one of y, n, c or d." | |
2370 "Type one of y, n or c.")) | |
2371 (ding) | |
2372 (sit-for 1) | |
2373 (setq quit-flag nil) | |
2374 (efs-kill-ftp-buffer-with-prompt proc buffer)))))) | |
2375 | |
2376 (defun efs-barf-if-not-directory (directory) | |
2377 ;; Signal an error if DIRECTORY is not one. | |
2378 (or (file-directory-p directory) | |
2379 (signal 'file-error | |
2380 (list "Opening directory" | |
2381 (if (file-exists-p directory) | |
2382 "not a directory" | |
2383 "no such file or directory") | |
2384 directory)))) | |
2385 | |
2386 (defun efs-call-cont (cont &rest args) | |
2387 "Call the function specified by CONT. | |
2388 CONT can be either a function or a list of a function and some args. | |
2389 The first parameters passed to the function will be ARGS. The remaining | |
2390 args will be taken from CONT if a list was passed." | |
2391 (if cont | |
2392 (let ((efs-nested-cmd t)) ; let-bound so that conts don't pop any queues | |
2393 (efs-save-buffer-excursion | |
2394 (if (and (listp cont) | |
2395 (not (eq (car cont) 'lambda))) | |
2396 (apply (car cont) (append args (cdr cont))) | |
2397 (apply cont args)))))) | |
2398 | |
2399 (defun efs-replace-path-component (fullpath path) | |
2400 "For FULLPATH matching efs-path-regexp replace the path component with PATH." | |
2401 (efs-save-match-data | |
2402 (if (string-match efs-path-root-regexp fullpath) | |
2403 (concat (substring fullpath 0 (match-end 0)) path) | |
2404 path))) | |
2405 | |
2406 (defun efs-abort-recursive-edit-and-then (fun &rest args) | |
2407 ;; Does an abort-recursive-edit, and runs fun _after_ emacs returns to | |
2408 ;; top level. | |
2409 (if (get-process "efs-abort-recursive-edit") | |
2410 ;; Don't queue these things. Clean them out. | |
2411 (delete-process "efs-abort-recursive-edit")) | |
2412 (or efs-suppress-abort-recursive-edit-and-then | |
2413 (progn | |
2414 (setq efs-abort-recursive-edit-data (cons (nth 1 (current-time)) | |
2415 (cons fun args))) | |
2416 (condition-case nil | |
2417 (set-process-sentinel | |
2418 (let ((default-directory exec-directory) | |
2419 (process-connection-type nil)) | |
2420 (start-process "efs-abort-recursive-edit" nil "sleep" "0")) | |
2421 (function | |
2422 (lambda (proc string) | |
2423 (let ((data efs-abort-recursive-edit-data)) | |
2424 (setq efs-abort-recursive-edit-data) | |
2425 (if (and data | |
2426 (integerp (car data)) | |
2427 (<= (- (nth 1 (current-time)) (car data)) | |
2428 efs-abort-recursive-edit-delay)) | |
2429 (apply (nth 1 data) (nthcdr 2 data))))))) | |
2430 (error nil)))) | |
2431 (abort-recursive-edit)) | |
2432 | |
2433 (defun efs-occur-in-string (char string) | |
2434 ;; Return the number of occurrences of CHAR in STRING. | |
2435 (efs-save-match-data | |
2436 (let ((regexp (regexp-quote (char-to-string char))) | |
2437 (count 0) | |
2438 (start 0)) | |
2439 (while (string-match regexp string start) | |
2440 (setq start (match-end 0) | |
2441 count (1+ count))) | |
2442 count))) | |
2443 | |
2444 (defun efs-parse-proc-name (proc) | |
2445 ;; Parses the name of process to return a list \(host user\). | |
2446 (efs-save-match-data | |
2447 (let ((name (process-name proc))) | |
2448 (and name | |
2449 (string-match "^\\*ftp \\([^@]*\\)@\\([^*]+\\)\\*$" name) | |
2450 (list (substring name (match-beginning 2) (match-end 2)) | |
2451 (substring name (match-beginning 1) (match-end 1))))))) | |
2452 | |
2453 ;;;; ------------------------------------------------------------ | |
2454 ;;;; Of Geography, connectivity, and the internet... Gateways. | |
2455 ;;;; ------------------------------------------------------------ | |
2456 | |
2457 (defun efs-use-gateway-p (host &optional opaque-p) | |
2458 ;; Returns whether to access this host via a gateway. | |
2459 ;; Returns the gateway type as a symbol. See efs-gateway-type <V>. | |
2460 ;; If optional OPAQUE-P is non-nil, only returns non-nil if the gateway | |
2461 ;; type is in the list efs-opaque-gateways <V>. | |
2462 (and efs-gateway-type | |
2463 host ;local host is nil | |
2464 (efs-save-match-data | |
2465 (and (not (string-match efs-ftp-local-host-regexp host)) | |
2466 (let ((type (car efs-gateway-type))) | |
2467 (if opaque-p | |
2468 (and (memq type efs-opaque-gateways) type) | |
2469 type)))))) | |
2470 | |
2471 (defun efs-local-to-gateway-filename (filename &optional reverse) | |
2472 ;; Converts a FILENAME on the local host to its name on the gateway, | |
2473 ;; using efs-gateway-mounted-dirs-alist. If REVERSE is non-nil, does just | |
2474 ;; that. If the there is no corresponding name because non of its parent | |
2475 ;; directories are mounted, returns nil. | |
2476 (if efs-gateway-mounted-dirs-alist | |
2477 (let ((len (length filename)) | |
2478 (alist efs-gateway-mounted-dirs-alist) | |
2479 result elt elt-len) | |
2480 (if reverse | |
2481 (while (setq elt (car alist)) | |
2482 (if (and (>= len (setq elt-len (length (cdr elt)))) | |
2483 (string-equal (cdr elt) (substring filename 0 elt-len))) | |
2484 (setq result (concat (car elt) | |
2485 (substring filename elt-len)) | |
2486 alist nil) | |
2487 (setq alist (cdr alist)))) | |
2488 (while (setq elt (car alist)) | |
2489 (if (and (>= len (setq elt-len (length (car elt)))) | |
2490 (string-equal (car elt) (substring filename 0 elt-len))) | |
2491 (setq result (concat (cdr elt) | |
2492 (substring filename elt-len)) | |
2493 alist nil) | |
2494 (setq alist (cdr alist))))) | |
2495 result))) | |
2496 | |
2497 ;;; ------------------------------------------------------------ | |
2498 ;;; Enhanced message support. | |
2499 ;;; ------------------------------------------------------------ | |
2500 | |
2501 (defun efs-message (fmt &rest args) | |
2502 "Output the given message, truncating to the size of the minibuffer window." | |
2503 (let ((msg (apply (function format) fmt args)) | |
2504 (max (window-width (minibuffer-window)))) | |
2505 (if (>= (length msg) max) | |
2506 (setq msg (concat "> " (substring msg (- 3 max))))) | |
2507 (message "%s" msg))) | |
2508 | |
2509 (defun efs-message-p () | |
2510 ;; Returns t, if efs is allowed to display a status message. | |
2511 (not | |
2512 (or (and (boundp 'dired-in-query) dired-in-query) | |
2513 (boundp 'search-message) | |
2514 cursor-in-echo-area | |
2515 (and (/= efs-message-interval 0) | |
2516 (let ((diff (- efs-last-message-time | |
2517 (setq efs-last-message-time | |
2518 (nth 1 (current-time)))))) | |
2519 (and | |
2520 (> diff (- efs-message-interval)) | |
2521 (< diff 0))))))) ; in case the clock wraps. | |
2522 | |
2523 (efs-define-fun efs-relativize-filename (file &optional dir new) | |
2524 "Abbreviate the given filename relative to DIR . | |
2525 If DIR is nil, use the value of `default-directory' for the currently selected | |
2526 window. If the optional parameter NEW is given and the | |
2527 non-directory parts match, only return the directory part of the file." | |
2528 (let* ((dir (or dir (save-excursion | |
2529 (set-buffer (window-buffer (selected-window))) | |
2530 default-directory))) | |
2531 (dlen (length dir)) | |
2532 (result file)) | |
2533 (and (> (length file) dlen) | |
2534 (string-equal (substring file 0 dlen) dir) | |
2535 (setq result (substring file dlen))) | |
2536 (and new | |
2537 (string-equal (file-name-nondirectory result) | |
2538 (file-name-nondirectory new)) | |
2539 (or (setq result (file-name-directory result)) | |
2540 (setq result "./"))) | |
2541 (abbreviate-file-name result))) | |
2542 | |
2543 ;;; ------------------------------------------------------------ | |
2544 ;;; Temporary file location and deletion... | |
2545 ;;; ------------------------------------------------------------ | |
2546 | |
2547 (defun efs-get-pid () | |
2548 ;; Half-hearted attempt to get the current process's id. | |
2549 (setq efs-pid (substring (make-temp-name "") 1))) | |
2550 | |
2551 (defun efs-make-tmp-name (host1 host2) | |
2552 ;; Returns the name of a new temp file, for moving data between HOST1 | |
2553 ;; and HOST2. This temp file must be directly accessible to the | |
2554 ;; FTP client connected to HOST1. Using nil for either HOST1 or | |
2555 ;; HOST2 means the local host. The return value is actually a list | |
2556 ;; whose car is the name of the temp file wrto to the local host | |
2557 ;; and whose cdr is the name of the temp file wrto to the host | |
2558 ;; on which the client connected to HOST1 is running. If the gateway | |
2559 ;; is only accessible by FTP, then the car of this may be in efs extended | |
2560 ;; file name syntax. | |
2561 (let ((pid (or efs-pid (efs-get-pid))) | |
2562 (start ?a) | |
2563 file entry template rem-template template-len) | |
2564 ;; Compute the templates. | |
2565 (if (null (and host1 (efs-use-gateway-p host1 t))) | |
2566 ;; file must be local | |
2567 (if (null (and host2 (efs-use-gateway-p host2 t))) | |
2568 (setq template efs-tmp-name-template) | |
2569 (setq template (or (efs-local-to-gateway-filename | |
2570 efs-gateway-tmp-name-template t) | |
2571 efs-tmp-name-template))) | |
2572 ;; file must be on the gateway -- make sure that the gateway | |
2573 ;; configuration is sensible. | |
2574 (efs-save-match-data | |
2575 (or (string-match efs-ftp-local-host-regexp efs-gateway-host) | |
2576 (error "Gateway %s must be directly ftp accessible." | |
2577 efs-gateway-host))) | |
2578 (setq rem-template efs-gateway-tmp-name-template | |
2579 template (or (efs-local-to-gateway-filename | |
2580 efs-gateway-tmp-name-template t) | |
2581 (format efs-path-format-string | |
2582 (efs-get-user efs-gateway-host) | |
2583 efs-gateway-host | |
2584 efs-gateway-tmp-name-template)) | |
2585 template-len (length template))) | |
2586 ;; Compute a new file name. | |
2587 (while (let (efs-verbose) | |
2588 (setq file (format "%s%c%s" template start pid) | |
2589 entry (intern file efs-tmp-name-obarray)) | |
2590 (or (memq entry efs-tmp-name-files) | |
2591 (file-exists-p file))) | |
2592 (if (> (setq start (1+ start)) ?z) | |
2593 (progn | |
2594 (setq template (concat template "X")) | |
2595 (setq start ?a)))) | |
2596 (setq efs-tmp-name-files | |
2597 (cons entry efs-tmp-name-files)) | |
2598 (if rem-template | |
2599 (cons file (concat rem-template (substring file template-len))) | |
2600 (cons file file)))) | |
2601 | |
2602 (defun efs-del-tmp-name (temp) | |
2603 ;; Deletes file TEMP, a string. | |
2604 (setq efs-tmp-name-files | |
2605 (delq (intern temp efs-tmp-name-obarray) | |
2606 efs-tmp-name-files)) | |
2607 (condition-case () | |
2608 (let (efs-verbose) | |
2609 (delete-file temp)) | |
2610 (error nil))) | |
2611 | |
2612 | |
2613 ;;;; ============================================================== | |
2614 ;;;; >4 | |
2615 ;;;; Hosts, Users, Accounts, and Passwords | |
2616 ;;;; ============================================================== | |
2617 ;;; | |
2618 ;;; A lot of the support for this type of thing is in efs-netrc.el. | |
2619 | |
2620 ;;;; ------------------------------------------------------------ | |
2621 ;;;; Password support. | |
2622 ;;;; ------------------------------------------------------------ | |
2623 | |
2624 (defun efs-lookup-passwd (host user) | |
2625 ;; Look up the password for HOST and USER. | |
2626 (let ((ent (efs-get-host-user-property host user 'passwd))) | |
2627 (and ent (efs-code-string ent)))) | |
2628 | |
2629 (defun efs-system-fqdn () | |
2630 "Returns a fully qualified domain name for the current host, if possible." | |
2631 (or efs-system-fqdn | |
2632 (setq efs-system-fqdn | |
2633 (let ((sys (system-name))) | |
2634 (if (string-match "\\." sys) | |
2635 sys | |
2636 (if efs-nslookup-program | |
2637 (let ((proc (let ((default-directory exec-directory) | |
2638 (process-connection-type nil)) | |
2639 (start-process " *nslookup*" " *nslookup*" | |
2640 efs-nslookup-program sys))) | |
2641 (res sys) | |
2642 (n 0)) | |
2643 (process-kill-without-query proc) | |
2644 (save-excursion | |
2645 (set-buffer (process-buffer proc)) | |
2646 (let ((quit-flag nil) | |
2647 (inhibit-quit nil)) | |
2648 (if efs-nslookup-threshold | |
2649 (progn | |
2650 (while (and (memq (process-status proc) | |
2651 '(run open)) | |
2652 (< n efs-nslookup-threshold)) | |
2653 (accept-process-output) | |
2654 (setq n (1+ n))) | |
2655 (if (>= n efs-nslookup-threshold) | |
2656 (progn | |
2657 (with-output-to-temp-buffer "*Help*" | |
2658 (princ (format "\ | |
2659 efs is unable to determine a fully qualified domain name | |
2660 for the local host to send as an anonymous ftp password. | |
2661 | |
2662 The function `system-name' is not returning a fully qualified | |
2663 domain name. An attempt to obtain a fully qualified domain name | |
2664 with `efs-nslookup-program' (currently set to \"%s\") has | |
2665 elicited no response from that program. Consider setting | |
2666 `efs-generate-anonymous-password' to an email address for anonymous | |
2667 ftp passwords. | |
2668 | |
2669 For more information see the documentation (use C-h v) for the | |
2670 variables `efs-nslookup-program' and `efs-nslookup-threshold'." | |
2671 efs-nslookup-program))) | |
2672 (error "No response from %s" | |
2673 efs-nslookup-program)))) | |
2674 (while (memq (process-status proc) '(run open)) | |
2675 (accept-process-output proc))) | |
2676 (goto-char (point-min)) | |
2677 (if (re-search-forward | |
2678 (format "^Name: *\\(%s\\.[^ \n\t]+\\)" | |
2679 sys) nil t) | |
2680 (setq res (buffer-substring | |
2681 (match-beginning 1) | |
2682 (match-end 1))) | |
2683 (kill-buffer (current-buffer))))) | |
2684 res) | |
2685 sys)))))) | |
2686 | |
2687 (defun efs-passwd-unique-list (alist) | |
2688 ;; Preserving the relative order of ALIST, remove all entries with duplicate | |
2689 ;; cars. | |
2690 (let (result) | |
2691 (while alist | |
2692 (or (assoc (car alist) result) | |
2693 (setq result (cons (car alist) result))) | |
2694 (setq alist (cdr alist))) | |
2695 (nreverse result))) | |
2696 | |
2697 (defun efs-get-passwd-list (user host) | |
2698 ;; Returns an alist of the form '((pass host user) ...). | |
2699 ;; The order is essentially arbitrary, except that entries with user | |
2700 ;; equal to USER will appear first. Followed by entries with host equal to | |
2701 ;; HOST. Also, there will be no entries with duplicate values of pass. | |
2702 (efs-parse-netrc) | |
2703 (let* ((user-template (concat "/" user)) | |
2704 (ulen (length user-template)) | |
2705 (hlen (length host)) | |
2706 primaries secondaries tertiaries) | |
2707 (efs-save-match-data | |
2708 (efs-map-hashtable | |
2709 (function | |
2710 (lambda (key passwd) | |
2711 (cond ((null passwd) nil) | |
2712 ((and (> (length key) ulen) | |
2713 (string-equal user-template | |
2714 (substring key (- ulen)))) | |
2715 (setq primaries (cons (list (efs-code-string passwd) | |
2716 (substring key 0 (- ulen)) | |
2717 (substring user-template 1)) | |
2718 primaries))) | |
2719 ((and (> (length key) hlen) | |
2720 (string-equal host (substring key 0 hlen)) | |
2721 (memq (aref key hlen) '(?/ ?.))) | |
2722 (if (string-match "/" key hlen) | |
2723 (setq secondaries | |
2724 (cons (list (efs-code-string passwd) | |
2725 (substring key 0 (match-beginning 0)) | |
2726 (substring key (match-end 0))) | |
2727 secondaries)))) | |
2728 ((string-match "/" key) | |
2729 (setq tertiaries | |
2730 (cons (list (efs-code-string passwd) | |
2731 (substring key 0 (match-beginning 0)) | |
2732 (substring key (match-end 0))) | |
2733 tertiaries)))))) | |
2734 efs-host-user-hashtable 'passwd)) | |
2735 (efs-passwd-unique-list (nconc primaries secondaries tertiaries)))) | |
2736 | |
2737 (defun efs-get-passwd (host user) | |
2738 "Given a HOST and USER, return the FTP password, prompting if it was not | |
2739 previously set." | |
2740 (efs-parse-netrc) | |
2741 | |
2742 ;; look up password in the hash table first; user might have overriden the | |
2743 ;; defaults. | |
2744 (cond ((efs-lookup-passwd host user)) | |
2745 | |
2746 ;; see if default user and password set from the .netrc file. | |
2747 ((and (stringp efs-default-user) | |
2748 efs-default-password | |
2749 (string-equal user efs-default-user)) | |
2750 (copy-sequence efs-default-password)) | |
2751 | |
2752 ;; anonymous ftp password is handled specially since there is an | |
2753 ;; unwritten rule about how that is used on the Internet. | |
2754 ((and (efs-anonymous-p user) | |
2755 efs-generate-anonymous-password) | |
2756 (if (stringp efs-generate-anonymous-password) | |
2757 (copy-sequence efs-generate-anonymous-password) | |
2758 (concat (user-login-name) "@" (efs-system-fqdn)))) | |
2759 | |
2760 ;; see if same user has logged in to other hosts; if so then prompt | |
2761 ;; with the password that was used there. | |
2762 (t | |
2763 (let (others defaults passwd) | |
2764 (unwind-protect | |
2765 (progn | |
2766 (setq others (efs-get-passwd-list user host) | |
2767 defaults (mapcar | |
2768 (function | |
2769 (lambda (x) | |
2770 (cons | |
2771 (format | |
2772 "Passwd for %s@%s (same as %s@%s): " | |
2773 user host (nth 2 x) (nth 1 x)) | |
2774 (car x)))) | |
2775 others)) | |
2776 (setq passwd | |
2777 (read-passwd | |
2778 (or defaults | |
2779 (format "Password for %s@%s: " user host))))) | |
2780 (while others | |
2781 (fillarray (car (car others)) 0) | |
2782 (setq others (cdr others)))) | |
2783 (or (null passwd) | |
2784 (and efs-high-security-hosts | |
2785 (efs-save-match-data | |
2786 (string-match efs-high-security-hosts | |
2787 (format "%s@%s" user host)))) | |
2788 (efs-set-passwd host user passwd)) | |
2789 passwd)))) | |
2790 | |
2791 ;;;; ------------------------------------------------------------ | |
2792 ;;;; Account support | |
2793 ;;;; ------------------------------------------------------------ | |
2794 | |
2795 (defun efs-get-account (host user &optional minidisk really) | |
2796 "Given a HOST, USER, and optional MINIDISK return the FTP account password. | |
2797 If the optional REALLY argument is given, prompts the user if it can't find | |
2798 one." | |
2799 (efs-parse-netrc) | |
2800 (let ((account (if minidisk | |
2801 (efs-get-hash-entry | |
2802 (concat (downcase host) "/" user "/" minidisk) | |
2803 efs-minidisk-hashtable | |
2804 (memq (efs-host-type host) | |
2805 efs-case-insensitive-host-types)) | |
2806 (efs-get-host-user-property host user 'account)))) | |
2807 (if account | |
2808 (efs-code-string account) | |
2809 ;; Do we really want to send the default-account passwd for all | |
2810 ;; minidisks? | |
2811 (if (and (stringp efs-default-user) | |
2812 (string-equal user efs-default-user) | |
2813 efs-default-account) | |
2814 efs-default-account | |
2815 (and really | |
2816 (let ((acct | |
2817 (read-passwd | |
2818 (if minidisk | |
2819 (format | |
2820 "Write access password for minidisk %s on %s@%s: " | |
2821 minidisk user host) | |
2822 (format | |
2823 "Account password for %s@%s: " user host))))) | |
2824 (or (and efs-high-security-hosts | |
2825 (efs-save-match-data | |
2826 efs-high-security-hosts | |
2827 (format "%s@%s" user host))) | |
2828 (efs-set-account host user minidisk acct)) | |
2829 acct)))))) | |
2830 | |
2831 ;;;; ------------------------------------------------------------- | |
2832 ;;;; Special classes of users. | |
2833 ;;;; ------------------------------------------------------------- | |
2834 | |
2835 (defun efs-anonymous-p (user) | |
2836 ;; Returns t if USER should be treated as an anonymous FTP login. | |
2837 (let ((user (downcase user))) | |
2838 (or (string-equal user "anonymous") (string-equal user "ftp")))) | |
2839 | |
2840 | |
2841 ;;;; ============================================================= | |
2842 ;;;; >5 | |
2843 ;;;; FTP client process, and server responses | |
2844 ;;;; ============================================================= | |
2845 | |
2846 ;;;; --------------------------------------------------------- | |
2847 ;;;; Support for asynch process queues. | |
2848 ;;;; --------------------------------------------------------- | |
2849 | |
2850 (defun efs-add-to-queue (host user item) | |
2851 "To the end of the command queue for HOST and USER, adds ITEM. | |
2852 Does nothing if there is no process buffer for HOST and USER." | |
2853 (let ((buff (efs-ftp-process-buffer host user))) | |
2854 (if (get-buffer buff) | |
2855 (save-excursion | |
2856 (set-buffer buff) | |
2857 (setq efs-process-q | |
2858 (nconc efs-process-q (list item))))))) | |
2859 | |
2860 ;;;; ------------------------------------------------------- | |
2861 ;;;; Error recovery for the process filter. | |
2862 ;;;; ------------------------------------------------------- | |
2863 | |
2864 ;;; Could make this better, but it's such an unlikely error to hit. | |
2865 (defun efs-process-scream-and-yell (line) | |
2866 (let* ((buff (buffer-name (current-buffer))) | |
2867 (host (and (string-match "@\\(.*\\)\\*$" buff) | |
2868 (substring buff (match-beginning 1) (match-end 1))))) | |
2869 (with-output-to-temp-buffer "*Help*" | |
2870 (princ | |
2871 (concat | |
2872 "efs is unable to identify the following reply code | |
2873 from the ftp server " host ":\n\n" line " | |
2874 | |
2875 Please send a bug report to ange@hplb.hpl.hp.com. | |
2876 In your report include a transcript of your\n" | |
2877 buff " buffer.")))) | |
2878 (error "Unable to identify server code.")) | |
2879 | |
2880 (defun efs-error (host user msg) | |
2881 "Signal \'ftp-error for the FTP connection for HOST and USER. | |
2882 The error gives the string MSG as text. The process buffer for the FTP | |
2883 is popped up in another window." | |
2884 (let ((cur (selected-window)) | |
2885 (pop-up-windows t) | |
2886 (buff (get-buffer (efs-ftp-process-buffer host user)))) | |
2887 (if buff | |
2888 (progn | |
2889 (pop-to-buffer buff) | |
2890 (goto-char (point-max)) | |
2891 (select-window cur)))) | |
2892 (signal 'ftp-error (list (format "FTP Error: %s" msg)))) | |
2893 | |
2894 ;;;; -------------------------------------------------------------------- | |
2895 ;;;; Process filter and supporting functions for handling FTP codes. | |
2896 ;;;; -------------------------------------------------------------------- | |
2897 | |
2898 (defun efs-process-handle-line (line proc) | |
2899 ;; Look at the given LINE from the ftp process PROC and try to catagorize it. | |
2900 (cond ((string-match efs-xfer-size-msgs line) | |
2901 (let ((n 1)) | |
2902 ;; this loop will bomb with an args out of range error at 10 | |
2903 (while (not (match-beginning n)) | |
2904 (setq n (1+ n))) | |
2905 (setq efs-process-xfer-size | |
2906 (ash (string-to-int (substring line | |
2907 (match-beginning n) | |
2908 (match-end n))) | |
2909 -10)))) | |
2910 | |
2911 ((string-match efs-multi-msgs line) | |
2912 (setq efs-process-result-cont-lines | |
2913 (concat efs-process-result-cont-lines line "\n"))) | |
2914 | |
2915 ((string-match efs-skip-msgs line)) | |
2916 | |
2917 ((string-match efs-cmd-ok-msgs line) | |
2918 (if (string-match efs-cmd-ok-cmds efs-process-cmd) | |
2919 (setq efs-process-busy nil | |
2920 efs-process-result nil | |
2921 efs-process-result-line line))) | |
2922 | |
2923 ((string-match efs-pending-msgs line) | |
2924 (if (string-match "^quote rnfr " efs-process-cmd) | |
2925 (setq efs-process-busy nil | |
2926 efs-process-result nil | |
2927 efs-process-result-line line))) | |
2928 | |
2929 ((string-match efs-bytes-received-msgs line) | |
2930 (if efs-process-server-confused | |
2931 (setq efs-process-busy nil | |
2932 efs-process-result nil | |
2933 efs-process-result-line line))) | |
2934 | |
2935 ((string-match efs-server-confused-msgs line) | |
2936 (setq efs-process-server-confused t)) | |
2937 | |
2938 ((string-match efs-good-msgs line) | |
2939 (setq efs-process-busy nil | |
2940 efs-process-result nil | |
2941 efs-process-result-line line)) | |
2942 | |
2943 ((string-match efs-fatal-msgs line) | |
2944 (set-process-sentinel proc nil) | |
2945 (delete-process proc) | |
2946 (setq efs-process-busy nil | |
2947 efs-process-result 'fatal | |
2948 efs-process-result-line line)) | |
2949 | |
2950 ((string-match efs-failed-msgs line) | |
2951 (setq efs-process-busy nil | |
2952 efs-process-result 'failed | |
2953 efs-process-result-line line)) | |
2954 | |
2955 ((string-match efs-unknown-response-msgs line) | |
2956 (setq efs-process-busy nil | |
2957 efs-process-result 'weird | |
2958 efs-process-result-line line) | |
2959 (efs-process-scream-and-yell line)))) | |
2960 | |
2961 (efs-define-fun efs-process-log-string (proc str) | |
2962 ;; For a given PROCESS, log the given STRING at the end of its | |
2963 ;; associated buffer. | |
2964 (let ((buff (get-buffer (process-buffer proc)))) | |
2965 (if buff | |
2966 (efs-save-buffer-excursion | |
2967 (set-buffer buff) | |
2968 (comint-output-filter proc str))))) | |
2969 | |
2970 (defun efs-process-filter (proc str) | |
2971 ;; Build up a complete line of output from the ftp PROCESS and pass it | |
2972 ;; on to efs-process-handle-line to deal with. | |
2973 (let ((inhibit-quit t) | |
2974 (buffer (get-buffer (process-buffer proc))) | |
2975 (efs-default-directory default-directory)) | |
2976 | |
2977 ;; see if the buffer is still around... it could have been deleted. | |
2978 (if buffer | |
2979 (efs-save-buffer-excursion | |
2980 (set-buffer (process-buffer proc)) | |
2981 (efs-save-match-data | |
2982 | |
2983 ;; handle hash mark printing | |
2984 (if efs-process-busy | |
2985 (setq str (efs-process-handle-hash str) | |
2986 efs-process-string (concat efs-process-string str))) | |
2987 (efs-process-log-string proc str) | |
2988 (while (and efs-process-busy | |
2989 (string-match "\n" efs-process-string)) | |
2990 (let ((line (substring efs-process-string | |
2991 0 | |
2992 (match-beginning 0)))) | |
2993 (setq efs-process-string (substring | |
2994 efs-process-string | |
2995 (match-end 0))) | |
2996 ;; If we are in synch with the client, we should | |
2997 ;; never get prompts in the wrong place. Just to be safe, | |
2998 ;; chew them off. | |
2999 (while (string-match efs-process-prompt-regexp line) | |
3000 (setq line (substring line (match-end 0)))) | |
3001 (efs-process-handle-line line proc))) | |
3002 | |
3003 ;; has the ftp client finished? if so then do some clean-up | |
3004 ;; actions. | |
3005 (if (not efs-process-busy) | |
3006 (progn | |
3007 (efs-correct-hash-mark-size) | |
3008 ;; reset process-kill-without-query | |
3009 (process-kill-without-query proc) | |
3010 ;; issue the "done" message since we've finished. | |
3011 (if (and efs-process-msg | |
3012 (efs-message-p) | |
3013 (null efs-process-result)) | |
3014 (progn | |
3015 | |
3016 (efs-message "%s...done" efs-process-msg) | |
3017 (setq efs-process-msg nil))) | |
3018 | |
3019 (if (and efs-process-nowait | |
3020 (null efs-process-cmd-waiting)) | |
3021 | |
3022 (progn | |
3023 ;; Is there a continuation we should be calling? | |
3024 ;; If so, we'd better call it, making sure we | |
3025 ;; only call it once. | |
3026 (if efs-process-continue | |
3027 (let ((cont efs-process-continue)) | |
3028 (setq efs-process-continue nil) | |
3029 (efs-call-cont | |
3030 cont | |
3031 efs-process-result | |
3032 efs-process-result-line | |
3033 efs-process-result-cont-lines))) | |
3034 ;; If the cmd was run asynch, run the next | |
3035 ;; cmd from the queue. For synch cmds, this | |
3036 ;; is done by efs-send-cmd. For asynch | |
3037 ;; cmds we don't care about | |
3038 ;; efs-nested-cmd, since nothing is | |
3039 ;; waiting for the cmd to complete. If | |
3040 ;; efs-process-cmd-waiting is t, exit | |
3041 ;; to let this command run. | |
3042 (if (and efs-process-q | |
3043 ;; Be careful to check efs-process-busy | |
3044 ;; again, because the cont may have started | |
3045 ;; some new ftp action. | |
3046 ;; wheels within wheels... | |
3047 (null efs-process-busy)) | |
3048 (let ((next (car efs-process-q))) | |
3049 (setq efs-process-q | |
3050 (cdr efs-process-q)) | |
3051 (apply 'efs-send-cmd | |
3052 efs-process-host | |
3053 efs-process-user | |
3054 next)))) | |
3055 | |
3056 (if efs-process-continue | |
3057 (let ((cont efs-process-continue)) | |
3058 (setq efs-process-continue nil) | |
3059 (efs-call-cont | |
3060 cont | |
3061 efs-process-result | |
3062 efs-process-result-line | |
3063 efs-process-result-cont-lines)))) | |
3064 | |
3065 ;; Update the mode line | |
3066 ;; We can't test nowait to see if we changed the | |
3067 ;; modeline in the first place, because conts | |
3068 ;; may be running now, which will confuse the issue. | |
3069 ;; The logic is simpler if we update the modeline | |
3070 ;; before the cont, but then the user sees the | |
3071 ;; modeline track the cont execution. It's dizzying. | |
3072 (if (and (or efs-mode-line-format | |
3073 efs-ftp-activity-function) | |
3074 (null efs-process-busy)) | |
3075 (efs-update-mode-line))))) | |
3076 | |
3077 ;; Trim buffer, if required. | |
3078 (and efs-max-ftp-buffer-size | |
3079 (zerop efs-process-cmd-counter) | |
3080 (> (point-max) efs-max-ftp-buffer-size) | |
3081 (= (point-min) 1) ; who knows, the user may have narrowed. | |
3082 (null (get-buffer-window (current-buffer))) | |
3083 (save-excursion | |
3084 (goto-char (/ efs-max-ftp-buffer-size 2)) | |
3085 (forward-line 1) | |
3086 (delete-region (point-min) (point)))))))) | |
3087 | |
3088 ;;;; ------------------------------------------------------------------ | |
3089 ;;;; Functions for counting hashes and reporting on bytes transferred. | |
3090 ;;;; ------------------------------------------------------------------ | |
3091 | |
3092 (defun efs-set-xfer-size (host user bytes) | |
3093 ;; Set the size of the next FTP transfer in bytes. | |
3094 (let ((proc (efs-get-process host user))) | |
3095 (if proc | |
3096 (let ((buf (process-buffer proc))) | |
3097 (if buf | |
3098 (save-excursion | |
3099 (set-buffer buf) | |
3100 (setq efs-process-xfer-size (ash bytes -10)))))))) | |
3101 | |
3102 (defun efs-guess-incoming-bin-hm-size () | |
3103 ;; Guess at the hash mark size for incoming binary transfers by taking | |
3104 ;; the average value for such transfers to other hosts. | |
3105 (let ((total 0) | |
3106 (n 0)) | |
3107 (efs-map-hashtable | |
3108 (function | |
3109 (lambda (host hm-size) | |
3110 (if hm-size (setq total (+ total hm-size) | |
3111 n (1+ n))))) | |
3112 efs-host-hashtable | |
3113 'incoming-bin-hm-size) | |
3114 (and (> n 0) (/ total n)))) | |
3115 | |
3116 (defun efs-set-hash-mark-unit (host user &optional incoming) | |
3117 ;; Sets the value of efs-process-hash-mark-unit according to the xfer-type. | |
3118 ;; efs-hash-mark-unit is the number of bytes represented by a hash mark, | |
3119 ;; in units of 16. If INCOMING is non-nil, the xfer will be a GET. | |
3120 (if efs-send-hash | |
3121 (let ((buff (efs-ftp-process-buffer host user)) | |
3122 (gate-p (efs-use-gateway-p host t))) | |
3123 (if buff | |
3124 (save-excursion | |
3125 (set-buffer buff) | |
3126 (setq efs-process-hash-mark-unit | |
3127 (ash (or | |
3128 (and incoming (eq efs-process-xfer-type 'image) | |
3129 (or (efs-get-host-property | |
3130 host 'incoming-bin-hm-size) | |
3131 (if gate-p | |
3132 efs-gateway-incoming-binary-hm-size | |
3133 efs-incoming-binary-hm-size) | |
3134 (let ((guess | |
3135 (efs-guess-incoming-bin-hm-size))) | |
3136 (and guess | |
3137 (efs-set-host-property | |
3138 host 'incoming-bin-hm-size | |
3139 guess))))) | |
3140 (if gate-p | |
3141 efs-gateway-hash-mark-size | |
3142 efs-hash-mark-size) | |
3143 1024) ; make sure that we have some integer | |
3144 -4))))))) | |
3145 | |
3146 (defun efs-correct-hash-mark-size () | |
3147 ;; Corrects the value of efs-{ascii,binary}-hash-mark-size. | |
3148 ;; Must be run in the process buffer. | |
3149 (and efs-send-hash | |
3150 efs-process-hash-mark-unit | |
3151 (> efs-process-xfer-size 0) | |
3152 (< efs-process-xfer-size 524288) ; 2^19, prevent overflows | |
3153 (> efs-process-hash-mark-count 0) | |
3154 (or (> efs-process-last-percent 100) | |
3155 (< (ash (* efs-process-hash-mark-unit | |
3156 (1+ efs-process-hash-mark-count )) -6) | |
3157 efs-process-xfer-size)) | |
3158 (let ((val (ash (/ (ash efs-process-xfer-size 6) | |
3159 efs-process-hash-mark-count) 4))) | |
3160 (if (and (eq efs-process-xfer-type 'image) | |
3161 (>= (length efs-process-cmd) 4) | |
3162 (string-equal (downcase (substring efs-process-cmd 0 4)) | |
3163 "get ")) | |
3164 (efs-set-host-property efs-process-host 'incoming-bin-hm-size val) | |
3165 (set (if (efs-use-gateway-p efs-process-host t) | |
3166 'efs-gateway-hash-mark-size | |
3167 'efs-hash-mark-size) | |
3168 val))))) | |
3169 | |
3170 (defun efs-process-handle-hash (str) | |
3171 ;; Remove hash marks from STRING and display count so far. | |
3172 (if (string-match "^#+$" str) | |
3173 (progn | |
3174 (setq efs-process-hash-mark-count | |
3175 (+ efs-process-hash-mark-count | |
3176 (- (match-end 0) (match-beginning 0)))) | |
3177 (and | |
3178 efs-process-msg | |
3179 efs-process-hash-mark-unit | |
3180 (not (and efs-process-nowait | |
3181 (or (eq efs-verbose 0) | |
3182 (eq (selected-window) (minibuffer-window))))) | |
3183 (efs-message-p) | |
3184 (let* ((big (> efs-process-hash-mark-count 65536)) ; 2^16 | |
3185 (kbytes (if big | |
3186 (* efs-process-hash-mark-unit | |
3187 (ash efs-process-hash-mark-count -6)) | |
3188 (ash (* efs-process-hash-mark-unit | |
3189 efs-process-hash-mark-count) | |
3190 -6)))) | |
3191 (if (zerop efs-process-xfer-size) | |
3192 (or (zerop kbytes) | |
3193 (efs-message "%s...%dk" efs-process-msg kbytes)) | |
3194 (let ((percent (if big | |
3195 (/ (* 100 (ash kbytes -7)) | |
3196 (ash efs-process-xfer-size -7)) | |
3197 (/ (* 100 kbytes) efs-process-xfer-size)))) | |
3198 ;; Don't display %'s betwwen 100 and 110 | |
3199 (and (> percent 100) (< percent 110) (setq percent 100)) | |
3200 ;; cut out the redisplay of identical %-age messages. | |
3201 (or (eq percent efs-process-last-percent) | |
3202 (progn | |
3203 (setq efs-process-last-percent percent) | |
3204 (efs-message "%s...%d%%" efs-process-msg percent))))))) | |
3205 (concat (substring str 0 (match-beginning 0)) | |
3206 (and (/= (length str) (match-end 0)) | |
3207 (substring str (1+ (match-end 0)))))) | |
3208 str)) | |
3209 | |
3210 ;;;; ------------------------------------------------------------------ | |
3211 ;;;; Keeping track of the number of active background connections. | |
3212 ;;;; ------------------------------------------------------------------ | |
3213 | |
3214 (defun efs-ftp-processes-active () | |
3215 ;; Return the number of FTP processes busy. | |
3216 (save-excursion | |
3217 (length | |
3218 (delq nil | |
3219 (mapcar | |
3220 (function | |
3221 (lambda (buff) | |
3222 (set-buffer buff) | |
3223 (and (boundp 'efs-process-busy) | |
3224 efs-process-busy))) | |
3225 (buffer-list)))))) | |
3226 | |
3227 (defun efs-update-mode-line () | |
3228 ;; Updates the mode with FTP activity, and runs `efs-ftp-activity-function'. | |
3229 (let ((num (efs-ftp-processes-active))) | |
3230 (if efs-mode-line-format | |
3231 (progn | |
3232 (if (zerop num) | |
3233 (setq efs-mode-line-string "") | |
3234 (setq efs-mode-line-string (format efs-mode-line-format num))) | |
3235 ;; fake emacs into re-calculating all the mode lines. | |
3236 (save-excursion (set-buffer (other-buffer))) | |
3237 (set-buffer-modified-p (buffer-modified-p)))) | |
3238 (if efs-ftp-activity-function | |
3239 (funcall efs-ftp-activity-function num)))) | |
3240 | |
3241 (defun efs-display-ftp-activity () | |
3242 "Displays the number of active background ftp sessions. | |
3243 Uses the variable `efs-mode-line-format' to determine how this will be | |
3244 displayed." | |
3245 (interactive) | |
3246 (or (memq 'efs-mode-line-string global-mode-string) | |
3247 (if global-mode-string | |
3248 (nconc global-mode-string '(efs-mode-line-string)) | |
3249 (setq global-mode-string '("" efs-mode-line-string))))) | |
3250 | |
3251 ;;;; ------------------------------------------------------------------- | |
3252 ;;;; Expiring inactive ftp buffers. | |
3253 ;;;; ------------------------------------------------------------------- | |
3254 | |
3255 (defun efs-start-polling () | |
3256 ;; Start polling FTP buffers, to look for idle ones. | |
3257 (or (null efs-expire-ftp-buffers) | |
3258 (let ((proc (get-process "efs poll"))) | |
3259 (or (and proc (eq (process-status proc) 'run)))) | |
3260 (let ((default-directory exec-directory) | |
3261 (process-connection-type nil) | |
3262 new-proc) | |
3263 (condition-case nil | |
3264 (delete-process "efs poll") | |
3265 (error nil)) | |
3266 (setq new-proc (start-process | |
3267 "efs poll" nil | |
3268 (concat exec-directory "wakeup") | |
3269 (int-to-string efs-ftp-buffer-poll-time))) | |
3270 (set-process-filter new-proc (function efs-expire-ftp-buffers-filter)) | |
3271 (process-kill-without-query new-proc)))) | |
3272 | |
3273 (defun efs-connection-visited-p (host user) | |
3274 ;; Returns t if there are any buffers visiting files on HOST and USER. | |
3275 (save-excursion | |
3276 (let ((list (buffer-list)) | |
3277 (case-fold (memq (efs-host-type host) | |
3278 efs-case-insensitive-host-types)) | |
3279 (visited nil) | |
3280 parsed) | |
3281 (setq host (downcase host)) | |
3282 (if case-fold (setq user (downcase user))) | |
3283 (while list | |
3284 (set-buffer (car list)) | |
3285 (if (or (and buffer-file-name | |
3286 (setq parsed (efs-ftp-path buffer-file-name)) | |
3287 (string-equal host (downcase (car parsed))) | |
3288 (string-equal user (if case-fold | |
3289 (downcase (nth 1 parsed)) | |
3290 (nth 1 parsed)))) | |
3291 (and (boundp 'dired-directory) | |
3292 (stringp dired-directory) | |
3293 efs-dired-host-type | |
3294 (setq parsed (efs-ftp-path dired-directory)) | |
3295 (string-equal host (downcase (car parsed))) | |
3296 (string-equal user (if case-fold | |
3297 (downcase (nth 1 parsed)) | |
3298 (nth 1 parsed))))) | |
3299 (setq visited t | |
3300 list nil) | |
3301 (setq list (cdr list)))) | |
3302 visited))) | |
3303 | |
3304 (defun efs-expire-ftp-buffers-filter (proc string) | |
3305 ;; Check all ftp buffers, and kill them if they have been inactive | |
3306 ;; for the minimum of efs-ftp-buffer-expire-time and their local | |
3307 ;; time out time. | |
3308 (if efs-expire-ftp-buffers | |
3309 (let ((list (buffer-list)) | |
3310 new-alist) | |
3311 (save-excursion | |
3312 (while list | |
3313 (set-buffer (car list)) | |
3314 (if (eq major-mode 'efs-mode) | |
3315 (let* ((proc (get-buffer-process (current-buffer))) | |
3316 (proc-p (and proc (memq (process-status proc) | |
3317 '(run open))))) | |
3318 (if (or efs-ftp-buffer-expire-time | |
3319 efs-process-idle-time | |
3320 (null proc-p)) | |
3321 (let ((elt (assq (car list) efs-ftp-buffer-alist)) | |
3322 (wind-p (get-buffer-window (car list)))) | |
3323 (if (or (null elt) (buffer-modified-p) | |
3324 efs-process-busy wind-p) | |
3325 (progn | |
3326 (setq new-alist (cons (cons (car list) 0) | |
3327 new-alist)) | |
3328 (or wind-p (set-buffer-modified-p nil))) | |
3329 (let ((idle (+ (cdr elt) | |
3330 efs-ftp-buffer-poll-time))) | |
3331 (if (and proc-p | |
3332 (< idle | |
3333 (if efs-ftp-buffer-expire-time | |
3334 (if efs-process-idle-time | |
3335 (min efs-ftp-buffer-expire-time | |
3336 efs-process-idle-time) | |
3337 efs-ftp-buffer-expire-time) | |
3338 efs-process-idle-time))) | |
3339 (progn | |
3340 (setq new-alist (cons (cons (car list) idle) | |
3341 new-alist)) | |
3342 (set-buffer-modified-p nil)) | |
3343 ;; If there are still buffers for host & user, | |
3344 ;; don't wipe the cache. | |
3345 (and proc | |
3346 (efs-connection-visited-p | |
3347 efs-process-host efs-process-user) | |
3348 (set-process-sentinel proc nil)) | |
3349 (kill-buffer (car list))))))))) | |
3350 (setq list (cdr list)))) | |
3351 (setq efs-ftp-buffer-alist new-alist)) | |
3352 (condition-case nil | |
3353 (delete-process "efs poll") | |
3354 (error nil)))) | |
3355 | |
3356 ;;;; ------------------------------------------------------------------- | |
3357 ;;;; When the FTP client process dies... | |
3358 ;;;; ------------------------------------------------------------------- | |
3359 | |
3360 (defun efs-process-sentinel (proc str) | |
3361 ;; When ftp process changes state, nuke all file-entries in cache. | |
3362 (let ((buff (process-buffer proc))) | |
3363 ;; If the client dies, make sure that efs doesn't think that | |
3364 ;; there is a running process. | |
3365 (save-excursion | |
3366 (condition-case nil | |
3367 (progn | |
3368 (set-buffer buff) | |
3369 (setq efs-process-busy nil)) | |
3370 (error nil))) | |
3371 (let ((parsed (efs-parse-proc-name proc))) | |
3372 (if parsed | |
3373 (progn | |
3374 (apply 'efs-wipe-file-entries parsed) | |
3375 (apply 'efs-wipe-from-ls-cache parsed)))) | |
3376 (if (or efs-mode-line-format efs-ftp-activity-function) | |
3377 (efs-update-mode-line)))) | |
3378 | |
3379 (defun efs-kill-ftp-process (buffer) | |
3380 "Kill an FTP connection and its associated process buffer. | |
3381 If the BUFFER's visited file name or default-directory is an efs remote | |
3382 file name, it is the connection for that file name that is killed." | |
3383 (interactive "bKill FTP process associated with buffer: ") | |
3384 (or buffer (setq buffer (current-buffer))) | |
3385 (save-excursion | |
3386 (set-buffer buffer) | |
3387 (if (eq major-mode 'efs-mode) | |
3388 (kill-buffer buffer) | |
3389 (let ((file (or (buffer-file-name) default-directory))) | |
3390 (if file | |
3391 (let ((parsed (efs-ftp-path (expand-file-name file)))) | |
3392 (if parsed | |
3393 (let ((host (nth 0 parsed)) | |
3394 (user (nth 1 parsed))) | |
3395 (kill-buffer | |
3396 (efs-ftp-process-buffer host user)))))))))) | |
3397 | |
3398 (defun efs-close-ftp-process (buffer) | |
3399 "Close an FTP connection. | |
3400 This kills the FTP client process, but unlike `efs-kill-ftp-process' this | |
3401 neither kills the process buffer, nor deletes cached data for the connection." | |
3402 (interactive "bClose FTP process associated with buffer: ") | |
3403 (or buffer (setq buffer (current-buffer))) | |
3404 (save-excursion | |
3405 (set-buffer buffer) | |
3406 (if (eq major-mode 'efs-mode) | |
3407 (let ((process (get-buffer-process buffer))) | |
3408 (if process | |
3409 (progn | |
3410 (set-process-sentinel process nil) | |
3411 (setq efs-process-busy nil | |
3412 efs-process-q nil) | |
3413 (if (or efs-mode-line-format efs-ftp-activity-function) | |
3414 (efs-update-mode-line)) | |
3415 (delete-process process)))) | |
3416 (let ((file (or (buffer-file-name) default-directory))) | |
3417 (if file | |
3418 (let ((parsed (efs-ftp-path (expand-file-name file)))) | |
3419 (if parsed | |
3420 (let ((process (get-process | |
3421 (format "*ftp %s@%s*" | |
3422 (nth 1 parsed) (car parsed))))) | |
3423 (if process | |
3424 (progn | |
3425 (set-buffer (process-buffer process)) | |
3426 (set-process-sentinel process nil) | |
3427 (setq efs-process-busy nil | |
3428 efs-process-q nil) | |
3429 (if (or efs-mode-line-format | |
3430 efs-ftp-activity-function) | |
3431 (efs-update-mode-line)) | |
3432 (delete-process process))))))))))) | |
3433 | |
3434 (defun efs-ping-ftp-connection (buffer) | |
3435 "Ping a connection by sending a NOOP command. | |
3436 Useful for waking up a possible expired connection." | |
3437 (interactive "bPing FTP connection associated with buffer: ") | |
3438 (or buffer (setq buffer (current-buffer))) | |
3439 (efs-save-buffer-excursion | |
3440 (set-buffer buffer) | |
3441 (let (file host user parsed) | |
3442 (if (or (and (eq major-mode 'efs-mode) | |
3443 (setq host efs-process-host | |
3444 user efs-process-user)) | |
3445 (and (setq file (or (buffer-file-name) default-directory)) | |
3446 (setq parsed (efs-ftp-path file)) | |
3447 (setq host (car parsed) | |
3448 user (nth 1 parsed)))) | |
3449 (or (car | |
3450 (efs-send-cmd | |
3451 host user '(quote noop) | |
3452 (format "Pinging connection %s@%s" user host))) | |
3453 (message "Connection %s@%s is alive." user host)))))) | |
3454 | |
3455 (defun efs-display-ftp-process-buffer (buffer) | |
3456 "Displays the FTP process buffer associated with the current buffer." | |
3457 (interactive "bDisplay FTP buffer associated with buffer: ") | |
3458 (if (null buffer) (setq buffer (current-buffer))) | |
3459 (let ((file (or (buffer-file-name) default-directory)) | |
3460 parsed proc-buffer) | |
3461 (if (and file (setq parsed (efs-ftp-path file)) | |
3462 (setq proc-buffer (get-buffer (efs-ftp-process-buffer | |
3463 (car parsed) | |
3464 (nth 1 parsed))))) | |
3465 (display-buffer proc-buffer) | |
3466 (error "Buffer %s not associated with an FTP process" buffer)))) | |
3467 | |
3468 ;;;; ------------------------------------------------------------------- | |
3469 ;;;; Starting the FTP client process | |
3470 ;;;; ------------------------------------------------------------------- | |
3471 | |
3472 (defun efs-ftp-process-buffer (host user) | |
3473 "Return name of the process buffer for ftp process for HOST and USER." | |
3474 ;; Host names on the internet are case-insensitive. | |
3475 (format efs-ftp-buffer-format user (downcase host))) | |
3476 | |
3477 (defun efs-pty-check (proc threshold) | |
3478 ;; Checks to see if PROC is a pty. Beware, it clobbers the process | |
3479 ;; filter, so run this before you set the filter. | |
3480 ;; THRESHOLD is an integer to tell it how long to wait for output. | |
3481 (sit-for 0) ; Update the display before doing any waiting. | |
3482 (let ((efs-pipe-p t) | |
3483 (n 0)) | |
3484 (set-process-filter proc (function (lambda (proc string) | |
3485 (setq efs-pipe-p nil)))) | |
3486 (while (and (< n threshold) efs-pipe-p) | |
3487 (accept-process-output) | |
3488 (setq n (1+ n))) | |
3489 (if efs-pipe-p | |
3490 (progn | |
3491 (sit-for 0) ; update display | |
3492 ;; Use a sleep-for as I don't want pty-checking to depend | |
3493 ;; on pending input. | |
3494 (sleep-for efs-pty-check-retry-time))) | |
3495 (accept-process-output) | |
3496 (if efs-pipe-p | |
3497 (if (or noninteractive | |
3498 (progn | |
3499 ;; in case the user typed something during the wait. | |
3500 (discard-input) | |
3501 (y-or-n-p | |
3502 (format "%s seems not a pty. Kill? " proc)))) | |
3503 (progn | |
3504 (kill-buffer (process-buffer proc)) | |
3505 (if (eq (selected-window) (minibuffer-window)) | |
3506 (abort-recursive-edit) | |
3507 (signal 'quit nil)))) | |
3508 ;; Need to send a \n to make sure, because sometimes we get the startup | |
3509 ;; prompt from a pipe. | |
3510 (sit-for 0) | |
3511 (process-send-string proc "\n") | |
3512 (setq efs-pipe-p t | |
3513 n 0) | |
3514 (while (and (< n threshold) efs-pipe-p) | |
3515 (accept-process-output) | |
3516 (setq n (1+ n))) | |
3517 (if efs-pipe-p | |
3518 (progn | |
3519 (sit-for 0) | |
3520 (sleep-for efs-pty-check-retry-time))) | |
3521 (accept-process-output) | |
3522 (if (and efs-pipe-p | |
3523 (or noninteractive | |
3524 (progn | |
3525 ;; in case the user typed something during the wait. | |
3526 (discard-input) | |
3527 (y-or-n-p | |
3528 (format "%s seems not a pty. Kill? " proc))))) | |
3529 (progn | |
3530 (kill-buffer (process-buffer proc)) | |
3531 (if (eq (selected-window) (minibuffer-window)) | |
3532 (abort-recursive-edit) | |
3533 (signal 'quit nil))))))) | |
3534 | |
3535 (defun efs-start-process (host user name) | |
3536 "Spawn a new ftp process ready to connect to machine HOST as USER. | |
3537 If HOST is only ftp-able through a gateway machine then spawn a shell | |
3538 on the gateway machine to do the ftp instead. NAME is the name of the | |
3539 process." | |
3540 (let* ((use-gateway (efs-use-gateway-p host)) | |
3541 (buffer (get-buffer-create (efs-ftp-process-buffer host user))) | |
3542 (process-connection-type t) | |
3543 (opaque-p (memq use-gateway efs-opaque-gateways)) | |
3544 proc) | |
3545 (save-excursion | |
3546 (set-buffer buffer) | |
3547 (efs-mode host user (if opaque-p | |
3548 efs-gateway-ftp-prompt-regexp | |
3549 efs-ftp-prompt-regexp))) | |
3550 (cond | |
3551 ((null use-gateway) | |
3552 (message "Opening FTP connection to %s..." host) | |
3553 (setq proc (apply 'start-process name buffer efs-ftp-program-name | |
3554 efs-ftp-program-args))) | |
3555 ((eq use-gateway 'interactive) | |
3556 (setq proc (efs-gwp-start host user name))) | |
3557 ((eq use-gateway 'remsh) | |
3558 (message "Opening FTP connection to %s via %s..." host efs-gateway-host) | |
3559 (setq proc (apply 'start-process name buffer (nth 1 efs-gateway-type) | |
3560 (append (list efs-gateway-host) | |
3561 (nth 2 efs-gateway-type) | |
3562 (list (nth 3 efs-gateway-type)) | |
3563 (nth 4 efs-gateway-type))))) | |
3564 ((memq use-gateway '(proxy raptor interlock kerberos)) | |
3565 (message "Opening FTP connection to %s via %s..." host efs-gateway-host) | |
3566 (setq proc (apply 'start-process name buffer (nth 1 efs-gateway-type) | |
3567 (nth 2 efs-gateway-type)))) | |
3568 ((eq use-gateway 'local) | |
3569 (message "Opening FTP connection to %s..." host) | |
3570 (setq proc (apply 'start-process name buffer (nth 1 efs-gateway-type) | |
3571 (nth 2 efs-gateway-type)))) | |
3572 ((error "Never heard of gateway type %s" use-gateway))) | |
3573 (process-kill-without-query proc) | |
3574 (if opaque-p | |
3575 (accept-process-output proc) | |
3576 (if efs-pty-check-threshold | |
3577 (efs-pty-check proc efs-pty-check-threshold) | |
3578 (accept-process-output proc))) | |
3579 (set-process-sentinel proc (function efs-process-sentinel)) | |
3580 (set-process-filter proc (function efs-process-filter)) | |
3581 (efs-start-polling) | |
3582 (save-excursion | |
3583 (set-buffer buffer) | |
3584 (goto-char (point-max)) | |
3585 (set-marker (process-mark proc) (point))) | |
3586 proc)) | |
3587 | |
3588 (defun efs-get-process-internal (host user) | |
3589 ;; Get's the first process for HOST and USER. If HOST runs a | |
3590 ;; a case insignificant OS, then case is not considered in USER. | |
3591 (let ((list (process-list)) | |
3592 (case-fold (memq (efs-host-type host) | |
3593 efs-case-insensitive-host-types)) | |
3594 (len (+ (length host) (length user) 7)) | |
3595 fmt name found) | |
3596 (setq host (downcase host)) | |
3597 (if case-fold (setq user (downcase user))) | |
3598 (while (and (not found) list) | |
3599 (setq name (process-name (car list))) | |
3600 (if (and (= (length name) len) | |
3601 (string-equal (substring name 0 5) "*ftp ") | |
3602 (string-equal | |
3603 (if case-fold (downcase (substring name 5)) (substring name 5)) | |
3604 (or fmt (setq fmt (format "%s@%s*" user host)))) | |
3605 (memq (process-status (car list)) '(run open))) | |
3606 (setq found (car list)) | |
3607 (setq list (cdr list)))) | |
3608 found)) | |
3609 | |
3610 ;; efs-guess-host-type calls this | |
3611 ;; function recursively. The (if (and proc... avoids an infinite | |
3612 ;; loop. We should make sure that this won't hang things if the | |
3613 ;; connection goes wrong. | |
3614 | |
3615 (defun efs-get-process (host user) | |
3616 "Return the process object for the FTP process for HOST and USER. | |
3617 Create a new process if needed." | |
3618 | |
3619 (let ((proc (efs-get-process-internal host user))) | |
3620 (if (and proc (memq (process-status proc) '(run open))) | |
3621 proc | |
3622 | |
3623 ;; Make sure that the process isn't around in some strange state. | |
3624 | |
3625 (setq host (downcase host)) | |
3626 (let ((name (concat "*ftp " user "@" host "*"))) | |
3627 (if proc (condition-case nil (delete-process proc) (error nil))) | |
3628 | |
3629 ;; grab a suitable process. | |
3630 (setq proc (efs-start-process host user name)) | |
3631 | |
3632 (efs-save-match-data | |
3633 (efs-save-buffer-excursion | |
3634 (set-buffer (process-buffer proc)) | |
3635 | |
3636 ;; Run any user-specified hooks. | |
3637 (run-hooks 'efs-ftp-startup-hook) | |
3638 | |
3639 ;; login to FTP server. | |
3640 (efs-login host user proc) | |
3641 | |
3642 ;; Beware, the process may have died if the login went bad. | |
3643 (if (memq (process-status proc) '(run open)) | |
3644 | |
3645 (progn | |
3646 ;; Tell client to send back hash-marks as progress. It isn't | |
3647 ;; usually fatal if this command fails. | |
3648 (efs-guess-hash-mark-size proc) | |
3649 | |
3650 ;; Run any user startup functions | |
3651 (let ((alist efs-ftp-startup-function-alist) | |
3652 (case-fold-search t)) | |
3653 (while alist | |
3654 (if (string-match (car (car alist)) host) | |
3655 (progn | |
3656 (funcall (cdr (car alist)) host user) | |
3657 (setq alist nil)) | |
3658 (setq alist (cdr alist))))) | |
3659 | |
3660 ;; Guess at the host type. | |
3661 (efs-guess-host-type host user) | |
3662 | |
3663 ;; Check the idle time. | |
3664 (efs-check-idle host user) | |
3665 | |
3666 proc) | |
3667 | |
3668 ;; Hopefully a recursive retry worked. | |
3669 (or (efs-get-process-internal host user) | |
3670 (error "No FTP process for %s@%s" user host))))))))) | |
3671 | |
3672 (defun efs-guess-hash-mark-size (proc) | |
3673 ;; Doesn't run efs-save-match-data. You must do that yourself. | |
3674 (if efs-send-hash | |
3675 (save-excursion | |
3676 (set-buffer (process-buffer proc)) | |
3677 (let ((line (nth 1 (efs-raw-send-cmd proc "hash"))) | |
3678 (gate-p (efs-use-gateway-p efs-process-host t))) | |
3679 ;; Don't guess if the hash-mark-size is already set. | |
3680 (or (if gate-p efs-gateway-hash-mark-size efs-hash-mark-size) | |
3681 (if (string-match efs-hash-mark-msgs line) | |
3682 (let ((size (substring line (match-beginning 1) | |
3683 (match-end 1)))) | |
3684 (if (string-match "^[0-9]+$" size) | |
3685 (set (if gate-p | |
3686 'efs-gateway-hash-mark-size | |
3687 'efs-hash-mark-size) | |
3688 (string-to-int size)))))))))) | |
3689 | |
3690 ;;;; ------------------------------------------------------------ | |
3691 ;;;; Simple FTP process shell support. | |
3692 ;;;; ------------------------------------------------------------ | |
3693 | |
3694 (defun efs-mode (host user prompt) | |
3695 "Major mode for interacting with an FTP process. | |
3696 The user interface for sending commands to the FTP process is `comint-mode'. | |
3697 For more information see the documentation for `comint-mode'. This command | |
3698 is not intended for interactive use. | |
3699 Takes arguments: HOST USER PROMPT | |
3700 | |
3701 Runs efs-mode-hook if it is not nil. | |
3702 | |
3703 Key map: | |
3704 \\{comint-mode-map}" | |
3705 (let ((proc (get-buffer-process (current-buffer)))) | |
3706 ;; Running comint-mode will kill-all-local-variables. | |
3707 (comint-mode) | |
3708 ;; All these variables are buffer local. | |
3709 (setq major-mode 'efs-mode | |
3710 mode-name "efs" | |
3711 default-directory (file-name-directory efs-tmp-name-template) | |
3712 comint-prompt-regexp prompt | |
3713 efs-process-host host | |
3714 efs-process-user user | |
3715 efs-process-prompt-regexp prompt) | |
3716 (set (make-local-variable 'paragraph-start) comint-prompt-regexp) | |
3717 ;; Old versions of comint don't have this. It does no harm for | |
3718 ;; the newer ones. | |
3719 (set (make-local-variable 'comint-last-input-start) (make-marker)) | |
3720 (goto-char (point-max)) | |
3721 ;; in case there is a running process | |
3722 (if proc (set-marker (process-mark proc) (point))) | |
3723 (run-hooks 'efs-mode-hook))) | |
3724 | |
3725 | |
3726 ;;;; ============================================================= | |
3727 ;;;; >6 | |
3728 ;;;; Sending commands to the FTP server. | |
3729 ;;;; ============================================================= | |
3730 | |
3731 ;;;; ------------------------------------------------------------- | |
3732 ;;;; General purpose functions for sending commands. | |
3733 ;;;; ------------------------------------------------------------- | |
3734 | |
3735 (defun efs-raw-send-cmd (proc cmd &optional msg pre-cont cont nowait) | |
3736 ;; Low-level routine to send the given ftp CMD to the ftp PROCESS. | |
3737 ;; MSG is an optional message to output before and after the command. | |
3738 ;; If PRE-CONT is non-nil, it is called immediately after execution | |
3739 ;; of the command starts, but without waiting for it to finish. | |
3740 ;; If CONT is non-NIL then it is either a function or a list of function and | |
3741 ;; some arguments. The function will be called when the ftp command has | |
3742 ;; completed. | |
3743 ;; If CONT is NIL then this routine will return \( RESULT . LINE \) where | |
3744 ;; RESULT is whether the command was successful, and LINE is the line from | |
3745 ;; the FTP process that caused the command to complete. | |
3746 ;; If NOWAIT is nil then we will wait for the command to complete before | |
3747 ;; returning. If NOWAIT is 0, then we will wait until the command starts, | |
3748 ;; executing before returning. NOWAIT of 1 is like 0, except that the modeline | |
3749 ;; will indicate an asynch FTP command. | |
3750 ;; If NOWAIT has any other value, then we will simply queue the | |
3751 ;; command. In all cases, CONT will still be called | |
3752 | |
3753 (if (memq (process-status proc) '(run open)) | |
3754 (efs-save-buffer-excursion | |
3755 (set-buffer (process-buffer proc)) | |
3756 | |
3757 (if efs-process-busy | |
3758 ;; This function will always wait on a busy process. | |
3759 ;; Queueing is done by efs-send-cmd. | |
3760 (let ((efs-process-cmd-waiting t)) | |
3761 (efs-kbd-quit-protect proc | |
3762 (while efs-process-busy | |
3763 (accept-process-output))))) | |
3764 | |
3765 (setq efs-process-string "" | |
3766 efs-process-result-line "" | |
3767 efs-process-result-cont-lines "" | |
3768 efs-process-busy t | |
3769 efs-process-msg (and efs-verbose msg) | |
3770 efs-process-continue cont | |
3771 efs-process-server-confused nil | |
3772 efs-process-nowait nowait | |
3773 efs-process-hash-mark-count 0 | |
3774 efs-process-last-percent -1 | |
3775 efs-process-xfer-size 0 | |
3776 efs-process-cmd-counter (% (1+ efs-process-cmd-counter) 16)) | |
3777 (process-kill-without-query proc t) | |
3778 (and efs-process-msg | |
3779 (efs-message-p) | |
3780 (efs-message "%s..." efs-process-msg)) | |
3781 (goto-char (point-max)) | |
3782 (move-marker comint-last-input-start (point)) | |
3783 (move-marker comint-last-input-end (point)) | |
3784 ;; don't insert the password into the buffer on the USER command. | |
3785 (efs-save-match-data | |
3786 (if (string-match efs-passwd-cmds cmd) | |
3787 (insert (setq efs-process-cmd | |
3788 (substring cmd 0 (match-end 0))) | |
3789 " Turtle Power!\n") | |
3790 (setq efs-process-cmd cmd) | |
3791 (insert cmd "\n"))) | |
3792 (process-send-string proc (concat cmd "\n")) | |
3793 (set-marker (process-mark proc) (point)) | |
3794 ;; Update the mode-line | |
3795 (if (and (or efs-mode-line-format efs-ftp-activity-function) | |
3796 (memq nowait '(t 1))) | |
3797 (efs-update-mode-line)) | |
3798 (if pre-cont | |
3799 (let ((efs-nested-cmd t)) | |
3800 (save-excursion | |
3801 (apply (car pre-cont) (cdr pre-cont))))) | |
3802 (prog1 | |
3803 (if nowait | |
3804 nil | |
3805 ;; hang around for command to complete | |
3806 ;; Some clients die after the command is sent, if the server | |
3807 ;; times out. Don't wait on dead processes. | |
3808 (efs-kbd-quit-protect proc | |
3809 (while (and efs-process-busy | |
3810 ;; Need to recheck nowait, since it may get reset | |
3811 ;; in a cont. | |
3812 (null efs-process-nowait) | |
3813 (memq (process-status proc) '(run open))) | |
3814 (accept-process-output proc))) | |
3815 | |
3816 ;; cont is called by the process filter | |
3817 (if cont | |
3818 ;; Return nil if a cont was called. | |
3819 ;; Can't return process-result | |
3820 ;; and process-line since executing | |
3821 ;; the cont may have changed | |
3822 ;; the state of the process buffer. | |
3823 nil | |
3824 (list efs-process-result | |
3825 efs-process-result-line | |
3826 efs-process-result-cont-lines))) | |
3827 | |
3828 ;; If the process died, the filter would have never got the chance | |
3829 ;; to call the cont. Try to jump start things. | |
3830 | |
3831 (if (and (not (memq (process-status proc) '(run open))) | |
3832 (string-equal efs-process-result-line "") | |
3833 cont | |
3834 (equal cont efs-process-continue)) | |
3835 (progn | |
3836 (setq efs-process-continue nil | |
3837 efs-process-busy nil) | |
3838 ;; The process may be in some strange state. Get rid of it. | |
3839 (condition-case nil (delete-process proc) (error nil)) | |
3840 (efs-call-cont cont 'fatal "" ""))))) | |
3841 | |
3842 (error "FTP process %s has died." (process-name proc)))) | |
3843 | |
3844 (efs-defun efs-quote-string nil (string &optional not-space) | |
3845 "Quote any characters in STRING that may confuse the ftp process. | |
3846 If NOT-SPACE is non-nil, then blank characters are not quoted, because | |
3847 it is assumed that the string will be surrounded by \"'s." | |
3848 (apply (function concat) | |
3849 (mapcar (function | |
3850 (lambda (char) | |
3851 (if (or (< char ?\ ) | |
3852 (and (null not-space) (= char ?\ )) | |
3853 (> char ?\~) | |
3854 (= char ?\") | |
3855 (= char ?\\)) | |
3856 (vector ?\\ char) | |
3857 (vector char)))) | |
3858 string))) | |
3859 | |
3860 (efs-defun efs-fix-path nil (path &optional reverse) | |
3861 "Convert PATH from a unix format to a non-unix format. | |
3862 If optional REVERSE, convert in the opposite direction." | |
3863 (identity path)) | |
3864 | |
3865 (efs-defun efs-fix-dir-path nil (dir-path) | |
3866 "Convert DIR-PATH from unix format to a non-unix format for a dir listing" | |
3867 ;; The default def runs for dos-distinct, ka9q, and all the unix's. | |
3868 ;; To be more careful about distinguishing dirs from plain files, | |
3869 ;; we append a ".". | |
3870 (let ((len (length dir-path))) | |
3871 (if (and (not (zerop len)) (= (aref dir-path (1- len)) ?/)) | |
3872 (concat dir-path ".") | |
3873 dir-path))) | |
3874 | |
3875 (defun efs-send-cmd (host user cmd | |
3876 &optional msg pre-cont cont nowait noretry) | |
3877 "Find an ftp process connected to HOST logged in as USER and send it CMD. | |
3878 MSG is an optional status message to be output before and after issuing the | |
3879 command. | |
3880 | |
3881 See the documentation for efs-raw-send-cmd for a description of CONT, PRE-CONT | |
3882 and NOWAIT. Normally, if the command fails it is retried. If NORETRY is | |
3883 non-nil, this is not done." | |
3884 ;; Handles conversion to remote pathname syntax and remote ls option | |
3885 ;; capability. Also, sends umask if nec. | |
3886 | |
3887 (let ((proc (efs-get-process host user))) | |
3888 | |
3889 (if (and | |
3890 (eq nowait t) | |
3891 (save-excursion | |
3892 (set-buffer (process-buffer proc)) | |
3893 (or efs-process-busy | |
3894 efs-process-cmd-waiting))) | |
3895 | |
3896 (progn | |
3897 (efs-add-to-queue | |
3898 host user | |
3899 ;; Not nec. to store host and user, because the queue is for | |
3900 ;; a specific host user pair anyway. Because the queue is always | |
3901 ;; examined when efs-process-busy | |
3902 ;; is nil, it should be impossible to get into a loop | |
3903 ;; where we keep re-queueing over and over. To be on the safe | |
3904 ;; side, store nowait as 1. | |
3905 (list cmd msg pre-cont cont 1 noretry)) | |
3906 nil) | |
3907 | |
3908 ;; Send a command. | |
3909 | |
3910 (let (cmd-string afsc-result afsc-line afsc-cont-lines) | |
3911 | |
3912 (let ((efs-nested-cmd t) | |
3913 (cmd0 (car cmd)) | |
3914 (cmd1 (nth 1 cmd)) | |
3915 (cmd2 (nth 2 cmd)) | |
3916 (cmd3 (nth 3 cmd))) | |
3917 | |
3918 (cond | |
3919 | |
3920 ((eq cmd0 'quote) | |
3921 ;; QUOTEd commands | |
3922 (cond | |
3923 | |
3924 ((eq cmd1 'site) | |
3925 ;; SITE commands | |
3926 (cond | |
3927 ((memq cmd2 '(umask idle dos exec nfs group gpass)) | |
3928 ;; For UMASK cmd3 = value of umask | |
3929 ;; For IDLE cmd3 = idle setting, or nil if we're querying. | |
3930 ;; For DOS and NFS cmd3 is nil. | |
3931 ;; For EXEC cmd3 is the command to be exec'ed -- a string. | |
3932 (if cmd3 (setq cmd3 (concat " " cmd3))) | |
3933 (setq cmd-string (concat "quote site " (symbol-name cmd2) | |
3934 cmd3))) | |
3935 ((eq cmd2 'chmod) | |
3936 (let* ((host-type (efs-host-type host user)) | |
3937 (cmd4 (efs-quote-string | |
3938 host-type (efs-fix-path host-type (nth 4 cmd))))) | |
3939 (setq cmd-string (concat "quote site chmod " cmd3 " " | |
3940 cmd4)))) | |
3941 (t (error "efs: Don't know how to send %s %s %s %s" | |
3942 cmd0 cmd1 cmd2 cmd3)))) | |
3943 | |
3944 ((memq cmd1 '(pwd xpwd syst pasv noop)) | |
3945 (setq cmd-string (concat "quote " (symbol-name cmd1)))) | |
3946 | |
3947 ;; PORT command (cmd2 is IP + port address) | |
3948 ((eq cmd1 'port) | |
3949 (setq cmd-string (concat "quote port " cmd2))) | |
3950 | |
3951 ((memq cmd1 '(appe retr)) | |
3952 (let ((host-type (efs-host-type host user))) | |
3953 ;; Set an xfer type | |
3954 (if cmd3 (efs-set-xfer-type host user cmd3 t)) | |
3955 (setq cmd2 (efs-quote-string host-type | |
3956 (efs-fix-path host-type cmd2)) | |
3957 cmd-string (concat "quote " (symbol-name cmd1) " " | |
3958 cmd2)))) | |
3959 | |
3960 ((eq cmd1 'stor) | |
3961 (let ((host-type (efs-host-type host user))) | |
3962 (if (memq host-type efs-unix-host-types) | |
3963 (efs-set-umask host user)) | |
3964 ;; Set an xfer type | |
3965 (if cmd3 (efs-set-xfer-type host user cmd3 t)) | |
3966 (setq cmd2 (efs-quote-string host-type | |
3967 (efs-fix-path host-type cmd2)) | |
3968 cmd-string (concat "quote stor " cmd2)))) | |
3969 | |
3970 ((memq cmd1 '(size mdtm rnfr)) | |
3971 (let ((host-type (efs-host-type host user))) | |
3972 (setq cmd2 (efs-quote-string host-type | |
3973 (efs-fix-path host-type cmd2)) | |
3974 cmd-string (concat "quote " | |
3975 (symbol-name cmd1) " " cmd2)))) | |
3976 | |
3977 ((memq cmd1 '(pass user)) | |
3978 (setq cmd-string (concat "quote " (symbol-name cmd1) " " cmd2))) | |
3979 | |
3980 (t | |
3981 (error "efs: Don't know how to send %s %s %s %s" | |
3982 cmd0 cmd1 cmd2 cmd3)))) | |
3983 | |
3984 ;; TYPE command | |
3985 ((eq cmd0 'type) | |
3986 (setq cmd-string (concat "type " (symbol-name cmd1)))) | |
3987 | |
3988 ;; DIR command | |
3989 ;; cmd == 'dir "remote-path" "local-path" "ls-switches" | |
3990 ((memq cmd0 '(dir nlist)) | |
3991 (let ((host-type (efs-host-type host user)) | |
3992 (listing-type (efs-listing-type host user))) | |
3993 (setq cmd1 (efs-fix-dir-path host-type cmd1)) | |
3994 (cond | |
3995 ((memq listing-type efs-nlist-listing-types) | |
3996 (setq cmd-string (concat efs-nlist-cmd " " | |
3997 (efs-quote-string host-type cmd1) | |
3998 " " cmd2))) | |
3999 ((or (memq host-type efs-dumb-host-types) | |
4000 (null cmd3)) | |
4001 (setq cmd-string (format "%s %s %s" | |
4002 (if (eq cmd0 'nlist) | |
4003 efs-nlist-cmd | |
4004 "dir") | |
4005 (efs-quote-string host-type cmd1) | |
4006 cmd2))) | |
4007 ((setq cmd-string | |
4008 (format "%s \"%s %s\" %s" | |
4009 (if (eq cmd0 'nlist) | |
4010 efs-nlist-cmd | |
4011 "ls") | |
4012 cmd3 (efs-quote-string host-type cmd1 t) | |
4013 ;; cmd2 is a temp file, not nec. to quote. | |
4014 cmd2)))))) | |
4015 | |
4016 ;; First argument is the remote pathname | |
4017 ((memq cmd0 '(delete mkdir rmdir cd)) | |
4018 (let ((host-type (efs-host-type host user))) | |
4019 (setq cmd1 (efs-quote-string host-type | |
4020 (efs-fix-path host-type cmd1)) | |
4021 cmd-string (concat (symbol-name cmd0) " " cmd1)))) | |
4022 | |
4023 ;; GET command | |
4024 ((eq cmd0 'get) | |
4025 (let ((host-type (efs-host-type host user))) | |
4026 (if cmd3 (efs-set-xfer-type host user cmd3)) | |
4027 (efs-set-hash-mark-unit host user t) | |
4028 (setq cmd1 (efs-quote-string host-type | |
4029 (efs-fix-path host-type cmd1)) | |
4030 cmd2 (efs-quote-string host-type cmd2) | |
4031 cmd-string (concat "get " cmd1 " " cmd2)))) | |
4032 | |
4033 ;; PUT command | |
4034 ((eq cmd0 'put) | |
4035 (let ((host-type (efs-host-type host user))) | |
4036 (if (memq host-type efs-unix-host-types) | |
4037 (efs-set-umask host user)) | |
4038 (if cmd3 (efs-set-xfer-type host user cmd3)) | |
4039 (efs-set-hash-mark-unit host user) | |
4040 (setq cmd2 (efs-quote-string host-type | |
4041 (efs-fix-path host-type cmd2)) | |
4042 cmd1 (efs-quote-string host-type cmd1) | |
4043 cmd-string (concat "put " cmd1 " " cmd2)))) | |
4044 | |
4045 ;; APPEND command | |
4046 ((eq cmd0 'append) | |
4047 (let ((host-type (efs-host-type host user))) | |
4048 (if cmd3 (efs-set-xfer-type host user cmd3)) | |
4049 (efs-set-hash-mark-unit host user) | |
4050 (setq cmd2 (efs-quote-string host-type | |
4051 (efs-fix-path host-type cmd2)) | |
4052 cmd1 (efs-quote-string host-type cmd1) | |
4053 cmd-string (concat "append " cmd1 " " cmd2)))) | |
4054 | |
4055 ;; CHMOD command | |
4056 ((eq cmd0 'chmod) | |
4057 (let ((host-type (efs-host-type host user))) | |
4058 (setq cmd2 (efs-quote-string host-type | |
4059 (efs-fix-path host-type cmd2)) | |
4060 cmd-string (concat "chmod " cmd1 " " cmd2)))) | |
4061 | |
4062 ;; Both arguments are remote pathnames | |
4063 ((eq cmd0 'rename) | |
4064 (let ((host-type (efs-host-type host user))) | |
4065 (setq cmd1 (efs-quote-string host-type | |
4066 (efs-fix-path host-type cmd1)) | |
4067 cmd2 (efs-quote-string host-type | |
4068 (efs-fix-path host-type cmd2)) | |
4069 cmd-string (concat "rename " cmd1 " " cmd2)))) | |
4070 | |
4071 (t | |
4072 (error "efs: Don't know how to send %s %s %s %s" | |
4073 cmd0 cmd1 cmd2 cmd3)))) | |
4074 | |
4075 ;; Actually send the resulting command. | |
4076 ;; Why do we use this complicated binding of afsc-{result,line}, | |
4077 ;; rather then use the fact that efs-raw-send-cmd returns? | |
4078 ;; Because efs-raw-send-cmd returns the result of the first | |
4079 ;; attempt only. efs-send-cmd should return the result of | |
4080 ;; the retry, if one was necessary. | |
4081 ;; Maybe it would be better if efs-raw-send-cmd returned | |
4082 ;; the result of cont, if nowait was nil? Or maybe still return | |
4083 ;; \(result line \)? As long as nowait is nil, it should | |
4084 ;; return something useful. | |
4085 | |
4086 ;; Beware, if some of the above FTP commands had to restart | |
4087 ;; the process, PROC won't be set to the right process object. | |
4088 (setq proc (efs-get-process host user)) | |
4089 | |
4090 (efs-raw-send-cmd | |
4091 proc | |
4092 cmd-string | |
4093 msg | |
4094 pre-cont | |
4095 (efs-cont (result line cont-lines) (host user proc cmd msg pre-cont | |
4096 cont nowait noretry) | |
4097 (cond ((and (null noretry) (eq result 'fatal)) | |
4098 (let ((retry | |
4099 (efs-send-cmd | |
4100 host user cmd msg pre-cont cont | |
4101 (if (eq nowait t) 1 nowait) t))) | |
4102 (or cont nowait | |
4103 (setq afsc-result (car retry) | |
4104 afsc-line (nth 1 retry) | |
4105 afsc-cont-lines (nth 2 retry))))) | |
4106 ((and (eq result 'failed) | |
4107 (or (memq (car cmd) '(append rename put)) | |
4108 (and (eq (car cmd) 'quote) | |
4109 (eq (nth 1 cmd) 'stor))) | |
4110 (efs-save-match-data | |
4111 (string-match efs-write-protect-msgs line))) | |
4112 (let ((retry (efs-write-recover | |
4113 (efs-host-type host) | |
4114 line cont-lines host user cmd msg pre-cont | |
4115 cont nowait noretry))) | |
4116 (or cont nowait | |
4117 (setq afsc-result (car retry) | |
4118 afsc-line (nth 1 retry) | |
4119 afsc-cont-lines (nth 2 retry))))) | |
4120 | |
4121 (t (if cont | |
4122 (efs-call-cont cont result line cont-lines) | |
4123 (or nowait | |
4124 (setq afsc-result result | |
4125 afsc-line line | |
4126 afsc-cont-lines cont-lines)))))) | |
4127 nowait) | |
4128 | |
4129 (prog1 | |
4130 (if (or nowait cont) | |
4131 nil | |
4132 (list afsc-result afsc-line afsc-cont-lines)) | |
4133 | |
4134 ;; Check the queue | |
4135 (or nowait | |
4136 efs-nested-cmd | |
4137 (let ((buff (efs-ftp-process-buffer host user))) | |
4138 (if (get-buffer buff) | |
4139 (save-excursion | |
4140 (set-buffer buff) | |
4141 (if efs-process-q | |
4142 (let ((next (car efs-process-q))) | |
4143 (setq efs-process-q (cdr efs-process-q)) | |
4144 (apply 'efs-send-cmd host user next)))))))))))) | |
4145 | |
4146 (efs-defun efs-write-recover nil | |
4147 (line cont-lines host user cmd msg pre-cont cont nowait noretry) | |
4148 "Called when a write command fails with `efs-write-protect-msgs'. | |
4149 Should return \(result line cont-lines\), like `efs-raw-send-cmd'." | |
4150 ;; This default version doesn't do anything. | |
4151 (if cont | |
4152 (progn | |
4153 (efs-call-cont cont 'failed line cont-lines) | |
4154 nil) | |
4155 (if nowait nil (list 'failed line cont-lines)))) | |
4156 | |
4157 ;;;; --------------------------------------------------------------------- | |
4158 ;;;; The login sequence. (The follows RFC959 rather tightly. If a server | |
4159 ;;;; can't even get the login codes right, it is | |
4160 ;;;; pretty much scrap metal.) | |
4161 ;;;; --------------------------------------------------------------------- | |
4162 | |
4163 (defun efs-nslookup-host (host) | |
4164 "Attempt to resolve the given HOSTNAME using nslookup if possible." | |
4165 (interactive "sHost: ") | |
4166 (if efs-nslookup-program | |
4167 (let* ((default-directory exec-directory) | |
4168 (default-major-mode 'fundamental-mode) | |
4169 (process-connection-type nil) | |
4170 (proc (start-process " *nslookup*" " *nslookup*" | |
4171 efs-nslookup-program host)) | |
4172 (res host)) | |
4173 (process-kill-without-query proc) | |
4174 (save-excursion | |
4175 (set-buffer (process-buffer proc)) | |
4176 (let ((quit-flag nil) | |
4177 (inhibit-quit nil)) | |
4178 (while (memq (process-status proc) '(run open)) | |
4179 (accept-process-output proc))) | |
4180 (goto-char (point-min)) | |
4181 (if (re-search-forward | |
4182 "Name:.*\nAddress\\(es\\)?: *\\([.0-9]+\\)$" nil t) | |
4183 (setq res (buffer-substring (match-beginning 2) | |
4184 (match-end 2)))) | |
4185 (kill-buffer (current-buffer))) | |
4186 (if (interactive-p) | |
4187 (message "%s: %s" host res)) | |
4188 res) | |
4189 (if (interactive-p) | |
4190 (message | |
4191 "No nslookup program. See the variable efs-nslookup-program.")) | |
4192 host)) | |
4193 | |
4194 (defun efs-login (host user proc) | |
4195 "Connect to the FTP-server on HOST as USER. | |
4196 PROC is the process to the FTP-client. Doesn't call efs-save-match-data. | |
4197 You must do that yourself." | |
4198 (let ((gate (efs-use-gateway-p host))) | |
4199 (if (eq gate 'kerberos) | |
4200 (progn | |
4201 (setq proc (efs-kerberos-login host user proc)) | |
4202 (efs-login-send-user host user proc gate)) | |
4203 (let ((to (if (memq gate '(proxy local raptor)) | |
4204 efs-gateway-host | |
4205 host)) | |
4206 port cmd result) | |
4207 (if (string-match "#" to) | |
4208 (setq port (substring to (match-end 0)) | |
4209 to (substring to 0 (match-beginning 0)))) | |
4210 (and efs-nslookup-on-connect | |
4211 (string-match "[^0-9.]" to) | |
4212 (setq to (efs-nslookup-host to))) | |
4213 (setq cmd (concat "open " to)) | |
4214 (if port (setq cmd (concat cmd " " port))) | |
4215 | |
4216 ;; Send OPEN command. | |
4217 (setq result (efs-raw-send-cmd proc cmd nil)) | |
4218 | |
4219 (and (eq gate 'interlock) (string-match "^331 " (nth 1 result)) | |
4220 (setq result (efs-login-send-pass | |
4221 efs-gateway-host | |
4222 (efs-get-user efs-gateway-host) proc))) | |
4223 | |
4224 ;; Analyze result of OPEN. | |
4225 (if (car result) | |
4226 (progn | |
4227 (condition-case nil (delete-process proc) (error nil)) | |
4228 (efs-error host user (concat "OPEN request failed: " | |
4229 (nth 1 result)))) | |
4230 (efs-login-send-user host user proc gate)))))) | |
4231 | |
4232 (defun efs-login-send-user (host user proc &optional gate retry) | |
4233 "Send user command to HOST and USER. PROC is the ftp client process. | |
4234 Optional argument GATE specifies which type of gateway is being used. | |
4235 RETRY argument specifies to try twice if we get a 421 response." | |
4236 (let ((cmd (cond | |
4237 ((memq gate '(local proxy interlock)) | |
4238 (format "quote USER \"%s\"@%s" user | |
4239 (if (and efs-nslookup-on-connect | |
4240 (string-match "[^0-9.]" host)) | |
4241 (efs-nslookup-host host) | |
4242 host))) | |
4243 ((eq gate 'raptor) | |
4244 (format "quote USER \"%s\"@%s %s" user | |
4245 (if (and efs-nslookup-on-connect | |
4246 (string-match "[^0-9.]" host)) | |
4247 (efs-nslookup-host host) | |
4248 host) | |
4249 (nth 3 efs-gateway-type))) | |
4250 ((eq gate 'kerberos) | |
4251 (let ((to host) | |
4252 port) | |
4253 (if (string-match "#" host) | |
4254 (progn | |
4255 (setq to (substring host 0 (match-beginning 0)) | |
4256 port (substring host (match-end 0))) | |
4257 (and efs-nslookup-on-connect | |
4258 (string-match "[^0-9.]" to) | |
4259 (efs-nslookup-host to)) | |
4260 (setq to (concat to "@" port)))) | |
4261 (format "quote user \"%s\"@%s" user to))) | |
4262 (t | |
4263 (format "quote user \"%s\"" user)))) | |
4264 (msg (format "Logging in as user %s%s..." user | |
4265 (if (memq gate '(proxy local raptor kerberos)) | |
4266 (concat "@" host) ""))) | |
4267 result code) | |
4268 | |
4269 ;; Send the message by hand so that we can report on the size | |
4270 ;; of the MOTD. | |
4271 (message msg) | |
4272 | |
4273 ;; Send USER command. | |
4274 (setq result (efs-raw-send-cmd proc cmd nil)) | |
4275 | |
4276 ;; Analyze result of USER (this follows RFC959 strictly) | |
4277 (if (< (length (nth 1 result)) 4) | |
4278 (progn | |
4279 (condition-case nil (delete-process proc) (error nil)) | |
4280 (efs-error host user | |
4281 (concat "USER request failed: " (nth 1 result)))) | |
4282 | |
4283 (setq code (substring (nth 1 result) 0 4)) | |
4284 (cond | |
4285 | |
4286 ((string-equal "331 " code) | |
4287 ;; Need password | |
4288 (setq result (efs-login-send-pass host user proc gate))) | |
4289 | |
4290 ((string-equal "332 " code) | |
4291 ;; Need an account, but no password | |
4292 (setq result (efs-login-send-acct host user proc gate))) | |
4293 | |
4294 ((null (car result)) | |
4295 ;; logged in proceed | |
4296 nil) | |
4297 | |
4298 ((and (or (string-equal "530 " code) (string-equal "421 " code)) | |
4299 (efs-anonymous-p user) | |
4300 (or (string-match efs-too-many-users-msgs (nth 1 result)) | |
4301 (string-match efs-too-many-users-msgs (nth 2 result)))) | |
4302 (if (save-window-excursion | |
4303 (condition-case nil | |
4304 (display-buffer (process-buffer proc)) | |
4305 (error nil)) | |
4306 (y-or-n-p (format | |
4307 "Too many users for %s@%s. Try again? " | |
4308 user host))) | |
4309 (progn | |
4310 ;; Set result to nil if we are doing a retry, so done | |
4311 ;; message only gets sent once. | |
4312 (setq result nil) | |
4313 (if (string-equal code "530 ") | |
4314 (efs-login-send-user host user proc gate t) | |
4315 (efs-get-process host user))) | |
4316 (signal 'quit nil))) | |
4317 | |
4318 ((and retry (string-equal code "421 ")) | |
4319 (setq result nil) | |
4320 (efs-get-process host user)) | |
4321 | |
4322 (t ; bombed | |
4323 (condition-case nil (delete-process proc) (error nil)) | |
4324 ;; Wrong username? | |
4325 (efs-set-user host nil) | |
4326 (efs-error host user | |
4327 (concat "USER request failed: " (nth 1 result))))) | |
4328 (and (null (car result)) | |
4329 (stringp (nth 2 result)) | |
4330 (message "%sdone%s" msg | |
4331 (let ((n (efs-occur-in-string ?\n (nth 2 result)))) | |
4332 (if (> n 1) | |
4333 (format "; MOTD of %d lines" n) | |
4334 ""))))))) | |
4335 | |
4336 (defun efs-login-send-pass (host user proc &optional gate) | |
4337 "Sends password to HOST and USER. PROC is the ftp client process. | |
4338 Doesn't call efs-save-match data. You must do that yourself." | |
4339 ;; Note that efs-get-password always returns something. | |
4340 ;; It prompts the user if necessary. Even if the returned password is | |
4341 ;; \"\", send it, because we wouldn't be running this function | |
4342 ;; if the server wasn't insisting on a password. | |
4343 (let* ((pass "") | |
4344 (qpass "") | |
4345 (cmd "") | |
4346 (result (unwind-protect | |
4347 (progn | |
4348 (condition-case nil | |
4349 (setq pass (efs-get-passwd host user)) | |
4350 (quit (condition-case nil | |
4351 (kill-buffer (process-buffer proc)) | |
4352 (error nil)) | |
4353 (signal 'quit nil))) | |
4354 (setq cmd (concat | |
4355 "quote pass " | |
4356 (setq qpass (efs-quote-string nil pass t)))) | |
4357 (efs-raw-send-cmd proc cmd)) | |
4358 (fillarray pass 0) | |
4359 (fillarray qpass 0) | |
4360 (fillarray cmd 0))) | |
4361 (code (and (>= (length (nth 1 result)) 4) | |
4362 (substring (nth 1 result) 0 4)))) | |
4363 (or code (setq code "")) | |
4364 ;; Analyze the result. | |
4365 (cond | |
4366 ((string-equal code "332 ") | |
4367 ;; require an account passwd | |
4368 (setq result (efs-login-send-acct host user proc gate))) | |
4369 ((null (car result)) | |
4370 ;; logged in proceed | |
4371 nil) | |
4372 ((or (string-equal code "530 ") (string-equal code "421 ")) | |
4373 ;; Give the user another chance | |
4374 (condition-case nil | |
4375 (if (efs-anonymous-p user) | |
4376 (if (or (string-match efs-too-many-users-msgs (nth 1 result)) | |
4377 (string-match efs-too-many-users-msgs (nth 2 result))) | |
4378 (if (save-window-excursion | |
4379 (condition-case nil | |
4380 (display-buffer (process-buffer proc)) | |
4381 (error nil)) | |
4382 (y-or-n-p (format | |
4383 "Too many users for %s@%s. Try again? " | |
4384 user host))) | |
4385 (progn | |
4386 ;; Return nil if we are doing a retry, so done | |
4387 ;; message only gets sent once. | |
4388 (setq result nil) | |
4389 (if (string-equal code "530 ") | |
4390 (efs-login-send-user host user proc gate) | |
4391 (efs-get-process host user))) | |
4392 (signal 'quit nil)) | |
4393 (unwind-protect | |
4394 (efs-set-passwd | |
4395 host user | |
4396 (save-window-excursion | |
4397 (condition-case nil | |
4398 (display-buffer (process-buffer proc)) | |
4399 (error nil)) | |
4400 (setq pass | |
4401 (read-passwd | |
4402 (format | |
4403 "Password for %s@%s failed. Try again: " | |
4404 user host))))) | |
4405 (fillarray pass 0)) | |
4406 (setq result nil) | |
4407 (efs-login-send-user host user proc gate)) | |
4408 (unwind-protect | |
4409 (efs-set-passwd | |
4410 host user | |
4411 (setq pass | |
4412 (read-passwd | |
4413 (format "Password for %s@%s failed. Try again: " | |
4414 user host)))) | |
4415 (fillarray pass 0)) | |
4416 (setq result nil) | |
4417 (efs-login-send-user host user proc gate)) | |
4418 (quit (condition-case nil (delete-process proc) (error nil)) | |
4419 (efs-set-user host nil) | |
4420 (efs-set-passwd host user nil) | |
4421 (signal 'quit nil)) | |
4422 (error (condition-case nil (delete-process proc) (error nil)) | |
4423 (efs-set-user host nil) | |
4424 (efs-set-passwd host user nil) | |
4425 (efs-error host user "PASS request failed.")))) | |
4426 (t ; bombed for unexplained reasons | |
4427 (condition-case nil (delete-process proc) (error nil)) | |
4428 (efs-error host user (concat "PASS request failed: " (nth 1 result))))) | |
4429 result)) | |
4430 | |
4431 (defun efs-login-send-acct (host user proc &optional gate) | |
4432 "Sends account password to HOST and USER. PROC is the ftp client process. | |
4433 Doesn't call efs-save-match data. You must do that yourself." | |
4434 (let* ((acct "") | |
4435 (qacct "") | |
4436 (cmd "") | |
4437 (result (unwind-protect | |
4438 (progn | |
4439 ;; The raptor gateway requires us to send a gateway | |
4440 ;; authentication password for account. What if the | |
4441 ;; remote server wants one too? | |
4442 (setq acct (if (eq gate 'raptor) | |
4443 (efs-get-account | |
4444 efs-gateway-host | |
4445 (nth 3 efs-gateway-type) nil t) | |
4446 (efs-get-account host user nil t)) | |
4447 qacct (efs-quote-string nil acct t) | |
4448 cmd (concat "quote acct " qacct)) | |
4449 (efs-raw-send-cmd proc cmd)) | |
4450 (fillarray acct 0) | |
4451 (fillarray qacct 0) | |
4452 (fillarray cmd 0)))) | |
4453 ;; Analyze the result | |
4454 (cond | |
4455 ((null (car result)) | |
4456 ;; logged in proceed | |
4457 nil) | |
4458 ((eq (car result) 'failed) | |
4459 ;; Give the user another chance | |
4460 (condition-case nil | |
4461 (progn | |
4462 (unwind-protect | |
4463 (progn | |
4464 (setq acct (read-passwd | |
4465 (format | |
4466 "Account password for %s@%s failed. Try again: " | |
4467 user host))) | |
4468 (or (and efs-high-security-hosts | |
4469 (string-match efs-high-security-hosts | |
4470 (format "%s@%s" user host))) | |
4471 (efs-set-account host user nil acct))) | |
4472 (fillarray acct 0)) | |
4473 (setq result (efs-login-send-user host user proc gate))) | |
4474 (quit (condition-case nil (delete-process proc) (error nil))) | |
4475 (error (condition-case nil (delete-process proc) (error nil)) | |
4476 (efs-error host user "ACCT request failed.")))) | |
4477 (t ; bombed for unexplained reasons | |
4478 (condition-case nil (delete-process proc) (error nil)) | |
4479 (efs-error host user (concat "ACCT request failed: " (nth 1 result))))) | |
4480 result)) | |
4481 | |
4482 ;;;; ---------------------------------------------------------------------- | |
4483 ;;;; Changing working directory. | |
4484 ;;;; ---------------------------------------------------------------------- | |
4485 | |
4486 (defun efs-raw-send-cd (host user dir &optional no-error) | |
4487 ;; If NO-ERROR, doesn't barf, but just returns success (t) or failure (nil). | |
4488 ;; This does not use efs-send-cmd. | |
4489 ;; Also DIR must be in the syntax of the remote host-type. | |
4490 (let* ((cmd (concat "cd " dir)) | |
4491 cd-result cd-line) | |
4492 (efs-raw-send-cmd | |
4493 (efs-get-process host user) | |
4494 cmd nil nil | |
4495 (efs-cont (result line cont-lines) (cmd) | |
4496 (if (eq result 'fatal) | |
4497 (efs-raw-send-cmd | |
4498 (efs-get-process host user) | |
4499 cmd nil nil | |
4500 (function (lambda (result line cont-lines) | |
4501 (setq cd-result result | |
4502 cd-line line)))) | |
4503 (setq cd-result result | |
4504 cd-line line)))) | |
4505 (if no-error | |
4506 (null cd-result) | |
4507 (if cd-result | |
4508 (efs-error host user (concat "CD failed: " cd-line)))))) | |
4509 | |
4510 ;;;; -------------------------------------------------------------- | |
4511 ;;;; Getting a PWD. | |
4512 ;;;; -------------------------------------------------------------- | |
4513 | |
4514 (defun efs-unquote-quotes (string) | |
4515 ;; Unquote \"\"'s in STRING to \". | |
4516 (let ((start 0) | |
4517 new) | |
4518 (while (string-match "\"\"" string start) | |
4519 (setq new (concat new (substring | |
4520 string start (1+ (match-beginning 0)))) | |
4521 start (match-end 0))) | |
4522 (if new | |
4523 (concat new (substring string start)) | |
4524 string))) | |
4525 | |
4526 (efs-defun efs-send-pwd nil (host user &optional xpwd) | |
4527 "Attempts to get the current working directory for the given HOST/USER pair. | |
4528 Returns \( DIR . LINE \) where DIR is either the directory or NIL if not found, | |
4529 and LINE is the relevant success or fail line from the FTP-server. If the | |
4530 optional arg XPWD is given, uses this server command instead of PWD." | |
4531 (let* ((result (efs-send-cmd host user | |
4532 (list 'quote (if xpwd 'xpwd 'pwd)) | |
4533 "Getting pwd")) | |
4534 (line (nth 1 result)) | |
4535 dir) | |
4536 (or (car result) | |
4537 (efs-save-match-data | |
4538 (if (string-match "\"\\(.*\\)\"[^\"]*$" line) | |
4539 (setq dir (efs-unquote-quotes (substring line (match-beginning 1) | |
4540 (match-end 1)))) | |
4541 (if (string-match " \\([^ ]+\\) " line) ; stone-age servers! | |
4542 (setq dir (substring line | |
4543 (match-beginning 1) | |
4544 (match-end 1))))))) | |
4545 (cons dir line))) | |
4546 | |
4547 (efs-defun efs-send-pwd super-dumb-unix (host user &optional xpwd) | |
4548 ;; Guess at the pwd for a unix host that doesn't support pwd. | |
4549 (if (efs-anonymous-p user) | |
4550 ;; guess | |
4551 (cons "/" "") | |
4552 ;; Who knows? | |
4553 (message "Can't obtain pwd for %s" host) | |
4554 (ding) | |
4555 (sleep-for 2) | |
4556 (message "All file names must be specified as full paths.") | |
4557 (cons nil ""))) | |
4558 | |
4559 ;;;; -------------------------------------------------------- | |
4560 ;;;; Getting the SIZE of a remote file. | |
4561 ;;;; -------------------------------------------------------- | |
4562 | |
4563 (defun efs-send-size (host user file) | |
4564 "For HOST and USER, get the size of FILE in bytes. | |
4565 This returns a list \( SIZE . LINE \), where SIZE is the file size in bytes, | |
4566 or nil if this couldn't be determined, and LINE is the output line of the | |
4567 FTP server." | |
4568 (efs-save-match-data | |
4569 (let ((result (efs-send-cmd host user (list 'quote 'size file)))) | |
4570 (setcar result | |
4571 (and (null (car result)) | |
4572 (string-match "^213 +\\([0-9]+\\)$" (nth 1 result)) | |
4573 (string-to-int | |
4574 (substring | |
4575 (cdr result) | |
4576 (match-beginning 1) (match-end 1))))) | |
4577 result))) | |
4578 | |
4579 ;;;; ------------------------------------------------------------ | |
4580 ;;;; umask support | |
4581 ;;;; ------------------------------------------------------------ | |
4582 | |
4583 (defun efs-umask (user) | |
4584 "Returns the umask that efs will use for USER. | |
4585 If USER is root or anonymous, then the values of efs-root-umask | |
4586 and efs-anonymous-umask, respectively, take precedence, to be followed | |
4587 by the value of efs-umask, and if this is nil, it returns your current | |
4588 umask on the local machine. Returns nil if this can't be determined." | |
4589 (or | |
4590 (and (string-equal user "root") efs-root-umask) | |
4591 (and (efs-anonymous-p user) | |
4592 efs-anonymous-umask) | |
4593 efs-umask | |
4594 (let* ((shell (or (and (boundp 'explicit-shell-file-name) | |
4595 explicit-shell-file-name) | |
4596 (getenv "ESHELL") | |
4597 (getenv "SHELL") | |
4598 "/bin/sh")) | |
4599 (default-major-mode 'fundamental-mode) | |
4600 (default-directory exec-directory) | |
4601 (buff (get-buffer-create " *efs-umask-data*"))) | |
4602 (unwind-protect | |
4603 (save-excursion | |
4604 (set-buffer buff) | |
4605 (call-process shell nil buff nil "-c" "umask") | |
4606 (goto-char (point-min)) | |
4607 (if (re-search-forward "[0-7]?[0-7]?[0-7]" nil t) | |
4608 (string-to-int (buffer-substring (match-beginning 0) | |
4609 (match-end 0))))) | |
4610 (kill-buffer buff))))) | |
4611 | |
4612 (defun efs-send-umask (host user mask) | |
4613 "Sets the umask on HOST for USER to MASK. | |
4614 Returns t for success, nil for failure." | |
4615 (interactive | |
4616 (let* ((path (or buffer-file-name | |
4617 (and (eq major-mode 'dired-mode) | |
4618 dired-directory))) | |
4619 (parsed (and path (efs-ftp-path path))) | |
4620 (default-host (car parsed)) | |
4621 (default-user (nth 1 parsed)) | |
4622 (default-mask (efs-umask default-user))) | |
4623 (list | |
4624 (read-string "Host: " default-host) | |
4625 (read-string "User: " default-user) | |
4626 (read-string "Umask: " (int-to-string default-mask))))) | |
4627 (let (int-mask) | |
4628 (if (integerp mask) | |
4629 (setq int-mask mask | |
4630 mask (int-to-string mask)) | |
4631 (setq int-mask (string-to-int mask))) | |
4632 (or (string-match "^ *[0-7]?[0-7]?[0-7] *$" mask) | |
4633 (error "Invalid umask %s" mask)) | |
4634 (efs-send-cmd host user | |
4635 (list 'quote 'site 'umask mask) | |
4636 (concat "Setting umask to " mask) | |
4637 (list | |
4638 (function | |
4639 (lambda (int-mask) | |
4640 (let ((buff (efs-ftp-process-buffer host user))) | |
4641 (if (get-buffer buff) | |
4642 (save-excursion | |
4643 (set-buffer buff) | |
4644 (setq efs-process-umask int-mask)))))) | |
4645 int-mask) | |
4646 (efs-cont (result line cont-lines) (host user mask) | |
4647 (if result | |
4648 (let ((buff (efs-ftp-process-buffer host user))) | |
4649 (efs-set-host-property host 'umask-failed t) | |
4650 (if (get-buffer buff) | |
4651 (save-excursion | |
4652 (set-buffer buff) | |
4653 (setq efs-process-umask nil))) | |
4654 (message | |
4655 "Unable to set umask to %s on %s" mask host) | |
4656 (if efs-ding-on-umask-failure | |
4657 (progn | |
4658 (ding) | |
4659 (sit-for 1)))))) | |
4660 0))) ; Do this NOWAIT = 0 | |
4661 | |
4662 (defun efs-set-umask (host user) | |
4663 "Sets the umask for HOST and USER, if it has not already been set." | |
4664 (save-excursion | |
4665 (set-buffer (process-buffer (efs-get-process host user))) | |
4666 (if (or efs-process-umask (efs-get-host-property host 'umask-failed)) | |
4667 nil | |
4668 (let ((umask (efs-umask user))) | |
4669 (efs-send-umask host user umask) | |
4670 t)))) ; Tell the caller that we did something. | |
4671 | |
4672 (defun efs-modes-from-umask (umask) | |
4673 ;; Given the 3 digit octal integer umask, returns the decimal integer | |
4674 ;; according to chmod that a file would be written with. | |
4675 ;; Assumes only ordinary files, so ignores x bits. | |
4676 (let* ((others (% umask 10)) | |
4677 (umask (/ umask 10)) | |
4678 (group (% umask 10)) | |
4679 (umask (/ umask 10)) | |
4680 (owner (% umask 10)) | |
4681 (factor 1)) | |
4682 (apply '+ | |
4683 (mapcar | |
4684 (function | |
4685 (lambda (x) | |
4686 (prog1 | |
4687 (* factor (- 6 (- x (% x 2)))) | |
4688 (setq factor (* factor 8))))) | |
4689 (list others group owner))))) | |
4690 | |
4691 ;;;; ------------------------------------------------------------ | |
4692 ;;;; Idle time manipulation. | |
4693 ;;;; ------------------------------------------------------------ | |
4694 | |
4695 (defun efs-check-idle (host user) | |
4696 ;; We just toss it in the queue to run whenever there's time. | |
4697 ;; Just fail quietly if this doesn't work. | |
4698 (if (and (or efs-maximize-idle efs-expire-ftp-buffers) | |
4699 (memq (efs-host-type host) efs-idle-host-types) | |
4700 (null (efs-get-host-property host 'idle-failed))) | |
4701 (let ((buffname (efs-ftp-process-buffer host user))) | |
4702 (efs-add-to-queue | |
4703 host user | |
4704 (list '(quote site idle) | |
4705 nil nil | |
4706 (efs-cont (result line cont-lines) (host user buffname) | |
4707 (efs-save-match-data | |
4708 (if (and (null result) | |
4709 (string-match efs-idle-msgs line)) | |
4710 (let ((max (substring line (match-beginning 2) | |
4711 (match-end 2)))) | |
4712 (if (get-buffer buffname) | |
4713 (save-excursion | |
4714 (set-buffer buffname) | |
4715 (setq efs-process-idle-time | |
4716 (string-to-int | |
4717 (substring line (match-beginning 1) | |
4718 (match-end 1)))))) | |
4719 (if (and efs-maximize-idle | |
4720 (not (efs-anonymous-p user))) | |
4721 (efs-add-to-queue | |
4722 host user | |
4723 (list | |
4724 (list 'quote 'site 'idle max) | |
4725 nil nil | |
4726 (efs-cont (result line cont-lines) (buffname | |
4727 max) | |
4728 (and (null result) | |
4729 (get-buffer buffname) | |
4730 (save-excursion | |
4731 (set-buffer buffname) | |
4732 (setq efs-process-idle-time | |
4733 (string-to-int max))))) | |
4734 0)))) | |
4735 (efs-set-host-property host 'idle-failed t)))) | |
4736 0 nil))))) ; Using NOWAIT = 0 inhibits mode line toggling. | |
4737 | |
4738 | |
4739 ;;;; ------------------------------------------------------------ | |
4740 ;;;; Sending the SYST command for system type. | |
4741 ;;;; ------------------------------------------------------------ | |
4742 | |
4743 (defun efs-get-syst (host user) | |
4744 "Use SYST to get the remote system type. | |
4745 Returns the system type as a string if this succeeds, otherwise nil." | |
4746 (let* ((result (efs-send-cmd host user '(quote syst))) | |
4747 (line (nth 1 result))) | |
4748 (efs-save-match-data | |
4749 (and (null (car result)) | |
4750 (string-match efs-syst-msgs line) | |
4751 (substring line (match-end 0)))))) | |
4752 | |
4753 ;;;; ------------------------------------------------------------ | |
4754 ;;;; File transfer representation type support | |
4755 ;;;; ------------------------------------------------------------ | |
4756 | |
4757 ;;; Legal representation types are: image, ascii, ebcdic, tenex | |
4758 | |
4759 (efs-defun efs-file-type nil (path) | |
4760 ;; Returns the file type for PATH, the full efs path, with filename FILE. | |
4761 ;; The return value is one of 'text, '8-binary, or '36-binary. | |
4762 (let ((parsed (efs-ftp-path path))) | |
4763 (efs-save-match-data | |
4764 (cond | |
4765 ;; There is no special significance to temp names, but we assume that | |
4766 ;; they exist on an 8-bit byte machine. | |
4767 ((or (null path) | |
4768 (let ((temp (intern-soft path efs-tmp-name-obarray))) | |
4769 (and temp (memq temp efs-tmp-name-files)))) | |
4770 '8-binary) | |
4771 ((and (null parsed) (file-exists-p path)) | |
4772 (efs-local-file-type path)) | |
4773 ;; test special hosts | |
4774 ((and parsed | |
4775 efs-binary-file-host-regexp | |
4776 (let ((case-fold-search t)) | |
4777 (string-match efs-binary-file-host-regexp (car parsed)))) | |
4778 '8-binary) | |
4779 (t | |
4780 ;; Test file names | |
4781 (let ((file (efs-internal-file-name-nondirectory | |
4782 (or (nth 2 parsed) path)))) | |
4783 (cond | |
4784 ;; test for PDP-10 binaries | |
4785 ((and efs-36-bit-binary-file-name-regexp | |
4786 (string-match efs-36-bit-binary-file-name-regexp file)) | |
4787 '36-binary) | |
4788 ((and efs-binary-file-name-regexp | |
4789 (string-match efs-binary-file-name-regexp file)) | |
4790 '8-binary) | |
4791 ((and efs-text-file-name-regexp | |
4792 (string-match efs-text-file-name-regexp file)) | |
4793 'text) | |
4794 ;; by default | |
4795 (t | |
4796 '8-binary)))))))) | |
4797 | |
4798 (efs-define-fun efs-local-file-type (file) | |
4799 ;; Looks at the beginning (magic-cookie) of a local file to determine | |
4800 ;; if it is a text file or not. If it's not a text file, it doesn't care | |
4801 ;; about what type of binary file, so this doesn't really look for a magic | |
4802 ;; cookie. | |
4803 ;; Doesn't call efs-save-match-data. The caller should do so. | |
4804 (save-excursion | |
4805 (set-buffer (get-buffer-create efs-data-buffer-name)) | |
4806 (erase-buffer) | |
4807 (insert-file-contents file nil 0 16) | |
4808 (if (looking-at "[ -~\n\r\C-L]*\\'") | |
4809 'text | |
4810 '8-binary))) | |
4811 | |
4812 (defun efs-rationalize-file-type (f-type t-type) | |
4813 ;; When the original and new names for a file indicate | |
4814 ;; different file types, this function applies an ad hoc heuristic | |
4815 ;; to return a single file type. | |
4816 (cond | |
4817 ((eq f-type t-type) | |
4818 f-type) | |
4819 ((memq '36-binary (list f-type t-type)) | |
4820 '36-binary) | |
4821 ((memq '8-binary (list f-type t-type)) | |
4822 '8-binary) | |
4823 (t | |
4824 'text))) | |
4825 | |
4826 (defun efs-prompt-for-transfer-type (arg) | |
4827 "Toggles value of efs-prompt-for-transfer-type. | |
4828 With prefix arg, turns prompting on if arg is positive, otherwise turns | |
4829 prompting off." | |
4830 (interactive "P") | |
4831 (if (if arg | |
4832 (> (prefix-numeric-value arg) 0) | |
4833 (null efs-prompt-for-transfer-type)) | |
4834 ;; turn prompting on | |
4835 (prog1 | |
4836 (setq efs-prompt-for-transfer-type t) | |
4837 (message "Prompting for FTP transfer TYPE is on.")) | |
4838 (prog1 | |
4839 (setq efs-prompt-for-transfer-type nil) | |
4840 (message "Prompting for FTP transfer TYPE is off.")))) | |
4841 | |
4842 (defun efs-read-xfer-type (path) | |
4843 ;; Prompt for the transfer type to use for PATH | |
4844 (let ((type | |
4845 (completing-read | |
4846 (format "FTP transfer TYPE for %s: " (efs-relativize-filename path)) | |
4847 '(("binary") ("image") ("ascii") ("ebcdic") ("tenex")) | |
4848 nil t))) | |
4849 (if (string-equal type "binary") | |
4850 'image | |
4851 (intern type)))) | |
4852 | |
4853 (defun efs-xfer-type (f-host-type f-path t-host-type t-path | |
4854 &optional via-local) | |
4855 ;; Returns the transfer type for transferring a file. | |
4856 ;; F-HOST-TYPE = the host type of the machine on which the file is from. | |
4857 ;; F-PATH = path, in full efs-syntax, of the original file | |
4858 ;; T-HOST-TYPE = host-type of the machine to which the file is being | |
4859 ;; transferred. | |
4860 ;; VIA-LOCAL = non-nil of the file is being moved through the local, or | |
4861 ;; a gateway machine. | |
4862 ;; Set F-PATH or T-PATH to nil, to indicate that the file is being | |
4863 ;; transferred from/to a temporary file, whose name has no significance. | |
4864 (let (temp) | |
4865 (and f-path | |
4866 (setq temp (intern-soft f-path efs-tmp-name-obarray)) | |
4867 (memq temp efs-tmp-name-files) | |
4868 (setq f-path nil)) | |
4869 (and t-path | |
4870 (setq temp (intern-soft t-path efs-tmp-name-obarray)) | |
4871 (memq temp efs-tmp-name-files) | |
4872 (setq t-path nil))) | |
4873 (if (or (null (or f-host-type t-host-type)) (null (or f-path t-path))) | |
4874 'image ; local copy? | |
4875 (if efs-prompt-for-transfer-type | |
4876 (efs-read-xfer-type (if f-path f-path t-path)) | |
4877 (let ((f-fs (cdr (assq f-host-type efs-file-type-alist))) | |
4878 (t-fs (cdr (assq t-host-type efs-file-type-alist)))) | |
4879 (if (and f-fs t-fs | |
4880 (if efs-treat-crlf-as-nl | |
4881 (and (eq (car f-fs) (car t-fs)) | |
4882 (eq (nth 1 f-fs) (nth 1 t-fs)) | |
4883 (let ((f2-fs (nth 2 f-fs)) | |
4884 (t2-fs (nth 2 t-fs))) | |
4885 (or (eq f2-fs t2-fs) | |
4886 (and (memq f2-fs '(file-crlf file-nl)) | |
4887 (memq t2-fs '(file-crlf file-nl)))))) | |
4888 (equal f-fs t-fs))) | |
4889 'image | |
4890 (let ((type (cond | |
4891 ((and f-path t-path) | |
4892 (efs-rationalize-file-type | |
4893 (efs-file-type t-host-type t-path) | |
4894 (efs-file-type f-host-type f-path))) | |
4895 (f-path | |
4896 (efs-file-type f-host-type f-path)) | |
4897 (t-path | |
4898 (efs-file-type t-host-type t-path))))) | |
4899 (cond | |
4900 ((eq type '36-binary) | |
4901 'image) | |
4902 ((eq type '8-binary) | |
4903 (if (or (eq (car f-fs) '36-bit-wa) | |
4904 (eq (car t-fs) '36-bit-wa)) | |
4905 'tenex | |
4906 'image)) | |
4907 (t ; handles 'text | |
4908 (if (and t-fs f-fs (eq (nth 1 f-fs) 'ebcdic) | |
4909 (eq (nth 1 t-fs) 'ebcdic) (null via-local)) | |
4910 'ebcdic | |
4911 'ascii))))))))) | |
4912 | |
4913 (defun efs-set-xfer-type (host user type &optional clientless) | |
4914 ;; Sets the xfer type for HOST and USER to TYPE. | |
4915 ;; If the connection is already using the required type, does nothing. | |
4916 ;; If clientless is non-nil, we are using a quoted xfer command, and | |
4917 ;; need to check if the client has changed things. | |
4918 (save-excursion | |
4919 (let ((buff (process-buffer (efs-get-process host user)))) | |
4920 (set-buffer buff) | |
4921 (or (if (and clientless efs-process-client-altered-xfer-type) | |
4922 (or (eq type efs-process-client-altered-xfer-type) | |
4923 (setq efs-process-client-altered-xfer-type nil)) | |
4924 ;; We are sending a non-clientless command, so the client | |
4925 ;; gets back in synch. | |
4926 (setq efs-process-client-altered-xfer-type nil) | |
4927 (and efs-process-xfer-type | |
4928 (eq type efs-process-xfer-type))) | |
4929 (let ((otype efs-process-xfer-type)) | |
4930 ;; Set this now in anticipation that the TYPE command will work, | |
4931 ;; in case other commands, such as efs-set-hash-mark-unit want to | |
4932 ;; grok this before the TYPE command completes. | |
4933 (setq efs-process-xfer-type type) | |
4934 (efs-send-cmd | |
4935 host user (list 'type type) | |
4936 nil nil | |
4937 (efs-cont (result line cont-lines) (host user type otype buff) | |
4938 (if result | |
4939 (unwind-protect | |
4940 (efs-error host user (format "TYPE %s failed: %s" | |
4941 (upcase (symbol-name type)) | |
4942 line)) | |
4943 (if (get-buffer buff) | |
4944 (save-excursion | |
4945 (set-buffer buff) | |
4946 (setq efs-process-xfer-type otype)))))) | |
4947 0)))))) ; always send type commands NOWAIT = 0 | |
4948 | |
4949 | |
4950 ;;;; ------------------------------------------------------------ | |
4951 ;;;; Obtaining DIR listings. | |
4952 ;;;; ------------------------------------------------------------ | |
4953 | |
4954 (defun efs-ls-guess-switches () | |
4955 ;; Tries to determine what would be the most useful switches | |
4956 ;; to use for a DIR listing. | |
4957 (if (and (boundp 'dired-listing-switches) | |
4958 (stringp dired-listing-switches) | |
4959 (efs-parsable-switches-p dired-listing-switches t)) | |
4960 dired-listing-switches | |
4961 "-al")) | |
4962 | |
4963 (efs-defun efs-ls-dumb-check nil (line host file path lsargs msg noparse | |
4964 noerror nowait cont) | |
4965 nil) | |
4966 | |
4967 (efs-defun efs-ls-dumb-check unknown (line host file path lsargs | |
4968 msg noparse noerror nowait cont) | |
4969 ;; Checks to see if the host type might be dumb unix. If so, returns the | |
4970 ;; listing otherwise nil. | |
4971 (and | |
4972 lsargs | |
4973 (string-match | |
4974 ;; Some CMU servers return a 530 here. 550 is correct. | |
4975 (concat "^5[35]0 \\(The file \\)?" | |
4976 (regexp-quote (concat lsargs " " path))) | |
4977 ;; 550 is for a non-accessible file -- RFC959 | |
4978 line) | |
4979 (progn | |
4980 (if (eq (efs-host-type host) 'apollo-unix) | |
4981 (efs-add-host 'dumb-apollo-unix host) | |
4982 (efs-add-host 'dumb-unix host)) | |
4983 ;; try again | |
4984 (if nowait | |
4985 t ; return t if asynch | |
4986 ; This is because dumb-check can't run asynch. | |
4987 ; This means that we can't recognize dumb hosts asynch. | |
4988 ; Shouldn't be a problem. | |
4989 (efs-ls file nil | |
4990 (if (eq msg t) | |
4991 (format "Relisting %s" (efs-relativize-filename file)) | |
4992 msg) | |
4993 noparse noerror nowait cont))))) | |
4994 | |
4995 ;; With no-error nil, this function returns: | |
4996 ;; an error if file is not an efs-path | |
4997 ;; (This should never happen.) | |
4998 ;; an error if either the listing is unreadable or there is an ftp error. | |
4999 ;; the listing (a string), if everything works. | |
5000 ;; | |
5001 ;; With no-error t, it returns: | |
5002 ;; an error if not an efs-path | |
5003 ;; error if listing is unreable (most likely caused by a slow connection) | |
5004 ;; nil if ftp error (this is because although asking to list a nonexistent | |
5005 ;; directory on a remote unix machine usually (except | |
5006 ;; maybe for dumb hosts) returns an ls error, but no | |
5007 ;; ftp error, if the same is done on a VMS machine, | |
5008 ;; an ftp error is returned. Need to trap the error | |
5009 ;; so we can go on and try to list the parent.) | |
5010 ;; the listing, if everything works. | |
5011 | |
5012 (defun efs-ls (file lsargs msg &optional noparse noerror nowait cont nlist) | |
5013 "Return the output of a `DIR' or `ls' command done over ftp. | |
5014 FILE is the full name of the remote file, LSARGS is any args to pass to the | |
5015 `ls' command. MSG is a message to be displayed while listing, if MSG is given | |
5016 as t, a suitable message will be computed. If nil, no message will be | |
5017 displayed. If NOPARSE is non-nil, then the listing will not be parsed and | |
5018 stored in internal cache. Otherwise, the listing will be parsed, if LSARGS | |
5019 allow it. If NOERROR is non-nil, then we return nil if the listing fails, | |
5020 rather than signal an error. If NOWAIT is non-nil, we do the listing | |
5021 asynchronously, returning nil. If CONT is non-nil it is called with first | |
5022 argument the listing string." | |
5023 ;; If lsargs are nil, this forces a one-time only dumb listing using dir. | |
5024 (setq file (efs-expand-file-name file)) | |
5025 (let ((parsed (efs-ftp-path file))) | |
5026 (if parsed | |
5027 (let* ((host (nth 0 parsed)) | |
5028 (user (nth 1 parsed)) | |
5029 (path (nth 2 parsed)) | |
5030 (host-type (efs-host-type host user)) | |
5031 (listing-type (efs-listing-type host user)) | |
5032 (parse (cond | |
5033 ((null noparse) | |
5034 (efs-parsable-switches-p lsargs t)) | |
5035 ((eq noparse 'parse) | |
5036 t) | |
5037 (t nil))) | |
5038 (switches lsargs) | |
5039 cache) | |
5040 | |
5041 (if (memq host-type efs-dumb-host-types) | |
5042 (setq lsargs nil)) | |
5043 (if (and (null efs-ls-uncache) | |
5044 (setq cache | |
5045 (or (efs-get-from-ls-cache file switches) | |
5046 (and switches | |
5047 (efs-convert-from-ls-cache | |
5048 file switches host-type listing-type))))) | |
5049 ;; The listing is in the mail, errr... cache. | |
5050 (let (listing) | |
5051 (if (stringp cache) | |
5052 (setq listing cache) | |
5053 (setq listing (car cache)) | |
5054 (if (and parse (null (nth 1 cache))) | |
5055 (save-excursion | |
5056 (set-buffer | |
5057 (let ((default-major-mode 'fundamental-mode)) | |
5058 (get-buffer-create | |
5059 efs-data-buffer-name))) | |
5060 (erase-buffer) | |
5061 (insert listing) | |
5062 (goto-char (point-min)) | |
5063 (efs-set-files | |
5064 file | |
5065 (efs-parse-listing listing-type | |
5066 host user path | |
5067 file lsargs)) | |
5068 ;; Note that we have parsed it now. | |
5069 (setcar (cdr cache) t)))) | |
5070 (if cont (efs-call-cont cont listing)) | |
5071 listing) | |
5072 | |
5073 (if cache | |
5074 (efs-del-from-ls-cache file nil nil)) | |
5075 ;; Need to get the listing via FTP. | |
5076 (let* ((temp (efs-make-tmp-name host nil)) | |
5077 (temp-file (car temp)) | |
5078 listing-result) | |
5079 (efs-send-cmd | |
5080 host user | |
5081 (list (if nlist 'nlist 'dir) path (cdr temp) lsargs) | |
5082 (if (eq msg t) | |
5083 (format "Listing %s" (efs-relativize-filename file)) | |
5084 msg) | |
5085 nil | |
5086 (efs-cont (result line cont-lines) | |
5087 (host-type listing-type host user temp-file path | |
5088 switches file lsargs noparse parse noerror | |
5089 msg nowait cont) | |
5090 ;; The client flipped to ascii, remember this. | |
5091 (let ((buff (get-buffer | |
5092 (efs-ftp-process-buffer host user)))) | |
5093 (if buff | |
5094 (efs-save-buffer-excursion | |
5095 (set-buffer buff) | |
5096 (setq efs-process-client-altered-xfer-type | |
5097 'ascii)))) | |
5098 (unwind-protect | |
5099 (if result | |
5100 (or (setq listing-result | |
5101 (efs-ls-dumb-check | |
5102 (and (or (eq host-type 'unknown) | |
5103 (eq listing-type 'unix:unknown)) | |
5104 'unknown) | |
5105 line host file path lsargs msg | |
5106 noparse noerror nowait cont)) | |
5107 ;; If dumb-check returns non-nil | |
5108 ;; then it would have handled any error recovery | |
5109 ;; and conts. listing-result would only be set to | |
5110 ;; t if nowait was non-nil. Therefore, the final | |
5111 ;; return for efs-ls could never be t, even if I | |
5112 ;; set listing-result to t here. | |
5113 (if noerror | |
5114 (if cont | |
5115 (efs-call-cont cont nil)) | |
5116 (efs-error host user | |
5117 (concat "DIR failed: " | |
5118 line)))) | |
5119 | |
5120 ;; listing worked | |
5121 (if (efs-ftp-path temp-file) | |
5122 (efs-add-file-entry (efs-host-type efs-gateway-host) | |
5123 temp-file nil nil nil)) | |
5124 (save-excursion | |
5125 ;; A hack to get around a jka-compr problem. | |
5126 ;; Do we still need it? | |
5127 (let ((default-major-mode 'fundamental-mode) | |
5128 efs-verbose jka-compr-enabled) | |
5129 (set-buffer (get-buffer-create | |
5130 efs-data-buffer-name)) | |
5131 (erase-buffer) | |
5132 (if (or (file-readable-p temp-file) | |
5133 (sleep-for efs-retry-time) | |
5134 (file-readable-p temp-file)) | |
5135 (insert-file-contents temp-file) | |
5136 (efs-error host user | |
5137 (format | |
5138 "list data file %s not readable" | |
5139 temp-file)))) | |
5140 (if parse | |
5141 (progn | |
5142 (efs-set-files | |
5143 file | |
5144 (efs-parse-listing listing-type host user path | |
5145 file lsargs)) | |
5146 ;; Parsing may update the host type. | |
5147 (and lsargs (memq (efs-host-type host) | |
5148 efs-dumb-host-types) | |
5149 (setq lsargs nil)))) | |
5150 (let ((listing (buffer-string))) | |
5151 (efs-add-to-ls-cache file lsargs listing parse) | |
5152 (if (and (null lsargs) switches) | |
5153 ;; Try to convert | |
5154 (let ((conv (efs-get-ls-converter switches))) | |
5155 (and conv | |
5156 (setq conv (assoc | |
5157 (char-to-string 0) | |
5158 conv)) | |
5159 (funcall (cdr conv) listing-type nil) | |
5160 (setq listing (buffer-string))))) | |
5161 (or nowait (setq listing-result listing)) | |
5162 ;; Call the ls cont, with first arg the | |
5163 ;; listing string. | |
5164 (if cont | |
5165 (efs-call-cont cont listing))))) | |
5166 (efs-del-tmp-name temp-file))) | |
5167 nowait) | |
5168 (and (null nowait) listing-result)))) | |
5169 (error "Attempt to get a remote listing for the local file %s" file)))) | |
5170 | |
5171 | |
5172 ;;;; =============================================================== | |
5173 ;;;; >7 | |
5174 ;;;; Parsing and storing remote file system data. | |
5175 ;;;; =============================================================== | |
5176 | |
5177 ;;; The directory listing parsers do some host type guessing. | |
5178 ;;; Most of the host type guessing is done when the PWD output | |
5179 ;;; is parsed. A bit is done when the error codes for DIR are | |
5180 ;;; analyzed. | |
5181 | |
5182 ;;;; ----------------------------------------------------------- | |
5183 ;;;; Caching directory listings. | |
5184 ;;;; ----------------------------------------------------------- | |
5185 | |
5186 ;;; Aside from storing files data in a hashtable, a limited number | |
5187 ;;; of listings are stored in complete form in `efs-ls-cache'. | |
5188 | |
5189 (defun efs-del-from-ls-cache (file &optional parent-p dir-p) | |
5190 ;; Deletes from the ls cache the listing for FILE. | |
5191 ;; With optional PARENT-P, deletes any entry for the parent | |
5192 ;; directory of FILE too. | |
5193 ;; If DIR-P is non-nil, then the directory listing of FILE is to be deleted. | |
5194 (if dir-p | |
5195 (setq file (file-name-as-directory file)) | |
5196 (setq file (directory-file-name file))) | |
5197 (setq file (efs-canonize-file-name file)) | |
5198 (if parent-p | |
5199 (setq parent-p (file-name-directory | |
5200 (if dir-p | |
5201 (directory-file-name file) | |
5202 file)))) | |
5203 (setq efs-ls-cache | |
5204 (delq nil | |
5205 (mapcar | |
5206 (if parent-p | |
5207 (function | |
5208 (lambda (x) | |
5209 (let ((f-ent (car x))) | |
5210 (and (not (string-equal file f-ent)) | |
5211 (not (string-equal parent-p f-ent)) | |
5212 x)))) | |
5213 (function | |
5214 (lambda (x) | |
5215 (and (not (string-equal file (car x))) | |
5216 x)))) | |
5217 efs-ls-cache)))) | |
5218 | |
5219 (defun efs-wipe-from-ls-cache (host user) | |
5220 ;; Remove from efs-ls-cache all listings for HOST and USER. | |
5221 (let ((host (downcase host)) | |
5222 (case-insens (memq (efs-host-type host) | |
5223 efs-case-insensitive-host-types))) | |
5224 (if case-insens (setq user (downcase user))) | |
5225 (setq efs-ls-cache | |
5226 (delq nil | |
5227 (mapcar | |
5228 (function | |
5229 (lambda (x) | |
5230 (let ((parsed (efs-ftp-path (car x)))) | |
5231 (and (not | |
5232 (and (string-equal (car parsed) host) | |
5233 (string-equal (if case-insens | |
5234 (downcase (nth 1 parsed)) | |
5235 (nth 1 parsed)) | |
5236 user))) | |
5237 x)))) | |
5238 efs-ls-cache))))) | |
5239 | |
5240 (defun efs-get-from-ls-cache (file switches) | |
5241 ;; Returns the value in `ls-cache' for FILE and SWITCHES. | |
5242 ;; Returns a list consisting of the listing string, and whether its | |
5243 ;; already been parsed. This list is eq to the nthcdr 2 of the actual | |
5244 ;; cache entry, so you can setcar it. | |
5245 ;; For dumb listings, SWITCHES will be nil. | |
5246 (let ((list efs-ls-cache) | |
5247 (switches (efs-canonize-switches switches)) | |
5248 (file (efs-canonize-file-name file))) | |
5249 (catch 'done | |
5250 (while list | |
5251 (if (and (string-equal file (car (car list))) | |
5252 (string-equal switches (nth 1 (car list)))) | |
5253 (throw 'done (nthcdr 2 (car list))) | |
5254 (setq list (cdr list))))))) | |
5255 | |
5256 (defun efs-add-to-ls-cache (file switches listing parsed) | |
5257 ;; Only call after efs-get-from-cache returns nil, to avoid duplicate | |
5258 ;; entries. PARSED should be t, if the listing has already been parsed. | |
5259 (and (> efs-ls-cache-max 0) | |
5260 (let ((switches (efs-canonize-switches switches)) | |
5261 (file (efs-canonize-file-name file))) | |
5262 (if (= efs-ls-cache-max 1) | |
5263 (setq efs-ls-cache | |
5264 (list (list file switches listing parsed))) | |
5265 (if (>= (length efs-ls-cache) efs-ls-cache-max) | |
5266 (setcdr (nthcdr (- efs-ls-cache-max 2) efs-ls-cache) nil)) | |
5267 (setq efs-ls-cache (cons (list file switches listing parsed) | |
5268 efs-ls-cache)))))) | |
5269 | |
5270 ;;;; -------------------------------------------------------------- | |
5271 ;;;; Converting listings from cache. | |
5272 ;;;; -------------------------------------------------------------- | |
5273 | |
5274 (defun efs-get-ls-converter (to-switches) | |
5275 ;; Returns converter alist for TO-SWITCHES | |
5276 (efs-get-hash-entry (efs-canonize-switches to-switches) | |
5277 efs-ls-converter-hashtable)) | |
5278 | |
5279 (defun efs-add-ls-converter (to-switches from-switches converter) | |
5280 ;; Adds an entry to `efs-ls-converter-hashtable'. | |
5281 ;; If from-switches is t, the converter converts from internal files | |
5282 ;; hashtable. | |
5283 (let* ((to-switches (efs-canonize-switches to-switches)) | |
5284 (ent (efs-get-hash-entry to-switches efs-ls-converter-hashtable)) | |
5285 (add (cons (or (eq from-switches t) | |
5286 (efs-canonize-switches from-switches)) | |
5287 converter))) | |
5288 (if ent | |
5289 (or (member add ent) | |
5290 (nconc ent (list add))) | |
5291 (efs-put-hash-entry to-switches (list add) efs-ls-converter-hashtable)))) | |
5292 | |
5293 (defun efs-convert-from-ls-cache (file switches host-type listing-type) | |
5294 ;; Returns a listing by converting the switches from a cached listing. | |
5295 (let ((clist (efs-get-ls-converter switches)) | |
5296 (dir-p (= ?/ (aref file (1- (length file))))) | |
5297 elt listing result regexp alist) | |
5298 (while file ; this loop will iterate at most twice. | |
5299 (setq alist clist) | |
5300 (while alist | |
5301 (setq elt (car alist)) | |
5302 (if (eq (car elt) t) | |
5303 (if (and dir-p (setq result (funcall (cdr elt) host-type | |
5304 (let ((efs-ls-uncache t)) | |
5305 (efs-get-files file)) | |
5306 regexp))) | |
5307 (setq alist nil | |
5308 file nil) | |
5309 (setq alist (cdr alist))) | |
5310 (if (and (setq listing | |
5311 (efs-get-from-ls-cache file (car elt))) | |
5312 (save-excursion | |
5313 (set-buffer | |
5314 (let ((default-major-mode 'fundamental-mode)) | |
5315 (get-buffer-create efs-data-buffer-name))) | |
5316 (erase-buffer) | |
5317 (insert (car listing)) | |
5318 (and (funcall (cdr elt) listing-type regexp) | |
5319 (setq result (buffer-string))))) | |
5320 (setq alist nil | |
5321 file nil) | |
5322 (setq alist (cdr alist))))) | |
5323 ;; Look for wildcards. | |
5324 (if (and file (null dir-p) (null regexp)) | |
5325 (setq regexp (efs-shell-regexp-to-regexp | |
5326 (file-name-nondirectory file)) | |
5327 file (file-name-directory file) | |
5328 dir-p t) | |
5329 (setq file nil))) | |
5330 result)) | |
5331 | |
5332 ;;; Define some converters | |
5333 | |
5334 (defun efs-unix-t-converter-sort-pred (elt1 elt2) | |
5335 (let* ((data1 (car elt1)) | |
5336 (data2 (car elt2)) | |
5337 (year1 (car data1)) | |
5338 (year2 (car data2)) | |
5339 (month1 (nth 1 data1)) | |
5340 (month2 (nth 1 data2)) | |
5341 (day1 (nth 2 data1)) | |
5342 (day2 (nth 2 data2)) | |
5343 (hour1 (nth 3 data1)) | |
5344 (hour2 (nth 3 data2)) | |
5345 (minutes1 (nth 4 data1)) | |
5346 (minutes2 (nth 4 data2))) | |
5347 (if year1 | |
5348 (and year2 | |
5349 (or (> year1 year2) | |
5350 (and (= year1 year2) | |
5351 (or (> month1 month2) | |
5352 (and (= month1 month2) | |
5353 (> day1 day2)))))) | |
5354 (if year2 | |
5355 t | |
5356 (or (> month1 month2) | |
5357 (and (= month1 month2) | |
5358 (or (> day1 day2) | |
5359 (and (= day1 day2) | |
5360 (or (> hour1 hour2) | |
5361 (and (= hour1 hour2) | |
5362 (> minutes1 minutes2))))))))))) | |
5363 | |
5364 (defun efs-unix-t-converter (&optional regexp reverse) | |
5365 (if regexp | |
5366 nil | |
5367 (goto-char (point-min)) | |
5368 (efs-save-match-data | |
5369 (if (re-search-forward efs-month-and-time-regexp nil t) | |
5370 (let ((current-month (cdr (assoc (substring | |
5371 (current-time-string) 4 7) | |
5372 efs-month-alist))) | |
5373 list-start start end list year month day hour minutes) | |
5374 (beginning-of-line) | |
5375 (setq list-start (point)) | |
5376 (while (progn | |
5377 (setq start (point)) | |
5378 (forward-line 1) | |
5379 (setq end (point)) | |
5380 (goto-char start) | |
5381 (re-search-forward efs-month-and-time-regexp end t)) | |
5382 ;; Need to measure wrto the current month | |
5383 ;; There is a bug here if because of time-zone shifts, the | |
5384 ;; local machine and the remote one are on different months. | |
5385 (setq month (% (+ (- 11 current-month) | |
5386 (cdr (assoc | |
5387 (buffer-substring (match-beginning 2) | |
5388 (match-end 2)) | |
5389 efs-month-alist))) 12) | |
5390 day (string-to-int | |
5391 (buffer-substring (match-beginning 3) (match-end 3))) | |
5392 year (buffer-substring (match-beginning 4) (match-end 4))) | |
5393 (if (string-match ":" year) | |
5394 (setq hour (string-to-int (substring year 0 | |
5395 (match-beginning 0))) | |
5396 minutes (string-to-int (substring year (match-end 0))) | |
5397 year nil) | |
5398 (setq hour nil | |
5399 minutes nil | |
5400 year (string-to-int year))) | |
5401 (setq list (cons | |
5402 (cons | |
5403 (list year month day hour minutes) | |
5404 (buffer-substring start end)) | |
5405 list)) | |
5406 (goto-char end)) | |
5407 (setq list | |
5408 (mapcar 'cdr | |
5409 (sort list 'efs-unix-t-converter-sort-pred))) | |
5410 (if reverse (setq list (nreverse list))) | |
5411 (delete-region list-start (point)) | |
5412 (apply 'insert list) | |
5413 t))))) | |
5414 | |
5415 (efs-defun efs-t-converter nil (&optional regexp reverse) | |
5416 ;; Converts listing without the t-switch, to ones with it. | |
5417 nil) ; by default assume that we cannot work. | |
5418 | |
5419 (efs-fset 'efs-t-converter 'unix 'efs-unix-t-converter) | |
5420 (efs-fset 'efs-t-converter 'sysV-unix 'efs-unix-t-converter) | |
5421 (efs-fset 'efs-t-converter 'apollo-unix 'efs-unix-t-converter) | |
5422 (efs-fset 'efs-t-converter 'bsd-unix 'efs-unix-t-converter) | |
5423 (efs-fset 'efs-t-converter 'dumb-unix 'efs-unix-t-converter) | |
5424 (efs-fset 'efs-t-converter 'dumb-apollo-unix 'efs-unix-t-converter) | |
5425 (efs-fset 'efs-t-converter 'super-dumb-unix 'efs-unix-t-converter) | |
5426 | |
5427 (defun efs-rt-converter (listing-type &optional regexp) | |
5428 ;; Reverse time sorting | |
5429 (efs-t-converter listing-type regexp t)) | |
5430 | |
5431 (defun efs-unix-alpha-converter (&optional regexp reverse) | |
5432 (if regexp | |
5433 nil | |
5434 (goto-char (point-min)) | |
5435 (efs-save-match-data | |
5436 (if (re-search-forward efs-month-and-time-regexp nil t) | |
5437 (let (list list-start end start next) | |
5438 (beginning-of-line) | |
5439 (setq list-start (point)) | |
5440 (while (progn | |
5441 (setq start (point)) | |
5442 (end-of-line) | |
5443 (setq end (point) | |
5444 next (1+ end)) | |
5445 (goto-char start) | |
5446 (re-search-forward efs-month-and-time-regexp end t)) | |
5447 ;; Need to measure wrto the current month | |
5448 ;; There is a bug here if because of time-zone shifts, the | |
5449 ;; local machine and the remote one are on different months. | |
5450 (setq list | |
5451 (cons | |
5452 (cons (buffer-substring (point) end) | |
5453 (buffer-substring start next)) | |
5454 list)) | |
5455 (goto-char next)) | |
5456 (delete-region list-start (point)) | |
5457 (apply 'insert | |
5458 (mapcar 'cdr | |
5459 (sort list (if reverse | |
5460 (function | |
5461 (lambda (x y) | |
5462 (string< (car y) (car x)))) | |
5463 (function | |
5464 (lambda (x y) | |
5465 (string< (car x) (car y)))))))) | |
5466 t))))) | |
5467 | |
5468 (efs-defun efs-alpha-converter nil (&optional regexp reverse) | |
5469 ;; Converts listing to lexigraphical order. | |
5470 nil) ; by default assume that we cannot work. | |
5471 | |
5472 (efs-fset 'efs-alpha-converter 'unix 'efs-unix-alpha-converter) | |
5473 (efs-fset 'efs-alpha-converter 'sysV-unix 'efs-unix-alpha-converter) | |
5474 (efs-fset 'efs-alpha-converter 'apollo-unix 'efs-unix-alpha-converter) | |
5475 (efs-fset 'efs-alpha-converter 'bsd-unix 'efs-unix-alpha-converter) | |
5476 (efs-fset 'efs-alpha-converter 'dumb-unix 'efs-unix-alpha-converter) | |
5477 (efs-fset 'efs-alpha-converter 'dumb-apollo-unix 'efs-unix-alpha-converter) | |
5478 (efs-fset 'efs-alpha-converter 'super-dumb-unix 'efs-unix-alpha-converter) | |
5479 | |
5480 (defun efs-ralpha-converter (listing-type &optional regexp) | |
5481 ;; Reverse alphabetic | |
5482 (efs-alpha-converter listing-type regexp t)) | |
5483 | |
5484 (defun efs-unix-S-converter (&optional regexp reverse) | |
5485 (if regexp | |
5486 nil | |
5487 (goto-char (point-min)) | |
5488 (efs-save-match-data | |
5489 (if (re-search-forward efs-month-and-time-regexp nil t) | |
5490 (let (list list-start start next) | |
5491 (beginning-of-line) | |
5492 (setq list-start (point)) | |
5493 (while (progn | |
5494 (setq start (point)) | |
5495 (forward-line 1) | |
5496 (setq next (point)) | |
5497 (goto-char start) | |
5498 (re-search-forward efs-month-and-time-regexp next t)) | |
5499 ;; Need to measure wrto the current month | |
5500 ;; There is a bug here if because of time-zone shifts, the | |
5501 ;; local machine and the remote one are on different months. | |
5502 (setq list | |
5503 (cons | |
5504 (cons (string-to-int | |
5505 (buffer-substring (match-beginning 1) | |
5506 (match-end 1))) | |
5507 (buffer-substring start next)) | |
5508 list)) | |
5509 (goto-char next)) | |
5510 (delete-region list-start (point)) | |
5511 (apply 'insert | |
5512 (mapcar 'cdr | |
5513 (sort list (if reverse | |
5514 (function | |
5515 (lambda (x y) | |
5516 (< (car x) (car y)))) | |
5517 (function | |
5518 (lambda (x y) | |
5519 (> (car x) (car y)))))))) | |
5520 t))))) | |
5521 | |
5522 (efs-defun efs-S-converter nil (&optional regexp reverse) | |
5523 ;; Converts listing without the S-switch, to ones with it. | |
5524 nil) ; by default assume that we cannot work. | |
5525 | |
5526 (efs-fset 'efs-S-converter 'unix 'efs-unix-S-converter) | |
5527 (efs-fset 'efs-S-converter 'sysV-unix 'efs-unix-S-converter) | |
5528 (efs-fset 'efs-S-converter 'apollo-unix 'efs-unix-S-converter) | |
5529 (efs-fset 'efs-S-converter 'bsd-unix 'efs-unix-S-converter) | |
5530 (efs-fset 'efs-S-converter 'dumb-unix 'efs-unix-S-converter) | |
5531 (efs-fset 'efs-S-converter 'dumb-apollo-unix 'efs-unix-S-converter) | |
5532 (efs-fset 'efs-S-converter 'super-dumb-unix 'efs-unix-S-converter) | |
5533 | |
5534 (defun efs-rS-converter (listing-type &optional regexp) | |
5535 ;; Reverse S switch. | |
5536 (efs-S-converter listing-type regexp t)) | |
5537 | |
5538 (defun efs-unix-X-converter (&optional regexp reverse) | |
5539 (if regexp | |
5540 nil | |
5541 (goto-char (point-min)) | |
5542 (efs-save-match-data | |
5543 (if (re-search-forward efs-month-and-time-regexp nil t) | |
5544 (let (next list list-start fnstart eol start end link-p) | |
5545 (beginning-of-line) | |
5546 (setq list-start (point)) | |
5547 (while (progn | |
5548 (setq start (point)) | |
5549 (skip-chars-forward "0-9 ") | |
5550 (setq link-p (= (following-char) ?l)) | |
5551 (end-of-line) | |
5552 (setq eol (point) | |
5553 next (1+ eol)) | |
5554 (goto-char start) | |
5555 (re-search-forward efs-month-and-time-regexp eol t)) | |
5556 ;; Need to measure wrto the current month | |
5557 ;; There is a bug here if because of time-zone shifts, the | |
5558 ;; local machine and the remote one are on different months. | |
5559 (setq fnstart (point)) | |
5560 (or (and link-p (search-forward " -> " eol t) | |
5561 (goto-char (match-beginning 0))) | |
5562 (goto-char eol)) | |
5563 (setq end (point)) | |
5564 (skip-chars-backward "^." fnstart) | |
5565 (setq list | |
5566 (cons | |
5567 (cons | |
5568 (if (= (point) fnstart) | |
5569 "" | |
5570 (buffer-substring (point) end)) | |
5571 (buffer-substring start next)) | |
5572 list)) | |
5573 (goto-char next)) | |
5574 (delete-region list-start (point)) | |
5575 (apply 'insert | |
5576 (mapcar 'cdr | |
5577 (sort list (if reverse | |
5578 (function | |
5579 (lambda (x y) | |
5580 (string< (car y) (car x)))) | |
5581 (function | |
5582 (lambda (x y) | |
5583 (string< (car x) (car y)))))))) | |
5584 t))))) | |
5585 | |
5586 (efs-defun efs-X-converter nil (&optional regexp reverse) | |
5587 ;; Sort on file name extension. By default do nothing | |
5588 nil) | |
5589 | |
5590 (defun efs-rX-converter (listing-type &optional regexp) | |
5591 (efs-X-converter listing-type regexp t)) | |
5592 | |
5593 (efs-fset 'efs-X-converter 'unix 'efs-unix-X-converter) | |
5594 (efs-fset 'efs-X-converter 'sysV-unix 'efs-unix-X-converter) | |
5595 (efs-fset 'efs-X-converter 'apollo-unix 'efs-unix-X-converter) | |
5596 (efs-fset 'efs-X-converter 'bsd-unix 'efs-unix-X-converter) | |
5597 (efs-fset 'efs-X-converter 'dumb-unix 'efs-unix-X-converter) | |
5598 (efs-fset 'efs-X-converter 'dumb-apollo-unix 'efs-unix-X-converter) | |
5599 (efs-fset 'efs-X-converter 'super-dumb-unix 'efs-unix-X-converter) | |
5600 | |
5601 ;;; Brief listings | |
5602 | |
5603 ;;; The following functions do a heap better at packing than | |
5604 ;;; the usual ls listing. A variable column width is used. | |
5605 (defun efs-column-widths (columns list &optional across) | |
5606 ;; Returns the column widths for breaking LIST into | |
5607 ;; COLUMNS number of columns. | |
5608 (cond | |
5609 ((null list) | |
5610 nil) | |
5611 ((= columns 1) | |
5612 (list (apply 'max (mapcar 'length list)))) | |
5613 ((let* ((len (length list)) | |
5614 (col-length (/ len columns)) | |
5615 (remainder (% len columns)) | |
5616 (i 0) | |
5617 (j 0) | |
5618 (max-width 0) | |
5619 widths padding) | |
5620 (if (zerop remainder) | |
5621 (setq padding 0) | |
5622 (setq col-length (1+ col-length) | |
5623 padding (- columns remainder))) | |
5624 (setq list (nconc (copy-sequence list) (make-list padding nil))) | |
5625 (setcdr (nthcdr (1- (+ len padding)) list) list) | |
5626 (while (< i columns) | |
5627 (while (< j col-length) | |
5628 (setq max-width (max max-width (length (car list))) | |
5629 list (if across (nthcdr columns list) (cdr list)) | |
5630 j (1+ j))) | |
5631 (setq widths (cons (+ max-width 2) widths) | |
5632 max-width 0 | |
5633 j 0 | |
5634 i (1+ i)) | |
5635 (if across (setq list (cdr list)))) | |
5636 (setcar widths (- (car widths) 2)) | |
5637 (nreverse widths))))) | |
5638 | |
5639 (defun efs-calculate-columns (list &optional across) | |
5640 ;; Returns a list of integers which are the column widths that best pack | |
5641 ;; LIST, a list of strings, onto the screen. | |
5642 (and list | |
5643 (let* ((width (1- (window-width))) | |
5644 (columns (max 1 (/ width | |
5645 (+ 2 (apply 'max (mapcar 'length list)))))) | |
5646 col-list last-col-list) | |
5647 (while (<= (apply '+ (setq col-list | |
5648 (efs-column-widths columns list across))) | |
5649 width) | |
5650 (setq columns (1+ columns) | |
5651 last-col-list col-list)) | |
5652 (or last-col-list col-list)))) | |
5653 | |
5654 (defun efs-format-columns-of-files (files &optional across) | |
5655 ;; Returns the number of lines used. | |
5656 ;; If ACROSS is non-nil, sorts across rather than down the buffer, like | |
5657 ;; ls -x | |
5658 ;; A beefed up version of the function in dired. Thanks Sebastian. | |
5659 (and files | |
5660 (let* ((columns (efs-calculate-columns files across)) | |
5661 (ncols (length columns)) | |
5662 (ncols1 (1- ncols)) | |
5663 (nfiles (length files)) | |
5664 (nrows (+ (/ nfiles ncols) | |
5665 (if (zerop (% nfiles ncols)) 0 1))) | |
5666 (space-left (- (window-width) (apply '+ columns) 1)) | |
5667 (stretch (/ space-left ncols1)) | |
5668 (float-stretch (if (zerop ncols1) 0 (% space-left ncols1))) | |
5669 (i 0) | |
5670 (j 0) | |
5671 (result "") | |
5672 file padding) | |
5673 (setq files (nconc (copy-sequence files) ; fill up with empty fns | |
5674 (make-list (- (* ncols nrows) nfiles) ""))) | |
5675 (setcdr (nthcdr (1- (length files)) files) files) ; make circular | |
5676 (while (< j nrows) | |
5677 (while (< i ncols) | |
5678 (setq result (concat result (setq file (car files)))) | |
5679 (setq padding (- (nth i columns) (length file))) | |
5680 (or (= i ncols1) | |
5681 (progn | |
5682 (setq padding (+ padding stretch)) | |
5683 (if (< i float-stretch) (setq padding (1+ padding))))) | |
5684 (setq result (concat result (make-string padding ?\ ))) | |
5685 (setq files (if across (cdr files) (nthcdr nrows files)) | |
5686 i (1+ i))) | |
5687 (setq result (concat result "\n")) | |
5688 (setq i 0 | |
5689 j (1+ j)) | |
5690 (or across (setq files (cdr files)))) | |
5691 result))) | |
5692 | |
5693 (defun efs-brief-converter (host-type file-table F a A p x C &optional regexp) | |
5694 ;; Builds a brief directory listing for file cache, with | |
5695 ;; possible switches F, a, A, p, x. | |
5696 (efs-save-match-data | |
5697 (let (list ent modes) | |
5698 (efs-map-hashtable | |
5699 (function | |
5700 (lambda (key val) | |
5701 (if (and | |
5702 (efs-really-file-p host-type key val) | |
5703 (or a | |
5704 (and A (not (or (string-equal "." key) | |
5705 (string-equal ".." key)))) | |
5706 (/= (string-to-char key) ?.)) | |
5707 (or (null regexp) | |
5708 (string-match regexp key))) | |
5709 (setq ent (car val) | |
5710 modes (nth 3 val) | |
5711 list (cons | |
5712 (cond ((null (or F p)) | |
5713 key) | |
5714 ((eq t ent) | |
5715 (concat key "/")) | |
5716 ((cond | |
5717 ((null F) | |
5718 key) | |
5719 ((stringp ent) | |
5720 (concat key "@")) | |
5721 ((null modes) | |
5722 key) | |
5723 ((eq (string-to-char modes) ?s) | |
5724 ;; a socket | |
5725 (concat key "=")) | |
5726 ((or | |
5727 (memq (elt modes 3) '(?x ?s ?t)) | |
5728 (memq (elt modes 6) '(?x ?s ?t)) | |
5729 (memq (elt modes 9) '(?x ?s ?t))) | |
5730 (concat key "*")) | |
5731 (t | |
5732 key)))) | |
5733 list))))) | |
5734 file-table) | |
5735 (setq list (sort list 'string<)) | |
5736 (if (or C x) | |
5737 (efs-format-columns-of-files list x) | |
5738 (concat (mapconcat 'identity list "\n") "\n"))))) | |
5739 | |
5740 ;;; Store converters. | |
5741 | |
5742 ;; The cheaters. | |
5743 (efs-add-ls-converter "-al" nil (function | |
5744 (lambda (listing-type &optional regexp) | |
5745 (null regexp)))) | |
5746 (efs-add-ls-converter "-Al" nil (function | |
5747 (lambda (listing-type &optional regexp) | |
5748 (null regexp)))) | |
5749 (efs-add-ls-converter "-alF" nil (function | |
5750 (lambda (listing-type &optional regexp) | |
5751 (null regexp)))) | |
5752 (efs-add-ls-converter "-AlF" nil (function | |
5753 (lambda (listing-type &optional regexp) | |
5754 (null regexp)))) | |
5755 | |
5756 (efs-add-ls-converter "-alt" "-al" 'efs-t-converter) | |
5757 (efs-add-ls-converter "-Alt" "-Al" 'efs-t-converter) | |
5758 (efs-add-ls-converter "-lt" "-l" 'efs-t-converter) | |
5759 (efs-add-ls-converter "-altF" "-alF" 'efs-t-converter) | |
5760 (efs-add-ls-converter "-AltF" "-AlF" 'efs-t-converter) | |
5761 (efs-add-ls-converter "-ltF" "-lF" 'efs-t-converter) | |
5762 (efs-add-ls-converter "-alt" nil 'efs-t-converter) | |
5763 (efs-add-ls-converter "-altF" nil 'efs-t-converter) | |
5764 (efs-add-ls-converter "-Alt" nil 'efs-t-converter) ; cheating a bit | |
5765 (efs-add-ls-converter "-AltF" nil 'efs-t-converter) ; cheating a bit | |
5766 | |
5767 (efs-add-ls-converter "-altr" "-al" 'efs-rt-converter) | |
5768 (efs-add-ls-converter "-Altr" "-Al" 'efs-rt-converter) | |
5769 (efs-add-ls-converter "-ltr" "-l" 'efs-rt-converter) | |
5770 (efs-add-ls-converter "-altFr" "-alF" 'efs-rt-converter) | |
5771 (efs-add-ls-converter "-AltFr" "-AlF" 'efs-rt-converter) | |
5772 (efs-add-ls-converter "-ltFr" "-lF" 'efs-rt-converter) | |
5773 (efs-add-ls-converter "-altr" nil 'efs-rt-converter) | |
5774 (efs-add-ls-converter "-Altr" nil 'efs-rt-converter) | |
5775 | |
5776 (efs-add-ls-converter "-alr" "-alt" 'efs-alpha-converter) | |
5777 (efs-add-ls-converter "-Alr" "-Alt" 'efs-alpha-converter) | |
5778 (efs-add-ls-converter "-lr" "-lt" 'efs-alpha-converter) | |
5779 (efs-add-ls-converter "-alFr" "-alFt" 'efs-alpha-converter) | |
5780 (efs-add-ls-converter "-AlFr" "-AlFt" 'efs-alpha-converter) | |
5781 (efs-add-ls-converter "-lFr" "-lFt" 'efs-alpha-converter) | |
5782 | |
5783 (efs-add-ls-converter "-al" "-alt" 'efs-alpha-converter) | |
5784 (efs-add-ls-converter "-Al" "-Alt" 'efs-alpha-converter) | |
5785 (efs-add-ls-converter "-l" "-lt" 'efs-alpha-converter) | |
5786 (efs-add-ls-converter "-alF" "-alFt" 'efs-alpha-converter) | |
5787 (efs-add-ls-converter "-AlF" "-AlFt" 'efs-alpha-converter) | |
5788 (efs-add-ls-converter "-lF" "-lFt" 'efs-alpha-converter) | |
5789 (efs-add-ls-converter nil "-alt" 'efs-alpha-converter) | |
5790 | |
5791 (efs-add-ls-converter "-alr" "-al" 'efs-ralpha-converter) | |
5792 (efs-add-ls-converter "-Alr" "-Al" 'efs-ralpha-converter) | |
5793 (efs-add-ls-converter "-lr" "-l" 'efs-ralpha-converter) | |
5794 (efs-add-ls-converter "-alFr" "-alF" 'efs-ralpha-converter) | |
5795 (efs-add-ls-converter "-lAFr" "-lAF" 'efs-ralpha-converter) | |
5796 (efs-add-ls-converter "-lFr" "-lF" 'efs-ralpha-converter) | |
5797 (efs-add-ls-converter "-alr" nil 'efs-ralpha-converter) | |
5798 | |
5799 (efs-add-ls-converter "-alr" "-alt" 'efs-ralpha-converter) | |
5800 (efs-add-ls-converter "-Alr" "-Alt" 'efs-ralpha-converter) | |
5801 (efs-add-ls-converter "-lr" "-lt" 'efs-ralpha-converter) | |
5802 (efs-add-ls-converter "-alFr" "-alFt" 'efs-ralpha-converter) | |
5803 (efs-add-ls-converter "-lAFr" "-lAFt" 'efs-ralpha-converter) | |
5804 (efs-add-ls-converter "-lFr" "-lFt" 'efs-ralpha-converter) | |
5805 | |
5806 (efs-add-ls-converter "-alS" "-al" 'efs-S-converter) | |
5807 (efs-add-ls-converter "-AlS" "-Al" 'efs-S-converter) | |
5808 (efs-add-ls-converter "-lS" "-l" 'efs-S-converter) | |
5809 (efs-add-ls-converter "-alSF" "-alF" 'efs-S-converter) | |
5810 (efs-add-ls-converter "-AlSF" "-AlF" 'efs-S-converter) | |
5811 (efs-add-ls-converter "-lSF" "-lF" 'efs-S-converter) | |
5812 (efs-add-ls-converter "-alS" nil 'efs-S-converter) | |
5813 | |
5814 (efs-add-ls-converter "-alSr" "-al" 'efs-rS-converter) | |
5815 (efs-add-ls-converter "-AlSr" "-Al" 'efs-rS-converter) | |
5816 (efs-add-ls-converter "-lSr" "-l" 'efs-rS-converter) | |
5817 (efs-add-ls-converter "-alSFr" "-alF" 'efs-rS-converter) | |
5818 (efs-add-ls-converter "-AlSFr" "-AlF" 'efs-rS-converter) | |
5819 (efs-add-ls-converter "-lSFr" "-lF" 'efs-rS-converter) | |
5820 (efs-add-ls-converter "-alSr" nil 'efs-rS-converter) | |
5821 | |
5822 (efs-add-ls-converter "-alS" "-alt" 'efs-S-converter) | |
5823 (efs-add-ls-converter "-AlS" "-Alt" 'efs-S-converter) | |
5824 (efs-add-ls-converter "-lS" "-lt" 'efs-S-converter) | |
5825 (efs-add-ls-converter "-alSF" "-alFt" 'efs-S-converter) | |
5826 (efs-add-ls-converter "-AlSF" "-AlFt" 'efs-S-converter) | |
5827 (efs-add-ls-converter "-lSF" "-lFt" 'efs-S-converter) | |
5828 | |
5829 (efs-add-ls-converter "-alSr" "-alt" 'efs-rS-converter) | |
5830 (efs-add-ls-converter "-AlSr" "-Alt" 'efs-rS-converter) | |
5831 (efs-add-ls-converter "-lSr" "-lt" 'efs-rS-converter) | |
5832 (efs-add-ls-converter "-alSFr" "-alFt" 'efs-rS-converter) | |
5833 (efs-add-ls-converter "-AlSFr" "-AlFt" 'efs-rS-converter) | |
5834 (efs-add-ls-converter "-lSFr" "-lFt" 'efs-rS-converter) | |
5835 | |
5836 (efs-add-ls-converter "-AlX" nil 'efs-X-converter) | |
5837 (efs-add-ls-converter "-alX" nil 'efs-X-converter) | |
5838 (efs-add-ls-converter "-AlXr" nil 'efs-rX-converter) | |
5839 (efs-add-ls-converter "-alXr" nil 'efs-rX-converter) | |
5840 | |
5841 (efs-add-ls-converter "-alX" "-al" 'efs-X-converter) | |
5842 (efs-add-ls-converter "-AlX" "-Al" 'efs-X-converter) | |
5843 (efs-add-ls-converter "-lX" "-l" 'efs-X-converter) | |
5844 (efs-add-ls-converter "-alXF" "-alF" 'efs-X-converter) | |
5845 (efs-add-ls-converter "-AlXF" "-AlF" 'efs-X-converter) | |
5846 (efs-add-ls-converter "-lXF" "-lF" 'efs-X-converter) | |
5847 | |
5848 (efs-add-ls-converter "-alXr" "-al" 'efs-rX-converter) | |
5849 (efs-add-ls-converter "-AlXr" "-Al" 'efs-rX-converter) | |
5850 (efs-add-ls-converter "-lXr" "-l" 'efs-rX-converter) | |
5851 (efs-add-ls-converter "-alXFr" "-alF" 'efs-rX-converter) | |
5852 (efs-add-ls-converter "-AlXFr" "-AlF" 'efs-rX-converter) | |
5853 (efs-add-ls-converter "-lXFr" "-lF" 'efs-rX-converter) | |
5854 | |
5855 ;;; Converters for efs-files-hashtable | |
5856 | |
5857 (efs-add-ls-converter | |
5858 "" t (function | |
5859 (lambda (host-type files &optional regexp) | |
5860 (efs-brief-converter host-type files | |
5861 nil nil nil nil nil nil regexp)))) | |
5862 (efs-add-ls-converter | |
5863 "-C" t (function | |
5864 (lambda (host-type files &optional regexp) | |
5865 (efs-brief-converter host-type files | |
5866 nil nil nil nil nil t regexp)))) | |
5867 (efs-add-ls-converter | |
5868 "-F" t (function | |
5869 (lambda (host-type files &optional regexp) | |
5870 (efs-brief-converter host-type files | |
5871 t nil nil nil nil nil regexp)))) | |
5872 (efs-add-ls-converter | |
5873 "-p" t (function | |
5874 (lambda (host-type files &optional regexp) | |
5875 (efs-brief-converter host-type files | |
5876 nil nil nil t nil nil regexp)))) | |
5877 (efs-add-ls-converter | |
5878 "-CF" t (function | |
5879 (lambda (host-type files &optional regexp) | |
5880 (efs-brief-converter host-type files | |
5881 t nil nil nil nil t regexp)))) | |
5882 (efs-add-ls-converter | |
5883 "-Cp" t (function | |
5884 (lambda (host-type files &optional regexp) | |
5885 (efs-brief-converter host-type files nil nil nil t nil t regexp)))) | |
5886 (efs-add-ls-converter | |
5887 "-x" t (function | |
5888 (lambda (host-type files &optional regexp) | |
5889 (efs-brief-converter host-type files | |
5890 nil nil nil nil t nil regexp)))) | |
5891 (efs-add-ls-converter | |
5892 "-xF" t (function | |
5893 (lambda (host-type files &optional regexp) | |
5894 (efs-brief-converter host-type files t nil nil nil t nil regexp)))) | |
5895 (efs-add-ls-converter | |
5896 "-xp" t (function | |
5897 (lambda (host-type files &optional regexp) | |
5898 (efs-brief-converter host-type files nil nil nil t t nil regexp)))) | |
5899 (efs-add-ls-converter | |
5900 "-Ca" t (function | |
5901 (lambda (host-type files &optional regexp) | |
5902 (efs-brief-converter host-type files nil t nil nil nil t regexp)))) | |
5903 (efs-add-ls-converter | |
5904 "-CFa" t (function | |
5905 (lambda (host-type files &optional regexp) | |
5906 (efs-brief-converter host-type files t t nil nil nil t regexp)))) | |
5907 (efs-add-ls-converter | |
5908 "-Cpa" t (function | |
5909 (lambda (host-type files &optional regexp) | |
5910 (efs-brief-converter host-type files nil t nil t nil t regexp)))) | |
5911 (efs-add-ls-converter | |
5912 "-xa" t (function | |
5913 (lambda (host-type files &optional regexp) | |
5914 (efs-brief-converter host-type files nil t nil nil t nil regexp)))) | |
5915 (efs-add-ls-converter | |
5916 "-xFa" t (function | |
5917 (lambda (host-type files &optional regexp) | |
5918 (efs-brief-converter host-type files t t nil nil t nil regexp)))) | |
5919 (efs-add-ls-converter | |
5920 "-xpa" t (function | |
5921 (lambda (host-type files &optional regexp) | |
5922 (efs-brief-converter host-type files nil t nil t t nil regexp)))) | |
5923 (efs-add-ls-converter | |
5924 "-CA" t (function | |
5925 (lambda (host-type files &optional regexp) | |
5926 (efs-brief-converter host-type files nil nil t nil nil t regexp)))) | |
5927 (efs-add-ls-converter | |
5928 "-CFA" t (function | |
5929 (lambda (host-type files &optional regexp) | |
5930 (efs-brief-converter host-type files t nil t nil nil t regexp)))) | |
5931 (efs-add-ls-converter | |
5932 "-CpA" t (function | |
5933 (lambda (host-type files &optional regexp) | |
5934 (efs-brief-converter host-type files nil nil t t nil t regexp)))) | |
5935 (efs-add-ls-converter | |
5936 "-xA" t (function | |
5937 (lambda (host-type files &optional regexp) | |
5938 (efs-brief-converter host-type files nil nil t nil t nil regexp)))) | |
5939 (efs-add-ls-converter | |
5940 "-xFA" t (function | |
5941 (lambda (host-type files &optional regexp) | |
5942 (efs-brief-converter host-type files t nil t nil t nil regexp)))) | |
5943 (efs-add-ls-converter | |
5944 "-xpA" t (function | |
5945 (lambda (host-type files &optional regexp) | |
5946 (efs-brief-converter host-type files nil nil t t t nil regexp)))) | |
5947 | |
5948 ;;;; ------------------------------------------------------------ | |
5949 ;;;; Directory Listing Parsers | |
5950 ;;;; ------------------------------------------------------------ | |
5951 | |
5952 (defconst efs-unix:dl-listing-regexp | |
5953 "^[^ \n\t]+\n? +\\([0-9]+\\|-\\|=\\) ") | |
5954 | |
5955 ;; Note to progammers: | |
5956 ;; Below are a series of macros and functions used for parsing unix | |
5957 ;; file listings. They are intended only to be used together, so be careful | |
5958 ;; about using them out of context. | |
5959 | |
5960 (defmacro efs-ls-parse-file-line () | |
5961 ;; Extract the filename, size, and permission string from the current | |
5962 ;; line of a dired-like listing. Assumes that the point is at | |
5963 ;; the beginning of the line, leaves it just before the size entry. | |
5964 ;; Returns a list (name size perm-string nlinks owner). | |
5965 ;; If there is no file on the line, returns nil. | |
5966 (` (let ((eol (save-excursion (end-of-line) (point))) | |
5967 name size modes nlinks owner) | |
5968 (skip-chars-forward " 0-9" eol) | |
5969 (and | |
5970 (looking-at efs-modes-links-owner-regexp) | |
5971 (setq modes (buffer-substring (match-beginning 1) | |
5972 (match-end 1)) | |
5973 nlinks (string-to-int (buffer-substring (match-beginning 2) | |
5974 (match-end 2))) | |
5975 owner (buffer-substring (match-beginning 3) (match-end 3))) | |
5976 (re-search-forward efs-month-and-time-regexp eol t) | |
5977 (setq name (buffer-substring (point) eol) | |
5978 size (string-to-int (buffer-substring (match-beginning 1) | |
5979 (match-end 1)))) | |
5980 (list name size modes nlinks owner))))) | |
5981 | |
5982 (defun efs-relist-symlink (host user symlink path switches) | |
5983 ;; Does a re-list of a single symlink in efs-data-buffer-name-2, | |
5984 ;; HOST = remote host | |
5985 ;; USER = remote username | |
5986 ;; SYMLINK = symbolic link name as a remote fullpath | |
5987 ;; PATH = efs full path syntax for the dir. being listed | |
5988 ;; SWITCHES = ls switches to use for the re-list | |
5989 ;; Returns (symlink-name symlink-target), as given by the listing. Returns | |
5990 ;; nil if the listing fails. | |
5991 ;; Does NOT correct for any symlink marking. | |
5992 (let* ((temp (efs-make-tmp-name host nil)) | |
5993 (temp-file (car temp)) | |
5994 (default-major-mode 'fundamental-mode) | |
5995 spot) | |
5996 (unwind-protect | |
5997 (and | |
5998 (prog1 | |
5999 (null | |
6000 (car | |
6001 (efs-send-cmd host user | |
6002 (list 'dir symlink (cdr temp) switches) | |
6003 (format "Listing %s" | |
6004 (efs-relativize-filename | |
6005 (efs-replace-path-component | |
6006 path symlink)))))) | |
6007 ;; Put the old message back. | |
6008 (if (and efs-verbose | |
6009 (not (and (boundp 'dired-in-query) dired-in-query))) | |
6010 (message "Listing %s..." | |
6011 (efs-relativize-filename path)))) | |
6012 (save-excursion | |
6013 (if (efs-ftp-path temp-file) | |
6014 (efs-add-file-entry (efs-host-type efs-gateway-host) | |
6015 temp-file nil nil nil)) | |
6016 (set-buffer (get-buffer-create efs-data-buffer-name-2)) | |
6017 (erase-buffer) | |
6018 (if (or (file-readable-p temp-file) | |
6019 (sleep-for efs-retry-time) | |
6020 (file-readable-p temp-file)) | |
6021 (let (efs-verbose) | |
6022 (insert-file-contents temp-file)) | |
6023 (efs-error host user | |
6024 (format | |
6025 "list data file %s not readable" temp-file))) | |
6026 (skip-chars-forward " 0-9") | |
6027 (and | |
6028 (eq (following-char) ?l) | |
6029 (re-search-forward efs-month-and-time-regexp nil t) | |
6030 (setq spot (point)) | |
6031 (re-search-forward " -> " nil t) | |
6032 (progn | |
6033 (end-of-line) | |
6034 (list | |
6035 ;; We might get the full path in the listing. | |
6036 (file-name-nondirectory | |
6037 (buffer-substring spot (match-beginning 0))) | |
6038 (buffer-substring (match-end 0) (point))))))) | |
6039 (efs-del-tmp-name temp-file)))) | |
6040 | |
6041 (defun efs-ls-sysV-p (host user dir linkname path) | |
6042 ;; Returns t if the symlink is listed in sysV style. i.e. The | |
6043 ;; symlink name is marked with an @. | |
6044 ;; HOST = remote host name | |
6045 ;; USER = remote user name | |
6046 ;; DIR = directory being listed as a remote full path. | |
6047 ;; LINKNAME = relative name of symbolic link as derived from an ls -..F... | |
6048 ;; this is assumed to end with an @ | |
6049 ;; PATH = efs full path synatx for the directory | |
6050 (let ((link (car (efs-relist-symlink | |
6051 host user | |
6052 (concat dir (substring linkname 0 -1)) | |
6053 path "-lFd" )))) | |
6054 (and link (string-equal link linkname)))) | |
6055 | |
6056 (defun efs-ls-next-p (host user dir linkname target path) | |
6057 ;; Returns t is the symlink is marked in the NeXT style. | |
6058 ;; i.e. The symlink destination is marked with an @. | |
6059 ;; This assumes that the host-type has already been identified | |
6060 ;; as NOT sysV-unix, and that target ends in an "@". | |
6061 ;; HOST = remote host name | |
6062 ;; USER = remote user name | |
6063 ;; DIR = remote directory being listed, as a remore full path | |
6064 ;; LINKNAME = relative name of symbolic link | |
6065 ;; Since we've eliminated sysV, it won't be marked with an @ | |
6066 ;; TARGET = target of symbolic link, as derived from an ls -..F.. | |
6067 ;; PATH = directory being listed in full efs path syntax. | |
6068 (let ((no-F-target (nth 1 (efs-relist-symlink | |
6069 host user | |
6070 (concat dir linkname) | |
6071 path "-ld")))) | |
6072 (and no-F-target | |
6073 (string-equal (concat no-F-target "@") target)))) | |
6074 | |
6075 ;; This deals with the F switch. Should also do something about | |
6076 ;; unquoting names obtained with the SysV b switch and the GNU Q | |
6077 ;; switch. See Sebastian's dired-get-filename. | |
6078 | |
6079 (defun efs-ls-parser (host-type host user dir path switches) | |
6080 ;; Meant to be called by efs-parse-listing. | |
6081 ;; Assumes that point is at the beginning of the first file line. | |
6082 ;; Assumes that SWITCHES has already been bound to nil for a dumb host. | |
6083 ;; HOST-TYPE is the remote host-type | |
6084 ;; HOST is the remote host name | |
6085 ;; USER is the remote user name | |
6086 ;; DIR is the remote directory as a full path | |
6087 ;; PATH is the directory in full efs syntax, and directory syntax. | |
6088 ;; SWITCHES is the ls listing switches | |
6089 (let ((tbl (efs-make-hashtable)) | |
6090 (used-F (and switches (string-match "F" switches))) | |
6091 (old-tbl (efs-get-files-hashtable-entry path)) | |
6092 file-type symlink directory file size modes nlinks owner) | |
6093 (while (setq file (efs-ls-parse-file-line)) | |
6094 (setq size (nth 1 file) | |
6095 modes (nth 2 file) | |
6096 nlinks (nth 3 file) | |
6097 owner (nth 4 file) | |
6098 file (car file) | |
6099 file-type (string-to-char modes) | |
6100 directory (eq file-type ?d)) | |
6101 (if (eq file-type ?l) | |
6102 (if (string-match " -> " file) | |
6103 (setq symlink (substring file (match-end 0)) | |
6104 file (substring file 0 (match-beginning 0))) | |
6105 ;; Shouldn't happen | |
6106 (setq symlink "")) | |
6107 (setq symlink nil)) | |
6108 (if used-F | |
6109 ;; The F-switch jungle | |
6110 (let ((socket (eq file-type ?s)) | |
6111 (fifo (eq file-type ?p)) | |
6112 (executable | |
6113 (and (not symlink) ; x bits don't mean a thing for symlinks | |
6114 (or (memq (elt modes 3) '(?x ?s ?t)) | |
6115 (memq (elt modes 6) '(?x ?s ?t)) | |
6116 (memq (elt modes 9) '(?x ?s ?t)))))) | |
6117 ;; Deal with marking of directories, executables, and sockets. | |
6118 (if (or (and executable (string-match "*$" file)) | |
6119 (and socket (string-match "=$" file)) | |
6120 (and fifo (string-match "|$" file))) | |
6121 (setq file (substring file 0 -1)) | |
6122 ;; Do the symlink dance. | |
6123 (if symlink | |
6124 (let ((fat-p (string-match "@$" file)) | |
6125 (sat-p (string-match "@$" symlink))) | |
6126 (cond | |
6127 ;; Those that mark the file | |
6128 ((and (memq host-type '(sysV-unix apollo-unix)) fat-p) | |
6129 (setq file (substring file 0 -1))) | |
6130 ;; Those that mark nothing | |
6131 ((memq host-type '(bsd-unix dumb-unix))) | |
6132 ;; Those that mark the target | |
6133 ((and (eq host-type 'next-unix) sat-p) | |
6134 (setq symlink (substring symlink 0 -1))) | |
6135 ;; We don't know | |
6136 ((eq host-type 'unix) | |
6137 (if fat-p | |
6138 (cond | |
6139 ((efs-ls-sysV-p host user dir | |
6140 file path) | |
6141 (setq host-type 'sysV-unix | |
6142 file (substring file 0 -1)) | |
6143 (efs-add-host 'sysV-unix host) | |
6144 (efs-add-listing-type 'sysV-unix host user)) | |
6145 ((and sat-p | |
6146 (efs-ls-next-p host user dir file symlink | |
6147 path)) | |
6148 (setq host-type 'next-unix | |
6149 symlink (substring symlink 0 -1)) | |
6150 (efs-add-host 'next-unix host) | |
6151 (efs-add-listing-type 'next-unix host user)) | |
6152 (t | |
6153 (setq host-type 'bsd-unix) | |
6154 (efs-add-host 'bsd-unix host) | |
6155 (efs-add-listing-type 'bsd-unix host user))) | |
6156 (if (and sat-p | |
6157 (efs-ls-next-p host user dir file | |
6158 symlink path)) | |
6159 (progn | |
6160 (setq host-type 'next-unix | |
6161 symlink (substring symlink 0 -1)) | |
6162 (efs-add-host 'next-unix host) | |
6163 (efs-add-listing-type 'next-unix host user)) | |
6164 (setq host-type 'bsd-unix) | |
6165 (efs-add-host 'bsd-unix host) | |
6166 (efs-add-listing-type 'bsd-unix host user))))) | |
6167 ;; Look out for marking of symlink | |
6168 ;; If we really wanted to, at this point we | |
6169 ;; could distinguish aix from hp-ux, ultrix, irix and a/ux, | |
6170 ;; allowing us to skip the re-list in the future, for the | |
6171 ;; later 4 host types. Another version... | |
6172 (if (string-match "[=|*]$" symlink) | |
6173 (let ((relist (efs-relist-symlink | |
6174 host user (concat dir file) | |
6175 path "-dl"))) | |
6176 (if relist (setq symlink (nth 1 relist)))))))))) | |
6177 ;; Strip / off the end unconditionally. It's not a valid file character | |
6178 ;; anyway. | |
6179 (if (string-match "/$" file) (setq file (substring file 0 -1))) | |
6180 (let ((mdtm (and old-tbl (nth 5 (efs-get-hash-entry file old-tbl))))) | |
6181 (if mdtm | |
6182 (efs-put-hash-entry file (list (or symlink directory) size owner | |
6183 modes nlinks mdtm) tbl) | |
6184 (efs-put-hash-entry file (list (or symlink directory) size owner | |
6185 modes nlinks) tbl))) | |
6186 (forward-line 1)) | |
6187 (efs-put-hash-entry "." '(t) tbl) | |
6188 (efs-put-hash-entry ".." '(t) tbl) | |
6189 tbl)) | |
6190 | |
6191 (efs-defun efs-parse-listing nil (host user dir path &optional switches) | |
6192 ;; Parse the a listing which is assumed to be from some type of unix host. | |
6193 ;; Note that efs-key will be bound to the actual host type. | |
6194 ;; HOST = remote host name | |
6195 ;; USER = remote user name | |
6196 ;; DIR = directory as a remote full path | |
6197 ;; PATH = directory in full efs path syntax | |
6198 ;; SWITCHES = ls switches used for the listing | |
6199 (efs-save-match-data | |
6200 (cond | |
6201 ;; look for total line | |
6202 ((looking-at "^total [0-9]+$") | |
6203 (forward-line 1) | |
6204 ;; Beware of machines that put a blank line after the totals line. | |
6205 (skip-chars-forward " \t\n") | |
6206 (efs-ls-parser efs-key host user dir path switches)) | |
6207 ;; look for errors | |
6208 ((looking-at "[^\n]+\\( not found\\|: Not a directory\\)\n\\'") | |
6209 ;; It's an ls error message. | |
6210 nil) | |
6211 ((eobp) ; i.e. zerop buffer-size | |
6212 nil) ; assume an ls error message | |
6213 ;; look for listings without total lines | |
6214 ((re-search-forward efs-month-and-time-regexp nil t) | |
6215 (beginning-of-line) | |
6216 (efs-ls-parser efs-key host user dir path switches)) | |
6217 (t nil)))) | |
6218 | |
6219 (efs-defun efs-parse-listing unix:unknown | |
6220 (host user dir path &optional switches) | |
6221 ;; Parse the a listing which is assumed to be from some type of unix host, | |
6222 ;; possibly one doing a dl listing. | |
6223 ;; HOST = remote host name | |
6224 ;; USER = remote user name | |
6225 ;; DIR = directory as a remote full path | |
6226 ;; PATH = directory in full efs path syntax | |
6227 ;; SWITCHES = ls switches used for the listing | |
6228 (efs-save-match-data | |
6229 (cond | |
6230 ;; look for total line | |
6231 ((looking-at "^total [0-9]+$") | |
6232 (forward-line 1) | |
6233 ;; Beware of machines that put a blank line after the totals line. | |
6234 (skip-chars-forward " \t\n") | |
6235 ;; This will make the listing-type track the host-type. | |
6236 (efs-add-listing-type nil host user) | |
6237 (efs-ls-parser 'unix host user dir path switches)) | |
6238 ;; look for errors | |
6239 ((looking-at "[^\n]+\\( not found\\|: Not a directory\\)\n\\'") | |
6240 ;; It's an ls error message. | |
6241 nil) | |
6242 ((eobp) ; i.e. zerop buffer-size | |
6243 nil) ; assume an ls error message | |
6244 ;; look for listings without total lines | |
6245 ((and (re-search-forward efs-month-and-time-regexp nil t) | |
6246 (progn | |
6247 (beginning-of-line) | |
6248 (looking-at efs-modes-links-owner-regexp))) | |
6249 (efs-add-listing-type nil host user) | |
6250 (efs-ls-parser 'unix host user dir path switches)) | |
6251 ;; look for dumb listings | |
6252 ((re-search-forward | |
6253 (concat (regexp-quote switches) | |
6254 " not found\\|\\(^ls: +illegal option -- \\)") | |
6255 (save-excursion (end-of-line) (point)) t) | |
6256 (if (eq (efs-host-type host) 'apollo-unix) | |
6257 (progn | |
6258 (efs-add-host 'dumb-apollo-unix host) | |
6259 (efs-add-listing-type 'dumb-apollo-unix host user)) | |
6260 (efs-add-host 'dumb-unix host) | |
6261 (efs-add-listing-type 'dumb-unix host user)) | |
6262 (if (match-beginning 1) | |
6263 ;; Need to try to list again. | |
6264 (let ((efs-ls-uncache t)) | |
6265 (efs-ls | |
6266 path nil (format "Relisting %s" (efs-relativize-filename path)) t) | |
6267 (goto-char (point-min)) | |
6268 (efs-parse-listing nil host user dir path switches)) | |
6269 (if (re-search-forward "^total [0-9]+$" nil t) | |
6270 (progn | |
6271 (beginning-of-line) | |
6272 (delete-region (point-min) (point)) | |
6273 (forward-line 1) | |
6274 (efs-ls-parser 'dumb-unix host user dir path switches))))) | |
6275 ;; Look for dl listings. | |
6276 ((re-search-forward efs-unix:dl-listing-regexp nil t) | |
6277 (efs-add-host 'unix host) | |
6278 (efs-add-listing-type 'unix:dl host user) | |
6279 (efs-parse-listing 'unix:dl host user dir path switches)) | |
6280 ;; don't know, return nil | |
6281 (t nil)))) | |
6282 | |
6283 (defun efs-ls-parse-1-liner (filename buffer &optional symlink) | |
6284 ;; Parse a 1-line listing for FILENAME in BUFFER, and update | |
6285 ;; the cached info for FILENAME. | |
6286 ;; Optional SYMLINK arg gives the expected target of a symlink. | |
6287 ;; Since one-line listings are usually used to update info for | |
6288 ;; newly created files, we usually know what sort of a file to expect. | |
6289 ;; Actually trying to parse out the symlink target could be impossible | |
6290 ;; for some types of switches. | |
6291 (efs-save-buffer-excursion | |
6292 (set-buffer buffer) | |
6293 (goto-char (point-min)) | |
6294 (skip-chars-forward " 0-9") | |
6295 (efs-save-match-data | |
6296 (let (modes nlinks owner size) | |
6297 (and | |
6298 (looking-at efs-modes-links-owner-regexp) | |
6299 (setq modes (buffer-substring (match-beginning 1) (match-end 1)) | |
6300 nlinks (string-to-int (buffer-substring (match-beginning 2) | |
6301 (match-end 2))) | |
6302 owner (buffer-substring (match-beginning 3) (match-end 3))) | |
6303 (re-search-forward efs-month-and-time-regexp nil t) | |
6304 (setq size (string-to-int (buffer-substring (match-beginning 1) | |
6305 (match-end 1)))) | |
6306 (let* ((filename (directory-file-name filename)) | |
6307 (files (efs-get-files-hashtable-entry | |
6308 (file-name-directory filename)))) | |
6309 (if files | |
6310 (let* ((key (efs-get-file-part filename)) | |
6311 (ignore-case (memq (efs-host-type | |
6312 (car (efs-ftp-path filename))) | |
6313 efs-case-insensitive-host-types)) | |
6314 (ent (efs-get-hash-entry key files ignore-case)) | |
6315 (mdtm (nth 5 ent)) | |
6316 type) | |
6317 (if (= (string-to-char modes) ?l) | |
6318 (setq type | |
6319 (cond | |
6320 ((stringp symlink) | |
6321 symlink) | |
6322 ((stringp (car ent)) | |
6323 (car ent)) | |
6324 (t ; something weird happened. | |
6325 ""))) | |
6326 (if (= (string-to-char modes) ?d) | |
6327 (setq type t))) | |
6328 (efs-put-hash-entry | |
6329 key (list type size owner modes nlinks mdtm) | |
6330 files ignore-case))))))))) | |
6331 | |
6332 (efs-defun efs-update-file-info nil (file buffer &optional symlink) | |
6333 "For FILE, update cache information from a single file listing in BUFFER." | |
6334 ;; By default, this does nothing. | |
6335 nil) | |
6336 | |
6337 (efs-defun efs-update-file-info unix (file buffer &optional symlink) | |
6338 (efs-ls-parse-1-liner file buffer)) | |
6339 (efs-defun efs-update-file-info sysV-unix (file buffer &optional symlink) | |
6340 (efs-ls-parse-1-liner file buffer)) | |
6341 (efs-defun efs-update-file-info bsd-unix (file buffer &optional symlink) | |
6342 (efs-ls-parse-1-liner file buffer)) | |
6343 (efs-defun efs-update-file-info next-unix (file buffer &optional symlink) | |
6344 (efs-ls-parse-1-liner file buffer)) | |
6345 (efs-defun efs-update-file-info apollo-unix (file buffer &optional symlink) | |
6346 (efs-ls-parse-1-liner file buffer)) | |
6347 (efs-defun efs-update-file-info dumb-unix (file buffer &optional symlink) | |
6348 (efs-ls-parse-1-liner file buffer)) | |
6349 (efs-defun efs-update-file-info dumb-apollo-unix | |
6350 (file buffer &optional symlink) | |
6351 (efs-ls-parse-1-liner file buffer)) | |
6352 (efs-defun efs-update-file-info super-dumb-unix (file buffer &optional symlink) | |
6353 (efs-ls-parse-1-liner file buffer)) | |
6354 | |
6355 ;;;; ---------------------------------------------------------------- | |
6356 ;;;; The 'unknown listing parser. This does some host-type guessing. | |
6357 ;;;; ---------------------------------------------------------------- | |
6358 | |
6359 ;;; Regexps for host and listing type guessing from the listing syntax. | |
6360 | |
6361 (defconst efs-ka9q-listing-regexp | |
6362 (concat | |
6363 "^\\([0-9,.]+\\|No\\) files\\. [0-9,.]+ bytes free\\. " | |
6364 "Disk size [0-9,]+ bytes\\.$")) | |
6365 ;; This version of the regexp is really for hosts which allow some switches, | |
6366 ;; but not ours. Rather than determine which switches we could be using | |
6367 ;; we just assume that it's dumb. | |
6368 (defconst efs-dumb-unix-listing-regexp | |
6369 (concat | |
6370 "^[Uu]sage: +ls +-[a-zA-Z0-9]+[ \n]\\|" | |
6371 ;; Unitree server | |
6372 "^Error getting stats for \"-[a-zA-Z0-9]+\"")) | |
6373 | |
6374 (defconst efs-dos-distinct-date-and-time-regexp | |
6375 (concat | |
6376 " \\(Jan\\|Feb\\|Mar\\|Apr\\|May\\|Jun\\|Jul\\|Aug\\|Sep\\|Oct" | |
6377 "\\|Nov\\|Dec\\) [ 0-3][0-9],[12][90][0-9][0-9] " | |
6378 "[ 12][0-9]:[0-5][0-9] ")) | |
6379 ;; Regexp to match the output from the hellsoft ftp server to an | |
6380 ;; ls -al. Unfortunately, this looks a lot like some unix ls error | |
6381 ;; messages. | |
6382 (defconst efs-hell-listing-regexp | |
6383 (concat | |
6384 "ls: file or directory not found\n\\'\\|" | |
6385 "[-d]\\[[-A-Z][-A-Z][-A-Z][-A-Z][-A-Z][-A-Z][-A-Z]\\]")) | |
6386 | |
6387 (efs-defun efs-parse-listing unknown | |
6388 (host user dir path &optional switches) | |
6389 "Parse the current buffer which is assumed to contain a dir listing. | |
6390 Return a hashtable as the result. If the listing is not really a | |
6391 directory listing, then return nil. | |
6392 | |
6393 HOST is the remote host's name. | |
6394 USER is the remote user name. | |
6395 DIR is the directory as a full remote path. | |
6396 PATH is the directory in full efs path synatx. | |
6397 SWITCHES are the switches passed to ls. If SWITCHES is nil, then a | |
6398 dumb \(with dir\) listing has been done." | |
6399 (efs-save-match-data | |
6400 (cond | |
6401 | |
6402 ;; look for total line | |
6403 ((looking-at "^total [0-9]+$") | |
6404 (efs-add-host 'unix host) | |
6405 (forward-line 1) | |
6406 ;; Beware of machines that put a blank line after the totals line. | |
6407 (skip-chars-forward " \t\n") | |
6408 (efs-ls-parser 'unix host user dir path switches)) | |
6409 | |
6410 ;; Look for hellsoft. Need to do this before looking | |
6411 ;; for ls errors, since the hellsoft output looks a lot like an ls error. | |
6412 ((looking-at efs-hell-listing-regexp) | |
6413 (if (null (car (efs-send-cmd host user '(quote site dos)))) | |
6414 (let* ((key (concat host "/" user "/~")) | |
6415 (tilde (efs-get-hash-entry | |
6416 key efs-expand-dir-hashtable))) | |
6417 (efs-add-host 'hell host) | |
6418 ;; downcase the expansion of ~ | |
6419 (if (and tilde (string-match "^[^a-z]+$" tilde)) | |
6420 (efs-put-hash-entry key (downcase tilde) | |
6421 efs-expand-dir-hashtable)) | |
6422 ;; Downcase dir, in case its got some upper case stuff in it. | |
6423 (setq dir (downcase dir) | |
6424 path (efs-replace-path-component path dir)) | |
6425 (let ((efs-ls-uncache t)) | |
6426 ;; This will force the data buffer to be re-filled | |
6427 (efs-ls path nil (format "Relisting %s" | |
6428 (efs-relativize-filename path)) | |
6429 t)) | |
6430 (efs-parse-listing 'hell host user dir path)) | |
6431 ;; Don't know, give unix a try. | |
6432 (efs-add-host 'unix host) | |
6433 nil)) | |
6434 | |
6435 ;; look for ls errors | |
6436 ((looking-at "[^\n]+\\( not found\\|: Not a directory\\)\n\\'") | |
6437 ;; It's an ls error message. | |
6438 (efs-add-host 'unix host) | |
6439 nil) | |
6440 | |
6441 ((eobp) ; i.e. (zerop (buffer-size)) | |
6442 ;; This could be one of: | |
6443 ;; (1) An Ultrix ls error message | |
6444 ;; (2) A listing with the A switch of an empty directory | |
6445 ;; on a machine which doesn't give a total line. | |
6446 ;; (3) The result of an attempt at an nlist. (This would mean a | |
6447 ;; dumb host.) | |
6448 ;; (4) The twilight zone. | |
6449 (cond | |
6450 ((save-excursion | |
6451 (set-buffer (process-buffer | |
6452 (efs-get-process host user))) | |
6453 (save-excursion | |
6454 (goto-char (point-max)) | |
6455 (and | |
6456 ;; The dir ftp output starts with a 200 cmd. | |
6457 (re-search-backward "^150 " nil t) | |
6458 ;; We never do an nlist (it's a short listing). | |
6459 ;; If the machine thinks that we did, it's dumb. | |
6460 (looking-at "[^\n]+ NLST ")))) | |
6461 ;; It's dumb-unix or ka9q. Anything else? | |
6462 ;; This will re-fill the data buffer with a dumb listing. | |
6463 (let ((efs-ls-uncache t)) | |
6464 (efs-ls path nil (format "Relisting %s" | |
6465 (efs-relativize-filename path)) | |
6466 t)) | |
6467 (cond | |
6468 ;; check for dumb-unix | |
6469 ((re-search-forward efs-month-and-time-regexp nil t) | |
6470 (efs-add-host 'dumb-unix host) | |
6471 (beginning-of-line) | |
6472 (efs-parse-listing 'dumb-unix host user dir path)) | |
6473 ;; check for ka9q | |
6474 ((save-excursion | |
6475 (goto-char (point-max)) | |
6476 (forward-line -1) | |
6477 (looking-at efs-ka9q-listing-regexp)) | |
6478 (efs-add-host 'ka9q host) | |
6479 (efs-parse-listing 'ka9q host user dir path)) | |
6480 (t ; Don't know, try unix. | |
6481 (efs-add-host 'unix host) | |
6482 nil))) | |
6483 ;; check for Novell Netware | |
6484 ((null (car (efs-send-cmd host user '(quote site nfs)))) | |
6485 (efs-add-host 'netware host) | |
6486 (let ((efs-ls-uncache t)) | |
6487 (efs-ls path nil (format "Relisting %s" | |
6488 (efs-relativize-filename path)) | |
6489 t)) | |
6490 (efs-parse-listing 'netware host user dir path)) | |
6491 (t | |
6492 ;; Assume (1), an Ultrix error message. | |
6493 (efs-add-host 'unix host) | |
6494 nil))) | |
6495 | |
6496 ;; unix without a total line | |
6497 ((re-search-forward efs-month-and-time-regexp nil t) | |
6498 (efs-add-host 'unix host) | |
6499 (beginning-of-line) | |
6500 (efs-ls-parser 'unix host user dir path switches)) | |
6501 | |
6502 ;; Now we look for host-types, or listing-types which are auto-rec | |
6503 ;; by the listing parser, because it's not possible to pick them out | |
6504 ;; from a pwd. | |
6505 | |
6506 ;; check for dumb-unix | |
6507 ;; (Guessing of dumb-unix hosts which return an ftp error message is | |
6508 ;; done in efs-ls.) | |
6509 ((re-search-forward efs-dumb-unix-listing-regexp nil t) | |
6510 (efs-add-host 'dumb-unix host) | |
6511 ;; This will force the data buffer to be re-filled | |
6512 (let ((efs-ls-uncache t)) | |
6513 (efs-ls path nil (format "Relisting %s" | |
6514 (efs-relativize-filename path)) | |
6515 t)) | |
6516 (efs-parse-listing 'dumb-unix host user dir path)) | |
6517 | |
6518 ;; check for Distinct's DOS ftp server | |
6519 ((re-search-forward efs-dos-distinct-date-and-time-regexp nil t) | |
6520 (efs-add-host 'dos-distinct host) | |
6521 (efs-parse-listing 'dos-distinct host user dir path)) | |
6522 | |
6523 ;; check for KA9Q pseudo-unix (LINUX?) | |
6524 ((save-excursion | |
6525 (goto-char (point-max)) | |
6526 (forward-line -1) | |
6527 (looking-at efs-ka9q-listing-regexp)) | |
6528 (efs-add-host 'ka9q host) | |
6529 ;; This will re-fill the data buffer. | |
6530 ;; Need to do this because ka9q is a dumb host. | |
6531 (let ((efs-ls-uncache t)) | |
6532 (efs-ls path nil (format "Relisting %s" | |
6533 (efs-relativize-filename path)) | |
6534 t)) | |
6535 (efs-parse-listing 'ka9q host user dir path)) | |
6536 | |
6537 ;; Check for a unix descriptive (dl) listing | |
6538 ;; Do this last, because it's hard to guess. | |
6539 ((re-search-forward efs-unix:dl-listing-regexp nil t) | |
6540 (efs-add-host 'unix host) | |
6541 (efs-add-listing-type 'unix:dl host user) | |
6542 (efs-parse-listing 'unix:dl host user dir path switches)) | |
6543 | |
6544 ;; Don't know what's going on. Return nil, and assume unix. | |
6545 (t | |
6546 (efs-add-host 'unix host) | |
6547 nil)))) | |
6548 | |
6549 ;;;; ------------------------------------------------------------ | |
6550 ;;;; Directory information hashtable. | |
6551 ;;;; ------------------------------------------------------------ | |
6552 | |
6553 (efs-defun efs-really-file-p nil (file ent) | |
6554 ;; efs-files-hashtable sometimes contains fictitious entries, when | |
6555 ;; some OS's allow a file to be accessed by another name. For example, | |
6556 ;; in VMS the highest version of a file may be accessed by omitting the | |
6557 ;; the file version number. This function should return t if the | |
6558 ;; filename FILE is really a file. ENT is the hash entry of the file. | |
6559 t) | |
6560 | |
6561 (efs-defun efs-add-file-entry nil (path type size owner | |
6562 &optional modes nlinks mdtm) | |
6563 ;; Add a new file entry for PATH | |
6564 ;; TYPE is nil for a plain file, t for a directory, and a string | |
6565 ;; (the target of the link) for a symlink. | |
6566 ;; SIZE is the size of the file in bytes. | |
6567 ;; OWNER is the owner of the file, as a string. | |
6568 ;; MODES is the file modes, as a string. In Unix, this will be 10 cars. | |
6569 ;; NLINKS is the number of links for the file. | |
6570 ;; MDTM is the last modtime obtained for the file. This is for | |
6571 ;; short-term cache only, as emacs often has sequences of functions | |
6572 ;; doing modtime lookup. If you really want to be sure of the modtime, | |
6573 ;; use efs-get-file-mdtm, which asks the remote server. | |
6574 | |
6575 (and (eq type t) | |
6576 (setq path (directory-file-name path))) | |
6577 (let ((files (efs-get-files-hashtable-entry (file-name-directory path)))) | |
6578 (if files | |
6579 (efs-put-hash-entry | |
6580 (efs-get-file-part path) | |
6581 (cond (mdtm | |
6582 (list type size owner modes nlinks | |
6583 mdtm)) | |
6584 (nlinks | |
6585 (list type size owner modes nlinks)) | |
6586 (modes (list type size owner modes)) | |
6587 (t (list type size owner))) | |
6588 files | |
6589 (memq efs-key efs-case-insensitive-host-types))) | |
6590 (efs-del-from-ls-cache path t nil))) | |
6591 | |
6592 (efs-defun efs-delete-file-entry nil (path &optional dir-p) | |
6593 "Delete the file entry for PATH, if its directory info exists." | |
6594 (if dir-p | |
6595 (progn | |
6596 (setq path (file-name-as-directory path)) | |
6597 (efs-del-hash-entry (efs-canonize-file-name path) | |
6598 efs-files-hashtable) | |
6599 ;; Note that file-name-as-directory followed by | |
6600 ;; (substring path 0 -1) | |
6601 ;; serves to canonicalize directory file names to their unix form. | |
6602 ;; i.e. in VMS, FOO.DIR -> FOO/ -> FOO | |
6603 ;; PATH is supposed to be s fully expanded efs-style path. | |
6604 (setq path (substring path 0 -1)))) | |
6605 (let ((files (efs-get-files-hashtable-entry (file-name-directory path)))) | |
6606 (if files | |
6607 (efs-del-hash-entry | |
6608 (efs-get-file-part path) | |
6609 files | |
6610 (memq (efs-host-type (car (efs-ftp-path path))) | |
6611 efs-case-insensitive-host-types)))) | |
6612 (efs-del-from-ls-cache path t nil) | |
6613 (if dir-p (efs-del-from-ls-cache path nil t))) | |
6614 | |
6615 (defun efs-set-files (directory files) | |
6616 "For DIRECTORY, set or change the associated FILES hashtable." | |
6617 (if files | |
6618 (efs-put-hash-entry | |
6619 (efs-canonize-file-name (file-name-as-directory directory)) | |
6620 files efs-files-hashtable))) | |
6621 | |
6622 (defun efs-parsable-switches-p (switches &optional full-dir) | |
6623 ;; Returns non-nil if SWITCHES would give an ls listing suitable for parsing | |
6624 ;; If FULL-DIR is non-nil, the switches must be suitable for parsing a full | |
6625 ;; ditectory. | |
6626 (or (null switches) | |
6627 (efs-save-match-data | |
6628 (and (string-match "[aA]" switches) | |
6629 ;; g is not good enough, need l or o for owner. | |
6630 (string-match "[lo]" switches) | |
6631 ;; L shows link target, rather than link. We need both. | |
6632 (not (string-match "[RfL]" switches)) | |
6633 (not (and full-dir (string-match "d" switches))))))) | |
6634 | |
6635 (defun efs-get-files (directory &optional no-error) | |
6636 "For DIRECTORY, return a hashtable of file entries. | |
6637 This will give an error or return nil, depending on the value of | |
6638 NO-ERROR, if a listing for DIRECTORY cannot be obtained." | |
6639 (let ((directory (file-name-as-directory directory))) | |
6640 (or (efs-get-files-hashtable-entry directory) | |
6641 (and (efs-ls directory (efs-ls-guess-switches) t 'parse no-error) | |
6642 (efs-get-files-hashtable-entry directory))))) | |
6643 | |
6644 (efs-defun efs-allow-child-lookup nil (host user dir file) | |
6645 ;; Returns non-nil if in directory DIR, FILE could possibly be a subdir | |
6646 ;; according to its file-name syntax, and therefore a child listing should | |
6647 ;; be attempted. Note that DIR is in directory syntax. | |
6648 ;; i.e. /foo/bar/, not /foo/bar. | |
6649 ;; Deal with dired. Anything else? | |
6650 (not (and (boundp 'dired-local-variables-file) | |
6651 (stringp dired-local-variables-file) | |
6652 (string-equal dired-local-variables-file file)))) | |
6653 | |
6654 (defmacro efs-ancestral-check (host-type path ignore-case) | |
6655 ;; Checks to see if something in a path's ancient parentage | |
6656 ;; would make it impossible for the path to exist in the directory | |
6657 ;; tree. In this case it returns nil. Otherwise returns t (there | |
6658 ;; is essentially no information returned in this case, the file | |
6659 ;; may exist or not). | |
6660 ;; This macro should make working with RCS more efficient. | |
6661 ;; It also helps with FTP servers that go into fits if we ask to | |
6662 ;; list a non-existent dir. | |
6663 ;; Yes, I know that the function mapped over the hashtable can | |
6664 ;; be written more cleanly with a concat, but this is faster. | |
6665 ;; concat's cause a lot of consing. So do regexp-quote's, but we can't | |
6666 ;; avoid it. | |
6667 ;; Probably doesn't make much sense for this to be an efs-defun, since | |
6668 ;; the host-type dependence is very mild. | |
6669 (` | |
6670 (let ((path (, path)) ; expand once | |
6671 (ignore-case (, ignore-case)) | |
6672 str) | |
6673 ;; eliminate flat file systems -- should have a constant for this | |
6674 (or (memq (, host-type) '(mts cms mvs cms-knet)) | |
6675 (efs-save-match-data | |
6676 (catch 'foo | |
6677 (efs-map-hashtable | |
6678 (function | |
6679 (lambda (key val) | |
6680 (and (eq (string-match (regexp-quote key) path) 0) | |
6681 (setq str (substring path (match-end 0))) | |
6682 (string-match "^[^/]+" str) | |
6683 (not (efs-hash-entry-exists-p | |
6684 (substring str 0 (match-end 0)) | |
6685 val ignore-case)) | |
6686 (throw 'foo nil)))) | |
6687 efs-files-hashtable) | |
6688 t)))))) | |
6689 | |
6690 (defun efs-file-entry-p (path) | |
6691 ;; Return whether there is a file entry for PATH. | |
6692 ;; Under no circumstances does this cause FTP activity. | |
6693 (let* ((path (directory-file-name (efs-canonize-file-name path))) | |
6694 (dir (file-name-directory path)) | |
6695 (file (efs-get-file-part path)) | |
6696 (tbl (efs-get-files-hashtable-entry dir))) | |
6697 (and tbl (efs-hash-entry-exists-p | |
6698 file tbl | |
6699 (memq (efs-host-type (car (efs-ftp-path dir))) | |
6700 efs-case-insensitive-host-types)) t))) | |
6701 | |
6702 (defun efs-get-file-entry (path) | |
6703 "Return the given file entry for PATH. | |
6704 This is a list of the form \(type size owner modes nlinks modtm\), | |
6705 where type is nil for a normal file, t for a directory, and a string for a | |
6706 symlink, size is the size of the file in bytes, if known, and modes are | |
6707 the permission modes of the file as a string. modtm is short-term the | |
6708 cache of the file modtime. It is not used by `verify-visited-file-modtime'. | |
6709 If the file isn't in the hashtable, this returns nil." | |
6710 (let* ((path (directory-file-name (efs-canonize-file-name path))) | |
6711 (dir (file-name-directory path)) | |
6712 (file (efs-get-file-part path)) | |
6713 (parsed (efs-ftp-path dir)) | |
6714 (host (car parsed)) | |
6715 (host-type (efs-host-type host)) | |
6716 (ent (efs-get-files-hashtable-entry dir)) | |
6717 (ignore-case (memq host-type efs-case-insensitive-host-types))) | |
6718 (if ent | |
6719 (efs-get-hash-entry file ent ignore-case) | |
6720 (let ((user (nth 1 parsed)) | |
6721 (r-dir (nth 2 parsed))) | |
6722 (and (efs-ancestral-check host-type path ignore-case) | |
6723 (or (and efs-allow-child-lookup | |
6724 (efs-allow-child-lookup host-type | |
6725 host user r-dir file) | |
6726 (setq ent (efs-get-files path t)) | |
6727 (efs-get-hash-entry "." ent)) | |
6728 ;; i.e. it's a directory by child lookup | |
6729 (efs-get-hash-entry | |
6730 file (efs-get-files dir) ignore-case))))))) | |
6731 | |
6732 (defun efs-wipe-file-entries (host user) | |
6733 "Remove cache data for all files on HOST and USER. | |
6734 This replaces the file entry information hashtable with one that | |
6735 doesn't have any entries for the given HOST, USER pair." | |
6736 (let ((new-tbl (efs-make-hashtable (length efs-files-hashtable))) | |
6737 (host (downcase host)) | |
6738 (case-fold (memq (efs-host-type host) | |
6739 efs-case-insensitive-host-types))) | |
6740 (if case-fold (setq user (downcase user))) | |
6741 (efs-map-hashtable | |
6742 (function | |
6743 (lambda (key val) | |
6744 (let ((parsed (efs-ftp-path key))) | |
6745 (if parsed | |
6746 (let ((h (nth 0 parsed)) | |
6747 (u (nth 1 parsed))) | |
6748 (or (and (string-equal host (downcase h)) | |
6749 (string-equal user (if case-fold (downcase u) u))) | |
6750 (efs-put-hash-entry key val new-tbl))))))) | |
6751 efs-files-hashtable) | |
6752 (setq efs-files-hashtable new-tbl))) | |
6753 | |
6754 | |
6755 ;;;; ============================================================ | |
6756 ;;;; >8 | |
6757 ;;;; Redefinitions of standard GNU Emacs functions. | |
6758 ;;;; ============================================================ | |
6759 | |
6760 ;;;; ------------------------------------------------------------ | |
6761 ;;;; expand-file-name and friends... | |
6762 ;;;; ------------------------------------------------------------ | |
6763 | |
6764 ;; New filename expansion code for efs. | |
6765 ;; The overall structure is based around the following internal | |
6766 ;; functions and macros. Since these are internal, they do NOT | |
6767 ;; call efs-save-match-data. This is done by their calling | |
6768 ;; function. | |
6769 ;; | |
6770 ;; efs-expand-tilde | |
6771 ;; - expands all ~ constructs, both local and remote. | |
6772 ;; efs-short-circuit-file-name | |
6773 ;; - short-circuits //'s and /~'s, for both local and remote paths. | |
6774 ;; efs-de-dot-file-name | |
6775 ;; - canonizes /../ and /./'s in both local and remote paths. | |
6776 ;; | |
6777 ;; The following two functions overload existing emacs functions. | |
6778 ;; They are the entry points to this filename expansion code, and as such | |
6779 ;; call efs-save-match-data. | |
6780 ;; | |
6781 ;; efs-expand-file-name | |
6782 ;; efs-substitute-in-file-name | |
6783 | |
6784 ;;; utility macros | |
6785 | |
6786 (defmacro efs-short-circuit-file-name (filename) | |
6787 ;; Short-circuits //'s and /~'s in filenames. | |
6788 ;; Returns a list consisting of the local path, | |
6789 ;; host-type, host, user. For local hosts, | |
6790 ;; host-type, host, and user are all nil. | |
6791 (` | |
6792 (let ((start 0) | |
6793 (string (, filename)) | |
6794 backskip regexp lbackskip | |
6795 lregexp parsed host-type host user) | |
6796 | |
6797 (if efs-local-apollo-unix | |
6798 (setq lregexp ".//+" | |
6799 lbackskip 2) | |
6800 (setq lregexp "//+" | |
6801 lbackskip 1)) | |
6802 | |
6803 ;; Short circuit /user@mach: roots. It is important to do this | |
6804 ;; now to avoid unnecessary ftp connections. | |
6805 | |
6806 (while (string-match efs-path-root-short-circuit-regexp string start) | |
6807 (setq start (1+ (match-beginning 0)))) | |
6808 (or (zerop start) (setq string (substring string start) | |
6809 start 0)) | |
6810 | |
6811 ;; identify remote root | |
6812 | |
6813 (if (setq parsed (efs-ftp-path-macro string)) | |
6814 (if (memq (setq string (nth 2 parsed) | |
6815 host-type | |
6816 (efs-host-type (setq host (car parsed)) | |
6817 (setq user (nth 1 parsed)))) | |
6818 '(apollo-unix dumb-apollo-unix)) | |
6819 (setq regexp ".//+" | |
6820 backskip 2) | |
6821 (setq regexp "//+" | |
6822 backskip 1)) | |
6823 (setq regexp lregexp | |
6824 backskip lbackskip)) | |
6825 | |
6826 ;; Now short-circuit in an apollo and efs sensitive way. | |
6827 | |
6828 (while (cond ((string-match regexp string start) | |
6829 (setq start (- (match-end 0) backskip))) | |
6830 ((string-match "/~" string start) | |
6831 (setq start (1- (match-end 0))))) | |
6832 | |
6833 (and host-type | |
6834 (null efs-short-circuit-to-remote-root) | |
6835 (setq host-type nil | |
6836 regexp lregexp | |
6837 backskip lbackskip))) | |
6838 (or (zerop start) (setq string (substring string start))) | |
6839 (list string host-type (and host-type host) (and host-type user))))) | |
6840 | |
6841 (defmacro efs-expand-tilde (tilde host-type host user) | |
6842 ;; Expands a TILDE (~ or ~sandy type construction) | |
6843 ;; Takes as an arg a filename (not directory name!) | |
6844 ;; and returns a filename. HOST-TYPE is the type of remote host. | |
6845 ;; nil is the type of the local host. | |
6846 (` | |
6847 (if (, host-type) ; nil host-type is the local machine | |
6848 (let* ((host (downcase (, host))) | |
6849 (host-type (, host-type)) | |
6850 (ignore-case (memq host-type | |
6851 efs-case-insensitive-host-types)) | |
6852 (tilde (, tilde)) | |
6853 (user (, user)) | |
6854 (key (concat host "/" user "/" tilde)) | |
6855 (res (efs-get-hash-entry | |
6856 key efs-expand-dir-hashtable ignore-case))) | |
6857 (or res | |
6858 ;; for real accounts on unix systems, use the get trick | |
6859 (and (not (efs-anonymous-p user)) | |
6860 (memq host-type efs-unix-host-types) | |
6861 (let ((line (nth 1 (efs-send-cmd | |
6862 host user | |
6863 (list 'get tilde "/dev/null") | |
6864 (format "expanding %s" tilde))))) | |
6865 (setq res | |
6866 (and (string-match efs-expand-dir-msgs line) | |
6867 (substring line | |
6868 (match-beginning 1) | |
6869 (match-end 1)))) | |
6870 (if res | |
6871 (progn | |
6872 (setq res (efs-internal-directory-file-name res)) | |
6873 (efs-put-hash-entry | |
6874 key res efs-expand-dir-hashtable ignore-case) | |
6875 res)))) | |
6876 (progn | |
6877 (setq res | |
6878 (if (string-equal tilde "~") | |
6879 (car (efs-send-pwd | |
6880 host-type host user)) | |
6881 (let* ((home-key (concat host "/" user "/~")) | |
6882 (home (efs-get-hash-entry | |
6883 home-key efs-expand-dir-hashtable | |
6884 ignore-case)) | |
6885 pwd-result) | |
6886 (if home | |
6887 (setq home | |
6888 (efs-fix-path | |
6889 host-type | |
6890 (efs-internal-file-name-as-directory | |
6891 host-type home))) | |
6892 (if (setq home | |
6893 (car | |
6894 (setq pwd-result | |
6895 (efs-send-pwd | |
6896 host-type | |
6897 host user)))) | |
6898 (efs-put-hash-entry | |
6899 home-key | |
6900 (efs-internal-directory-file-name | |
6901 (efs-fix-path host-type home 'reverse)) | |
6902 efs-expand-dir-hashtable ignore-case) | |
6903 (efs-error host user | |
6904 (concat "PWD failed: " | |
6905 (cdr pwd-result))))) | |
6906 (unwind-protect | |
6907 (and (efs-raw-send-cd host user | |
6908 (efs-fix-path | |
6909 host-type tilde) t) | |
6910 (car | |
6911 (efs-send-pwd | |
6912 host-type host user))) | |
6913 (efs-raw-send-cd host user home))))) | |
6914 (if res | |
6915 (progn | |
6916 (setq res (efs-internal-directory-file-name | |
6917 (efs-fix-path host-type res 'reverse))) | |
6918 (efs-put-hash-entry | |
6919 key res efs-expand-dir-hashtable ignore-case) | |
6920 res))) | |
6921 (if (string-equal tilde "~") | |
6922 (error "Cannot get home directory on %s" host) | |
6923 (error "User %s is not known on %s" (substring tilde 1) host)))) | |
6924 ;; local machine | |
6925 (efs-real-expand-file-name (, tilde))))) | |
6926 | |
6927 (defmacro efs-de-dot-file-name (string) | |
6928 ;; Takes a string as arguments, and removes /../'s and /./'s. | |
6929 (` | |
6930 (let ((string (, string)) | |
6931 (start 0) | |
6932 new make-dir) | |
6933 ;; to make the regexp's simpler, canonicalize to directory name. | |
6934 (if (setq make-dir (string-match "/\\.\\.?$" string)) | |
6935 (setq string (concat string "/"))) | |
6936 (while (string-match "/\\./" string start) | |
6937 (setq new (concat new | |
6938 (substring string | |
6939 start (match-beginning 0))) | |
6940 start (1- (match-end 0)))) | |
6941 | |
6942 (if new (setq string (concat new (substring string start)))) | |
6943 | |
6944 (while (string-match "/[^/]+/\\.\\./" string) | |
6945 ;; Is there a way to avoid all this concating and copying? | |
6946 (setq string (concat (substring string 0 (1+ (match-beginning 0))) | |
6947 (substring string (match-end 0))))) | |
6948 | |
6949 ;; Do /../ and //../ special cases. They should expand to | |
6950 ;; / and //, respectively. | |
6951 (if (string-match "^\\(/+\\)\\.\\./" string) | |
6952 (setq string (concat (substring string 0 (match-end 1)) | |
6953 (substring string (match-end 0))))) | |
6954 | |
6955 (if (and make-dir | |
6956 (not (string-match "^/+$" string))) | |
6957 (substring string 0 -1) | |
6958 string)))) | |
6959 | |
6960 (defun efs-substitute-in-file-name (string) | |
6961 "Documented as original." | |
6962 ;; Because of the complicated interaction between short-circuiting | |
6963 ;; and environment variable substitution, this can't call the macro | |
6964 ;; efs-short-circuit-file-name. | |
6965 (efs-save-match-data | |
6966 (let ((start 0) | |
6967 var new root backskip regexp lbackskip | |
6968 lregexp parsed fudge-host-type rstart error) | |
6969 | |
6970 (if efs-local-apollo-unix | |
6971 (setq lregexp ".//+" | |
6972 lbackskip 2) | |
6973 (setq lregexp "//+" | |
6974 lbackskip 1)) | |
6975 | |
6976 ;; Subst. existing env variables | |
6977 (while (string-match "\\$" string start) | |
6978 (setq new (concat new (substring string start (match-beginning 0))) | |
6979 start (match-end 0)) | |
6980 (cond ((eq (string-match "\\$" string start) start) | |
6981 (setq start (1+ start) | |
6982 new (concat new "$$"))) | |
6983 ((eq (string-match "{" string start) start) | |
6984 (if (and (string-match "}" string start) | |
6985 (setq var (getenv | |
6986 (substring string (1+ start) | |
6987 (1- (match-end 0)))))) | |
6988 (setq start (match-end 0) | |
6989 new (concat new var)) | |
6990 (setq new (concat new "$")))) | |
6991 ((eq (string-match "[a-zA-Z0-9]+" string start) start) | |
6992 (if (setq var (getenv | |
6993 (substring string start (match-end 0)))) | |
6994 (setq start (match-end 0) | |
6995 new (concat new var)) | |
6996 (setq new (concat new "$")))) | |
6997 ((setq new (concat new "$"))))) | |
6998 (if new (setq string (concat new (substring string start)) | |
6999 start 0)) | |
7000 | |
7001 ;; Short circuit /user@mach: roots. It is important to do this | |
7002 ;; now to avoid unnecessary ftp connections. | |
7003 | |
7004 (while (string-match efs-path-root-short-circuit-regexp | |
7005 string start) | |
7006 (setq start (1+ (match-beginning 0)))) | |
7007 (or (zerop start) (setq string (substring string start) | |
7008 start 0)) | |
7009 | |
7010 ;; Look for invalid environment variables in the root. If one is found, | |
7011 ;; we set the host-type to 'unix. Since we can't login in to determine | |
7012 ;; it. There is a good chance that we will bomb later with an error, | |
7013 ;; but the day may yet be saved if the root is short-circuited off. | |
7014 | |
7015 (if (string-match efs-path-root-regexp string) | |
7016 (progn | |
7017 (setq root (substring string 0 (match-end 0)) | |
7018 start (match-end 0)) | |
7019 (if (string-match "[^$]\\(\\$\\$\\)*\\$[^$]" root) | |
7020 (progn | |
7021 (setq rstart (1- (match-end 0)) | |
7022 fudge-host-type t) | |
7023 (cond | |
7024 ((eq (elt root rstart) ?{) | |
7025 (setq | |
7026 error | |
7027 (if (string-match "}" root rstart) | |
7028 (concat | |
7029 "Subsituting non-existent environment variable " | |
7030 (substring root (1+ rstart) (match-beginning 0))) | |
7031 "Missing \"}\" in environment-variable substitution"))) | |
7032 ((eq (string-match "[A-Za-z0-9]+" root rstart) rstart) | |
7033 (setq | |
7034 error | |
7035 (concat | |
7036 "Subsituting non-existent environment variable " | |
7037 (substring root rstart (match-beginning 0))))) | |
7038 (t | |
7039 (setq | |
7040 error | |
7041 "Bad format environment-variable substitution"))))) | |
7042 (setq root (efs-unquote-dollars root) | |
7043 parsed (efs-ftp-path root)) | |
7044 | |
7045 (if (and (not fudge-host-type) | |
7046 ;; This may trigger an FTP connection | |
7047 (memq (efs-host-type (car parsed) (nth 1 parsed)) | |
7048 '(apollo-unix dumb-apollo-unix))) | |
7049 (setq regexp ".//+" | |
7050 backskip 2) | |
7051 (setq regexp "//+" | |
7052 backskip 1))) | |
7053 ;; no root, we're local | |
7054 (setq regexp lregexp | |
7055 backskip lbackskip)) | |
7056 | |
7057 ;; Now short-circuit in an apollo and efs sensitive way. | |
7058 | |
7059 (while (cond ((string-match regexp string start) | |
7060 (setq start (- (match-end 0) backskip))) | |
7061 ((string-match "/~" string start) | |
7062 (setq start (1- (match-end 0))))) | |
7063 | |
7064 (and root | |
7065 (null efs-short-circuit-to-remote-root) | |
7066 (setq root nil | |
7067 regexp lregexp | |
7068 backskip lbackskip))) | |
7069 | |
7070 ;; If we still have a bad root, barf. | |
7071 (if (and root error) (error error)) | |
7072 | |
7073 ;; look for non-existent evironment variables in the path | |
7074 | |
7075 (if (string-match | |
7076 "\\([^$]\\|^\\)\\(\\$\\$\\)*\\$\\([^$]\\|$\\)" string start) | |
7077 (progn | |
7078 (setq start (match-beginning 3)) | |
7079 (cond | |
7080 ((eq (length string) start) | |
7081 (error "Empty string is an invalid environment variable")) | |
7082 ((eq (elt string start) ?{) | |
7083 (if (string-match "}" string start) | |
7084 (error | |
7085 "Subsituting non-existent environment variable %s" | |
7086 (substring string (1+ start) (match-end 0))) | |
7087 (error | |
7088 "Missing \"}\" in environment-variable substitution"))) | |
7089 ((eq (string-match "[A-Za-z0-9]+" string start) start) | |
7090 (error | |
7091 "Subsituting non-existent environment variable %s" | |
7092 (substring string start (match-end 0)))) | |
7093 (t | |
7094 (error | |
7095 "Bad format environment-variable substitution"))))) | |
7096 | |
7097 (if root | |
7098 (concat root | |
7099 (efs-unquote-dollars | |
7100 (if (zerop start) | |
7101 string | |
7102 (substring string start)))) | |
7103 (efs-unquote-dollars | |
7104 (if (zerop start) | |
7105 string | |
7106 (substring string start))))))) | |
7107 | |
7108 (defun efs-expand-file-name (name &optional default) | |
7109 "Documented as original." | |
7110 (let (s-c-res path host user host-type) | |
7111 (efs-save-match-data | |
7112 (or (file-name-absolute-p name) | |
7113 (setq name (concat | |
7114 (file-name-as-directory | |
7115 (or default default-directory)) | |
7116 name))) | |
7117 (setq s-c-res (efs-short-circuit-file-name name) | |
7118 path (car s-c-res) | |
7119 host-type (nth 1 s-c-res) | |
7120 host (nth 2 s-c-res) | |
7121 user (nth 3 s-c-res)) | |
7122 (cond ((string-match "^~[^/]*" path) | |
7123 (let ((start (match-end 0))) | |
7124 (setq path (concat | |
7125 (efs-expand-tilde | |
7126 (substring path 0 start) | |
7127 host-type host user) | |
7128 (substring path start))))) | |
7129 ((and host-type (not (file-name-absolute-p path))) | |
7130 ;; We expand the empty string to a directory. | |
7131 ;; This can be more efficient for filename | |
7132 ;; completion. It's also consistent with non-unix. | |
7133 (let ((tilde (efs-expand-tilde | |
7134 "~" host-type host user))) | |
7135 (if (string-equal tilde "/") | |
7136 (setq path (concat "/" path)) | |
7137 (setq path (concat tilde "/" path)))))) | |
7138 | |
7139 (setq path (efs-de-dot-file-name path)) | |
7140 (if host-type | |
7141 (format efs-path-format-string user host path) | |
7142 path)))) | |
7143 | |
7144 ;;;; ------------------------------------------------------------ | |
7145 ;;;; Other functions for manipulating file names. | |
7146 ;;;; ------------------------------------------------------------ | |
7147 | |
7148 (defun efs-internal-file-name-extension (filename) | |
7149 ;; Returns the extension for file name FN. | |
7150 (save-match-data | |
7151 (let ((file (file-name-sans-versions (file-name-nondirectory filename)))) | |
7152 (if (string-match "\\.[^.]*\\'" file) | |
7153 (substring file (match-beginning 0)) | |
7154 "")))) | |
7155 | |
7156 (defun efs-file-name-as-directory (name) | |
7157 ;; version of file-name-as-directory for remote files. | |
7158 ;; Usually just appends a / if there isn't one already. | |
7159 ;; For some systems, it may also remove .DIR like extensions. | |
7160 (let* ((parsed (efs-ftp-path name)) | |
7161 (file (nth 2 parsed))) | |
7162 (if (string-equal file "") | |
7163 name | |
7164 (efs-internal-file-name-as-directory | |
7165 (efs-host-type (car parsed) (nth 1 parsed)) name)))) | |
7166 | |
7167 (efs-defun efs-internal-file-name-as-directory nil (name) | |
7168 ;; By default, simply adds a trailing /, if there isn't one. | |
7169 ;; Note that for expanded filenames, it pays to call this rather | |
7170 ;; than efs-file-name-as-directory. | |
7171 (let (file-name-handler-alist) | |
7172 (file-name-as-directory name))) | |
7173 | |
7174 (defun efs-file-name-directory (name) | |
7175 ;; file-name-directory for remote files. Takes care not to | |
7176 ;; turn /user@host: into /. | |
7177 (let ((path (nth 2 (efs-ftp-path name))) | |
7178 file-name-handler-alist) | |
7179 (if (or (string-equal path "") | |
7180 (and (= (string-to-char path) ?~) | |
7181 (not | |
7182 (efs-save-match-data | |
7183 (string-match "/" path 1))))) | |
7184 name | |
7185 (if (efs-save-match-data | |
7186 (not (string-match "/" path))) | |
7187 (efs-replace-path-component name "") | |
7188 (file-name-directory name))))) | |
7189 | |
7190 (defun efs-file-name-nondirectory (name) | |
7191 ;; Computes file-name-nondirectory for remote files. | |
7192 ;; For expanded filenames, can just call efs-internal-file-name-nondirectory. | |
7193 (let ((file (nth 2 (efs-ftp-path name)))) | |
7194 (if (or (string-equal file "") | |
7195 (and (= (string-to-char file) ?~) | |
7196 (not | |
7197 (efs-save-match-data | |
7198 (string-match "/" file 1))))) | |
7199 "" | |
7200 (if (efs-save-match-data | |
7201 (not (string-match "/" file))) | |
7202 file | |
7203 (efs-internal-file-name-nondirectory name))))) | |
7204 | |
7205 (defun efs-internal-file-name-nondirectory (name) | |
7206 ;; Version of file-name-nondirectory, without the efs-file-handler-function. | |
7207 ;; Useful to call this, if we have already decomposed the filename. | |
7208 (let (file-name-handler-alist) | |
7209 (file-name-nondirectory name))) | |
7210 | |
7211 (defun efs-directory-file-name (dir) | |
7212 ;; Computes directory-file-name for remote files. | |
7213 ;; Needs to be careful not to turn /foo@bar:/ into /foo@bar: | |
7214 (let ((parsed (efs-ftp-path dir))) | |
7215 (if (string-equal "/" (nth 2 parsed)) | |
7216 dir | |
7217 (efs-internal-directory-file-name dir)))) | |
7218 | |
7219 (defun efs-internal-directory-file-name (dir) | |
7220 ;; Call this if you want to apply directory-file-name to the remote | |
7221 ;; part of a efs-style path. Don't call for non-efs-style paths, | |
7222 ;; as this short-circuits the file-name-handler-alist completely. | |
7223 (let (file-name-handler-alist) | |
7224 (directory-file-name dir))) | |
7225 | |
7226 (efs-defun efs-remote-directory-file-name nil (dir) | |
7227 "Returns the file name on the remote system of directory DIR. | |
7228 If the remote system is not unix, this may not be the same as the file name | |
7229 of the directory in efs's internal cache." | |
7230 (directory-file-name dir)) | |
7231 | |
7232 (defun efs-file-name-sans-versions (filename &optional keep-backup-versions) | |
7233 ;; Version of file-name-sans-versions for remote files. | |
7234 (or (file-name-absolute-p filename) | |
7235 (setq filename (expand-file-name filename))) | |
7236 (let ((parsed (efs-ftp-path filename))) | |
7237 (efs-internal-file-name-sans-versions | |
7238 (efs-host-type (car parsed) (nth 1 parsed)) | |
7239 filename keep-backup-versions))) | |
7240 | |
7241 (efs-defun efs-internal-file-name-sans-versions nil | |
7242 (filename &optional keep-backup-versions) | |
7243 (let (file-name-handler-alist) | |
7244 (file-name-sans-versions filename keep-backup-versions))) | |
7245 | |
7246 (defun efs-diff-latest-backup-file (fn) | |
7247 ;; Version of diff latest backup file for remote files. | |
7248 ;; Accomodates non-unix. | |
7249 ;; Returns the latest backup for fn, according to the numbering | |
7250 ;; of the backups. Does not check file-newer-than-file-p. | |
7251 (let ((parsed (efs-ftp-path fn))) | |
7252 (efs-internal-diff-latest-backup-file | |
7253 (efs-host-type (car parsed) (nth 1 parsed)) fn))) | |
7254 | |
7255 (efs-defun efs-internal-diff-latest-backup-file nil (fn) | |
7256 ;; Default behaviour is the behaviour in diff.el | |
7257 (let (file-name-handler-alist) | |
7258 (diff-latest-backup-file fn))) | |
7259 | |
7260 (defun efs-unhandled-file-name-directory (filename) | |
7261 ;; Calculate a default unhandled directory for an efs buffer. | |
7262 ;; This is used to compute directories in which to execute | |
7263 ;; processes. This is relevant to V19 only. Doesn't do any harm for | |
7264 ;; older versions though. It would be nice if this wasn't such a | |
7265 ;; kludge. | |
7266 (file-name-directory efs-tmp-name-template)) | |
7267 | |
7268 (defun efs-file-truename (filename) | |
7269 ;; Calculates a remote file's truename, if this isn't inhibited. | |
7270 (let ((filename (expand-file-name filename))) | |
7271 (if (and efs-compute-remote-buffer-file-truename | |
7272 (memq (efs-host-type (car (efs-ftp-path filename))) | |
7273 efs-unix-host-types)) | |
7274 (efs-internal-file-truename filename) | |
7275 filename))) | |
7276 | |
7277 (defun efs-internal-file-truename (filename) | |
7278 ;; Internal function so that we don't keep checking | |
7279 ;; efs-compute-remote-buffer-file-truename, etc, as we recurse. | |
7280 (let ((dir (efs-file-name-directory filename)) | |
7281 target dirfile) | |
7282 ;; Get the truename of the directory. | |
7283 (setq dirfile (efs-directory-file-name dir)) | |
7284 ;; If these are equal, we have the (or a) root directory. | |
7285 (or (string= dir dirfile) | |
7286 (setq dir (efs-file-name-as-directory | |
7287 (efs-internal-file-truename dirfile)))) | |
7288 (if (equal ".." (efs-file-name-nondirectory filename)) | |
7289 (efs-directory-file-name (efs-file-name-directory | |
7290 (efs-directory-file-name dir))) | |
7291 (if (equal "." (efs-file-name-nondirectory filename)) | |
7292 (efs-directory-file-name dir) | |
7293 ;; Put it back on the file name. | |
7294 (setq filename (concat dir (efs-file-name-nondirectory filename))) | |
7295 ;; Is the file name the name of a link? | |
7296 (setq target (efs-file-symlink-p filename)) | |
7297 (if target | |
7298 ;; Yes => chase that link, then start all over | |
7299 ;; since the link may point to a directory name that uses links. | |
7300 ;; We can't safely use expand-file-name here | |
7301 ;; since target might look like foo/../bar where foo | |
7302 ;; is itself a link. Instead, we handle . and .. above. | |
7303 (if (file-name-absolute-p target) | |
7304 (efs-internal-file-truename target) | |
7305 (efs-internal-file-truename (concat dir target))) | |
7306 ;; No, we are done! | |
7307 filename))))) | |
7308 | |
7309 | |
7310 ;;;; ---------------------------------------------------------------- | |
7311 ;;;; I/O functions | |
7312 ;;;; ---------------------------------------------------------------- | |
7313 | |
7314 (efs-define-fun efs-set-buffer-file-name (filename) | |
7315 ;; Sets the buffer local variables for filename appropriately. | |
7316 ;; A special function because Lucid and FSF do this differently. | |
7317 ;; This default behaviour is the lowest common denominator. | |
7318 (setq buffer-file-name filename)) | |
7319 | |
7320 (defun efs-write-region (start end filename &optional append visit &rest args) | |
7321 ;; write-region for remote files. | |
7322 ;; This version accepts the V19 interpretation for the arg VISIT. | |
7323 ;; However, making use of this within V18 may cause errors to crop up. | |
7324 ;; ARGS should catch the MULE coding-system argument. | |
7325 (if (stringp visit) (setq visit (expand-file-name visit))) | |
7326 (setq filename (expand-file-name filename)) | |
7327 (let ((parsed (efs-ftp-path filename)) | |
7328 ;; Make sure that the after-write-region-hook isn't called inside | |
7329 ;; the file-handler-alist | |
7330 (after-write-region-hook nil)) | |
7331 (if parsed | |
7332 (let* ((host (car parsed)) | |
7333 (user (nth 1 parsed)) | |
7334 (host-type (efs-host-type host user)) | |
7335 (temp (car (efs-make-tmp-name nil host))) | |
7336 (type (efs-xfer-type nil nil host-type filename)) | |
7337 (abbr (and (or (stringp visit) (eq t visit) (null visit)) | |
7338 (efs-relativize-filename | |
7339 (if (stringp visit) visit filename)))) | |
7340 (buffer (current-buffer)) | |
7341 (b-file-name buffer-file-name) | |
7342 (mod-p (buffer-modified-p))) | |
7343 (unwind-protect | |
7344 (progn | |
7345 (condition-case err | |
7346 (progn | |
7347 (unwind-protect | |
7348 (let ((executing-macro t)) | |
7349 ;; let-bind executing-macro to inhibit messaging. | |
7350 ;; Setting VISIT to 'quiet is more elegant. | |
7351 ;; But in Emacs 18, doing it this way allows | |
7352 ;; us to modify the visited file modtime, so | |
7353 ;; that undo's show the buffer modified. | |
7354 (apply 'write-region start end | |
7355 temp nil visit args)) | |
7356 ;; buffer-modified-p is now correctly set | |
7357 (setq buffer-file-name b-file-name) | |
7358 ;; File modtime is bogus, so clear. | |
7359 (clear-visited-file-modtime)) | |
7360 (efs-copy-file-internal | |
7361 temp nil filename parsed (if append 'append t) | |
7362 nil (and abbr (format "Writing %s" abbr)) | |
7363 ;; cont | |
7364 (efs-cont (result line cont-lines) (filename buffer | |
7365 visit) | |
7366 (if result | |
7367 (signal 'ftp-error | |
7368 (list "Opening output file" | |
7369 (format "FTP Error: \"%s\"" line) | |
7370 filename))) | |
7371 ;; The new file entry will be added by | |
7372 ;; efs-copy-file-internal. | |
7373 (cond | |
7374 ((eq visit t) | |
7375 ;; This will run asynch. | |
7376 (efs-save-buffer-excursion | |
7377 (set-buffer buffer) | |
7378 (efs-set-buffer-file-name filename) | |
7379 (efs-set-visited-file-modtime))) | |
7380 ((stringp visit) | |
7381 (efs-save-buffer-excursion | |
7382 (set-buffer buffer) | |
7383 (efs-set-buffer-file-name visit) | |
7384 (set-visited-file-modtime))))) | |
7385 nil type)) | |
7386 (error | |
7387 ;; restore buffer-modified-p | |
7388 (let (file-name-handler-alist) | |
7389 (set-buffer-modified-p mod-p)) | |
7390 (signal (car err) (cdr err)))) | |
7391 (if (or (eq visit t) | |
7392 (and (stringp visit) | |
7393 (efs-ftp-path visit))) | |
7394 (efs-set-buffer-mode))) | |
7395 (efs-del-tmp-name temp)) | |
7396 (and abbr (efs-message "Wrote %s" abbr))) | |
7397 (if (and (stringp visit) (efs-ftp-path visit)) | |
7398 (progn | |
7399 (apply 'write-region start end filename append visit args) | |
7400 (efs-set-buffer-file-name visit) | |
7401 (efs-set-visited-file-modtime) | |
7402 (efs-set-buffer-mode)) | |
7403 (error "efs-write-region called for a local file"))))) | |
7404 | |
7405 (defun efs-insert-file-contents (filename &optional visit &rest args) | |
7406 ;; Inserts file contents for remote files. | |
7407 ;; The additional ARGS covers V19 BEG and END. Should also handle the | |
7408 ;; CODING-SYSTEM arg for mule. Hope the two don't trip over each other. | |
7409 (barf-if-buffer-read-only) | |
7410 (unwind-protect | |
7411 (let* ((filename (expand-file-name filename)) | |
7412 (parsed (efs-ftp-path filename)) | |
7413 (host (car parsed)) | |
7414 (host-type (efs-host-type host)) | |
7415 (user (nth 1 parsed)) | |
7416 (path (nth 2 parsed)) | |
7417 (buffer (current-buffer))) | |
7418 | |
7419 (if (or (file-exists-p filename) | |
7420 (let* ((res (and | |
7421 (not (efs-get-host-property host 'rnfr-failed)) | |
7422 (efs-send-cmd | |
7423 host user (list 'quote 'rnfr path)))) | |
7424 (line (nth 1 res))) | |
7425 ;; RNFR returns a 550 if the file doesn't exist. | |
7426 (if (and line (>= (length line) 4) | |
7427 (string-equal "550 " (substring line 0 4))) | |
7428 nil | |
7429 (if (car res) (efs-set-host-property host 'rnfr-failed t)) | |
7430 (efs-del-from-ls-cache filename t nil) | |
7431 (efs-del-hash-entry | |
7432 (efs-canonize-file-name (file-name-directory filename)) | |
7433 efs-files-hashtable) | |
7434 (file-exists-p filename)))) | |
7435 | |
7436 (let ((temp (concat | |
7437 (car (efs-make-tmp-name nil host)) | |
7438 (efs-internal-file-name-extension filename))) | |
7439 (type (efs-xfer-type host-type filename nil nil)) | |
7440 (abbr (efs-relativize-filename filename)) | |
7441 (temp (concat (car (efs-make-tmp-name nil host)) | |
7442 (or (substring abbr (string-match "\\." abbr)) ""))) | |
7443 (i-f-c-size 0)) | |
7444 | |
7445 (unwind-protect | |
7446 (efs-copy-file-internal | |
7447 filename parsed temp nil t nil | |
7448 (format "Retrieving %s" abbr) | |
7449 (efs-cont (result line cont-lines) (filename visit buffer | |
7450 host-type | |
7451 temp args) | |
7452 (if result | |
7453 (signal 'ftp-error | |
7454 (list "Opening input file" | |
7455 (format "FTP Error: \"%s\"" | |
7456 line) | |
7457 filename)) | |
7458 (if (eq host-type 'coke) | |
7459 (efs-coke-insert-beverage-contents buffer filename | |
7460 line) | |
7461 (efs-save-buffer-excursion | |
7462 (set-buffer buffer) | |
7463 (if (or (file-readable-p temp) | |
7464 (sleep-for efs-retry-time) | |
7465 ;; Wait for file to hopefully appear. | |
7466 (file-readable-p temp)) | |
7467 | |
7468 (setq i-f-c-size | |
7469 (nth 1 (apply 'insert-file-contents | |
7470 temp visit args))) | |
7471 (signal 'ftp-error | |
7472 (list | |
7473 "Opening input file:" | |
7474 (format | |
7475 "FTP Error: %s not arrived or readable" | |
7476 filename)))) | |
7477 ;; This is done asynch | |
7478 (if visit | |
7479 (let ((buffer-file-name filename)) | |
7480 (efs-set-visited-file-modtime))))))) | |
7481 nil type) | |
7482 (efs-del-tmp-name temp)) | |
7483 ;; Return (FILENAME SIZE) | |
7484 (list filename i-f-c-size)) | |
7485 (signal 'file-error (list "Opening input file" filename)))) | |
7486 ;; Set buffer-file-name at the very last, so if anything bombs, we're | |
7487 ;; not visiting. | |
7488 (if visit | |
7489 (efs-set-buffer-file-name filename)))) | |
7490 | |
7491 (defun efs-revert-buffer (arg noconfirm) | |
7492 "Revert this buffer from a remote file using ftp." | |
7493 (let ((opoint (point))) | |
7494 (cond ((null buffer-file-name) | |
7495 (error "Buffer does not seem to be associated with any file")) | |
7496 ((or noconfirm | |
7497 (yes-or-no-p (format "Revert buffer from file %s? " | |
7498 buffer-file-name))) | |
7499 (let ((buffer-read-only nil)) | |
7500 ;; Set buffer-file-name to nil | |
7501 ;; so that we don't try to lock the file. | |
7502 (let ((buffer-file-name nil)) | |
7503 (unlock-buffer) | |
7504 (erase-buffer)) | |
7505 (insert-file-contents buffer-file-name t)) | |
7506 (goto-char (min opoint (point-max))) | |
7507 (after-find-file nil) | |
7508 t)))) | |
7509 | |
7510 (defun efs-recover-file (file) | |
7511 ;; Version of recover file for remote files, and remote autosave files too. | |
7512 (if (auto-save-file-name-p file) (error "%s is an auto-save file" file)) | |
7513 (let* ((file-name (let ((buffer-file-name file)) (make-auto-save-file-name))) | |
7514 (file-name-parsed (efs-ftp-path file-name)) | |
7515 (file-parsed (efs-ftp-path file)) | |
7516 (efs-ls-uncache t)) | |
7517 (cond ((not (file-newer-than-file-p file-name file)) | |
7518 (error "Auto-save file %s not current" file-name)) | |
7519 ((save-window-excursion | |
7520 (or (eq system-type 'vax-vms) | |
7521 (progn | |
7522 (with-output-to-temp-buffer "*Directory*" | |
7523 (buffer-disable-undo standard-output) | |
7524 (if file-parsed | |
7525 (progn | |
7526 (princ (format "On the host %s:\n" | |
7527 (car file-parsed))) | |
7528 (princ | |
7529 (let ((default-directory exec-directory)) | |
7530 (efs-ls file (if (file-symlink-p file) | |
7531 "-lL" "-l") | |
7532 t t)))) | |
7533 (princ "On the local host:\n") | |
7534 (let ((default-directory exec-directory)) | |
7535 (call-process "ls" nil standard-output nil | |
7536 (if (file-symlink-p file) "-lL" "-l") | |
7537 file))) | |
7538 (princ "\nAUTO SAVE FILE on the ") | |
7539 (if file-name-parsed | |
7540 (progn | |
7541 (princ (format "host %s:\n" | |
7542 (car file-name-parsed))) | |
7543 (princ | |
7544 (efs-ls file-name | |
7545 (if (file-symlink-p file-name) "-lL" "-l") | |
7546 t t))) | |
7547 (princ "local host:\n") | |
7548 (let ((default-directory exec-directory)) | |
7549 (call-process "ls" nil standard-output nil | |
7550 "-l" file-name))) | |
7551 (princ "\nFile modification times are given in ") | |
7552 (princ "the local time of each host.\n")) | |
7553 (save-excursion | |
7554 (set-buffer "*Directory*") | |
7555 (goto-char (point-min)) | |
7556 (while (not (eobp)) | |
7557 (end-of-line) | |
7558 (if (> (current-column) (window-width)) | |
7559 (progn | |
7560 (skip-chars-backward " \t") | |
7561 (skip-chars-backward "^ \t\n") | |
7562 (if (> (current-column) 12) | |
7563 (progn | |
7564 (delete-horizontal-space) | |
7565 (insert "\n "))))) | |
7566 (forward-line 1)) | |
7567 (set-buffer-modified-p nil) | |
7568 (goto-char (point-min))))) | |
7569 (yes-or-no-p (format "Recover using this auto save file? "))) | |
7570 (switch-to-buffer (find-file-noselect file t)) | |
7571 (let ((buffer-read-only nil)) | |
7572 (erase-buffer) | |
7573 (insert-file-contents file-name nil)) | |
7574 (after-find-file nil)) | |
7575 (t (error "Recover-file cancelled.")))) | |
7576 ;; This is no longer done in V19. However, I like the caution for | |
7577 ;; remote files, where file-newer-than-file-p may lie. | |
7578 (setq buffer-auto-save-file-name nil) | |
7579 (message "Auto-save off in this buffer till you do M-x auto-save-mode.")) | |
7580 | |
7581 ;;;; ------------------------------------------------------------------ | |
7582 ;;;; Attributes of files. | |
7583 ;;;; ------------------------------------------------------------------ | |
7584 | |
7585 (defun efs-file-symlink-p (file) | |
7586 ;; Version of file-symlink-p for remote files. | |
7587 ;; Call efs-expand-file-name rather than the normal | |
7588 ;; expand-file-name to stop loops when using a package that | |
7589 ;; redefines both file-symlink-p and expand-file-name. | |
7590 ;; Do not use efs-get-file-entry, because a child-lookup won't do. | |
7591 (let* ((file (efs-expand-file-name file)) | |
7592 (ignore-case (memq (efs-host-type (car (efs-ftp-path file))) | |
7593 efs-case-insensitive-host-types)) | |
7594 (file-type (car (efs-get-hash-entry | |
7595 (efs-get-file-part file) | |
7596 (efs-get-files (file-name-directory file)) | |
7597 ignore-case)))) | |
7598 (and (stringp file-type) | |
7599 (if (file-name-absolute-p file-type) | |
7600 (efs-replace-path-component file file-type) | |
7601 file-type)))) | |
7602 | |
7603 (defun efs-file-exists-p (path) | |
7604 ;; file-exists-p for remote file. Uses the cache if possible. | |
7605 (let* ((path (expand-file-name path)) | |
7606 (parsed (efs-ftp-path path))) | |
7607 (efs-internal-file-exists-p (efs-host-type (car parsed) (nth 1 parsed)) | |
7608 path))) | |
7609 | |
7610 (efs-defun efs-internal-file-exists-p nil (path) | |
7611 (and (efs-get-file-entry path) t)) | |
7612 | |
7613 (defun efs-file-directory-p (file) | |
7614 (let* ((file (expand-file-name file)) | |
7615 (parsed (efs-ftp-path file))) | |
7616 (efs-internal-file-directory-p (efs-host-type (car parsed) (nth 1 parsed)) | |
7617 file))) | |
7618 | |
7619 (efs-defun efs-internal-file-directory-p nil (path) | |
7620 ;; Version of file-directory-p for remote files. | |
7621 (let ((parsed (efs-ftp-path path))) | |
7622 (or (string-equal (nth 2 parsed) "/") ; root is always a directory | |
7623 (let ((file-ent (car (efs-get-file-entry | |
7624 (efs-internal-file-name-as-directory | |
7625 (efs-host-type (car parsed) (nth 1 parsed)) | |
7626 path))))) | |
7627 ;; We do a file-name-as-directory on path here because some | |
7628 ;; machines (VMS) use a .DIR to indicate the filename associated | |
7629 ;; with a directory. This needs to be canonicalized. | |
7630 (if (stringp file-ent) | |
7631 (efs-internal-file-directory-p | |
7632 nil | |
7633 (efs-chase-symlinks | |
7634 ;; efs-internal-directory-file-name | |
7635 ;; only loses for paths where the remote file | |
7636 ;; is /. This has been eliminated. | |
7637 (efs-internal-directory-file-name path))) | |
7638 file-ent))))) | |
7639 | |
7640 (defun efs-file-attributes (file) | |
7641 ;; Returns file-file-attributes for a remote file. | |
7642 ;; For the file modtime does not return efs's cached value, as that | |
7643 ;; corresponds to buffer-file-modtime (i.e. the modtime of the file | |
7644 ;; the last time the buffer was vsisted or saved). Caching modtimes | |
7645 ;; does not make much sense, as they are usually used to determine | |
7646 ;; if a cache is stale. The modtime if a remote file can be obtained with | |
7647 ;; efs-get-file-mdtm. This is _not_ returned for the 5th entry here, | |
7648 ;; because it requires an FTP transaction, and a priori we don't know | |
7649 ;; if the caller actually cares about this info. Having file-attributes | |
7650 ;; return such a long list of info is not well suited to remote files, | |
7651 ;; as some of this info may be costly to obtain. | |
7652 (let* ((file (expand-file-name file)) | |
7653 (ent (efs-get-file-entry file))) | |
7654 (if ent | |
7655 (let* ((parsed (efs-ftp-path file)) | |
7656 (host (nth 0 parsed)) | |
7657 (user (nth 1 parsed)) | |
7658 (path (nth 2 parsed)) | |
7659 (type (car ent)) | |
7660 (size (or (nth 1 ent) -1)) | |
7661 (owner (nth 2 ent)) | |
7662 (modes (nth 3 ent)) | |
7663 ;; Hack to give remote files a "unique" "inode number". | |
7664 ;; It's actually the sum of the characters in its name. | |
7665 ;; It's not even really unique. | |
7666 (inode (apply '+ | |
7667 (nconc (mapcar 'identity host) | |
7668 (mapcar 'identity user) | |
7669 (mapcar 'identity | |
7670 (efs-internal-directory-file-name | |
7671 path))))) | |
7672 (nlinks (or (nth 4 ent) -1))) ; return -1 if we don't know | |
7673 (list | |
7674 (if (and (stringp type) (file-name-absolute-p type)) | |
7675 (efs-replace-path-component file type) | |
7676 type) ;0 file type | |
7677 nlinks ;1 link count | |
7678 (if owner ;2 uid | |
7679 ;; Not really a unique integer, | |
7680 ;; just a half-hearted attempt | |
7681 (apply '+ (mapcar 'identity owner)) | |
7682 -1) | |
7683 -1 ;3 gid | |
7684 '(0 0) ;4 atime | |
7685 '(0 0) ;5 mtime | |
7686 '(0 0) ;6 ctime | |
7687 size ;7 size | |
7688 (or modes ;8 mode | |
7689 (concat | |
7690 (cond ((stringp type) "l") | |
7691 (type "d") | |
7692 (t "-")) | |
7693 "?????????")) | |
7694 nil ;9 gid weird (Who knows if the gid | |
7695 ; would be changed?) | |
7696 inode ;10 inode | |
7697 -1 ;11 device number [v19 only] | |
7698 ))))) | |
7699 | |
7700 (defun efs-file-writable-p (file) | |
7701 ;; file-writable-p for remote files. | |
7702 ;; Does not attempt to open the file, but just looks at the cached file | |
7703 ;; modes. | |
7704 (let* ((file (expand-file-name file)) | |
7705 (ent (efs-get-file-entry file))) | |
7706 (if (and ent (or (not (stringp (car ent))) | |
7707 (setq file (efs-chase-symlinks file) | |
7708 ent (efs-get-file-entry file)))) | |
7709 (let* ((owner (nth 2 ent)) | |
7710 (modes (nth 3 ent)) | |
7711 (parsed (efs-ftp-path file)) | |
7712 (host-type (efs-host-type (car parsed))) | |
7713 (user (nth 1 parsed))) | |
7714 (if (memq host-type efs-unix-host-types) | |
7715 (setq host-type 'unix)) | |
7716 (efs-internal-file-writable-p host-type user owner modes)) | |
7717 (let ((dir (file-name-directory file))) | |
7718 (and | |
7719 (not (string-equal dir file)) | |
7720 (file-directory-p dir) | |
7721 (file-writable-p dir)))))) | |
7722 | |
7723 (efs-defun efs-internal-file-writable-p nil (user owner modes) | |
7724 ;; By default, we'll just guess yes. | |
7725 t) | |
7726 | |
7727 (efs-defun efs-internal-file-writable-p unix (user owner modes) | |
7728 (if (and modes | |
7729 (not (string-equal user "root"))) | |
7730 (null | |
7731 (null | |
7732 (if (string-equal user owner) | |
7733 (memq ?w (list (aref modes 2) (aref modes 5) | |
7734 (aref modes 8))) | |
7735 (memq ?w (list (aref modes 5) (aref modes 8)))))) | |
7736 t)) ; guess | |
7737 | |
7738 (defun efs-file-readable-p (file) | |
7739 ;; Version of file-readable-p that works for remote files. | |
7740 ;; Works by checking efs's cache of the file modes. | |
7741 (let* ((file (expand-file-name file)) | |
7742 (ent (efs-get-file-entry file))) | |
7743 (and ent | |
7744 (or (not (stringp (car ent))) | |
7745 (setq ent (efs-get-file-entry (efs-chase-symlinks file)))) | |
7746 ;; file exists | |
7747 (let* ((parsed (efs-ftp-path file)) | |
7748 (owner (nth 2 ent)) | |
7749 (modes (nth 3 ent)) | |
7750 (host-type (efs-host-type (car parsed))) | |
7751 (user (nth 1 parsed))) | |
7752 (if (memq host-type efs-unix-host-types) | |
7753 (setq host-type 'unix)) | |
7754 (efs-internal-file-readable-p host-type user owner modes))))) | |
7755 | |
7756 (efs-defun efs-internal-file-readable-p nil (user owner modes) | |
7757 ;; Guess t by default | |
7758 t) | |
7759 | |
7760 (efs-defun efs-internal-file-readable-p unix (user owner modes) | |
7761 (if (and modes | |
7762 (not (string-equal user "root"))) | |
7763 (null | |
7764 (null | |
7765 (if (string-equal user owner) | |
7766 (memq ?r (list (aref modes 1) (aref modes 4) | |
7767 (aref modes 7))) | |
7768 (memq ?r (list (aref modes 4) (aref modes 7)))))) | |
7769 t)) ; guess | |
7770 | |
7771 (defun efs-file-executable-p (file) | |
7772 ;; Version of file-executable-p for remote files. | |
7773 (let ((ent (efs-get-file-entry file))) | |
7774 (and ent | |
7775 (or (not (stringp (car ent))) | |
7776 (setq ent (efs-get-file-entry (efs-chase-symlinks file)))) | |
7777 ;; file exists | |
7778 (let* ((parsed (efs-ftp-path file)) | |
7779 (owner (nth 2 ent)) | |
7780 (modes (nth 3 ent)) | |
7781 (host-type (efs-host-type (car parsed))) | |
7782 (user (nth 1 parsed))) | |
7783 (if (memq host-type efs-unix-host-types) | |
7784 (setq host-type 'unix)) | |
7785 (efs-internal-file-executable-p host-type user owner modes))))) | |
7786 | |
7787 (efs-defun efs-internal-file-executable-p nil (user owner modes) | |
7788 ;; Guess t by default | |
7789 t) | |
7790 | |
7791 (efs-defun efs-internal-file-executable-p unix (user owner modes) | |
7792 (if (and modes | |
7793 (not (string-equal user "root"))) | |
7794 (null | |
7795 (null | |
7796 (if (string-equal user owner) | |
7797 (memq ?x (list (aref modes 3) (aref modes 6) | |
7798 (aref modes 9))) | |
7799 (memq ?x (list (aref modes 6) (aref modes 9)))))) | |
7800 t)) ; guess | |
7801 | |
7802 (defun efs-file-accessible-directory-p (dir) | |
7803 ;; Version of file-accessible-directory-p for remote directories. | |
7804 (let ((file (directory-file-name dir))) | |
7805 (and (efs-file-directory-p file) (efs-file-executable-p file)))) | |
7806 | |
7807 ;;;; -------------------------------------------------------------- | |
7808 ;;;; Listing directories. | |
7809 ;;;; -------------------------------------------------------------- | |
7810 | |
7811 (defun efs-shell-regexp-to-regexp (regexp) | |
7812 ;; Converts a shell regexp to an emacs regexp. | |
7813 ;; Probably full of bugs. Tries to follow csh globbing. | |
7814 (let ((curly 0) | |
7815 backslash) | |
7816 (concat "^" | |
7817 (mapconcat | |
7818 (function | |
7819 (lambda (char) | |
7820 (cond | |
7821 (backslash | |
7822 (setq backslash nil) | |
7823 (regexp-quote (char-to-string char))) | |
7824 ((and (> curly 0) (eq char ?,)) | |
7825 "\\|") | |
7826 ((memq char '(?[ ?])) | |
7827 (char-to-string char)) | |
7828 ((eq char ??) | |
7829 ".") | |
7830 ((eq char ?\\) | |
7831 (setq backslash t) | |
7832 "") | |
7833 ((eq char ?*) | |
7834 ".*") | |
7835 ((eq char ?{) | |
7836 (setq curly (1+ curly)) | |
7837 "\\(") | |
7838 ((and (eq char ?}) (> curly 0)) | |
7839 (setq curly (1- curly)) | |
7840 "\\)") | |
7841 (t (regexp-quote (char-to-string char)))))) | |
7842 regexp nil) | |
7843 "$"))) | |
7844 | |
7845 | |
7846 ;;; Getting directory listings. | |
7847 | |
7848 (defun efs-directory-files (directory &optional full match nosort) | |
7849 ;; Returns directory-files for remote directories. | |
7850 ;; NOSORT is a V19 arg. | |
7851 (let* ((directory (expand-file-name directory)) | |
7852 (parsed (efs-ftp-path directory)) | |
7853 (directory (efs-internal-file-name-as-directory | |
7854 (efs-host-type (car parsed) (nth 1 parsed)) directory)) | |
7855 files) | |
7856 (efs-barf-if-not-directory directory) | |
7857 (setq files (efs-hash-table-keys (efs-get-files directory) nosort)) | |
7858 (cond | |
7859 ((null (or full match)) | |
7860 files) | |
7861 (match ; this is slow case | |
7862 (let (res f) | |
7863 (efs-save-match-data | |
7864 (while files | |
7865 (setq f (if full (concat directory (car files)) (car files)) | |
7866 files (cdr files)) | |
7867 (if (string-match match f) | |
7868 (setq res (nconc res (list f)))))) | |
7869 res)) | |
7870 (full | |
7871 (mapcar (function | |
7872 (lambda (fn) | |
7873 (concat directory fn))) | |
7874 files))))) | |
7875 | |
7876 (defun efs-list-directory (dirname &optional verbose) | |
7877 ;; Version of list-directory for remote directories. | |
7878 ;; If verbose is nil, it gets its information from efs's | |
7879 ;; internal cache. | |
7880 (let* ((dirname (expand-file-name (or dirname default-directory))) | |
7881 header) | |
7882 (if (file-directory-p dirname) | |
7883 (setq dirname (file-name-as-directory dirname))) | |
7884 (setq header dirname) | |
7885 (with-output-to-temp-buffer "*Directory*" | |
7886 (buffer-disable-undo standard-output) | |
7887 (princ "Directory ") | |
7888 (princ header) | |
7889 (terpri) | |
7890 (princ | |
7891 (efs-ls dirname (if verbose | |
7892 list-directory-verbose-switches | |
7893 list-directory-brief-switches) | |
7894 t))))) | |
7895 | |
7896 ;;;; ------------------------------------------------------------------- | |
7897 ;;;; Manipulating buffers. | |
7898 ;;;; ------------------------------------------------------------------- | |
7899 | |
7900 (defun efs-get-file-buffer (file) | |
7901 ;; Version of get-file-buffer for remote files. Needs to fuss over things | |
7902 ;; like OS's which are case-insens. for file names. | |
7903 (let ((file (efs-canonize-file-name (expand-file-name file))) | |
7904 (buff-list (buffer-list)) | |
7905 buff-name) | |
7906 (catch 'match | |
7907 (while buff-list | |
7908 (and (setq buff-name (buffer-file-name (car buff-list))) | |
7909 (= (length buff-name) (length file)) ; efficiency hack | |
7910 (string-equal (efs-canonize-file-name buff-name) file) | |
7911 (throw 'match (car buff-list))) | |
7912 (setq buff-list (cdr buff-list)))))) | |
7913 | |
7914 (defun efs-create-file-buffer (filename) | |
7915 ;; Version of create-file-buffer for remote file names. | |
7916 (let* ((parsed (efs-ftp-path (expand-file-name filename))) | |
7917 (file (nth 2 parsed)) | |
7918 (host (car parsed)) | |
7919 (host-type (efs-host-type host)) | |
7920 (buff (cond | |
7921 ((null efs-fancy-buffer-names) | |
7922 (if (string-equal file "/") | |
7923 "/" | |
7924 (efs-internal-file-name-nondirectory | |
7925 (efs-internal-directory-file-name file)))) | |
7926 ((stringp efs-fancy-buffer-names) | |
7927 (format efs-fancy-buffer-names | |
7928 (if (string-equal file "/") | |
7929 "/" | |
7930 (efs-internal-file-name-nondirectory | |
7931 (efs-internal-directory-file-name file))) | |
7932 (substring host 0 (string-match "\\." host 1)))) | |
7933 (t ; efs-fancy-buffer-names had better be a function | |
7934 (funcall efs-fancy-buffer-names host | |
7935 (nth 1 parsed) file))))) | |
7936 (if (memq host-type efs-case-insensitive-host-types) | |
7937 (cond ((eq efs-buffer-name-case 'down) | |
7938 (setq buff (downcase buff))) | |
7939 ((eq efs-buffer-name-case 'up) | |
7940 (setq buff (upcase buff))))) | |
7941 (get-buffer-create (generate-new-buffer-name buff)))) | |
7942 | |
7943 (defun efs-set-buffer-mode () | |
7944 "Set correct modes for the current buffer if it is visiting a remote file." | |
7945 (if (and (stringp buffer-file-name) | |
7946 (efs-ftp-path buffer-file-name)) | |
7947 (progn | |
7948 (auto-save-mode efs-auto-save) | |
7949 (set (make-local-variable 'revert-buffer-function) | |
7950 'efs-revert-buffer) | |
7951 (set (make-local-variable 'default-directory-function) | |
7952 'efs-default-dir-function)))) | |
7953 | |
7954 ;;;; --------------------------------------------------------- | |
7955 ;;;; Functions for doing backups. | |
7956 ;;;; --------------------------------------------------------- | |
7957 | |
7958 (defun efs-backup-buffer () | |
7959 ;; Version of backup-buffer for buffers visiting remote files. | |
7960 (if efs-make-backup-files | |
7961 (let* ((parsed (efs-ftp-path buffer-file-name)) | |
7962 (host (car parsed)) | |
7963 (host-type (efs-host-type (car parsed)))) | |
7964 (if (or (not (listp efs-make-backup-files)) | |
7965 (memq host-type efs-make-backup-files)) | |
7966 (efs-internal-backup-buffer | |
7967 host host-type (nth 1 parsed) (nth 2 parsed)))))) | |
7968 | |
7969 (defun efs-internal-backup-buffer (host host-type user remote-path) | |
7970 ;; This is almost a copy of the function in files.el, modified | |
7971 ;; to check to see if the backup file exists, before deleting it. | |
7972 ;; It also supports efs-backup-by-copying, and tries to do the | |
7973 ;; right thing about backup-by-copying-when-mismatch. Only called | |
7974 ;; for remote files. | |
7975 ;; Set the umask now, so that `setmodes' knows about it. | |
7976 (efs-set-umask host user) | |
7977 (let ((ent (efs-get-file-entry (expand-file-name buffer-file-name))) | |
7978 ;; Never do version-control if the remote operating system is doing it. | |
7979 (version-control (if (memq host-type efs-version-host-types) | |
7980 'never | |
7981 version-control)) | |
7982 modstring) | |
7983 (and make-backup-files | |
7984 (not buffer-backed-up) | |
7985 ent ; i.e. file-exists-p | |
7986 (not (eq t (car ent))) | |
7987 (or (null (setq modstring (nth 3 ent))) | |
7988 (not (memq host-type efs-unix-host-types)) | |
7989 (memq (aref modstring 0) '(?- ?l))) | |
7990 (or (< (length remote-path) 5) | |
7991 (not (string-equal "/tmp/" (substring remote-path 0 5)))) | |
7992 (condition-case () | |
7993 (let* ((backup-info (find-backup-file-name buffer-file-name)) | |
7994 (backupname (car backup-info)) | |
7995 (targets (cdr backup-info)) | |
7996 (links (nth 4 ent)) | |
7997 setmodes) | |
7998 (condition-case () | |
7999 (if (or file-precious-flag | |
8000 (stringp (car ent)) ; symlinkp | |
8001 efs-backup-by-copying | |
8002 (and backup-by-copying-when-linked | |
8003 links (> links 1)) | |
8004 (and backup-by-copying-when-mismatch | |
8005 (not | |
8006 (if (memq | |
8007 host-type | |
8008 efs-case-insensitive-host-types) | |
8009 (string-equal | |
8010 (downcase user) (downcase (nth 2 ent))) | |
8011 (string-equal user (nth 2 ent)))))) | |
8012 (copy-file buffer-file-name backupname t t) | |
8013 (condition-case () | |
8014 (if (file-exists-p backupname) | |
8015 (delete-file backupname)) | |
8016 (file-error nil)) | |
8017 (rename-file buffer-file-name backupname t) | |
8018 (setq setmodes (file-modes backupname))) | |
8019 (file-error | |
8020 ;; If trouble writing the backup, write it in ~. | |
8021 (setq backupname (expand-file-name "~/%backup%~")) | |
8022 (message | |
8023 "Cannot write backup file; backing up in ~/%%backup%%~") | |
8024 (sleep-for 1) | |
8025 (copy-file buffer-file-name backupname t t))) | |
8026 (setq buffer-backed-up t) | |
8027 ;; Starting with 19.26, trim-versions-without-asking | |
8028 ;; has been renamed to delete-old-verions. | |
8029 (if (and targets | |
8030 (or (if (boundp 'trim-versions-without-asking) | |
8031 trim-versions-without-asking | |
8032 (and | |
8033 (boundp 'delete-old-versions) | |
8034 delete-old-versions)) | |
8035 (y-or-n-p (format | |
8036 "Delete excess backup versions of %s? " | |
8037 buffer-file-name)))) | |
8038 (while targets | |
8039 (condition-case () | |
8040 (delete-file (car targets)) | |
8041 (file-error nil)) | |
8042 (setq targets (cdr targets)))) | |
8043 ;; If the file was already written with the right modes, | |
8044 ;; don't return set-modes. | |
8045 (and setmodes | |
8046 (null | |
8047 (let ((buff (get-buffer | |
8048 (efs-ftp-process-buffer host user)))) | |
8049 (and buff | |
8050 (save-excursion | |
8051 (set-buffer buff) | |
8052 (and (integerp efs-process-umask) | |
8053 (= (efs-modes-from-umask efs-process-umask) | |
8054 setmodes)))))) | |
8055 setmodes)) | |
8056 (file-error nil))))) | |
8057 | |
8058 ;;;; ------------------------------------------------------------ | |
8059 ;;;; Redefinition for Emacs file mode support | |
8060 ;;;; ------------------------------------------------------------ | |
8061 | |
8062 (defmacro efs-build-mode-string-element (int suid-p sticky-p) | |
8063 ;; INT is between 0 and 7. | |
8064 ;; If SUID-P is non-nil, we are building the 3-char string for either | |
8065 ;; the owner or group, and the s[ug]id bit is set. | |
8066 ;; If STICKY-P is non-nil, we are building the string for other perms, | |
8067 ;; and the sticky bit is set. | |
8068 ;; It doesn't make sense for both SUID-P and STICKY-P be non-nil! | |
8069 (` (let* ((int (, int)) | |
8070 (suid-p (, suid-p)) | |
8071 (sticky-p (, sticky-p)) | |
8072 (read-bit (if (memq int '(4 5 6 7)) "r" "-")) | |
8073 (write-bit (if (memq int '(2 3 6 7)) "w" "-")) | |
8074 (x-bit (if (memq int '(1 3 5 7)) | |
8075 (cond (suid-p "s") (sticky-p "t") ("x")) | |
8076 (cond (suid-p "S") (sticky-p "T") ("-"))))) | |
8077 (concat read-bit write-bit x-bit)))) | |
8078 | |
8079 (defun efs-mode-string (int) | |
8080 ;; Takes an octal integer between 0 and 7777, and returns the 9 character | |
8081 ;; mode string. | |
8082 (let* ((other-int (% int 10)) | |
8083 (int (/ int 10)) | |
8084 (group-int (% int 10)) | |
8085 (int (/ int 10)) | |
8086 (owner-int (% int 10)) | |
8087 (int (/ int 10)) | |
8088 (suid (memq int '(4 5 6 7))) | |
8089 (sgid (memq int '(2 3 6 7))) | |
8090 (sticky (memq int '(1 3 5 7)))) | |
8091 (concat (efs-build-mode-string-element owner-int suid nil) | |
8092 (efs-build-mode-string-element group-int sgid nil) | |
8093 (efs-build-mode-string-element other-int nil sticky)))) | |
8094 | |
8095 (defun efs-set-file-modes (file mode) | |
8096 ;; set-file-modes for remote files. | |
8097 ;; For remote files, if mode is nil, does nothing. | |
8098 ;; This is because efs-file-modes returns nil if the modes | |
8099 ;; of a remote file couldn't be determined, even if the file exists. | |
8100 (and mode | |
8101 (let* ((file (expand-file-name file)) | |
8102 (parsed (efs-ftp-path file)) | |
8103 (host (car parsed)) | |
8104 (user (nth 1 parsed)) | |
8105 (r-file (nth 2 parsed)) | |
8106 ;; convert to octal, and keep only 12 lowest order bits. | |
8107 (omode (format "%o" (- mode (lsh (lsh mode -12) 12))))) | |
8108 (if (or (efs-get-host-property host 'chmod-failed) | |
8109 (null (memq (efs-host-type host user) efs-unix-host-types))) | |
8110 (message "Unable to set file modes for %s to %s." file omode) | |
8111 (efs-send-cmd | |
8112 host user | |
8113 (list 'quote 'site 'chmod omode r-file) | |
8114 nil nil | |
8115 (efs-cont (result line cont-lines) (host file r-file omode) | |
8116 (if result | |
8117 (progn | |
8118 (efs-set-host-property host 'chmod-failed t) | |
8119 (message "CHMOD %s failed for %s on %s." omode r-file host) | |
8120 (if efs-ding-on-chmod-failure | |
8121 (progn (ding) (sit-for 1)))) | |
8122 (let ((ent (efs-get-file-entry file))) | |
8123 (if ent | |
8124 (let* ((type | |
8125 (cond | |
8126 ((null (car ent)) "-") | |
8127 ((eq (car ent) t) "d") | |
8128 ((stringp (car ent)) "s") | |
8129 (t | |
8130 (error | |
8131 "Weird error in efs-set-file-modes")))) | |
8132 (mode-string (concat | |
8133 type | |
8134 (efs-mode-string | |
8135 (string-to-int omode)))) | |
8136 (tail (nthcdr 3 ent))) | |
8137 (if (consp tail) | |
8138 (setcar tail mode-string) | |
8139 (efs-add-file-entry nil file (car ent) (nth 1 ent) | |
8140 (nth 2 ent) mode-string))))))) | |
8141 0)))) ; It should be safe to do this NOWAIT = 0 | |
8142 ;; set-file-modes returns nil | |
8143 nil) | |
8144 | |
8145 (defmacro efs-parse-mode-element (modes) | |
8146 ;; Parses MODES, a string of three chars, and returns an integer | |
8147 ;; between 0 and 7 according to how unix file modes are represented | |
8148 ;; for chmod. | |
8149 (` (if (= (length (, modes)) 3) | |
8150 (let ((list (mapcar | |
8151 (function (lambda (char) | |
8152 (if (memq char '( ?- ?S ?T)) 0 1))) | |
8153 (, modes)))) | |
8154 ;; Convert to octal | |
8155 (+ (* (car list) 4) (* (nth 1 list) 2) (nth 2 list))) | |
8156 (error "Can't parse modes %s" (, modes))))) | |
8157 | |
8158 (defun efs-parse-mode-string (string) | |
8159 ;; Parse a 9-character mode string, and return what it represents | |
8160 ;; as a decimal integer. | |
8161 (let ((owner (efs-parse-mode-element (substring string 0 3))) | |
8162 (group (efs-parse-mode-element (substring string 3 6))) | |
8163 (other (efs-parse-mode-element (substring string 6 9))) | |
8164 (owner-x (elt string 2)) | |
8165 (group-x (elt string 5)) | |
8166 (other-x (elt string 8))) | |
8167 (+ (* (+ (if (memq owner-x '(?s ?S)) 4 0) | |
8168 (if (memq group-x '(?s ?S)) 2 0) | |
8169 (if (memq other-x '(?t ?T)) 1 0)) | |
8170 512) | |
8171 (* owner 64) | |
8172 (* group 8) | |
8173 other))) | |
8174 | |
8175 (defun efs-file-modes (file) | |
8176 ;; Version of file-modes for remote files. | |
8177 ;; Returns nil if the file modes can't be determined, either because | |
8178 ;; the file doesn't exist, or for any other reason. | |
8179 (let* ((file (expand-file-name file)) | |
8180 (parsed (efs-ftp-path file))) | |
8181 (and (memq (efs-host-type (car parsed)) efs-unix-host-types) | |
8182 ;; Someday we should cache mode strings for non-unix, but they | |
8183 ;; won't be in unix format. Also, CHMOD doesn't work for non-unix | |
8184 ;; hosts, so returning this info to emacs is a waste. | |
8185 (let* ((ent (efs-get-file-entry file)) | |
8186 (modes (nth 3 ent))) | |
8187 (and modes | |
8188 (efs-parse-mode-string (substring modes 1))))))) | |
8189 | |
8190 ;;;; ------------------------------------------------------------ | |
8191 ;;;; Redefinition of Emacs file modtime support. | |
8192 ;;;; ------------------------------------------------------------ | |
8193 | |
8194 (defun efs-day-number (year month day) | |
8195 ;; Returns the day number within year of date. Taken from calendar.el, | |
8196 ;; by Edward Reingold. Thanks. | |
8197 ;; An explanation of the calculation can be found in PascAlgorithms by | |
8198 ;; Edward and Ruth Reingold, Scott-Foresman/Little, Brown, 1988. | |
8199 (let ((day-of-year (+ day (* 31 (1- month))))) | |
8200 (if (> month 2) | |
8201 (progn | |
8202 (setq day-of-year (- day-of-year (/ (+ 23 (* 4 month)) 10))) | |
8203 (if (zerop (% year 4)) | |
8204 (setq day-of-year (1+ day-of-year))))) | |
8205 day-of-year)) | |
8206 | |
8207 (defun efs-days-elapsed (year month day) | |
8208 ;; Number of days elapsed since Jan 1, `efs-time-zero' | |
8209 (+ (efs-day-number year month day) ; days this year | |
8210 (* 365 (- year efs-time-zero)) ; days in prior years | |
8211 (- (/ (max (1- year) efs-time-zero) 4) | |
8212 (/ efs-time-zero 4)) ; leap years | |
8213 -1 )) ; don't count today | |
8214 | |
8215 ;; 2^16 = 65536 | |
8216 ;; Use this to avoid overflows | |
8217 | |
8218 (defun efs-seconds-elapsed (year month day hours minutes seconds) | |
8219 ;; Computes the seconds elapsed from `efs-time-zero', in emacs' | |
8220 ;; format of a list of two integers, the first the higher 16-bits, | |
8221 ;; the second the lower 16-bits. | |
8222 (let* ((days (efs-days-elapsed year month day)) | |
8223 ;; compute hours | |
8224 (hours (+ (* 24 days) hours)) | |
8225 (high (lsh hours -16)) | |
8226 (low (- hours (lsh high 16))) | |
8227 ;; compute minutes | |
8228 (low (+ (* low 60) minutes)) | |
8229 (carry (lsh low -16)) | |
8230 (high (+ (* high 60) carry)) | |
8231 (low (- low (lsh carry 16))) | |
8232 ;; compute seconds | |
8233 (low (+ (* low 60) seconds)) | |
8234 (carry (lsh low -16)) | |
8235 (high (+ (* high 60) carry)) | |
8236 (low (- low (lsh carry 16)))) | |
8237 (list high low))) | |
8238 | |
8239 (defun efs-parse-mdtime (string) | |
8240 ;; Parse a string, which is assumed to be the result of an ftp MDTM command. | |
8241 (efs-save-match-data | |
8242 (if (string-match efs-mdtm-msgs string) | |
8243 (efs-seconds-elapsed | |
8244 (string-to-int (substring string 4 8)) | |
8245 (string-to-int (substring string 8 10)) | |
8246 (string-to-int (substring string 10 12)) | |
8247 (string-to-int (substring string 12 14)) | |
8248 (string-to-int (substring string 14 16)) | |
8249 (string-to-int (substring string 16 18)))))) | |
8250 | |
8251 (defun efs-parse-ctime (string) | |
8252 ;; Parse STRING which is assumed to be the result of a query over port 37. | |
8253 ;; Returns the number of seconds since the turn of the century, as a | |
8254 ;; list of two 16-bit integers. | |
8255 (and (= (length string) 4) | |
8256 (list (+ (lsh (aref string 0) 8) (aref string 1)) | |
8257 (+ (lsh (aref string 2) 8) (aref string 3))))) | |
8258 | |
8259 (defun efs-time-minus (time1 time2) | |
8260 ;; Subtract 32-bit integers, represented as two 16-bit integers. | |
8261 (let ((high (- (car time1) (car time2))) | |
8262 (low (- (nth 1 time1) (nth 1 time2)))) | |
8263 (cond | |
8264 ((and (< high 0) (> low 0)) | |
8265 (setq high (1+ high) | |
8266 low (- low 65536))) | |
8267 ((and (> high 0) (< low 0)) | |
8268 (setq high (1- high) | |
8269 low (+ 65536 low)))) | |
8270 (list high low))) | |
8271 | |
8272 (defun efs-time-greater (time1 time2) | |
8273 ;; Compare two 32-bit integers, each represented as a list of two 16-bit | |
8274 ;; integers. | |
8275 (or (> (car time1) (car time2)) | |
8276 (and (= (car time1) (car time2)) | |
8277 (> (nth 1 time1) (nth 1 time2))))) | |
8278 | |
8279 (defun efs-century-time (host &optional nowait cont) | |
8280 ;; Treat nil as the local host. | |
8281 ;; Returns the # of seconds since the turn of the century, according | |
8282 ;; to the system clock on host. | |
8283 ;; CONT is called with first arg HOST and second the # of seconds. | |
8284 (or host (setq host (system-name))) | |
8285 (efs-set-host-property host 'last-ctime nil) | |
8286 (efs-set-host-property host 'ctime-cont cont) | |
8287 (let ((name (format efs-ctime-process-name-format host)) | |
8288 proc) | |
8289 (condition-case nil (delete-process name) (error nil)) | |
8290 (if (and | |
8291 (or (efs-save-match-data (string-match efs-local-host-regexp host)) | |
8292 (string-equal host (system-name))) | |
8293 (setq proc (condition-case nil | |
8294 (open-network-stream name nil host 37) | |
8295 (error nil)))) | |
8296 (progn | |
8297 (set (intern name) "") | |
8298 (set-process-filter | |
8299 proc | |
8300 (function | |
8301 (lambda (proc string) | |
8302 (let ((name (process-name proc)) | |
8303 result) | |
8304 (set (intern name) (concat (symbol-value (intern name)) | |
8305 string)) | |
8306 (setq result (efs-parse-ctime | |
8307 (symbol-value (intern name)))) | |
8308 (if result | |
8309 (let* ((host (substring name 11 -1)) | |
8310 (cont (efs-get-host-property host 'ctime-cont))) | |
8311 (efs-set-host-property host 'last-ctime result) | |
8312 (condition-case nil (delete-process proc) (error nil)) | |
8313 (if cont | |
8314 (progn | |
8315 (efs-set-host-property host 'ctime-cont nil) | |
8316 (efs-call-cont cont host result))))))))) | |
8317 (set-process-sentinel | |
8318 proc | |
8319 (function | |
8320 (lambda (proc state) | |
8321 (let* ((name (process-name proc)) | |
8322 (host (substring name 11 -1)) | |
8323 (cont (efs-get-host-property host 'ctime-cont))) | |
8324 (makunbound (intern name)) | |
8325 (or (efs-get-host-property host 'last-ctime) | |
8326 (if cont | |
8327 (progn | |
8328 (efs-set-host-property host 'ctime-cont nil) | |
8329 (efs-call-cont cont host 'failed)))))))) | |
8330 (if nowait | |
8331 nil | |
8332 (let ((quit-flag nil) | |
8333 (inhibit-quit nil)) | |
8334 (while (memq (process-status proc) '(run open)) | |
8335 (accept-process-output))) | |
8336 (accept-process-output) | |
8337 (or (efs-get-host-property host 'last-ctime) | |
8338 'failed))) | |
8339 (if cont | |
8340 (progn | |
8341 (efs-set-host-property host 'ctime-cont nil) | |
8342 (efs-call-cont cont host 'failed))) | |
8343 (if nowait nil 'failed)))) | |
8344 | |
8345 (defun efs-clock-difference (host &optional nowait) | |
8346 ;; clock difference with the local host | |
8347 (let ((result (efs-get-host-property host 'clock-diff))) | |
8348 (or | |
8349 result | |
8350 (progn | |
8351 (efs-century-time | |
8352 host nowait | |
8353 (efs-cont (host result) (nowait) | |
8354 (if (eq result 'failed) | |
8355 (efs-set-host-property host 'clock-diff 'failed) | |
8356 (efs-century-time | |
8357 nil nowait | |
8358 (efs-cont (lhost lresult) (host result) | |
8359 (if (eq lresult 'failed) | |
8360 (efs-set-host-property host 'clock-diff 'failed) | |
8361 (efs-set-host-property host 'clock-diff | |
8362 (efs-time-minus result lresult)))))))) | |
8363 (and (null nowait) | |
8364 (or (efs-get-host-property host 'clock-diff) | |
8365 'failed)))))) | |
8366 | |
8367 (defun efs-get-file-mdtm (host user file path) | |
8368 "For HOST and USER, return FILE's last modification time. | |
8369 PATH is the file name in full efs syntax. | |
8370 Returns a list of two six-digit integers which represent the 16 high order | |
8371 bits, and 16 low order bits of the number of elapsed seconds since | |
8372 `efs-time-zero'" | |
8373 (and (null (efs-get-host-property host 'mdtm-failed)) | |
8374 (let ((result (efs-send-cmd host user (list 'quote 'mdtm file) | |
8375 (and (eq efs-verbose t) | |
8376 "Getting modtime"))) | |
8377 parsed) | |
8378 (if (and (null (car result)) | |
8379 (setq parsed (efs-parse-mdtime (nth 1 result)))) | |
8380 (let ((ent (efs-get-file-entry path))) | |
8381 (if ent | |
8382 (setcdr ent (list (nth 1 ent) (nth 2 ent) | |
8383 (nth 3 ent) (nth 4 ent) | |
8384 parsed))) | |
8385 parsed) | |
8386 (efs-save-match-data | |
8387 ;; The 550 error is for a nonexistent file. Actually implies | |
8388 ;; that MDTM works. | |
8389 (if (string-match "^550 " (nth 1 result)) | |
8390 '(0 0) | |
8391 (efs-set-host-property host 'mdtm-failed t) | |
8392 nil)))))) | |
8393 | |
8394 (efs-define-fun efs-set-emacs-bvf-mdtm (buffer mdtm) | |
8395 ;; Sets cached value for the buffer visited file modtime. | |
8396 (if (get-buffer buffer) | |
8397 (save-excursion | |
8398 (set-buffer buffer) | |
8399 (let (file-name-handler-alist) | |
8400 (set-visited-file-modtime mdtm))))) | |
8401 | |
8402 ;; (defun efs-set-visited-file-modtime (&optional time) | |
8403 ;; ;; For remote files sets the modtime for a buffer to be that of the | |
8404 ;; ;; visited file. With arg TIME sets the modtime to TIME. TIME must be a list | |
8405 ;; ;; of two 16-bit integers. | |
8406 ;; ;; The function set-visited-file-modtime is for emacs-19. It doesn't | |
8407 ;; ;; exist in emacs 18. If you're running efs, it will work in emacs 18 for | |
8408 ;; ;; remote files only. | |
8409 ;; (if time | |
8410 ;; (efs-set-emacs-bvf-mdtm (current-buffer) time) | |
8411 ;; (let* ((path buffer-file-name) | |
8412 ;; (parsed (efs-ftp-path path)) | |
8413 ;; (host (car parsed)) | |
8414 ;; (user (nth 1 parsed)) | |
8415 ;; (file (nth 2 parsed)) | |
8416 ;; (buffer (current-buffer))) | |
8417 ;; (if (efs-save-match-data | |
8418 ;; (and efs-verify-modtime-host-regexp | |
8419 ;; (string-match efs-verify-modtime-host-regexp host) | |
8420 ;; (or efs-verify-anonymous-modtime | |
8421 ;; (not (efs-anonymous-p user))) | |
8422 ;; (not (efs-get-host-property host 'mdtm-failed)))) | |
8423 ;; (efs-send-cmd | |
8424 ;; host user (list 'quote 'mdtm file) | |
8425 ;; nil nil | |
8426 ;; (efs-cont (result line cont-lines) (host user path buffer) | |
8427 ;; (let (modtime) | |
8428 ;; (if (and (null result) | |
8429 ;; (setq modtime (efs-parse-mdtime line))) | |
8430 ;; (let ((ent (efs-get-file-entry path))) | |
8431 ;; (if ent | |
8432 ;; (setcdr ent (list (nth 1 ent) (nth 2 ent) | |
8433 ;; (nth 3 ent) (nth 4 ent) | |
8434 ;; modtime))) | |
8435 ;; (setq buffer (and (setq buffer (get-buffer buffer)) | |
8436 ;; (buffer-name buffer))) | |
8437 ;; ;; Beware that since this is happening asynch, the buffer | |
8438 ;; ;; may have disappeared. | |
8439 ;; (and buffer (efs-set-emacs-bvf-mdtm buffer modtime))) | |
8440 ;; (efs-save-match-data | |
8441 ;; (or (string-match "^550 " line) | |
8442 ;; (efs-set-host-property host 'mdtm-failed t))) | |
8443 ;; (efs-set-emacs-bvf-mdtm buffer 0)))) ; store dummy values | |
8444 ;; 0) ; Always do this NOWAIT = 0 | |
8445 ;; (efs-set-emacs-bvf-mdtm buffer 0)) | |
8446 ;; nil) ; return NIL | |
8447 ;; )) | |
8448 | |
8449 (defvar efs-set-modtimes-synchronously nil | |
8450 "*Whether efs uses a synchronous FTP command to set the visited file modtime. | |
8451 Setting this variable to non-nil means that efs will set visited file modtimes | |
8452 synchronously. | |
8453 | |
8454 Asynchronous setting of visited file modtimes leaves a very small | |
8455 window where Emacs may fail to detect a super session. However, it gives | |
8456 faster user access to newly visited files.") | |
8457 | |
8458 | |
8459 (defun efs-set-visited-file-modtime (&optional time) | |
8460 ;; For remote files sets the modtime for a buffer to be that of the | |
8461 ;; visited file. With arg TIME sets the modtime to TIME. TIME must be a list | |
8462 ;; of two 16-bit integers. | |
8463 ;; The function set-visited-file-modtime is for emacs-19. It doesn't | |
8464 ;; exist in emacs 18. If you're running efs, it will work in emacs 18 for | |
8465 ;; remote files only. | |
8466 (if time | |
8467 (efs-set-emacs-bvf-mdtm (current-buffer) time) | |
8468 (let* ((path buffer-file-name) | |
8469 (parsed (efs-ftp-path path)) | |
8470 (host (car parsed)) | |
8471 (user (nth 1 parsed)) | |
8472 (file (nth 2 parsed)) | |
8473 (buffer (current-buffer))) | |
8474 (if (efs-save-match-data | |
8475 (and efs-verify-modtime-host-regexp | |
8476 (string-match efs-verify-modtime-host-regexp host) | |
8477 (or efs-verify-anonymous-modtime | |
8478 (not (efs-anonymous-p user))) | |
8479 (not (efs-get-host-property host 'mdtm-failed)))) | |
8480 (progn | |
8481 (or efs-set-modtimes-synchronously (clear-visited-file-modtime)) | |
8482 (efs-send-cmd | |
8483 host user (list 'quote 'mdtm file) | |
8484 nil nil | |
8485 (efs-cont (result line cont-lines) (host user path buffer) | |
8486 (let (modtime) | |
8487 (if (and (null result) | |
8488 (setq modtime (efs-parse-mdtime line))) | |
8489 (let ((ent (efs-get-file-entry path))) | |
8490 (if ent | |
8491 (setcdr ent (list (nth 1 ent) (nth 2 ent) | |
8492 (nth 3 ent) (nth 4 ent) | |
8493 modtime))) | |
8494 (setq buffer (and (setq buffer (get-buffer buffer)) | |
8495 (buffer-name buffer))) | |
8496 ;; Beware that since might be happening asynch, | |
8497 ;; the buffer may have disappeared. | |
8498 (and buffer (efs-set-emacs-bvf-mdtm buffer modtime))) | |
8499 (efs-save-match-data | |
8500 (or (string-match "^550 " line) | |
8501 (efs-set-host-property host 'mdtm-failed t))) | |
8502 (efs-set-emacs-bvf-mdtm buffer '(0 0))))) ; store dummy values | |
8503 (and (null efs-set-modtimes-synchronously) 0))) | |
8504 (efs-set-emacs-bvf-mdtm buffer '(0 0))) | |
8505 nil))) ; return NIL | |
8506 | |
8507 (defun efs-file-newer-than-file-p (file1 file2) | |
8508 ;; Version of file-newer-than-file-p for remote files. | |
8509 (let* ((file1 (expand-file-name file1)) | |
8510 (file2 (expand-file-name file2)) | |
8511 (parsed1 (efs-ftp-path file1)) | |
8512 (parsed2 (efs-ftp-path file2)) | |
8513 (host1 (car parsed1)) | |
8514 (host2 (car parsed2)) | |
8515 (user1 (nth 1 parsed1)) | |
8516 (user2 (nth 1 parsed2))) | |
8517 (cond | |
8518 ;; If the first file doedn't exist, or is remote but | |
8519 ;; we're not supposed to check modtimes on it, return nil. | |
8520 ((or (null (file-exists-p file1)) | |
8521 (and parsed1 | |
8522 (or | |
8523 (null efs-verify-modtime-host-regexp) | |
8524 (efs-get-host-property host1 'mdtm-failed) | |
8525 (not (string-match efs-verify-modtime-host-regexp host1)) | |
8526 (and (null efs-verify-anonymous-modtime) | |
8527 (efs-anonymous-p user1))))) | |
8528 nil) | |
8529 ;; If the same is true for the second file, return t. | |
8530 ((or (null (file-exists-p file2)) | |
8531 (and parsed2 | |
8532 (or | |
8533 (null efs-verify-modtime-host-regexp) | |
8534 (efs-get-host-property host2 'mdtm-failed) | |
8535 (not (string-match efs-verify-modtime-host-regexp host2)) | |
8536 (and (null efs-verify-anonymous-modtime) | |
8537 (efs-anonymous-p user2))))) | |
8538 t) | |
8539 ;; Calculate modtimes. If we get here, any remote files should | |
8540 ;; have a file entry. | |
8541 (t | |
8542 (let (mod1 mod2 shift1 shift2) | |
8543 (if parsed1 | |
8544 (let ((ent (efs-get-file-entry file1))) | |
8545 (setq mod1 (nth 5 ent) | |
8546 shift1 (efs-clock-difference host1)) | |
8547 (or mod1 | |
8548 (setq mod1 (efs-get-file-mdtm | |
8549 host1 user1 (nth 2 parsed1) file1)))) | |
8550 (setq mod1 (nth 5 (file-attributes file1)))) | |
8551 (if parsed2 | |
8552 (let ((ent (efs-get-file-entry file2))) | |
8553 (setq mod2 (nth 5 ent) | |
8554 shift2 (efs-clock-difference host2)) | |
8555 (or mod2 | |
8556 (setq mod2 (efs-get-file-mdtm | |
8557 host2 user2 (nth 2 parsed2) file2)))) | |
8558 (setq mod2 (nth 5 (file-attributes file2)))) | |
8559 ;; If we can't compute clock shifts, we act as if we don't | |
8560 ;; even know the modtime. Should we have more faith in ntp? | |
8561 (cond | |
8562 ((or (null mod1) (eq shift1 'failed)) | |
8563 nil) | |
8564 ((or (null mod2) (eq shift2 'failed)) | |
8565 t) | |
8566 ;; We get to compute something! | |
8567 (t | |
8568 (efs-time-greater | |
8569 (if shift1 (efs-time-minus mod1 shift1) mod1) | |
8570 (if shift2 (efs-time-minus mod2 shift2) mod2))))))))) | |
8571 | |
8572 (defun efs-verify-visited-file-modtime (buff) | |
8573 ;; Verifies the modtime for buffers visiting remote files. | |
8574 ;; Won't get called for buffer not visiting any file. | |
8575 (let ((buff (get-buffer buff))) | |
8576 (null | |
8577 (and buff ; return t if no buffer? Need to beware of multi-threading. | |
8578 (buffer-file-name buff) ; t if no file | |
8579 (let ((mdtm (save-excursion | |
8580 (set-buffer buff) | |
8581 (visited-file-modtime)))) | |
8582 (and | |
8583 (not (eq mdtm 0)) | |
8584 (not (equal mdtm '(0 0))) | |
8585 efs-verify-modtime-host-regexp | |
8586 (let* ((path (buffer-file-name buff)) | |
8587 (parsed (efs-ftp-path path)) | |
8588 (host (car parsed)) | |
8589 (user (nth 1 parsed)) | |
8590 nmdtm) | |
8591 (and | |
8592 (null (efs-get-host-property host 'mdtm-failed)) | |
8593 (efs-save-match-data | |
8594 (string-match | |
8595 efs-verify-modtime-host-regexp host)) | |
8596 (or efs-verify-anonymous-modtime | |
8597 (not (efs-anonymous-p user))) | |
8598 (setq nmdtm (efs-get-file-mdtm host user (nth 2 parsed) path)) | |
8599 (progn | |
8600 (or (equal nmdtm '(0 0)) | |
8601 (file-exists-p path) ; Make sure that there is an entry. | |
8602 (null | |
8603 (efs-get-files | |
8604 (file-name-directory | |
8605 (efs-internal-directory-file-name path)))) | |
8606 (efs-add-file-entry | |
8607 (efs-host-type host) path nil nil nil nil nil nmdtm)) | |
8608 (null (and (eq (cdr mdtm) (nth 1 nmdtm)) | |
8609 (eq (car mdtm) (car nmdtm))))))))))))) | |
8610 | |
8611 ;;;; ----------------------------------------------------------- | |
8612 ;;;; Redefinition of Emacs file name completion | |
8613 ;;;; ----------------------------------------------------------- | |
8614 | |
8615 (defmacro efs-set-completion-ignored-pattern () | |
8616 ;; Set regexp efs-completion-ignored-pattern | |
8617 ;; to use for filename completion. | |
8618 (` | |
8619 (or (equal efs-completion-ignored-extensions | |
8620 completion-ignored-extensions) | |
8621 (setq efs-completion-ignored-extensions | |
8622 completion-ignored-extensions | |
8623 efs-completion-ignored-pattern | |
8624 (mapconcat (function | |
8625 (lambda (s) (if (stringp s) | |
8626 (concat (regexp-quote s) "$") | |
8627 "/"))) ; / never in filename | |
8628 efs-completion-ignored-extensions | |
8629 "\\|"))))) | |
8630 | |
8631 (defun efs-file-entry-active-p (sym) | |
8632 ;; If the file entry is a symlink, returns whether the file pointed to | |
8633 ;; exists. | |
8634 ;; Note that DIR is dynamically bound. | |
8635 (let ((file-type (car (get sym 'val)))) | |
8636 (or (not (stringp file-type)) | |
8637 (file-exists-p (efs-chase-symlinks | |
8638 (expand-file-name file-type efs-completion-dir)))))) | |
8639 | |
8640 (defun efs-file-entry-not-ignored-p (sym) | |
8641 ;; If the file entry is not a directory (nor a symlink pointing to a | |
8642 ;; directory) returns whether the file (or file pointed to by the symlink) | |
8643 ;; is ignored by completion-ignored-extensions. | |
8644 (let ((file-type (car (get sym 'val))) | |
8645 (symname (symbol-name sym))) | |
8646 (if (stringp file-type) | |
8647 ;; Maybe file-truename would be better here, but it is very costly | |
8648 ;; to chase symlinks at every level over FTP. | |
8649 (let ((file (efs-chase-symlinks (expand-file-name | |
8650 file-type efs-completion-dir)))) | |
8651 (or (file-directory-p file) | |
8652 (and (file-exists-p file) | |
8653 (not (string-match efs-completion-ignored-pattern | |
8654 symname))))) | |
8655 (or file-type ; is a directory name | |
8656 (not (string-match efs-completion-ignored-pattern symname)))))) | |
8657 | |
8658 (defun efs-file-name-all-completions (file dir) | |
8659 ;; Does file-name-all-completions in remote directories. | |
8660 (efs-barf-if-not-directory dir) | |
8661 (let* ((efs-completion-dir (file-name-as-directory (expand-file-name dir))) | |
8662 (completion-ignore-case | |
8663 (memq (efs-host-type (car (efs-ftp-path efs-completion-dir))) | |
8664 efs-case-insensitive-host-types)) | |
8665 (tbl (efs-get-files efs-completion-dir)) | |
8666 (completions | |
8667 (all-completions file tbl | |
8668 (function efs-file-entry-active-p)))) | |
8669 ;; see whether each matching file is a directory or not... | |
8670 (mapcar | |
8671 ;; Since the entries in completions will match the case | |
8672 ;; of the entries in tbl, don't need to case-fold | |
8673 ;; in efs-get-hash-entry below. | |
8674 (function | |
8675 (lambda (file) | |
8676 (let ((ent (car (efs-get-hash-entry file tbl)))) | |
8677 (if (or (eq ent t) | |
8678 (and (stringp ent) | |
8679 (file-directory-p (efs-chase-symlinks | |
8680 (expand-file-name | |
8681 ent efs-completion-dir))))) | |
8682 (concat file "/") | |
8683 file)))) | |
8684 completions))) | |
8685 | |
8686 (defun efs-file-name-completion (file dir) | |
8687 ;; Does file name expansion in remote directories. | |
8688 (efs-barf-if-not-directory dir) | |
8689 (if (equal file "") | |
8690 "" | |
8691 (let* ((efs-completion-dir (file-name-as-directory (expand-file-name dir))) | |
8692 (completion-ignore-case | |
8693 (memq (efs-host-type (car (efs-ftp-path efs-completion-dir))) | |
8694 efs-case-insensitive-host-types)) | |
8695 (tbl (efs-get-files efs-completion-dir))) | |
8696 (efs-set-completion-ignored-pattern) | |
8697 (efs-save-match-data | |
8698 (or (efs-file-name-completion-1 | |
8699 file tbl efs-completion-dir | |
8700 (function efs-file-entry-not-ignored-p)) | |
8701 (efs-file-name-completion-1 | |
8702 file tbl efs-completion-dir | |
8703 (function efs-file-entry-active-p))))))) | |
8704 | |
8705 (defun efs-file-name-completion-1 (file tbl dir predicate) | |
8706 ;; Internal subroutine for efs-file-name-completion. Do not call this. | |
8707 (let ((bestmatch (try-completion file tbl predicate))) | |
8708 (if bestmatch | |
8709 (if (eq bestmatch t) | |
8710 (if (file-directory-p (expand-file-name file dir)) | |
8711 (concat file "/") | |
8712 t) | |
8713 (if (and (eq (try-completion bestmatch tbl predicate) t) | |
8714 (file-directory-p | |
8715 (expand-file-name bestmatch dir))) | |
8716 (concat bestmatch "/") | |
8717 bestmatch))))) | |
8718 | |
8719 ;;;; ---------------------------------------------------------- | |
8720 ;;;; Functions for loading lisp. | |
8721 ;;;; ---------------------------------------------------------- | |
8722 | |
8723 ;;; jka-load provided ideas here. Thanks, Jay. | |
8724 | |
8725 (defun efs-load-openp (str suffixes) | |
8726 ;; Given STR, searches load-path and efs-load-lisp-extensions | |
8727 ;; for the name of a file to load. Returns the full path, or nil | |
8728 ;; if none found. | |
8729 (let ((path-list (if (file-name-absolute-p str) t load-path)) | |
8730 root result) | |
8731 ;; If there is no load-path, at least try the default directory. | |
8732 (or path-list | |
8733 (setq path-list (list default-directory))) | |
8734 (while (and path-list (null result)) | |
8735 (if (eq path-list t) | |
8736 (setq path-list nil | |
8737 root str) | |
8738 (setq root (expand-file-name str (car path-list)) | |
8739 path-list (cdr path-list)) | |
8740 (or (file-name-absolute-p root) | |
8741 (setq root (expand-file-name root default-directory)))) | |
8742 (let ((suff-list suffixes)) | |
8743 (while (and suff-list (null result)) | |
8744 (let ((try (concat root (car suff-list)))) | |
8745 (if (or (not (file-readable-p try)) | |
8746 (file-directory-p try)) | |
8747 (setq suff-list (cdr suff-list)) | |
8748 (setq result try)))))) | |
8749 result)) | |
8750 | |
8751 (defun efs-load (file &optional noerror nomessage nosuffix) | |
8752 "Documented as original." | |
8753 (let ((filename (efs-load-openp | |
8754 file | |
8755 (if nosuffix '("") efs-load-lisp-extensions)))) | |
8756 (if (not filename) | |
8757 (and (null noerror) (error "Cannot open load file %s" file)) | |
8758 (let ((parsed (efs-ftp-path filename)) | |
8759 (after-load (and (boundp 'after-load-alist) | |
8760 (assoc file after-load-alist)))) | |
8761 (if parsed | |
8762 (let ((temp (car (efs-make-tmp-name nil (car parsed))))) | |
8763 (unwind-protect | |
8764 (progn | |
8765 (efs-copy-file-internal | |
8766 filename parsed temp nil t nil | |
8767 (format "Getting %s" filename)) | |
8768 (or (file-readable-p temp) | |
8769 (error | |
8770 "efs-load: temp file %s is unreadable" temp)) | |
8771 (or nomessage | |
8772 (message "Loading %s..." file)) | |
8773 ;; temp is an absolute filename, so load path | |
8774 ;; won't be searched. | |
8775 (let (after-load-alist) | |
8776 (efs-real-load temp t t t)) | |
8777 (or nomessage | |
8778 (message "Loading %s...done" file)) | |
8779 (if after-load (mapcar 'eval (cdr after-load))) | |
8780 t) ; return t if everything worked | |
8781 (efs-del-tmp-name temp))) | |
8782 (prog2 | |
8783 (or nomessage | |
8784 (message "Loading %s..." file)) | |
8785 (let (after-load-alist) | |
8786 (or (efs-real-load filename noerror t t) | |
8787 (setq after-load nil))) | |
8788 (or nomessage | |
8789 (message "Loading %s...done" file)) | |
8790 (if after-load (mapcar 'eval (cdr after-load))))))))) | |
8791 | |
8792 (defun efs-require (feature &optional filename) | |
8793 "Documented as original." | |
8794 (if (eq feature 'ange-ftp) (efs-require-scream-and-yell)) | |
8795 (if (featurep feature) | |
8796 feature | |
8797 (or filename (setq filename (symbol-name feature))) | |
8798 (let ((fullpath (efs-load-openp filename | |
8799 efs-load-lisp-extensions))) | |
8800 (if (not fullpath) | |
8801 (error "Cannot open load file: %s" filename) | |
8802 (let ((parsed (efs-ftp-path fullpath))) | |
8803 (if parsed | |
8804 (let ((temp (car (efs-make-tmp-name nil (car parsed))))) | |
8805 (unwind-protect | |
8806 (progn | |
8807 (efs-copy-file-internal | |
8808 fullpath parsed temp nil t nil | |
8809 (format "Getting %s" fullpath)) | |
8810 (or (file-readable-p temp) | |
8811 (error | |
8812 "efs-require: temp file %s is unreadable" temp)) | |
8813 (efs-real-require feature temp)) | |
8814 (efs-del-tmp-name temp))) | |
8815 (efs-real-require feature fullpath))))))) | |
8816 | |
8817 (defun efs-require-scream-and-yell () | |
8818 ;; Complain if something attempts to load ange-ftp. | |
8819 (with-output-to-temp-buffer "*Help*" | |
8820 (princ | |
8821 "Something tried to load ange-ftp. | |
8822 EFS AND ANGE-FTP DO NOT WORK TOGETHER. | |
8823 | |
8824 If the culprit package does need to access ange-ftp internal functions, | |
8825 then it should be adequate to simply remove the \(require 'ange-ftp\) | |
8826 line and let efs handle remote file access. Otherwise, it will need to | |
8827 be ported to efs. This may already have been done, and you can find out | |
8828 by sending an enquiry to efs-help@cuckoo.hpl.hp.com. | |
8829 | |
8830 Signalling an error with backtrace will allow you to determine which | |
8831 package was requiring ange-ftp.\n")) | |
8832 (select-window (get-buffer-window "*Help*")) | |
8833 (enlarge-window (- (count-lines (point-min) (point-max)) | |
8834 (window-height) -1)) | |
8835 (if (y-or-n-p "Signal error with backtrace? ") | |
8836 (let ((stack-trace-on-error t)) | |
8837 (error "Attempt to require ange-ftp")))) | |
8838 | |
8839 ;;;; ----------------------------------------------------------- | |
8840 ;;;; Redefinition of Emacs functions for reading file names. | |
8841 ;;;; ----------------------------------------------------------- | |
8842 | |
8843 (defun efs-unexpand-parsed-filename (host user path) | |
8844 ;; Replaces the home directory in path with "~". Returns the unexpanded | |
8845 ;; full-path. | |
8846 (let* ((path-len (length path)) | |
8847 (def-user (efs-get-user host)) | |
8848 (host-type (efs-host-type host user)) | |
8849 (ignore-case (memq host-type efs-case-insensitive-host-types))) | |
8850 (if (> path-len 1) | |
8851 (let* ((home (efs-expand-tilde "~" host-type host user)) | |
8852 (home-len (length home))) | |
8853 (if (and (> path-len home-len) | |
8854 (if ignore-case (string-equal (downcase home) | |
8855 (downcase | |
8856 (substring path | |
8857 0 home-len))) | |
8858 (string-equal home (substring path 0 home-len))) | |
8859 (= (aref path home-len) ?/)) | |
8860 (setq path (concat "~" (substring path home-len)))))) | |
8861 (if (if ignore-case (string-equal (downcase user) | |
8862 (downcase def-user)) | |
8863 (string-equal user def-user)) | |
8864 (format efs-path-format-without-user host path) | |
8865 (format efs-path-format-string user host path)))) | |
8866 | |
8867 (efs-define-fun efs-abbreviate-file-name (filename) | |
8868 ;; Version of abbreviate-file-name for remote files. | |
8869 (efs-save-match-data | |
8870 (let ((tail directory-abbrev-alist)) | |
8871 (while tail | |
8872 (if (string-match (car (car tail)) filename) | |
8873 (setq filename | |
8874 (concat (cdr (car tail)) | |
8875 (substring filename (match-end 0))))) | |
8876 (setq tail (cdr tail))) | |
8877 (apply 'efs-unexpand-parsed-filename (efs-ftp-path filename))))) | |
8878 | |
8879 (defun efs-default-dir-function () | |
8880 (let ((parsed (efs-ftp-path default-directory)) | |
8881 (dd default-directory)) | |
8882 (if parsed | |
8883 (efs-save-match-data | |
8884 (let ((tail directory-abbrev-alist)) | |
8885 (while tail | |
8886 (if (string-match (car (car tail)) dd) | |
8887 (setq dd (concat (cdr (car tail)) | |
8888 (substring dd (match-end 0))) | |
8889 parsed nil)) | |
8890 (setq tail (cdr tail))) | |
8891 (apply 'efs-unexpand-parsed-filename | |
8892 (or parsed (efs-ftp-path dd))))) | |
8893 default-directory))) | |
8894 | |
8895 (defun efs-re-read-dir (&optional dir) | |
8896 "Forces a re-read of the directory DIR. | |
8897 If DIR is omitted then it defaults to the directory part of the contents | |
8898 of the current buffer. This is so this function can be caled from the | |
8899 minibuffer." | |
8900 (interactive) | |
8901 (if dir | |
8902 (setq dir (expand-file-name dir)) | |
8903 (setq dir (file-name-directory (expand-file-name (buffer-string))))) | |
8904 (let ((parsed (efs-ftp-path dir))) | |
8905 (if parsed | |
8906 (let ((efs-ls-uncache t)) | |
8907 (efs-del-hash-entry (efs-canonize-file-name dir) | |
8908 efs-files-hashtable) | |
8909 (efs-get-files dir t))))) | |
8910 | |
8911 ;;;; --------------------------------------------------------------- | |
8912 ;;;; Creation and deletion of files and directories. | |
8913 ;;;; --------------------------------------------------------------- | |
8914 | |
8915 (defun efs-delete-file (file) | |
8916 ;; Deletes remote files. | |
8917 (let* ((file (expand-file-name file)) | |
8918 (parsed (efs-ftp-path file)) | |
8919 (host (car parsed)) | |
8920 (user (nth 1 parsed)) | |
8921 (host-type (efs-host-type host user)) | |
8922 (path (nth 2 parsed)) | |
8923 (abbr (efs-relativize-filename file)) | |
8924 (result (efs-send-cmd host user (list 'delete path) | |
8925 (format "Deleting %s" abbr)))) | |
8926 (if (car result) | |
8927 (signal 'ftp-error | |
8928 (list "Removing old name" | |
8929 (format "FTP Error: \"%s\"" (nth 1 result)) | |
8930 file))) | |
8931 (efs-delete-file-entry host-type file))) | |
8932 | |
8933 (defun efs-make-directory-internal (dir) | |
8934 ;; version of make-directory-internal for remote directories. | |
8935 (if (file-exists-p dir) | |
8936 (error "Cannot make directory %s: file already exists" dir) | |
8937 (let* ((parsed (efs-ftp-path dir)) | |
8938 (host (nth 0 parsed)) | |
8939 (user (nth 1 parsed)) | |
8940 (host-type (efs-host-type host user)) | |
8941 ;; Some ftp's on unix machines (at least on Suns) | |
8942 ;; insist that mkdir take a filename, and not a | |
8943 ;; directory-name name as an arg. Argh!! This is a bug. | |
8944 ;; Non-unix machines will probably always insist | |
8945 ;; that mkdir takes a directory-name as an arg | |
8946 ;; (as the ftp man page says it should). | |
8947 (path (if (or (memq host-type efs-unix-host-types) | |
8948 (memq host-type '(os2 dos))) | |
8949 (efs-internal-directory-file-name (nth 2 parsed)) | |
8950 (efs-internal-file-name-as-directory | |
8951 host-type (nth 2 parsed)))) | |
8952 (abbr (efs-relativize-filename dir)) | |
8953 (result (efs-send-cmd host user | |
8954 (list 'mkdir path) | |
8955 (format "Making directory %s" | |
8956 abbr)))) | |
8957 (if (car result) | |
8958 (efs-error host user | |
8959 (format "Could not make directory %s: %s" dir | |
8960 (nth 1 result)))) | |
8961 (efs-add-file-entry host-type dir t nil user)))) | |
8962 | |
8963 ;; V19 calls this function delete-directory. It used to be called | |
8964 ;; remove-directory. | |
8965 | |
8966 (defun efs-delete-directory (dir) | |
8967 ;; Version of delete-directory for remote directories. | |
8968 (if (file-directory-p dir) | |
8969 (let* ((parsed (efs-ftp-path dir)) | |
8970 (host (nth 0 parsed)) | |
8971 (user (nth 1 parsed)) | |
8972 (host-type (efs-host-type host user)) | |
8973 ;; Some ftp's on unix machines (at least on Suns) | |
8974 ;; insist that rmdir take a filename, and not a | |
8975 ;; directory-name name as an arg. Argh!! This is a bug. | |
8976 ;; Non-unix machines will probably always insist | |
8977 ;; that rmdir takes a directory-name as an arg | |
8978 ;; (as the ftp man page says it should). | |
8979 (path | |
8980 (if (or (memq host-type efs-unix-host-types) | |
8981 (memq host-type '(os2 dos))) | |
8982 (efs-internal-directory-file-name (nth 2 parsed)) | |
8983 (efs-internal-file-name-as-directory | |
8984 host-type (nth 2 parsed)))) | |
8985 (abbr (efs-relativize-filename dir)) | |
8986 (result (efs-send-cmd host user | |
8987 (list 'rmdir path) | |
8988 (format "Deleting directory %s" abbr)))) | |
8989 (if (car result) | |
8990 (efs-error host user | |
8991 (format "Could not delete directory %s: %s" | |
8992 dir (nth 1 result)))) | |
8993 (efs-delete-file-entry host-type dir t)) | |
8994 (error "Not a directory: %s" dir))) | |
8995 | |
8996 (defun efs-file-local-copy (file) | |
8997 ;; internal function for diff.el (dired 6.3 or later) | |
8998 ;; Makes a temp file containing the contents of file. | |
8999 ;; returns the name of the tmp file created, or nil if none is. | |
9000 ;; This function should have optional cont and nowait args. | |
9001 (let* ((file (expand-file-name file)) | |
9002 (tmp (car (efs-make-tmp-name nil (car (efs-ftp-path file)))))) | |
9003 (efs-copy-file-internal file (efs-ftp-path file) | |
9004 tmp nil t nil (format "Getting %s" file)) | |
9005 tmp)) | |
9006 | |
9007 (defun efs-diff/grep-del-temp-file (temp) | |
9008 ;; internal function for diff.el and grep.el | |
9009 ;; if TEMP is non-nil, deletes the temp file TEMP. | |
9010 ;; if TEMP is nil, does nothing. | |
9011 (and temp | |
9012 (efs-del-tmp-name temp))) | |
9013 | |
9014 ;;;; ------------------------------------------------------------ | |
9015 ;;;; File copying support... | |
9016 ;;;; ------------------------------------------------------------ | |
9017 | |
9018 ;;; - totally re-written 6/24/92. | |
9019 ;;; - re-written again 9/3/93 | |
9020 ;;; - and again 14/4/93 | |
9021 ;;; - and again 17/8/93 | |
9022 | |
9023 (defun efs-barf-or-query-if-file-exists (absname querystring interactive) | |
9024 (if (file-exists-p absname) | |
9025 (if (not interactive) | |
9026 (signal 'file-already-exists (list absname)) | |
9027 (if (not (yes-or-no-p (format "File %s already exists; %s anyway? " | |
9028 absname querystring))) | |
9029 (signal 'file-already-exists (list absname)))))) | |
9030 | |
9031 (defun efs-concatenate-files (file1 file2) | |
9032 ;; Concatenates file1 to file2. Both must be local files. | |
9033 ;; Needed because the efs version of copy-file understands | |
9034 ;; ok-if-already-exists = 'append | |
9035 (or (file-readable-p file1) | |
9036 (signal 'file-error | |
9037 (list (format "Input file %s not readable." file1)))) | |
9038 (or (file-writable-p file2) | |
9039 (signal 'file-error | |
9040 (list (format "Output file %s not writable." file2)))) | |
9041 (let ((default-directory exec-directory)) | |
9042 (call-process "sh" nil nil nil "-c" (format "cat %s >> %s" file1 file2)))) | |
9043 | |
9044 (defun efs-copy-add-file-entry (newname host-type user size append) | |
9045 ;; Add an entry in `efs-files-hashtable' for a file newly created via a copy. | |
9046 (if (eq size -1) (setq size nil)) | |
9047 (if append | |
9048 (let ((ent (efs-get-file-entry newname))) | |
9049 (if (and ent (null (car ent))) | |
9050 (if (and size (numberp (nth 1 ent))) | |
9051 (setcar (cdr ent) (+ size (nth 1 ent))) | |
9052 (setcar (cdr ent) nil)) | |
9053 ;; If the ent is a symlink or directory, don't overwrite that entry. | |
9054 (if (null ent) | |
9055 (efs-add-file-entry host-type newname nil nil nil)))) | |
9056 (efs-add-file-entry host-type newname nil size user))) | |
9057 | |
9058 (defun efs-copy-remote-to-remote (f-host-type f-host f-user f-path filename | |
9059 t-host-type t-host t-user | |
9060 t-path newname append msg cont | |
9061 nowait xfer-type) | |
9062 ;; Use a 3rd data connection to copy from F-HOST for F-USER to T-HOST | |
9063 ;; for T-USER. | |
9064 (if (efs-get-host-property t-host 'pasv-failed) | |
9065 ;; PASV didn't work before, don't try again. | |
9066 (if cont (efs-call-cont cont 'failed "" "")) | |
9067 (or xfer-type | |
9068 (setq xfer-type (efs-xfer-type f-host-type filename | |
9069 t-host-type newname))) | |
9070 (efs-send-cmd | |
9071 t-host t-user '(quote pasv) nil nil | |
9072 (efs-cont (pasv-result pasv-line pasv-cont-lines) | |
9073 (cont nowait f-host-type f-host f-user f-path filename | |
9074 t-host-type t-host t-user t-path newname xfer-type msg append) | |
9075 (efs-save-match-data | |
9076 (if (or pasv-result | |
9077 (not (string-match efs-pasv-msgs pasv-line))) | |
9078 (progn | |
9079 (efs-set-host-property t-host 'pasv-failed t) | |
9080 (if cont | |
9081 (efs-call-cont | |
9082 cont (or pasv-result 'failed) pasv-line pasv-cont-lines))) | |
9083 (let ((address (substring pasv-line (match-beginning 1) | |
9084 (match-end 1)))) | |
9085 (efs-send-cmd | |
9086 f-host f-user | |
9087 (list 'quote 'port address) nil nil | |
9088 (efs-cont (port-result port-line port-cont-lines) | |
9089 (cont f-host f-user f-host-type f-path filename | |
9090 xfer-type msg) | |
9091 (if port-result | |
9092 (if cont | |
9093 (efs-call-cont | |
9094 cont port-result port-line port-cont-lines) | |
9095 (efs-error f-host f-user | |
9096 (format "PORT failed for %s: %s" | |
9097 filename port-line))) | |
9098 (efs-send-cmd | |
9099 f-host f-user | |
9100 (list 'quote 'retr f-path xfer-type) | |
9101 msg nil | |
9102 (efs-cont (retr-result retr-line retr-cont-lines) | |
9103 (cont f-host f-user f-path) | |
9104 (and retr-result | |
9105 (null cont) | |
9106 (efs-error | |
9107 f-host f-user | |
9108 (format "RETR failed for %s: %s" | |
9109 f-path retr-line))) | |
9110 (if cont (efs-call-cont | |
9111 cont retr-result retr-line retr-cont-lines))) | |
9112 (if (eq nowait t) 1 nowait)))) | |
9113 1) ; can't ever wait on this command. | |
9114 (efs-send-cmd | |
9115 t-host t-user | |
9116 (list 'quote (if append 'appe 'stor) t-path xfer-type) | |
9117 nil nil | |
9118 (efs-cont (stor-result stor-line stor-cont-lines) | |
9119 (t-host t-user t-path t-host-type newname filename | |
9120 append) | |
9121 (if stor-result | |
9122 (efs-error | |
9123 t-host t-user (format "%s failed for %s: %s" | |
9124 (if append "APPE" "STOR") | |
9125 t-path stor-line)) | |
9126 (efs-copy-add-file-entry | |
9127 newname t-host-type t-user | |
9128 (nth 1 (efs-get-file-entry filename)) append))) | |
9129 (if (eq nowait t) 1 nowait)))))) | |
9130 nowait))) | |
9131 | |
9132 (defun efs-copy-on-remote (host user host-type filename newname filename-parsed | |
9133 newname-parsed keep-date append-p msg cont | |
9134 nowait xfer-type) | |
9135 ;; Uses site exec to copy the file on a remote host | |
9136 (let ((exec-cp (efs-get-host-property host 'exec-cp))) | |
9137 (if (or append-p | |
9138 (not (memq host-type efs-unix-host-types)) | |
9139 (efs-get-host-property host 'exec-failed) | |
9140 (eq exec-cp 'failed)) | |
9141 (efs-copy-via-temp filename filename-parsed newname newname-parsed | |
9142 append-p keep-date msg cont nowait xfer-type) | |
9143 (if (eq exec-cp 'works) | |
9144 (efs-send-cmd | |
9145 host user | |
9146 (list 'quote 'site 'exec | |
9147 (format "cp %s%s %s" (if keep-date "-p " "") | |
9148 (nth 2 filename-parsed) (nth 2 newname-parsed))) | |
9149 msg nil | |
9150 (efs-cont (result line cont-lines) (host user filename newname | |
9151 host-type filename-parsed | |
9152 newname-parsed | |
9153 keep-date append-p msg cont | |
9154 xfer-type nowait) | |
9155 (if result | |
9156 (progn | |
9157 (efs-set-host-property host 'exec-failed t) | |
9158 (efs-copy-via-temp filename filename-parsed newname | |
9159 newname-parsed append-p keep-date | |
9160 nil cont nowait xfer-type)) | |
9161 (efs-save-match-data | |
9162 (if (string-match "\n200-\\([^\n]*\\)" cont-lines) | |
9163 (let ((err (substring cont-lines (match-beginning 1) | |
9164 (match-end 1)))) | |
9165 (if cont | |
9166 (efs-call-cont cont 'failed err cont-lines) | |
9167 (efs-error host user err))) | |
9168 (efs-copy-add-file-entry | |
9169 newname host-type user | |
9170 (nth 7 (efs-file-attributes filename)) nil) | |
9171 (if cont (efs-call-cont cont nil line cont-lines)))))) | |
9172 nowait) | |
9173 (message "Checking for cp executable on %s..." host) | |
9174 (efs-send-cmd | |
9175 host user (list 'quote 'site 'exec "cp / /") nil nil | |
9176 (efs-cont (result line cont-lines) (host user filename newname | |
9177 host-type filename-parsed | |
9178 newname-parsed | |
9179 keep-date append-p msg cont | |
9180 xfer-type nowait) | |
9181 (efs-save-match-data | |
9182 (if (string-match "\n200-" cont-lines) | |
9183 (efs-set-host-property host 'exec-cp 'works) | |
9184 (efs-set-host-property host 'exec-cp 'failed))) | |
9185 (efs-copy-on-remote host user host-type filename newname | |
9186 filename-parsed newname-parsed keep-date | |
9187 append-p msg cont nowait xfer-type)) | |
9188 nowait))))) | |
9189 | |
9190 (defun efs-copy-via-temp (filename filename-parsed newname newname-parsed | |
9191 append keep-date msg cont nowait xfer-type) | |
9192 ;; Copies from FILENAME to NEWNAME via a temp file. | |
9193 (let* ((temp (car (if (efs-use-gateway-p (car filename-parsed) t) | |
9194 (efs-make-tmp-name (car filename-parsed) | |
9195 (car newname-parsed)) | |
9196 (efs-make-tmp-name (car newname-parsed) | |
9197 (car filename-parsed))))) | |
9198 (temp-parsed (efs-ftp-path temp))) | |
9199 (or xfer-type (setq xfer-type | |
9200 (efs-xfer-type | |
9201 (efs-host-type (car filename-parsed)) filename | |
9202 (efs-host-type (car newname-parsed)) newname | |
9203 t))) | |
9204 (efs-copy-file-internal | |
9205 filename filename-parsed temp temp-parsed t nil (if (eq 0 msg) 2 msg) | |
9206 (efs-cont (result line cont-lines) (newname newname-parsed temp | |
9207 temp-parsed append msg cont | |
9208 nowait xfer-type) | |
9209 (if result | |
9210 (progn | |
9211 (efs-del-tmp-name temp) | |
9212 (if cont | |
9213 (efs-call-cont cont result line cont-lines) | |
9214 (signal 'ftp-error | |
9215 (list "Opening input file" | |
9216 (format "FTP Error: \"%s\" " line) filename)))) | |
9217 (efs-copy-file-internal | |
9218 temp temp-parsed newname newname-parsed (if append 'append t) nil | |
9219 (if (eq msg 0) 1 msg) | |
9220 (efs-cont (result line cont-lines) (temp newname cont) | |
9221 (efs-del-tmp-name temp) | |
9222 (if cont | |
9223 (efs-call-cont cont result line cont-lines) | |
9224 (if result | |
9225 (signal 'ftp-error | |
9226 (list "Opening output file" | |
9227 (format "FTP Error: \"%s\" " line) newname))))) | |
9228 nowait xfer-type))) | |
9229 nowait xfer-type))) | |
9230 | |
9231 (defun efs-copy-file-internal (filename filename-parsed newname newname-parsed | |
9232 ok-if-already-exists keep-date | |
9233 &optional msg cont nowait xfer-type) | |
9234 ;; Internal function for copying a file from FILENAME to NEWNAME. | |
9235 ;; FILENAME-PARSED and NEWNAME-PARSED are the lists obtained by parsing | |
9236 ;; FILENAME and NEWNAME with efs-ftp-path. | |
9237 ;; If OK-IF-ALREADY-EXISTS is nil, then existing files will not be | |
9238 ;; overwritten. | |
9239 ;; If it is a number, then the user will be prompted about overwriting. | |
9240 ;; If it eq 'append, then an existing file will be appended to. | |
9241 ;; If it has anyother value, then existing files will be silently | |
9242 ;; overwritten. | |
9243 ;; If KEEP-DATE is t then we will attempt to reatin the date of the | |
9244 ;; original copy of the file. If this is a string, the modtime of the | |
9245 ;; NEWNAME will be set to this date. Must be in touch -t format. | |
9246 ;; If MSG is nil, then the copying will be done silently. | |
9247 ;; If it is a string, then that will be the massage displayed while copying. | |
9248 ;; If it is 0, then a suitable default message will be computed. | |
9249 ;; If it is 1, then a suitable default will be computed, assuming | |
9250 ;; that FILENAME is a temporary file, whose name is not suitable to use | |
9251 ;; in a status message. | |
9252 ;; If it is 2, then a suitable default will be used, assuming that | |
9253 ;; NEWNAME is a temporary file. | |
9254 ;; CONT is a continuation to call after completing the copy. | |
9255 ;; The first two args are RESULT and LINE, the result symbol and status | |
9256 ;; line of the FTP command. If more than one ftp command has been used, | |
9257 ;; then these values for the last FTP command are given. | |
9258 ;; NOWAIT can be either nil, 0, 1, t. See `efs-send-cmd' for an explanation. | |
9259 ;; XFER-TYPE is the transfer type to use for transferring the files. | |
9260 ;; If this is nil, than a suitable transfer type is computed. | |
9261 ;; Does not call expand-file-name. Do that yourself. | |
9262 | |
9263 ;; check to see if we can overwrite | |
9264 (if (or (not ok-if-already-exists) | |
9265 (numberp ok-if-already-exists)) | |
9266 (efs-barf-or-query-if-file-exists | |
9267 newname "copy to it" (numberp ok-if-already-exists))) | |
9268 (if (null (or filename-parsed newname-parsed)) | |
9269 ;; local to local copy | |
9270 (progn | |
9271 (if (eq ok-if-already-exists 'append) | |
9272 (efs-concatenate-files filename newname) | |
9273 (copy-file filename newname ok-if-already-exists keep-date)) | |
9274 (if cont | |
9275 (efs-call-cont cont nil "Copied locally" ""))) | |
9276 (let* ((f-host (car filename-parsed)) | |
9277 (f-user (nth 1 filename-parsed)) | |
9278 (f-path (nth 2 filename-parsed)) | |
9279 (f-host-type (efs-host-type f-host f-user)) | |
9280 (f-gate-p (efs-use-gateway-p f-host t)) | |
9281 (t-host (car newname-parsed)) | |
9282 (t-user (nth 1 newname-parsed)) | |
9283 (t-path (nth 2 newname-parsed)) | |
9284 (t-host-type (efs-host-type t-host t-user)) | |
9285 (t-gate-p (efs-use-gateway-p t-host t)) | |
9286 (append-p (eq ok-if-already-exists 'append)) | |
9287 gatename) | |
9288 | |
9289 (if (and (eq keep-date t) (null newname-parsed)) | |
9290 ;; f-host must be remote now. | |
9291 (setq keep-date filename)) | |
9292 | |
9293 (cond | |
9294 | |
9295 ;; Check to see if we can do a PUT | |
9296 ((or | |
9297 (and (null f-host) | |
9298 (or (null t-gate-p) | |
9299 (setq gatename (efs-local-to-gateway-filename filename)))) | |
9300 (and t-gate-p | |
9301 f-host | |
9302 (string-equal (downcase f-host) (downcase efs-gateway-host)) | |
9303 (if (memq f-host-type efs-case-insensitive-host-types) | |
9304 (string-equal (downcase f-user) | |
9305 (downcase (efs-get-user efs-gateway-host))) | |
9306 (string-equal f-user (efs-get-user efs-gateway-host))))) | |
9307 (or f-host (let (file-name-handler-alist) | |
9308 (if (file-exists-p filename) | |
9309 (cond | |
9310 ((file-directory-p filename) | |
9311 (signal 'file-error | |
9312 (list "Non-regular file" | |
9313 "is a directory" filename))) | |
9314 ((not (file-readable-p filename)) | |
9315 (signal 'file-error | |
9316 (list "Opening input file" | |
9317 "permission denied" filename)))) | |
9318 (signal 'file-error | |
9319 (list "Opening input file" | |
9320 "no such file or directory" filename))))) | |
9321 (or xfer-type | |
9322 (setq xfer-type | |
9323 (efs-xfer-type f-host-type filename t-host-type newname))) | |
9324 (let ((size (and (or (null f-host-type) | |
9325 (efs-file-entry-p filename)) | |
9326 (nth 7 (file-attributes filename))))) | |
9327 ;; -1 is a bogus size for remote files | |
9328 (if (eq size -1) (setq size nil)) | |
9329 (efs-send-cmd | |
9330 t-host t-user | |
9331 (list (if append-p 'append 'put) | |
9332 (if f-host | |
9333 f-path | |
9334 (or gatename filename)) | |
9335 t-path | |
9336 xfer-type) | |
9337 (cond ((eq msg 2) | |
9338 (concat (if append-p "Appending " "Putting ") | |
9339 (efs-relativize-filename filename))) | |
9340 ((eq msg 1) | |
9341 (concat (if append-p "Appending " "Putting ") | |
9342 (efs-relativize-filename newname))) | |
9343 ((eq msg 0) | |
9344 (concat (if append-p "Appending " "Copying ") | |
9345 (efs-relativize-filename filename) | |
9346 " to " | |
9347 (efs-relativize-filename | |
9348 newname (file-name-directory filename) filename))) | |
9349 (t msg)) | |
9350 (and size (list 'efs-set-xfer-size t-host t-user size)) | |
9351 (efs-cont (result line cont-lines) (newname t-host-type t-user size | |
9352 append-p cont) | |
9353 (if result | |
9354 (if cont | |
9355 (efs-call-cont cont result line cont-lines) | |
9356 (signal 'ftp-error | |
9357 (list "Opening output file" | |
9358 (format "FTP Error: \"%s\" " line) newname))) | |
9359 ;; add file entry | |
9360 (efs-copy-add-file-entry newname t-host-type t-user | |
9361 size append-p) | |
9362 (if cont | |
9363 (efs-call-cont cont result line cont-lines)))) | |
9364 nowait))) | |
9365 | |
9366 ;; Check to see if we can do a GET | |
9367 ((and | |
9368 ;; I think that giving the append arg, will cause this function | |
9369 ;; to make a temp file, recursively call itself, and append the temp | |
9370 ;; file to the local file. Hope it works out... | |
9371 (null append-p) | |
9372 (or | |
9373 (and (null t-host) | |
9374 (or (null f-gate-p) | |
9375 (setq gatename (efs-local-to-gateway-filename newname)))) | |
9376 (and f-gate-p | |
9377 t-host | |
9378 (string-equal (downcase t-host) (downcase efs-gateway-host)) | |
9379 (if (memq t-host-type efs-case-insensitive-host-types) | |
9380 (string-equal (downcase t-user) | |
9381 (downcase (efs-get-user efs-gateway-host))) | |
9382 (string-equal t-user (efs-get-user efs-gateway-host)))))) | |
9383 (or t-host (let (file-name-handler-alist) | |
9384 (cond ((not (file-writable-p newname)) | |
9385 (signal 'file-error | |
9386 (list "Opening output file" | |
9387 "permission denied" newname))) | |
9388 ((file-directory-p newname) | |
9389 (signal 'file-error | |
9390 (list "Opening output file" | |
9391 "is a directory" newname)))))) | |
9392 (or xfer-type | |
9393 (setq xfer-type | |
9394 (efs-xfer-type f-host-type filename t-host-type newname))) | |
9395 (let ((size (and (or (null f-host-type) | |
9396 (efs-file-entry-p filename)) | |
9397 (nth 7 (file-attributes filename))))) | |
9398 ;; -1 is a bogus size for remote files. | |
9399 (if (eq size -1) (setq size nil)) | |
9400 (efs-send-cmd | |
9401 f-host f-user | |
9402 (list 'get | |
9403 f-path | |
9404 (if t-host | |
9405 t-path | |
9406 (or gatename newname)) | |
9407 xfer-type) | |
9408 (cond ((eq msg 0) | |
9409 (concat "Copying " | |
9410 (efs-relativize-filename filename) | |
9411 " to " | |
9412 (efs-relativize-filename | |
9413 newname (file-name-directory filename) filename))) | |
9414 ((eq msg 2) | |
9415 (concat "Getting " (efs-relativize-filename filename))) | |
9416 ((eq msg 1) | |
9417 (concat "Getting " (efs-relativize-filename newname))) | |
9418 (t msg)) | |
9419 ;; If the server emits a efs-xfer-size-msgs, it will over-ride this. | |
9420 ;; With no xfer msg, this is will do the job. | |
9421 (and size (list 'efs-set-xfer-size f-host f-user size)) | |
9422 (efs-cont (result line cont-lines) (filename newname size | |
9423 t-host-type t-user | |
9424 cont keep-date) | |
9425 (if result | |
9426 (if cont | |
9427 (efs-call-cont cont result line cont-lines) | |
9428 (signal 'ftp-error | |
9429 (list "Opening input file" | |
9430 (format "FTP Error: \"%s\" " line) filename))) | |
9431 ;; Add a new file entry, if relevant. | |
9432 (if t-host-type | |
9433 ;; t-host will be equal to efs-gateway-host, if t-host-type | |
9434 ;; is non-nil. | |
9435 (efs-copy-add-file-entry newname t-host-type | |
9436 t-user size nil)) | |
9437 (if (and (null t-host-type) (stringp keep-date)) | |
9438 (efs-set-mdtm-of | |
9439 filename newname | |
9440 (and cont | |
9441 (efs-cont (result1 line1 cont-lines1) (result | |
9442 line cont-lines | |
9443 cont) | |
9444 (efs-call-cont cont result line cont-lines)))) | |
9445 (if cont | |
9446 (efs-call-cont cont result line cont-lines))))) | |
9447 nowait))) | |
9448 | |
9449 ;; Can we do a EXEC cp? | |
9450 ((and t-host f-host | |
9451 (string-equal (downcase t-host) (downcase f-host)) | |
9452 (if (memq t-host-type efs-case-insensitive-host-types) | |
9453 (string-equal (downcase t-user) (downcase f-user)) | |
9454 (string-equal t-user f-user))) | |
9455 (efs-copy-on-remote | |
9456 t-host t-user t-host-type filename newname filename-parsed | |
9457 newname-parsed keep-date append-p | |
9458 (cond ((eq msg 0) | |
9459 (concat "Copying " | |
9460 (efs-relativize-filename filename) | |
9461 " to " | |
9462 (efs-relativize-filename | |
9463 newname (file-name-directory filename) filename))) | |
9464 ((eq msg 1) | |
9465 (concat "Copying " (efs-relativize-filename newname))) | |
9466 ((eq msg 2) | |
9467 (concat "Copying " (efs-relativize-filename filename))) | |
9468 (t msg)) | |
9469 cont nowait xfer-type)) | |
9470 | |
9471 ;; Try for a copy with PASV | |
9472 ((and t-host f-host | |
9473 (not (and (string-equal (downcase t-host) (downcase f-host)) | |
9474 (if (memq t-host-type efs-case-insensitive-host-types) | |
9475 (string-equal (downcase t-user) (downcase f-user)) | |
9476 (string-equal t-user f-user)))) | |
9477 (or | |
9478 (and efs-gateway-host | |
9479 ;; The gateway should be able to talk to anything. | |
9480 (let ((gh (downcase efs-gateway-host))) | |
9481 (or (string-equal (downcase t-host) gh) | |
9482 (string-equal (downcase f-host) gh)))) | |
9483 (efs-save-match-data | |
9484 (eq (null (string-match efs-local-host-regexp t-host)) | |
9485 (null (string-match efs-local-host-regexp f-host)))))) | |
9486 (efs-copy-remote-to-remote | |
9487 f-host-type f-host f-user f-path filename | |
9488 t-host-type t-host t-user t-path newname | |
9489 append-p | |
9490 (cond ((eq msg 0) | |
9491 (concat "Copying " | |
9492 (efs-relativize-filename filename) | |
9493 " to " | |
9494 (efs-relativize-filename | |
9495 newname (file-name-directory filename) filename))) | |
9496 ((eq msg 1) | |
9497 (concat "Copying " (efs-relativize-filename newname))) | |
9498 ((eq msg 2) | |
9499 (concat "Copying " (efs-relativize-filename filename))) | |
9500 (t msg)) | |
9501 (efs-cont (result line cont-lines) | |
9502 (filename filename-parsed newname newname-parsed | |
9503 append-p keep-date msg cont nowait xfer-type) | |
9504 (if result | |
9505 ;; PASV didn't work. Do things the old-fashioned | |
9506 ;; way. | |
9507 (efs-copy-via-temp | |
9508 filename filename-parsed newname newname-parsed | |
9509 append-p keep-date msg cont nowait xfer-type) | |
9510 (if cont | |
9511 (efs-call-cont cont result line cont-lines)))) | |
9512 nowait xfer-type)) | |
9513 | |
9514 ;; Can't do anything direct. Divide and conquer. | |
9515 (t | |
9516 (efs-copy-via-temp filename filename-parsed newname newname-parsed | |
9517 append-p keep-date msg cont nowait xfer-type)))))) | |
9518 | |
9519 (defun efs-copy-file (filename newname &optional ok-if-already-exists | |
9520 keep-date nowait) | |
9521 ;; Version of copy file for remote files. Actually, will also work | |
9522 ;; for local files too, since efs-copy-file-internal can copy anything. | |
9523 ;; If called interactively, copies asynchronously. | |
9524 (setq filename (expand-file-name filename) | |
9525 newname (expand-file-name newname)) | |
9526 (if (eq ok-if-already-exists 'append) | |
9527 (setq ok-if-already-exists t)) | |
9528 (efs-copy-file-internal filename (efs-ftp-path filename) | |
9529 newname (efs-ftp-path newname) | |
9530 ok-if-already-exists keep-date 0 nil nowait)) | |
9531 | |
9532 ;;;; ------------------------------------------------------------ | |
9533 ;;;; File renaming support. | |
9534 ;;;; ------------------------------------------------------------ | |
9535 | |
9536 (defun efs-rename-get-file-list (dir ent) | |
9537 ;; From hashtable ENT for DIR returns a list of all files except "." | |
9538 ;; and "..". | |
9539 (let (list) | |
9540 (efs-map-hashtable | |
9541 (function | |
9542 (lambda (key val) | |
9543 (or (string-equal "." key) (string-equal ".." key) | |
9544 (setq list | |
9545 (cons (expand-file-name key dir) list))))) | |
9546 ent) | |
9547 list)) | |
9548 | |
9549 (defun efs-rename-get-files (dir cont nowait) | |
9550 ;; Obtains a list of files in directory DIR (except . and ..), and applies | |
9551 ;; CONT to the list. Doesn't return anything useful. | |
9552 (let* ((dir (file-name-as-directory dir)) | |
9553 (ent (efs-get-files-hashtable-entry dir))) | |
9554 (if ent | |
9555 (efs-call-cont cont (efs-rename-get-file-list dir ent)) | |
9556 (efs-ls | |
9557 dir (efs-ls-guess-switches) t nil t nowait | |
9558 (efs-cont (listing) (dir cont) | |
9559 (efs-call-cont | |
9560 cont (and listing | |
9561 (efs-rename-get-file-list | |
9562 dir (efs-get-files-hashtable-entry dir))))))))) | |
9563 | |
9564 (defun efs-rename-get-local-file-tree (dir) | |
9565 ;; Returns a list of the full directory tree under DIR, for DIR on the | |
9566 ;; local host. The list is in tree order. | |
9567 (let ((res (list dir))) | |
9568 (mapcar | |
9569 (function | |
9570 (lambda (file) | |
9571 (if (file-directory-p file) | |
9572 (nconc res (delq nil (mapcar | |
9573 (function | |
9574 (lambda (f) | |
9575 (and (not (string-equal "." f)) | |
9576 (not (string-equal ".." f)) | |
9577 (expand-file-name f file)))) | |
9578 (directory-files file))))))) | |
9579 res) | |
9580 res)) | |
9581 | |
9582 (defun efs-rename-get-remote-file-tree (next curr total cont nowait) | |
9583 ;; Builds a hierarchy of files. | |
9584 ;; NEXT is the next level so far. | |
9585 ;; CURR are unprocessed files in the current level. | |
9586 ;; TOTAL is the processed files so far. | |
9587 ;; CONT is a cont. function called on the total list after all files | |
9588 ;; are processed. | |
9589 ;; NOWAIT non-nil means run asynch. | |
9590 (or curr (setq curr next | |
9591 next nil)) | |
9592 (if curr | |
9593 (let ((file (car curr))) | |
9594 (setq curr (cdr curr) | |
9595 total (cons file total)) | |
9596 (if (file-directory-p file) | |
9597 (efs-rename-get-files | |
9598 file | |
9599 (efs-cont (list) (next curr total cont nowait) | |
9600 (efs-rename-get-remote-file-tree (nconc next list) | |
9601 curr total cont nowait)) | |
9602 nowait) | |
9603 (efs-rename-get-remote-file-tree next curr total cont nowait))) | |
9604 (efs-call-cont cont (nreverse total)))) | |
9605 | |
9606 (defun efs-rename-make-targets (files from-dir-len to-dir host user host-type | |
9607 cont nowait) | |
9608 ;; Make targets (copy a file or make a subdir) on local or host | |
9609 ;; for the files in list. Afterwhich, call CONT. | |
9610 (if files | |
9611 (let* ((from (car files)) | |
9612 (files (cdr files)) | |
9613 (to (concat to-dir (substring from from-dir-len)))) | |
9614 (if (file-directory-p from) | |
9615 (if host-type | |
9616 (let ((dir (nth 2 (efs-ftp-path to)))) | |
9617 (or (memq host-type efs-unix-host-types) | |
9618 (memq host-type '(dos os2)) | |
9619 (setq dir (efs-internal-file-name-as-directory nil dir))) | |
9620 (efs-send-cmd | |
9621 host user (list 'mkdir dir) | |
9622 (format "Making directory %s" (efs-relativize-filename to)) | |
9623 nil | |
9624 (efs-cont (res line cont-lines) (to files from-dir-len | |
9625 to-dir host user | |
9626 host-type cont nowait) | |
9627 (if res | |
9628 (if cont | |
9629 (efs-call-cont cont res line cont-lines) | |
9630 (signal 'ftp-error | |
9631 (list "Making directory" | |
9632 (format "FTP Error: \"%s\"" line) | |
9633 to))) | |
9634 (efs-rename-make-targets | |
9635 files from-dir-len to-dir host user | |
9636 host-type cont nowait))) | |
9637 nowait)) | |
9638 (condition-case nil | |
9639 (make-directory-internal to) | |
9640 (error (efs-call-cont | |
9641 cont 'failed (format "Failed to mkdir %s" to) ""))) | |
9642 (efs-rename-make-targets | |
9643 files from-dir-len to-dir host user host-type cont nowait)) | |
9644 (efs-copy-file-internal | |
9645 from (efs-ftp-path from) to (and host-type (efs-ftp-path to)) nil t | |
9646 (format "Renaming %s to %s" (efs-relativize-filename from) | |
9647 (efs-relativize-filename to)) | |
9648 (efs-cont (res line cont-lines) (from to files from-dir-len to-dir | |
9649 host user host-type cont | |
9650 nowait) | |
9651 (if res | |
9652 (if cont | |
9653 (efs-call-cont cont res line cont-lines) | |
9654 (signal 'ftp-error | |
9655 (list "Renaming" | |
9656 (format "FTP Error: \"%s\"" line) from to))) | |
9657 (efs-rename-make-targets | |
9658 files from-dir-len to-dir host user host-type cont nowait))) | |
9659 nowait))) | |
9660 (if cont (efs-call-cont cont nil "" "")))) | |
9661 | |
9662 (defun efs-rename-delete-on-local (files) | |
9663 ;; Delete the files FILES, and then run CONT. | |
9664 ;; FILES are assumed to be in inverse tree order. | |
9665 (message "Deleting files...") | |
9666 (mapcar | |
9667 (function | |
9668 (lambda (f) | |
9669 (condition-case nil | |
9670 (if (file-directory-p f) | |
9671 (delete-directory f) | |
9672 (delete-file f)) | |
9673 (file-error nil)))) ; don't complain if the file is already gone. | |
9674 files) | |
9675 (message "Deleting files...done")) | |
9676 | |
9677 (defun efs-rename-delete-on-remote (files host user host-type cont nowait) | |
9678 ;; Deletes the list FILES on a remote host. When done calls CONT. | |
9679 ;; FILES is assumed to be in reverse tree order. | |
9680 (if files | |
9681 (let* ((f (car files)) | |
9682 (rf (nth 2 (efs-ftp-path f)))) | |
9683 (progn | |
9684 (setq files (cdr files)) | |
9685 (if (file-directory-p f) | |
9686 (let ((rf (if (memq host-type (append efs-unix-host-types | |
9687 '(dos os2))) | |
9688 (efs-internal-directory-file-name f) | |
9689 (efs-internal-file-name-as-directory nil f)))) | |
9690 | |
9691 (efs-send-cmd | |
9692 host user (list 'rmdir rf) | |
9693 (concat "Deleting directory " (efs-relativize-filename f)) | |
9694 nil | |
9695 (efs-cont (res line cont-lines) (f files host user host-type | |
9696 cont nowait) | |
9697 (if (and res | |
9698 (efs-save-match-data | |
9699 (not (string-match "^550 " line)))) | |
9700 (if cont | |
9701 (efs-call-cont cont res line cont-lines) | |
9702 (signal 'ftp-error | |
9703 (list "Deleting directory" | |
9704 (format "FTP Error: \"%s\"" line) | |
9705 f))) | |
9706 (efs-rename-delete-on-remote | |
9707 files host user host-type cont nowait))) | |
9708 nowait)) | |
9709 (efs-send-cmd | |
9710 host user (list 'delete rf) | |
9711 (concat "Deleting " rf) | |
9712 nil | |
9713 (efs-cont (res line cont-lines) (f files host user host-type | |
9714 cont nowait) | |
9715 (if (and res | |
9716 (efs-save-match-data | |
9717 (not (string-match "^550 " line)))) | |
9718 (if cont | |
9719 (efs-call-cont cont res line cont-lines) | |
9720 (signal 'ftp-error | |
9721 (list "Deleting" | |
9722 (format "FTP Error: \"%s\"" line) | |
9723 f))) | |
9724 (efs-rename-delete-on-remote | |
9725 files host user host-type cont nowait))) | |
9726 nowait)))) | |
9727 (if cont (efs-call-cont cont nil "" "")))) | |
9728 | |
9729 (defun efs-rename-on-remote (host user old-path new-path old-file new-file | |
9730 msg nowait cont) | |
9731 ;; Run a rename command on the remote server. | |
9732 ;; OLD-PATH and NEW-PATH are in full efs syntax. | |
9733 ;; OLD-FILE and NEW-FILE are the remote full pathnames, not in efs syntax. | |
9734 (efs-send-cmd | |
9735 host user (list 'rename old-file new-file) msg nil | |
9736 (efs-cont (result line cont-lines) (cont old-path new-path host) | |
9737 (if result | |
9738 (progn | |
9739 (or (and (>= (length line) 4) | |
9740 (string-equal "550 " (substring line 0 4))) | |
9741 (efs-set-host-property host 'rnfr-failed t)) | |
9742 (if cont | |
9743 (efs-call-cont cont result line cont-lines) | |
9744 (signal 'ftp-error | |
9745 (list "Renaming" | |
9746 (format "FTP Error: \"%s\"" line) | |
9747 old-path new-path)))) | |
9748 (let ((entry (efs-get-file-entry old-path)) | |
9749 (host-type (efs-host-type host)) | |
9750 ;; If no file entry, do extra work on the hashtable, | |
9751 ;; rather than force a listing. | |
9752 (dir-p (or (not (efs-file-entry-p old-path)) | |
9753 (file-directory-p old-path)))) | |
9754 (apply 'efs-add-file-entry host-type new-path | |
9755 (eq (car entry) t) (cdr entry)) | |
9756 (efs-delete-file-entry host-type old-path) | |
9757 (if dir-p | |
9758 (let* ((old (efs-canonize-file-name | |
9759 (file-name-as-directory old-path))) | |
9760 (new (efs-canonize-file-name | |
9761 (file-name-as-directory new-path))) | |
9762 (old-len (length old)) | |
9763 (new-tbl (efs-make-hashtable | |
9764 (length efs-files-hashtable)))) | |
9765 (efs-map-hashtable | |
9766 (function | |
9767 (lambda (key val) | |
9768 (if (and (>= (length key) old-len) | |
9769 (string-equal (substring key 0 old-len) | |
9770 old)) | |
9771 (efs-put-hash-entry | |
9772 (concat new (substring key old-len)) val new-tbl) | |
9773 (efs-put-hash-entry key val new-tbl)))) | |
9774 efs-files-hashtable) | |
9775 (setq efs-files-hashtable new-tbl))) | |
9776 (if cont (efs-call-cont cont result line cont-lines))))) | |
9777 nowait)) | |
9778 | |
9779 (defun efs-rename-local-to-remote (filename newname newname-parsed | |
9780 msg cont nowait) | |
9781 ;; Renames a file from the local host to a remote host. | |
9782 (if (file-directory-p filename) | |
9783 (let* ((files (efs-rename-get-local-file-tree filename)) | |
9784 (to-dir (directory-file-name newname)) | |
9785 (filename (directory-file-name filename)) | |
9786 (len (length filename)) | |
9787 (t-parsed (efs-ftp-path to-dir)) | |
9788 (host (car t-parsed)) | |
9789 (user (nth 1 t-parsed)) | |
9790 (host-type (efs-host-type host))) | |
9791 ;; MSG is never passed here, instead messages are constructed | |
9792 ;; internally. I don't know how to use a single message | |
9793 ;; in a function which sends so many FTP commands. | |
9794 (efs-rename-make-targets | |
9795 files len to-dir host user host-type | |
9796 (efs-cont (result line cont-lines) (files filename newname cont) | |
9797 (if result | |
9798 (if cont | |
9799 (efs-call-cont cont result line cont-lines) | |
9800 (signal 'ftp-error | |
9801 (list "Renaming" (format "FTP Error: \"%s\"" line) | |
9802 filename newname))) | |
9803 (efs-rename-delete-on-local (nreverse files)) | |
9804 (if cont (efs-call-cont cont result line cont-lines)))) | |
9805 nowait)) | |
9806 (efs-copy-file-internal | |
9807 filename nil newname newname-parsed t t msg | |
9808 (efs-cont (result line cont-lines) (filename cont) | |
9809 (if result | |
9810 (if cont | |
9811 (efs-call-cont cont result line cont-lines) | |
9812 (signal 'ftp-error | |
9813 (list "Renaming" | |
9814 (format "FTP Error: \"%s\"" line) | |
9815 filename newname))) | |
9816 (condition-case nil | |
9817 (delete-file filename) | |
9818 (error nil)) | |
9819 (if cont (efs-call-cont cont result line cont-lines)))) | |
9820 nowait))) | |
9821 | |
9822 (defun efs-rename-from-remote (filename filename-parsed newname newname-parsed | |
9823 msg cont nowait) | |
9824 (let ((f-host (car filename-parsed)) | |
9825 (f-user (nth 1 filename-parsed)) | |
9826 (fast-nowait (if (eq nowait t) 1 nowait))) | |
9827 (if (file-directory-p filename) | |
9828 (let* ((t-host (car newname-parsed)) | |
9829 (t-user (nth 1 newname-parsed)) | |
9830 (t-host-type (and t-host (efs-host-type t-host))) | |
9831 (f-host-type (efs-host-type f-host))) | |
9832 (efs-rename-get-remote-file-tree | |
9833 nil (list filename) nil | |
9834 (efs-cont (list) (filename filename-parsed newname t-host t-user | |
9835 t-host-type f-host f-user f-host-type | |
9836 cont fast-nowait) | |
9837 (efs-rename-make-targets | |
9838 list (length filename) newname t-host t-user t-host-type | |
9839 (efs-cont (res line cont-lines) (filename newname f-host f-user | |
9840 f-host-type list cont | |
9841 fast-nowait) | |
9842 (if res | |
9843 (if cont | |
9844 (efs-call-cont cont res line cont-lines) | |
9845 (signal 'ftp-error | |
9846 (list "Renaming" | |
9847 (format "FTP Error: \"%s\"" line) | |
9848 filename newname))) | |
9849 (efs-rename-delete-on-remote | |
9850 (nreverse list) f-host f-user f-host-type cont | |
9851 fast-nowait))) | |
9852 fast-nowait)) nowait)) | |
9853 ;; Do things the simple way. | |
9854 (let ((f-path (nth 2 filename-parsed)) | |
9855 (f-abbr (efs-relativize-filename filename))) | |
9856 (efs-copy-file-internal | |
9857 filename filename-parsed newname newname-parsed t t msg | |
9858 (efs-cont (result line cont-lines) (filename newname f-host f-user | |
9859 f-path f-abbr | |
9860 cont fast-nowait) | |
9861 (if result | |
9862 (if cont | |
9863 (efs-call-cont cont result line cont-lines) | |
9864 (signal 'ftp-error | |
9865 (list "Renaming" | |
9866 (format "FTP Error: \"%s\"" line) | |
9867 filename newname))) | |
9868 (efs-send-cmd | |
9869 f-host f-user (list 'delete f-path) | |
9870 (format "Removing %s" f-abbr) nil | |
9871 (efs-cont (result line cont-lines) (filename f-host cont) | |
9872 (if result | |
9873 (if cont | |
9874 (efs-call-cont cont result line cont-lines) | |
9875 (signal 'ftp-error | |
9876 (list "Renaming" | |
9877 (format "Failed to remove %s" | |
9878 filename) | |
9879 "FTP Error: \"%s\"" line))) | |
9880 (efs-delete-file-entry (efs-host-type f-host) | |
9881 filename) | |
9882 (if cont | |
9883 (efs-call-cont cont result line cont-lines)))) | |
9884 fast-nowait))) nowait))))) | |
9885 | |
9886 (defun efs-rename-file-internal (filename newname ok-if-already-exists | |
9887 &optional msg cont nowait) | |
9888 ;; Internal version of rename-file for remote files. | |
9889 ;; Takes CONT and NOWAIT args. | |
9890 (let ((filename (expand-file-name filename)) | |
9891 (newname (expand-file-name newname))) | |
9892 (let ((f-parsed (efs-ftp-path filename)) | |
9893 (t-parsed (efs-ftp-path newname))) | |
9894 (if (null (or f-parsed t-parsed)) | |
9895 (progn | |
9896 ;; local rename | |
9897 (rename-file filename newname ok-if-already-exists) | |
9898 (if cont | |
9899 (efs-call-cont cont nil "Renamed locally" ""))) | |
9900 | |
9901 ;; check to see if we can overwrite | |
9902 (if (or (not ok-if-already-exists) | |
9903 (numberp ok-if-already-exists)) | |
9904 (efs-barf-or-query-if-file-exists | |
9905 newname "rename to it" (numberp ok-if-already-exists))) | |
9906 | |
9907 (let ((f-abbr (efs-relativize-filename filename)) | |
9908 (t-abbr (efs-relativize-filename newname | |
9909 (file-name-directory filename) | |
9910 filename))) | |
9911 (or msg (setq msg (format "Renaming %s to %s" f-abbr t-abbr))) | |
9912 (if f-parsed | |
9913 (let* ((f-host (car f-parsed)) | |
9914 (f-user (nth 1 f-parsed)) | |
9915 (f-path (nth 2 f-parsed)) | |
9916 (f-host-type (efs-host-type f-host))) | |
9917 (if (and t-parsed | |
9918 (string-equal (downcase f-host) | |
9919 (downcase (car t-parsed))) | |
9920 (not (efs-get-host-property f-host 'rnfr-failed)) | |
9921 (if (memq f-host-type efs-case-insensitive-host-types) | |
9922 (string-equal (downcase f-user) | |
9923 (downcase (nth 1 t-parsed))) | |
9924 (string-equal f-user (nth 1 t-parsed)))) | |
9925 ;; Can run a RENAME command on the server. | |
9926 (efs-rename-on-remote | |
9927 f-host f-user filename newname f-path (nth 2 t-parsed) | |
9928 msg nowait | |
9929 (efs-cont (result line cont-lines) (f-host | |
9930 filename | |
9931 newname | |
9932 ok-if-already-exists | |
9933 msg cont nowait) | |
9934 (if result | |
9935 (progn | |
9936 (efs-set-host-property f-host 'rnfr-failed t) | |
9937 (efs-rename-file-internal | |
9938 filename newname ok-if-already-exists msg cont | |
9939 (if (eq nowait t) 1 nowait))) | |
9940 (if cont | |
9941 (efs-call-cont cont result line cont-lines))))) | |
9942 ;; remote to remote | |
9943 (efs-rename-from-remote filename f-parsed newname t-parsed | |
9944 msg cont nowait))) | |
9945 ;; local to remote | |
9946 (efs-rename-local-to-remote | |
9947 filename newname t-parsed msg cont nowait))))))) | |
9948 | |
9949 (defun efs-rename-file (filename newname &optional ok-if-already-exists nowait) | |
9950 ;; Does file renaming for remote files. | |
9951 (efs-rename-file-internal filename newname ok-if-already-exists | |
9952 nil nil nowait)) | |
9953 | |
9954 ;;;; ------------------------------------------------------------ | |
9955 ;;;; Making symbolic and hard links. | |
9956 ;;;; ------------------------------------------------------------ | |
9957 | |
9958 ;;; These functions require that the remote FTP server understand | |
9959 ;;; SITE EXEC and that ln is in its the ftp-exec path. | |
9960 | |
9961 (defun efs-try-ln (host user cont nowait) | |
9962 ;; Do some preemptive testing to see if exec ln works | |
9963 (if (efs-get-host-property host 'exec-failed) | |
9964 (signal 'ftp-error (list "Unable to exec ln on host" host))) | |
9965 (let ((exec-ln (efs-get-host-property host 'exec-ln))) | |
9966 (cond | |
9967 ((eq exec-ln 'failed) | |
9968 (signal 'ftp-error (list "ln is not in FTP exec path on host" host))) | |
9969 ((eq exec-ln 'works) | |
9970 (efs-call-cont cont)) | |
9971 (t | |
9972 (message "Checking for ln executable on %s..." host) | |
9973 (efs-send-cmd | |
9974 host user '(quote site exec "ln / /") | |
9975 nil nil | |
9976 (efs-cont (result line cont-lines) (host user cont) | |
9977 (if result | |
9978 (progn | |
9979 (efs-set-host-property host 'exec-failed t) | |
9980 (efs-error host user (format "exec: %s" line))) | |
9981 ;; Look for an error message | |
9982 (if (efs-save-match-data | |
9983 (string-match "\n200-" cont-lines)) | |
9984 (progn | |
9985 (efs-set-host-property host 'exec-ln 'works) | |
9986 (efs-call-cont cont)) | |
9987 (efs-set-host-property host 'exec-ln 'failed) | |
9988 (efs-error host user | |
9989 (format "ln not in FTP exec path on host %s" host))))) | |
9990 nowait))))) | |
9991 | |
9992 (defun efs-make-symbolic-link-internal | |
9993 (target linkname &optional ok-if-already-exists cont nowait) | |
9994 ;; Makes remote symbolic links. Assumes that linkname is already expanded. | |
9995 (let* ((parsed (efs-ftp-path linkname)) | |
9996 (host (car parsed)) | |
9997 (user (nth 1 parsed)) | |
9998 (linkpath (nth 2 parsed)) | |
9999 (abbr (efs-relativize-filename linkname | |
10000 (file-name-directory target) target)) | |
10001 (tparsed (efs-ftp-path target)) | |
10002 (com-target target) | |
10003 cmd-string) | |
10004 (if (null (file-directory-p | |
10005 (file-name-directory linkname))) | |
10006 (if cont | |
10007 (efs-call-cont cont 'failed | |
10008 (format "no such file or directory, %s" linkname) | |
10009 "") | |
10010 (signal 'file-error (list "no such file or directory" linkname))) | |
10011 (if (or (not ok-if-already-exists) | |
10012 (numberp ok-if-already-exists)) | |
10013 (efs-barf-or-query-if-file-exists | |
10014 linkname "make symbolic link" (numberp ok-if-already-exists))) | |
10015 ;; Do this after above, so that hopefully the host type is sorted out | |
10016 ;; by now. | |
10017 (let ((host-type (efs-host-type host))) | |
10018 (if (or (not (memq host-type efs-unix-host-types)) | |
10019 (memq host-type efs-dumb-host-types) | |
10020 (efs-get-host-property host 'exec-failed)) | |
10021 (error "Unable to make symbolic links on %s." host))) | |
10022 ;; Be careful not to spoil relative links, or symlinks to other | |
10023 ;; machines, which maybe symlink-fix.el can sort out. | |
10024 (if (and tparsed | |
10025 (string-equal (downcase (car tparsed)) (downcase host)) | |
10026 (string-equal (nth 1 tparsed) user)) | |
10027 (setq com-target (nth 2 tparsed))) | |
10028 ;; symlinks only work for unix, so don't need to | |
10029 ;; convert pathnames. What about VOS? | |
10030 (setq cmd-string (concat "ln -sf " com-target " " linkpath)) | |
10031 (efs-try-ln | |
10032 host user | |
10033 (efs-cont () (host user cmd-string target linkname com-target | |
10034 abbr cont nowait) | |
10035 (efs-send-cmd | |
10036 host user (list 'quote 'site 'exec cmd-string) | |
10037 (format "Symlinking %s to %s" target abbr) | |
10038 nil | |
10039 (efs-cont (result line cont-lines) (host user com-target linkname | |
10040 cont) | |
10041 (if result | |
10042 (progn | |
10043 (efs-set-host-property host 'exec-failed t) | |
10044 (efs-error host user (format "exec: %s" line))) | |
10045 (efs-save-match-data | |
10046 (if (string-match "\n200-\\([^\n]*\\)" cont-lines) | |
10047 (let ((err (substring cont-lines (match-beginning 1) | |
10048 (match-end 1)))) | |
10049 (if cont | |
10050 (efs-call-cont cont 'failed err cont-lines) | |
10051 (efs-error host user err))) | |
10052 (efs-add-file-entry nil linkname com-target nil user) | |
10053 (if cont (efs-call-cont cont nil line cont-lines)))))) | |
10054 nowait)) | |
10055 nowait)))) | |
10056 | |
10057 (defun efs-make-symbolic-link (target linkname &optional ok-if-already-exists) | |
10058 ;; efs version of make-symbolic-link | |
10059 (let* ((linkname (expand-file-name linkname)) | |
10060 (parsed (efs-ftp-path linkname))) | |
10061 (if parsed | |
10062 (efs-make-symbolic-link-internal target linkname ok-if-already-exists) | |
10063 ;; Handler will match on either target or linkname. We are only | |
10064 ;; interested in the linkname. | |
10065 (let ((file-name-handler-alist (efs-file-name-handler-alist-sans-fn | |
10066 'efs-file-handler-function))) | |
10067 (make-symbolic-link target linkname ok-if-already-exists))))) | |
10068 | |
10069 (defun efs-add-name-to-file-internal | |
10070 (file newname &optional ok-if-already-exists cont nowait) | |
10071 ;; Makes remote symbolic links. Assumes that linkname is already expanded. | |
10072 (let* ((parsed (efs-ftp-path file)) | |
10073 (host (car parsed)) | |
10074 (user (nth 1 parsed)) | |
10075 (path (nth 2 parsed)) | |
10076 (nparsed (efs-ftp-path newname)) | |
10077 (nhost (car nparsed)) | |
10078 (nuser (nth 1 nparsed)) | |
10079 (npath (nth 2 nparsed)) | |
10080 (abbr (efs-relativize-filename newname | |
10081 (file-name-directory file))) | |
10082 (ent (efs-get-file-entry file)) | |
10083 cmd-string) | |
10084 (or (and (string-equal (downcase host) (downcase nhost)) | |
10085 (string-equal user nuser)) | |
10086 (error "Cannot create hard links between different host user pairs.")) | |
10087 (if (or (null ent) (stringp (car ent)) | |
10088 (not (file-directory-p | |
10089 (file-name-directory newname)))) | |
10090 (if cont | |
10091 (efs-call-cont cont 'failed | |
10092 (format "no such file or directory, %s %s" | |
10093 file newname) "") | |
10094 (signal 'file-error | |
10095 (list "no such file or directory" | |
10096 file newname))) | |
10097 (if (or (not ok-if-already-exists) | |
10098 (numberp ok-if-already-exists)) | |
10099 (efs-barf-or-query-if-file-exists | |
10100 newname "make hard link" (numberp ok-if-already-exists))) | |
10101 ;; Do this last, so that hopefully the host type is known. | |
10102 (let ((host-type (efs-host-type host))) | |
10103 (if (or (not (memq host-type efs-unix-host-types)) | |
10104 (memq host-type efs-dumb-host-types) | |
10105 (efs-get-host-property host 'exec-failed)) | |
10106 (error "Unable to make hard links on %s." host))) | |
10107 (setq cmd-string (concat "ln -f " path " " npath)) | |
10108 (efs-try-ln | |
10109 host user | |
10110 (efs-cont () (host user cmd-string file newname abbr cont nowait) | |
10111 (efs-send-cmd | |
10112 host user (list 'quote 'site 'exec cmd-string) | |
10113 (format "Adding to %s name %s" file abbr) | |
10114 nil | |
10115 (efs-cont (result line cont-lines) (host user file newname cont) | |
10116 (if result | |
10117 (progn | |
10118 (efs-set-host-property host 'exec-failed t) | |
10119 (efs-error host user (format "exec: %s" line))) | |
10120 (efs-save-match-data | |
10121 (if (string-match "\n200-\\([^\n]*\\)" cont-lines) | |
10122 (let ((err (substring cont-lines (match-beginning 1) | |
10123 (match-end 1)))) | |
10124 (if cont | |
10125 (efs-call-cont cont 'failed err cont-lines) | |
10126 (efs-error host user err))) | |
10127 (let ((ent (efs-get-file-entry file))) | |
10128 (if ent | |
10129 (let ((nlinks (nthcdr 4 ent)) | |
10130 new-nlinks) | |
10131 (and (integerp (car nlinks)) | |
10132 (setq new-nlinks (1+ (car nlinks))) | |
10133 (setcar nlinks new-nlinks)) | |
10134 (apply 'efs-add-file-entry nil newname ent) | |
10135 (if cont (efs-call-cont cont nil line cont-lines))) | |
10136 (let ((tbl (efs-get-files-hashtable-entry | |
10137 (file-name-directory | |
10138 (directory-file-name newname))))) | |
10139 (if tbl | |
10140 (efs-ls | |
10141 newname | |
10142 (concat (efs-ls-guess-switches) "d") t t nil | |
10143 nowait | |
10144 (efs-cont (listing) (newname cont line cont-lines) | |
10145 (efs-update-file-info | |
10146 newname efs-data-buffer-name) | |
10147 (if cont | |
10148 (efs-call-cont cont nil line cont-lines)))) | |
10149 (if cont | |
10150 (efs-call-cont cont nil line cont-lines)))))))))) | |
10151 nowait)) | |
10152 nowait)))) | |
10153 | |
10154 (defun efs-add-name-to-file (file newname &optional ok-if-already-exists) | |
10155 ;; efs version of add-name-to-file | |
10156 (efs-add-name-to-file-internal file newname ok-if-already-exists)) | |
10157 | |
10158 | |
10159 ;;;; ============================================================== | |
10160 ;;;; >9 | |
10161 ;;;; Multiple Host Type Support. | |
10162 ;;;; The initial host type guessing is done in the PWD code below. | |
10163 ;;;; If necessary, further guessing is done in the listing parser. | |
10164 ;;;; ============================================================== | |
10165 | |
10166 | |
10167 ;;;; -------------------------------------------------------------- | |
10168 ;;;; Functions for setting and retrieving host types. | |
10169 ;;;; -------------------------------------------------------------- | |
10170 | |
10171 (defun efs-add-host (type host) | |
10172 "Sets the TYPE of the remote host HOST. | |
10173 The host type is read with completion so this can be used to obtain a | |
10174 list of supported host types. HOST must be a string, giving the name of | |
10175 the host, exactly as given in file names. Setting the host type with | |
10176 this function is preferable to setting the efs-TYPE-host-regexp, as look up | |
10177 will be faster. Returns TYPE." | |
10178 ;; Since internet host names are always case-insensitive, we will cache | |
10179 ;; them in lower case. | |
10180 (interactive | |
10181 (list | |
10182 (intern | |
10183 (completing-read "Host type: " | |
10184 (mapcar | |
10185 (function (lambda (elt) | |
10186 (list (symbol-name (car elt))))) | |
10187 efs-host-type-alist) | |
10188 nil t)) | |
10189 (read-string "Host: " | |
10190 (let ((name (or (buffer-file-name) | |
10191 (and (eq major-mode 'dired-mode) | |
10192 dired-directory)))) | |
10193 (and name (car (efs-ftp-path name))))))) | |
10194 (setq host (downcase host)) | |
10195 (efs-set-host-property host 'host-type type) | |
10196 (prog1 | |
10197 (setq efs-host-cache host | |
10198 efs-host-type-cache type) | |
10199 (efs-set-process-host-type host))) | |
10200 | |
10201 (defun efs-set-process-host-type (host &optional user) | |
10202 ;; Sets the value of efs-process-host-type so that it is shown | |
10203 ;; on the mode-line. | |
10204 (let ((buff-list (buffer-list))) | |
10205 (save-excursion | |
10206 (while buff-list | |
10207 (set-buffer (car buff-list)) | |
10208 (if (equal efs-process-host host) | |
10209 (setq efs-process-host-type (concat " " (symbol-name | |
10210 (efs-host-type host)))) | |
10211 (and efs-show-host-type-in-dired | |
10212 (eq major-mode 'dired-mode) | |
10213 efs-dired-host-type | |
10214 (string-equal (downcase | |
10215 (car (efs-ftp-path default-directory))) | |
10216 (downcase host)) | |
10217 (if user | |
10218 (setq efs-dired-listing-type-string | |
10219 (concat | |
10220 " " | |
10221 (symbol-name (efs-listing-type host user)))) | |
10222 (or efs-dired-listing-type-string | |
10223 (setq efs-dired-listing-type-string | |
10224 (concat " " (symbol-name (efs-host-type host)))))))) | |
10225 (setq buff-list (cdr buff-list)))))) | |
10226 | |
10227 ;;;; ---------------------------------------------------------------- | |
10228 ;;;; Functions for setting and retrieving listings types. | |
10229 ;;;; ---------------------------------------------------------------- | |
10230 | |
10231 ;;; listing types?? | |
10232 ;;; These are distinguished from host types, in case some OS's have two | |
10233 ;;; breeds of listings. e.g. Unix descriptive listings. | |
10234 ;;; We also use this to support the myriad of DOS ftp servers. | |
10235 | |
10236 | |
10237 (defun efs-listing-type (host user) | |
10238 "Returns the type of listing used on HOST by USER. | |
10239 If there is no entry for a specialized listing, returns the host type." | |
10240 (or | |
10241 (efs-get-host-user-property host user 'listing-type) | |
10242 (efs-host-type host user))) | |
10243 | |
10244 (defun efs-add-listing-type (type host user) | |
10245 "Interactively adds the specialized listing type TYPE for HOST and USER | |
10246 to the listing type cache." | |
10247 (interactive | |
10248 (let ((name (or (buffer-file-name) | |
10249 (and (eq major-mode 'dired-mode) | |
10250 dired-directory)))) | |
10251 (list | |
10252 (intern | |
10253 (completing-read "Listing type: " | |
10254 (mapcar | |
10255 (function (lambda (elt) | |
10256 (list (symbol-name elt)))) | |
10257 efs-listing-types) | |
10258 nil t)) | |
10259 (read-string "Host: " | |
10260 (and name (car (efs-ftp-path name)))) | |
10261 (read-string "User: " | |
10262 (and name (nth 1 (efs-ftp-path name))))))) | |
10263 (efs-set-host-user-property host user 'listing-type type) | |
10264 (efs-set-process-host-type host user)) | |
10265 | |
10266 ;;;; -------------------------------------------------------------- | |
10267 ;;;; Auotomagic bug reporting for unrecognized host types. | |
10268 ;;;; -------------------------------------------------------------- | |
10269 | |
10270 (defun efs-scream-and-yell-1 (host user) | |
10271 ;; Internal for efs-scream-and-yell. | |
10272 (with-output-to-temp-buffer "*Help*" | |
10273 (princ | |
10274 (format | |
10275 "efs is unable to identify the remote host type of %s. | |
10276 | |
10277 Please report this as a bug. It would be very helpful | |
10278 if your bug report contained at least the PWD command | |
10279 within the *ftp %s@%s* buffer. | |
10280 If you know them, also send the operating system | |
10281 and ftp server types of the remote host." host user host))) | |
10282 (if (y-or-n-p "Would you like to submit a bug report now? ") | |
10283 (efs-report-bug host user | |
10284 "Bug occurred during efs-guess-host-type." t))) | |
10285 | |
10286 (defun efs-scream-and-yell (host user) | |
10287 ;; Advertises that something has gone wrong in identifying the host type. | |
10288 (if (eq (selected-window) (minibuffer-window)) | |
10289 (efs-abort-recursive-edit-and-then 'efs-scream-and-yell-1 host user) | |
10290 (efs-scream-and-yell-1 host user) | |
10291 (error "Unable to identify remote host type"))) | |
10292 | |
10293 ;;;; -------------------------------------------------------- | |
10294 ;;;; Guess at the host type using PWD syntax. | |
10295 ;;;; -------------------------------------------------------- | |
10296 | |
10297 ;; host-type path templates. These should match a pwd performed | |
10298 ;; as the first command after connecting. They should be as tight | |
10299 ;; as possible | |
10300 | |
10301 (defconst efs-unix-path-template "^/") | |
10302 (defconst efs-apollo-unix-path-template "^//") | |
10303 (defconst efs-cms-path-template | |
10304 (concat | |
10305 "^[-A-Z0-9$*][-A-Z0-9$*]?[-A-Z0-9$*]?[-A-Z0-9$*]?[-A-Z0-9$*]?" | |
10306 "[-A-Z0-9$*]?[-A-Z0-9$*]?[-A-Z0-9$*]?\\.[0-9][0-9][0-9A-Z]$\\|" | |
10307 ;; For the SFS version of CMS | |
10308 "^[-A-Z0-9]+:[-A-Z0-9$*]+\\.$")) | |
10309 | |
10310 (defconst efs-mvs-path-template "^'?[A-Z][0-9][0-9]?[0-9]?[0-9]?[0-9]?\\.'?") | |
10311 | |
10312 (defconst efs-guardian-path-template | |
10313 (concat | |
10314 "^\\(" | |
10315 "\\\\[A-Z0-9][A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?\\." | |
10316 "\\)?" | |
10317 "\\$[A-Z0-9][A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?\\." | |
10318 "[A-Z][A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?$")) | |
10319 ;; guardian and cms are very close to overlapping (they don't). Be careful. | |
10320 (defconst efs-vms-path-template | |
10321 "^[-A-Z0-9_$]+:\\[[-A-Z0-9_$]+\\(\\.[-A-Z0-9_$]+\\)*\\]$") | |
10322 (defconst efs-mts-path-template | |
10323 "^[A-Z0-9._][A-Z0-9._][A-Z0-9._][A-Z0-9._]:$") | |
10324 (defconst efs-ms-unix-path-template "^[A-Za-z0-9]:/") | |
10325 | |
10326 ;; Following two are for TI lisp machines. Note that lisp machines | |
10327 ;; do not have a default directory, but only a default pathname against | |
10328 ;; which relative pathnames are merged (Jamie tells me). | |
10329 (defconst efs-ti-explorer-pwd-line-template | |
10330 (let* ((excluded-chars ":;<>.#\n\r\t\\/a-z ") | |
10331 (token (concat "[^" excluded-chars "]+"))) | |
10332 (concat "^250 " | |
10333 token ": " ; host name | |
10334 token "\\(\\." token "\\)*; " ; directory | |
10335 "\\(\\*.\\*\\|\\*\\)#\\(\\*\\|>\\)" ; name, ext, version | |
10336 "$"))) ; "*.*#*" or "*.*#>" or "*#*" or "*#>" or "#*" ... | |
10337 (defconst efs-ti-twenex-path-template | |
10338 (let* ((excluded-chars ":;<>.#\n\r\t\\/a-z ") | |
10339 (token (concat "[^" excluded-chars "]+"))) | |
10340 (concat "^" | |
10341 token ":" ; host name | |
10342 "<\\(" token "\\)?\\(\\." token "\\)*>" ; directory | |
10343 "\\(\\*.\\*\\|\\*\\)" ; name and extension | |
10344 "$"))) | |
10345 | |
10346 (defconst efs-tops-20-path-template | |
10347 "^[-A-Z0-9_$]+:<[-A-Z0-9_$]\\(.[-A-Z0-9_$]+\\)*>$") | |
10348 (defconst efs-pc-path-template | |
10349 "^[a-zA-Z0-9]:\\\\\\([-_+=a-zA-Z0-9.]+\\\\\\)*[-_+=a-zA-Z0-9.]*$") | |
10350 (defconst efs-mpe-path-template | |
10351 (let ((token (concat "[A-Z][A-Z0-9]?[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?" | |
10352 "[A-Z0-9]?[A-Z0-9]?[A-Z0-9]?"))) | |
10353 (concat | |
10354 ;; optional session name | |
10355 "^\\(" token "\\)?," | |
10356 ;; username | |
10357 token "." | |
10358 ;; account | |
10359 token "," | |
10360 ;; group | |
10361 token "$"))) | |
10362 (defconst efs-vos-path-template | |
10363 (let ((token "[][@\\^`{}|~\"$+,---./:_a-zA-Z0-9]+")) | |
10364 (concat | |
10365 "%" token ; host | |
10366 "#" token ; disk | |
10367 "\\(>" token "\\)+" ; directories | |
10368 ))) | |
10369 (defconst efs-netware-path-template "^[-A-Z0-9_][-A-Z0-9_/]*:/") | |
10370 ;; Sometimes netware doesn't return a device to a PWD. Then it will be | |
10371 ;; recognized by the listing parser. | |
10372 | |
10373 (defconst efs-nos-ve-path-template "^:[A-Z0-9]") | |
10374 ;; Matches the path for NOS/VE | |
10375 | |
10376 (defconst efs-mvs-pwd-line-template | |
10377 ;; Not sure how the PWD parser will do with empty strings, so treate | |
10378 ;; this as a line regexp. | |
10379 "^257 \\([Nn]o prefix defined\\|\"\" is working directory\\)") | |
10380 (defconst efs-cms-pwd-line-template | |
10381 "^450 No current working directory defined$") | |
10382 (defconst efs-tops-20-pwd-line-template | |
10383 "^500 I never heard of the \\(XPWD\\|PWD\\) command\\. Try HELP\\.$") | |
10384 (defconst efs-dos:ftp-pwd-line-template | |
10385 "^250 Current working directory is +") | |
10386 (defconst efs-coke-pwd-line-template "^257 Current balance \\$[0-9]") | |
10387 | |
10388 (defconst efs-super-dumb-unix-tilde-regexp | |
10389 "^550 /.*: No such file or directory\\.?$") | |
10390 (defconst efs-cms-knet-tilde-regexp | |
10391 "^501 Invalid CMS fileid: ~$") | |
10392 | |
10393 | |
10394 ;; It might be nice to message users about the host type identified, | |
10395 ;; but there is so much other messaging going on, it would not be | |
10396 ;; seen. No point in slowing things down just so users can read | |
10397 ;; a host type message. | |
10398 | |
10399 (defun efs-guess-host-type (host user) | |
10400 "Guess the host type of HOST. | |
10401 Does a PWD and examines the directory syntax. The PWD is then cached for use | |
10402 in file name expansion." | |
10403 (let ((host-type (efs-host-type host)) | |
10404 (key (concat host "/" user "/~")) | |
10405 syst) | |
10406 (efs-save-match-data | |
10407 (if (eq host-type 'unknown) | |
10408 ;; Note that efs-host-type returns unknown as the default. | |
10409 ;; Since we don't yet know the host-type, we use the default | |
10410 ;; version of efs-send-pwd. We compensate if necessary | |
10411 ;; by looking at the entire line of output. | |
10412 (let* ((result (efs-send-pwd nil host user)) | |
10413 (dir (car result)) | |
10414 (line (cdr result))) | |
10415 (cond | |
10416 | |
10417 ;; First sift through process lines to see if we recognize | |
10418 ;; any pwd errors, or full line messages. | |
10419 | |
10420 ;; CMS | |
10421 ((string-match efs-cms-pwd-line-template line) | |
10422 (setq host-type (efs-add-host 'cms host) | |
10423 dir (concat "/" (if (> (length user) 8) | |
10424 (substring user 0 8) | |
10425 user) | |
10426 ".191")) | |
10427 (message | |
10428 "Unable to determine a \"home\" CMS minidisk. Assuming %s" | |
10429 dir) | |
10430 (sit-for 1)) | |
10431 | |
10432 ;; TOPS-20 | |
10433 ((string-match efs-tops-20-pwd-line-template line) | |
10434 (setq host-type (efs-add-host 'tops-20 host) | |
10435 dir (car (efs-send-pwd 'tops-20 host user)))) | |
10436 | |
10437 ;; TI-EXPLORER lisp machine. pwd works here, but the output | |
10438 ;; needs to be specially parsed since spaces separate | |
10439 ;; hostnames from dirs from filenames. | |
10440 ((string-match efs-ti-explorer-pwd-line-template line) | |
10441 (setq host-type (efs-add-host 'ti-explorer host) | |
10442 dir (substring line 4))) | |
10443 | |
10444 ;; FTP Software's DOS Server | |
10445 ((string-match efs-dos:ftp-pwd-line-template line) | |
10446 (setq host-type (efs-add-host 'dos host) | |
10447 dir (substring line (match-end 0))) | |
10448 (efs-add-listing-type 'dos:ftp host user)) | |
10449 | |
10450 ;; MVS | |
10451 ((string-match efs-mvs-pwd-line-template line) | |
10452 (setq host-type (efs-add-host 'mvs host) | |
10453 dir "")) ; "" will convert to /, which is always | |
10454 ; the mvs home dir. | |
10455 | |
10456 ;; COKE | |
10457 ((string-match efs-coke-pwd-line-template line) | |
10458 (setq host-type (efs-add-host 'coke host) | |
10459 dir "/")) | |
10460 | |
10461 ;; Try to get tilde. | |
10462 ((null dir) | |
10463 (let ((tilde (nth 1 (efs-send-cmd | |
10464 host user (list 'get "~" "/dev/null"))))) | |
10465 (cond | |
10466 ;; super dumb unix | |
10467 ((string-match efs-super-dumb-unix-tilde-regexp tilde) | |
10468 (setq dir (car (efs-send-pwd 'super-dumb-unix host user)) | |
10469 host-type (efs-add-host 'super-dumb-unix host))) | |
10470 | |
10471 ;; Try for cms-knet | |
10472 ((string-match efs-cms-knet-tilde-regexp tilde) | |
10473 (setq dir (car (efs-send-pwd 'cms-knet host user)) | |
10474 host-type (efs-add-host 'cms-knet host))) | |
10475 | |
10476 ;; We don't know. Scream and yell. | |
10477 (efs-scream-and-yell host user)))) | |
10478 | |
10479 ;; Now look at dir to determine host type | |
10480 | |
10481 ;; try for UN*X-y type stuff | |
10482 ((string-match efs-unix-path-template dir) | |
10483 (if | |
10484 ;; Check for apollo, so we know not to short-circuit //. | |
10485 (string-match efs-apollo-unix-path-template dir) | |
10486 (progn | |
10487 (setq host-type (efs-add-host 'apollo-unix host)) | |
10488 (efs-add-listing-type 'unix:unknown host user)) | |
10489 ;; could be ka9q, dos-distinct, plus any of the unix breeds, | |
10490 ;; except apollo. | |
10491 (if (setq syst (efs-get-syst host user)) | |
10492 (let ((case-fold-search t)) | |
10493 (cond | |
10494 ((string-match "\\bNetware\\b" syst) | |
10495 (setq host-type (efs-add-host 'netware host))) | |
10496 ((string-match "^Plan 9" syst) | |
10497 (setq host-type (efs-add-host 'plan9 host))) | |
10498 ((string-match "^UNIX" syst) | |
10499 (setq host-type (efs-add-host 'unix host)) | |
10500 (efs-add-listing-type 'unix:unknown host user))))))) | |
10501 | |
10502 ;; try for VMS | |
10503 ((string-match efs-vms-path-template dir) | |
10504 (setq host-type (efs-add-host 'vms host))) | |
10505 | |
10506 ;; try for MTS | |
10507 ((string-match efs-mts-path-template dir) | |
10508 (setq host-type (efs-add-host 'mts host))) | |
10509 | |
10510 ;; try for CMS | |
10511 ((string-match efs-cms-path-template dir) | |
10512 (setq host-type (efs-add-host 'cms host))) | |
10513 | |
10514 ;; try for Tandem's guardian OS | |
10515 ((string-match efs-guardian-path-template dir) | |
10516 (setq host-type (efs-add-host 'guardian host))) | |
10517 | |
10518 ;; Try for TOPS-20. pwd doesn't usually work for tops-20 | |
10519 ;; But who knows??? | |
10520 ((string-match efs-tops-20-path-template dir) | |
10521 (setq host-type (efs-add-host 'tops-20 host))) | |
10522 | |
10523 ;; Try for DOS or OS/2. | |
10524 ((string-match efs-pc-path-template dir) | |
10525 (let ((syst (efs-get-syst host user)) | |
10526 (case-fold-search t)) | |
10527 (if (and syst (string-match "^OS/2 " syst)) | |
10528 (setq host-type (efs-add-host 'os2 host)) | |
10529 (setq host-type (efs-add-host 'dos host))))) | |
10530 | |
10531 ;; try for TI-TWENEX lisp machine | |
10532 ((string-match efs-ti-twenex-path-template dir) | |
10533 (setq host-type (efs-add-host 'ti-twenex host))) | |
10534 | |
10535 ;; try for MPE | |
10536 ((string-match efs-mpe-path-template dir) | |
10537 (setq host-type (efs-add-host 'mpe host))) | |
10538 | |
10539 ;; try for VOS | |
10540 ((string-match efs-vos-path-template dir) | |
10541 (setq host-type (efs-add-host 'vos host))) | |
10542 | |
10543 ;; try for the microsoft server in unix mode | |
10544 ((string-match efs-ms-unix-path-template dir) | |
10545 (setq host-type (efs-add-host 'ms-unix host))) | |
10546 | |
10547 ;; Netware? | |
10548 ((string-match efs-netware-path-template dir) | |
10549 (setq host-type (efs-add-host 'netware host))) | |
10550 | |
10551 ;; Try for MVS | |
10552 ((string-match efs-mvs-path-template dir) | |
10553 (if (string-match "^'.+'$" dir) | |
10554 ;; broken MVS PWD quoting | |
10555 (setq dir (substring dir 1 -1))) | |
10556 (setq host-type (efs-add-host 'mvs host))) | |
10557 | |
10558 ;; Try for NOS/VE | |
10559 ((string-match efs-nos-ve-path-template dir) | |
10560 (setq host-type (efs-add-host 'nos-ve host))) | |
10561 | |
10562 ;; We don't know. Scream and yell. | |
10563 (t | |
10564 (efs-scream-and-yell host user))) | |
10565 | |
10566 ;; Now that we have done a pwd, might as well put it in | |
10567 ;; the expand-dir hashtable. | |
10568 (if dir | |
10569 (efs-put-hash-entry | |
10570 key | |
10571 (efs-internal-directory-file-name | |
10572 (efs-fix-path host-type dir 'reverse)) | |
10573 efs-expand-dir-hashtable | |
10574 (memq host-type efs-case-insensitive-host-types)))) | |
10575 | |
10576 ;; host-type has been identified by regexp, set the mode-line. | |
10577 (efs-set-process-host-type host user) | |
10578 | |
10579 ;; Some special cases, where we need to store the cwd on login. | |
10580 (if (not (efs-hash-entry-exists-p | |
10581 key efs-expand-dir-hashtable)) | |
10582 (cond | |
10583 ;; CMS: We will be doing cd's, so we'd better make sure that | |
10584 ;; we know where home is. | |
10585 ((eq host-type 'cms) | |
10586 (let* ((res (efs-send-pwd 'cms host user)) | |
10587 (dir (car res)) | |
10588 (line (cdr res))) | |
10589 (if (and dir (not (string-match | |
10590 efs-cms-pwd-line-template line))) | |
10591 (setq dir (concat "/" dir)) | |
10592 (setq dir (concat "/" (if (> (length user) 8) | |
10593 (substring user 0 8) | |
10594 user) | |
10595 ".191")) | |
10596 (message | |
10597 "Unable to determine a \"home\" CMS minidisk. Assuming %s" | |
10598 dir)) | |
10599 (efs-put-hash-entry | |
10600 key dir efs-expand-dir-hashtable | |
10601 (memq 'cms efs-case-insensitive-host-types)))) | |
10602 ;; MVS: pwd doesn't work in the root directory, so we stuff this | |
10603 ;; into the hashtable manually. | |
10604 ((eq host-type 'mvs) | |
10605 (efs-put-hash-entry key "/" efs-expand-dir-hashtable)) | |
10606 )))))) | |
10607 | |
10608 | |
10609 ;;;; ----------------------------------------------------------- | |
10610 ;;;; efs-autoloads | |
10611 ;;;; These provide the entry points for the non-unix packages. | |
10612 ;;;; ----------------------------------------------------------- | |
10613 | |
10614 (efs-autoload 'efs-fix-path vms "efs-vms") | |
10615 (efs-autoload 'efs-fix-path mts "efs-mts") | |
10616 (efs-autoload 'efs-fix-path cms "efs-cms") | |
10617 (efs-autoload 'efs-fix-path ti-explorer "efs-ti-explorer") | |
10618 (efs-autoload 'efs-fix-path ti-twenex "efs-ti-twenex") | |
10619 (efs-autoload 'efs-fix-path dos "efs-pc") | |
10620 (efs-autoload 'efs-fix-path mvs "efs-mvs") | |
10621 (efs-autoload 'efs-fix-path tops-20 "efs-tops-20") | |
10622 (efs-autoload 'efs-fix-path mpe "efs-mpe") | |
10623 (efs-autoload 'efs-fix-path os2 "efs-pc") | |
10624 (efs-autoload 'efs-fix-path vos "efs-vos") | |
10625 (efs-autoload 'efs-fix-path ms-unix "efs-ms-unix") | |
10626 (efs-autoload 'efs-fix-path netware "efs-netware") | |
10627 (efs-autoload 'efs-fix-path cms-knet "efs-cms-knet") | |
10628 (efs-autoload 'efs-fix-path guardian "efs-guardian") | |
10629 (efs-autoload 'efs-fix-path nos-ve "efs-nos-ve") | |
10630 | |
10631 (efs-autoload 'efs-fix-dir-path vms "efs-vms") | |
10632 (efs-autoload 'efs-fix-dir-path mts "efs-mts") | |
10633 (efs-autoload 'efs-fix-dir-path cms "efs-cms") | |
10634 (efs-autoload 'efs-fix-dir-path ti-explorer "efs-ti-explorer") | |
10635 (efs-autoload 'efs-fix-dir-path ti-twenex "efs-ti-twenex") | |
10636 (efs-autoload 'efs-fix-dir-path dos "efs-pc") | |
10637 (efs-autoload 'efs-fix-dir-path mvs "efs-mvs") | |
10638 (efs-autoload 'efs-fix-dir-path tops-20 "efs-tops-20") | |
10639 (efs-autoload 'efs-fix-dir-path mpe "efs-mpe") | |
10640 (efs-autoload 'efs-fix-dir-path os2 "efs-pc") | |
10641 (efs-autoload 'efs-fix-dir-path vos "efs-vos") | |
10642 (efs-autoload 'efs-fix-dir-path hell "efs-hell") | |
10643 (efs-autoload 'efs-fix-dir-path ms-unix "efs-ms-unix") | |
10644 (efs-autoload 'efs-fix-dir-path netware "efs-netware") | |
10645 (efs-autoload 'efs-fix-dir-path plan9 "efs-plan9") | |
10646 (efs-autoload 'efs-fix-dir-path cms-knet "efs-cms-knet") | |
10647 (efs-autoload 'efs-fix-dir-path guardian "efs-guardian") | |
10648 (efs-autoload 'efs-fix-dir-path nos-ve "efs-nos-ve") | |
10649 (efs-autoload 'efs-fix-dir-path coke "efs-coke") | |
10650 | |
10651 ;; A few need to autoload a pwd function | |
10652 (efs-autoload 'efs-send-pwd tops-20 "efs-tops-20") | |
10653 (efs-autoload 'efs-send-pwd cms-knet "efs-cms-knet") | |
10654 (efs-autoload 'efs-send-pwd ti-explorer "efs-ti-explorer") | |
10655 (efs-autoload 'efs-send-pwd hell "efs-hell") | |
10656 (efs-autoload 'efs-send-pwd mvs "efs-mvs") | |
10657 (efs-autoload 'efs-send-pwd coke "efs-coke") | |
10658 | |
10659 ;; A few packages are loaded by the listing parser. | |
10660 (efs-autoload 'efs-parse-listing ka9q "efs-ka9q") | |
10661 (efs-autoload 'efs-parse-listing unix:dl "efs-dl") | |
10662 (efs-autoload 'efs-parse-listing dos-distinct "efs-dos-distinct") | |
10663 (efs-autoload 'efs-parse-listing hell "efs-hell") | |
10664 (efs-autoload 'efs-parse-listing netware "efs-netware") | |
10665 | |
10666 ;; Packages that need to autoload for child-lookup | |
10667 (efs-autoload 'efs-allow-child-lookup plan9 "efs-plan9") | |
10668 (efs-autoload 'efs-allow-child-lookup coke "efs-coke") | |
10669 | |
10670 ;; Packages that need to autoload for file-exists-p and file-directory-p | |
10671 (efs-autoload 'efs-internal-file-exists-p guardian "efs-guardian") | |
10672 (efs-autoload 'efs-internal-file-directory-p guardian "efs-guardian") | |
10673 | |
10674 | |
10675 | |
10676 ;;;; ============================================================ | |
10677 ;;;; >10 | |
10678 ;;;; Attaching onto the appropriate Emacs version | |
10679 ;;;; ============================================================ | |
10680 | |
10681 ;;;; ------------------------------------------------------------------- | |
10682 ;;;; Connect to various hooks. | |
10683 ;;;; ------------------------------------------------------------------- | |
10684 | |
10685 (or (memq 'efs-set-buffer-mode find-file-hooks) | |
10686 (setq find-file-hooks | |
10687 (cons 'efs-set-buffer-mode find-file-hooks))) | |
10688 | |
10689 ;;; We are using our own dired.el, so this doesn't depend on Emacs flavour. | |
10690 | |
10691 (if (featurep 'dired) | |
10692 (require 'efs-dired) | |
10693 (add-hook 'dired-load-hook (function | |
10694 (lambda () | |
10695 (require 'efs-dired))))) | |
10696 | |
10697 ;;;; ------------------------------------------------------------ | |
10698 ;;;; Add to minor-mode-alist. | |
10699 ;;;; ------------------------------------------------------------ | |
10700 | |
10701 (or (assq 'efs-process-host-type minor-mode-alist) | |
10702 (if (assq 'dired-sort-mode minor-mode-alist) | |
10703 (let ((our-list | |
10704 (nconc | |
10705 (delq nil | |
10706 (list (assq 'dired-sort-mode minor-mode-alist) | |
10707 (assq 'dired-subdir-omit minor-mode-alist) | |
10708 (assq 'dired-marker-stack minor-mode-alist))) | |
10709 (list '(efs-process-host-type efs-process-host-type) | |
10710 '(efs-dired-listing-type | |
10711 efs-dired-listing-type-string)))) | |
10712 (old-list (delq | |
10713 (assq 'efs-process-host-type minor-mode-alist) | |
10714 (delq | |
10715 (assq 'efs-dired-listing-type minor-mode-alist) | |
10716 minor-mode-alist)))) | |
10717 (setq minor-mode-alist nil) | |
10718 (while old-list | |
10719 (or (assq (car (car old-list)) our-list) | |
10720 (setq minor-mode-alist (nconc minor-mode-alist | |
10721 (list (car old-list))))) | |
10722 (setq old-list (cdr old-list))) | |
10723 (setq minor-mode-alist (nconc our-list minor-mode-alist))) | |
10724 (setq minor-mode-alist | |
10725 (nconc | |
10726 (list '(efs-process-host-type efs-process-host-type) | |
10727 '(efs-dired-listing-type efs-dired-listing-type-string)) | |
10728 minor-mode-alist)))) | |
10729 | |
10730 ;;;; ------------------------------------------------------------ | |
10731 ;;;; File name handlers | |
10732 ;;;; ------------------------------------------------------------ | |
10733 | |
10734 (defun efs-file-handler-function (operation &rest args) | |
10735 "Function to call special file handlers for remote files." | |
10736 (let ((handler (get operation 'efs))) | |
10737 (if handler | |
10738 (apply handler args) | |
10739 (let ((inhibit-file-name-handlers | |
10740 (cons 'efs-file-handler-function | |
10741 (and (eq inhibit-file-name-operation operation) | |
10742 inhibit-file-name-handlers))) | |
10743 (inhibit-file-name-operation operation)) | |
10744 (apply operation args))))) | |
10745 | |
10746 (defun efs-sifn-handler-function (operation &rest args) | |
10747 ;; Handler function for substitute-in-file-name | |
10748 (if (eq operation 'substitute-in-file-name) | |
10749 (apply 'efs-substitute-in-file-name args) | |
10750 (let ((inhibit-file-name-handlers | |
10751 (cons 'efs-sifn-handler-function | |
10752 (and (eq operation inhibit-file-name-operation) | |
10753 inhibit-file-name-handlers))) | |
10754 (inhibit-file-name-operation operation)) | |
10755 (apply operation args)))) | |
10756 | |
10757 ;; Yes, this is what it looks like. I'm defining the handler to run our | |
10758 ;; version whenever there is an environment variable. | |
10759 | |
10760 (nconc file-name-handler-alist | |
10761 (list | |
10762 (cons "\\(^\\|[^$]\\)\\(\\$\\$\\)*\\$[{a-zA-Z0-9]" | |
10763 'efs-sifn-handler-function))) | |
10764 | |
10765 ;;;; ------------------------------------------------------------ | |
10766 ;;;; Necessary overloads. | |
10767 ;;;; ------------------------------------------------------------ | |
10768 | |
10769 ;;; The following functions are overloaded, instead of extended via | |
10770 ;;; the file-name-handler-alist. For various reasons, the | |
10771 ;;; file-name-handler-alist doesn't work for them. It would be nice if | |
10772 ;;; this list could be shortened in the future. | |
10773 | |
10774 ;; File name exansion. It is not until _after_ a file name has been | |
10775 ;; expanded that it is reasonable to test it for a file name handler. | |
10776 (efs-overwrite-fn "efs" 'expand-file-name) | |
10777 | |
10778 ;; Loading lisp files. The problem with using the file-name-handler-alist | |
10779 ;; here is that we don't know what is to be handled, until after searching | |
10780 ;; the load-path. The solution is to change the C code for Fload. | |
10781 ;; A patch to do this has been written by Jay Adams <jka@ece.cmu.edu>. | |
10782 (efs-overwrite-fn "efs" 'load) | |
10783 (efs-overwrite-fn "efs" 'require) | |
10784 | |
10785 ;;;; ------------------------------------------------------------ | |
10786 ;;;; Install the file handlers for efs-file-handler-function. | |
10787 ;;;; ------------------------------------------------------------ | |
10788 | |
10789 ;; I/O | |
10790 (put 'insert-file-contents 'efs 'efs-insert-file-contents) | |
10791 (put 'write-region 'efs 'efs-write-region) | |
10792 (put 'directory-files 'efs 'efs-directory-files) | |
10793 (put 'list-directory 'efs 'efs-list-directory) | |
10794 (put 'insert-directory 'efs 'efs-insert-directory) | |
10795 (put 'recover-file 'efs 'efs-recover-file) | |
10796 ;; file properties | |
10797 (put 'file-directory-p 'efs 'efs-file-directory-p) | |
10798 (put 'file-writable-p 'efs 'efs-file-writable-p) | |
10799 (put 'file-readable-p 'efs 'efs-file-readable-p) | |
10800 (put 'file-executable-p 'efs 'efs-file-executable-p) | |
10801 (put 'file-symlink-p 'efs 'efs-file-symlink-p) | |
10802 (put 'file-attributes 'efs 'efs-file-attributes) | |
10803 (put 'file-exists-p 'efs 'efs-file-exists-p) | |
10804 (put 'file-accessible-directory-p 'efs 'efs-file-accessible-directory-p) | |
10805 ;; manipulating file names | |
10806 (put 'file-name-directory 'efs 'efs-file-name-directory) | |
10807 (put 'file-name-nondirectory 'efs 'efs-file-name-nondirectory) | |
10808 (put 'file-name-as-directory 'efs 'efs-file-name-as-directory) | |
10809 (put 'directory-file-name 'efs 'efs-directory-file-name) | |
10810 (put 'abbreviate-file-name 'efs 'efs-abbreviate-file-name) | |
10811 (put 'file-name-sans-versions 'efs 'efs-file-name-sans-versions) | |
10812 (put 'unhandled-file-name-directory 'efs 'efs-unhandled-file-name-directory) | |
10813 (put 'diff-latest-backup-file 'efs 'efs-diff-latest-backup-file) | |
10814 (put 'file-truename 'efs 'efs-file-truename) | |
10815 ;; modtimes | |
10816 (put 'verify-visited-file-modtime 'efs 'efs-verify-visited-file-modtime) | |
10817 (put 'file-newer-than-file-p 'efs 'efs-file-newer-than-file-p) | |
10818 (put 'set-visited-file-modtime 'efs 'efs-set-visited-file-modtime) | |
10819 ;; file modes | |
10820 (put 'set-file-modes 'efs 'efs-set-file-modes) | |
10821 (put 'file-modes 'efs 'efs-file-modes) | |
10822 ;; buffers | |
10823 (put 'backup-buffer 'efs 'efs-backup-buffer) | |
10824 (put 'get-file-buffer 'efs 'efs-get-file-buffer) | |
10825 (put 'create-file-buffer 'efs 'efs-create-file-buffer) | |
10826 ;; creating and removing files | |
10827 (put 'delete-file 'efs 'efs-delete-file) | |
10828 (put 'copy-file 'efs 'efs-copy-file) | |
10829 (put 'rename-file 'efs 'efs-rename-file) | |
10830 (put 'file-local-copy 'efs 'efs-file-local-copy) | |
10831 (put 'make-directory-internal 'efs 'efs-make-directory-internal) | |
10832 (put 'delete-directory 'efs 'efs-delete-directory) | |
10833 (put 'add-name-to-file 'efs 'efs-add-name-to-file) | |
10834 (put 'make-symbolic-link 'efs 'efs-make-symbolic-link) | |
10835 ;; file name completion | |
10836 (put 'file-name-completion 'efs 'efs-file-name-completion) | |
10837 (put 'file-name-all-completions 'efs 'efs-file-name-all-completions) | |
10838 | |
10839 ;;;; ------------------------------------------------------------ | |
10840 ;;;; Finally run any load-hooks. | |
10841 ;;;; ------------------------------------------------------------ | |
10842 | |
10843 (run-hooks 'efs-load-hook) | |
10844 | |
10845 ;;; end of efs.el |