Don't quote lambda expressions with `quote'.
[bpt/emacs.git] / lisp / net / tramp-cache.el
CommitLineData
00d6fd04
MA
1;;; tramp-cache.el --- file information caching for Tramp
2
73b0cd50 3;; Copyright (C) 2000, 2005-2011 Free Software Foundation, Inc.
00d6fd04
MA
4
5;; Author: Daniel Pittman <daniel@inanna.danann.net>
6;; Michael Albinus <michael.albinus@gmx.de>
7;; Keywords: comm, processes
bd78fa1d 8;; Package: tramp
00d6fd04
MA
9
10;; This file is part of GNU Emacs.
11
874a927a 12;; GNU Emacs is free software: you can redistribute it and/or modify
00d6fd04 13;; it under the terms of the GNU General Public License as published by
874a927a
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
00d6fd04
MA
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
874a927a 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
00d6fd04
MA
24
25;;; Commentary:
26
27;; An implementation of information caching for remote files.
28
29;; Each connection, identified by a vector [method user host
30;; localname] or by a process, has a unique cache. We distinguish 3
31;; kind of caches, depending on the key:
32;;
33;; - localname is NIL. This are reusable properties. Examples:
34;; "remote-shell" identifies the POSIX shell to be called on the
35;; remote host, or "perl" is the command to be called on the remote
2c68ca0e 36;; host when starting a Perl script. These properties are saved in
00d6fd04
MA
37;; the file `tramp-persistency-file-name'.
38;;
39;; - localname is a string. This are temporary properties, which are
40;; related to the file localname is referring to. Examples:
41;; "file-exists-p" is t or nile, depending on the file existence, or
42;; "file-attributes" caches the result of the function
43;; `file-attributes'.
44;;
45;; - The key is a process. This are temporary properties related to
46;; an open connection. Examples: "scripts" keeps shell script
47;; definitions already sent to the remote shell, "last-cmd-time" is
48;; the time stamp a command has been sent to the remote process.
49
50;;; Code:
51
0f34aa77
MA
52(require 'tramp)
53(autoload 'time-stamp-string "time-stamp")
00d6fd04
MA
54
55;;; -- Cache --
56
0f34aa77 57;;;###tramp-autoload
00d6fd04
MA
58(defvar tramp-cache-data (make-hash-table :test 'equal)
59 "Hash table for remote files properties.")
60
61(defcustom tramp-persistency-file-name
62 (cond
63 ;; GNU Emacs.
d68b0220
MA
64 ((and (fboundp 'locate-user-emacs-file))
65 (expand-file-name (tramp-compat-funcall 'locate-user-emacs-file "tramp")))
00d6fd04
MA
66 ((and (boundp 'user-emacs-directory)
67 (stringp (symbol-value 'user-emacs-directory))
68 (file-directory-p (symbol-value 'user-emacs-directory)))
69 (expand-file-name "tramp" (symbol-value 'user-emacs-directory)))
70 ((and (not (featurep 'xemacs)) (file-directory-p "~/.emacs.d/"))
71 "~/.emacs.d/tramp")
72 ;; XEmacs.
73 ((and (boundp 'user-init-directory)
74 (stringp (symbol-value 'user-init-directory))
75 (file-directory-p (symbol-value 'user-init-directory)))
76 (expand-file-name "tramp" (symbol-value 'user-init-directory)))
77 ((and (featurep 'xemacs) (file-directory-p "~/.xemacs/"))
78 "~/.xemacs/tramp")
79 ;; For users without `~/.emacs.d/' or `~/.xemacs/'.
80 (t "~/.tramp"))
81 "File which keeps connection history for Tramp connections."
82 :group 'tramp
83 :type 'file)
84
7c3404ec
MA
85(defvar tramp-cache-data-changed nil
86 "Whether persistent cache data have been changed.")
87
0f34aa77 88;;;###tramp-autoload
00d6fd04
MA
89(defun tramp-get-file-property (vec file property default)
90 "Get the PROPERTY of FILE from the cache context of VEC.
91Returns DEFAULT if not set."
92 ;; Unify localname.
93 (setq vec (copy-sequence vec))
87bdd2c7 94 (aset vec 3 (tramp-run-real-handler 'directory-file-name (list file)))
00d6fd04
MA
95 (let* ((hash (or (gethash vec tramp-cache-data)
96 (puthash vec (make-hash-table :test 'equal)
97 tramp-cache-data)))
d5b5c94a
MA
98 (value (when (hash-table-p hash) (gethash property hash))))
99 (if
100 ;; We take the value only if there is any, and
4bc3c53d 101 ;; `remote-file-name-inhibit-cache' indicates that it is still
d5b5c94a
MA
102 ;; valid. Otherwise, DEFAULT is set.
103 (and (consp value)
4bc3c53d
MA
104 (or (null remote-file-name-inhibit-cache)
105 (and (integerp remote-file-name-inhibit-cache)
106 (<=
107 (tramp-time-diff (current-time) (car value))
108 remote-file-name-inhibit-cache))
109 (and (consp remote-file-name-inhibit-cache)
d5b5c94a 110 (tramp-time-less-p
4bc3c53d 111 remote-file-name-inhibit-cache (car value)))))
d5b5c94a
MA
112 (setq value (cdr value))
113 (setq value default))
114
00d6fd04 115 (tramp-message vec 8 "%s %s %s" file property value)
4bc3c53d
MA
116 (when (>= tramp-verbose 10)
117 (let* ((var (intern (concat "tramp-cache-get-count-" property)))
118 (val (or (ignore-errors (symbol-value var)) 0)))
119 (set var (1+ val))))
00d6fd04
MA
120 value))
121
0f34aa77 122;;;###tramp-autoload
00d6fd04
MA
123(defun tramp-set-file-property (vec file property value)
124 "Set the PROPERTY of FILE to VALUE, in the cache context of VEC.
125Returns VALUE."
126 ;; Unify localname.
127 (setq vec (copy-sequence vec))
87bdd2c7 128 (aset vec 3 (tramp-run-real-handler 'directory-file-name (list file)))
00d6fd04
MA
129 (let ((hash (or (gethash vec tramp-cache-data)
130 (puthash vec (make-hash-table :test 'equal)
131 tramp-cache-data))))
d5b5c94a
MA
132 ;; We put the timestamp there.
133 (puthash property (cons (current-time) value) hash)
00d6fd04 134 (tramp-message vec 8 "%s %s %s" file property value)
4bc3c53d
MA
135 (when (>= tramp-verbose 10)
136 (let* ((var (intern (concat "tramp-cache-set-count-" property)))
137 (val (or (ignore-errors (symbol-value var)) 0)))
138 (set var (1+ val))))
00d6fd04
MA
139 value))
140
0f34aa77
MA
141;;;###tramp-autoload
142(defmacro with-file-property (vec file property &rest body)
143 "Check in Tramp cache for PROPERTY, otherwise execute BODY and set cache.
144FILE must be a local file name on a connection identified via VEC."
145 `(if (file-name-absolute-p ,file)
146 (let ((value (tramp-get-file-property ,vec ,file ,property 'undef)))
147 (when (eq value 'undef)
148 ;; We cannot pass @body as parameter to
149 ;; `tramp-set-file-property' because it mangles our
150 ;; debug messages.
151 (setq value (progn ,@body))
152 (tramp-set-file-property ,vec ,file ,property value))
153 value)
154 ,@body))
155
03c1ad43 156;;;###tramp-autoload
0f34aa77
MA
157(put 'with-file-property 'lisp-indent-function 3)
158(put 'with-file-property 'edebug-form-spec t)
6139f995
MA
159(tramp-compat-font-lock-add-keywords
160 'emacs-lisp-mode '("\\<with-file-property\\>"))
0f34aa77
MA
161
162;;;###tramp-autoload
00d6fd04
MA
163(defun tramp-flush-file-property (vec file)
164 "Remove all properties of FILE in the cache context of VEC."
165 ;; Unify localname.
166 (setq vec (copy-sequence vec))
87bdd2c7 167 (aset vec 3 (tramp-run-real-handler 'directory-file-name (list file)))
00d6fd04
MA
168 (tramp-message vec 8 "%s" file)
169 (remhash vec tramp-cache-data))
170
0f34aa77 171;;;###tramp-autoload
00d6fd04
MA
172(defun tramp-flush-directory-property (vec directory)
173 "Remove all properties of DIRECTORY in the cache context of VEC.
174Remove also properties of all files in subdirectories."
87bdd2c7
MA
175 (let ((directory (tramp-run-real-handler
176 'directory-file-name (list directory))))
00d6fd04
MA
177 (tramp-message vec 8 "%s" directory)
178 (maphash
4f91a816 179 (lambda (key value)
5ec2288f 180 (when (and (stringp (tramp-file-name-localname key))
00d6fd04
MA
181 (string-match directory (tramp-file-name-localname key)))
182 (remhash key tramp-cache-data)))
183 tramp-cache-data)))
184
00d6fd04 185;; Reverting or killing a buffer should also flush file properties.
a7580c1c
MA
186;; They could have been changed outside Tramp. In eshell, "ls" would
187;; not show proper directory contents when a file has been copied or
188;; deleted before.
00d6fd04 189(defun tramp-flush-file-function ()
06207091 190 "Flush all Tramp cache properties from `buffer-file-name'."
a7580c1c
MA
191 (let ((bfn (if (stringp (buffer-file-name))
192 (buffer-file-name)
193 default-directory)))
194 (when (tramp-tramp-file-p bfn)
0f34aa77 195 (with-parsed-tramp-file-name bfn nil
00d6fd04
MA
196 (tramp-flush-file-property v localname)))))
197
198(add-hook 'before-revert-hook 'tramp-flush-file-function)
a7580c1c 199(add-hook 'eshell-pre-command-hook 'tramp-flush-file-function)
00d6fd04
MA
200(add-hook 'kill-buffer-hook 'tramp-flush-file-function)
201(add-hook 'tramp-cache-unload-hook
4f91a816 202 (lambda ()
00d6fd04
MA
203 (remove-hook 'before-revert-hook
204 'tramp-flush-file-function)
a7580c1c
MA
205 (remove-hook 'eshell-pre-command-hook
206 'tramp-flush-file-function)
00d6fd04
MA
207 (remove-hook 'kill-buffer-hook
208 'tramp-flush-file-function)))
209
210;;; -- Properties --
211
0f34aa77 212;;;###tramp-autoload
00d6fd04
MA
213(defun tramp-get-connection-property (key property default)
214 "Get the named PROPERTY for the connection.
215KEY identifies the connection, it is either a process or a vector.
216If the value is not set for the connection, returns DEFAULT."
217 ;; Unify key by removing localname from vector. Work with a copy in
218 ;; order to avoid side effects.
219 (when (vectorp key)
220 (setq key (copy-sequence key))
221 (aset key 3 nil))
222 (let* ((hash (gethash key tramp-cache-data))
223 (value (if (hash-table-p hash)
afae3a37
MA
224 (gethash property hash default)
225 default)))
00d6fd04
MA
226 (tramp-message key 7 "%s %s" property value)
227 value))
228
0f34aa77 229;;;###tramp-autoload
00d6fd04
MA
230(defun tramp-set-connection-property (key property value)
231 "Set the named PROPERTY of a connection to VALUE.
232KEY identifies the connection, it is either a process or a vector.
233PROPERTY is set persistent when KEY is a vector."
234 ;; Unify key by removing localname from vector. Work with a copy in
235 ;; order to avoid side effects.
236 (when (vectorp key)
237 (setq key (copy-sequence key))
238 (aset key 3 nil))
239 (let ((hash (or (gethash key tramp-cache-data)
240 (puthash key (make-hash-table :test 'equal)
241 tramp-cache-data))))
242 (puthash property value hash)
7c3404ec 243 (setq tramp-cache-data-changed t)
03c1ad43 244 (tramp-message key 7 "%s %s" property value)
00d6fd04
MA
245 value))
246
0f34aa77
MA
247;;;###tramp-autoload
248(defmacro with-connection-property (key property &rest body)
249 "Check in Tramp for property PROPERTY, otherwise executes BODY and set."
250 `(let ((value (tramp-get-connection-property ,key ,property 'undef)))
251 (when (eq value 'undef)
252 ;; We cannot pass ,@body as parameter to
253 ;; `tramp-set-connection-property' because it mangles our debug
254 ;; messages.
255 (setq value (progn ,@body))
256 (tramp-set-connection-property ,key ,property value))
257 value))
258
03c1ad43 259;;;###tramp-autoload
0f34aa77
MA
260(put 'with-connection-property 'lisp-indent-function 2)
261(put 'with-connection-property 'edebug-form-spec t)
6139f995
MA
262(tramp-compat-font-lock-add-keywords
263 'emacs-lisp-mode '("\\<with-connection-property\\>"))
0f34aa77
MA
264
265;;;###tramp-autoload
1a0b96d3 266(defun tramp-flush-connection-property (key)
00d6fd04 267 "Remove all properties identified by KEY.
1a0b96d3 268KEY identifies the connection, it is either a process or a vector."
00d6fd04
MA
269 ;; Unify key by removing localname from vector. Work with a copy in
270 ;; order to avoid side effects.
271 (when (vectorp key)
272 (setq key (copy-sequence key))
273 (aset key 3 nil))
e946faaf
MA
274 (tramp-message
275 key 7 "%s %s" key
674a9263
MA
276 (let ((hash (gethash key tramp-cache-data))
277 properties)
278 (if (hash-table-p hash)
279 (maphash
280 (lambda (x y) (add-to-list 'properties x 'append))
281 (gethash key tramp-cache-data)))
e946faaf 282 properties))
7c3404ec 283 (setq tramp-cache-data-changed t)
00d6fd04
MA
284 (remhash key tramp-cache-data))
285
0f34aa77 286;;;###tramp-autoload
726f0272 287(defun tramp-cache-print (table)
b08104a0 288 "Print hash table TABLE."
726f0272
MA
289 (when (hash-table-p table)
290 (let (result)
291 (maphash
4f91a816 292 (lambda (key value)
726f0272
MA
293 (let ((tmp (format
294 "(%s %s)"
295 (if (processp key)
296 (prin1-to-string (prin1-to-string key))
297 (prin1-to-string key))
298 (if (hash-table-p value)
299 (tramp-cache-print value)
300 (if (bufferp value)
301 (prin1-to-string (prin1-to-string value))
302 (prin1-to-string value))))))
303 (setq result (if result (concat result " " tmp) tmp))))
304 table)
305 result)))
306
0f34aa77 307;;;###tramp-autoload
b08104a0
MA
308(defun tramp-list-connections ()
309 "Return a list of all known connection vectors according to `tramp-cache'."
726f0272
MA
310 (let (result)
311 (maphash
4f91a816 312 (lambda (key value)
726f0272
MA
313 (when (and (vectorp key) (null (aref key 3)))
314 (add-to-list 'result key)))
315 tramp-cache-data)
316 result))
317
00d6fd04 318(defun tramp-dump-connection-properties ()
b08104a0 319 "Write persistent connection properties into file `tramp-persistency-file-name'."
00d6fd04 320 ;; We shouldn't fail, otherwise (X)Emacs might not be able to be closed.
03c1ad43
MA
321 (ignore-errors
322 (when (and (hash-table-p tramp-cache-data)
323 (not (zerop (hash-table-count tramp-cache-data)))
324 tramp-cache-data-changed
325 (stringp tramp-persistency-file-name))
326 (let ((cache (copy-hash-table tramp-cache-data)))
327 ;; Remove temporary data.
328 (maphash
4f91a816 329 (lambda (key value)
03c1ad43
MA
330 (if (and (vectorp key) (not (tramp-file-name-localname key)))
331 (progn
332 (remhash "process-name" value)
333 (remhash "process-buffer" value)
334 (remhash "first-password-request" value))
335 (remhash key cache)))
336 cache)
337 ;; Dump it.
338 (with-temp-buffer
339 (insert
340 ";; -*- emacs-lisp -*-"
341 ;; `time-stamp-string' might not exist in all (X)Emacs flavors.
342 (condition-case nil
343 (progn
344 (format
345 " <%s %s>\n"
346 (time-stamp-string "%02y/%02m/%02d %02H:%02M:%02S")
347 tramp-persistency-file-name))
348 (error "\n"))
349 ";; Tramp connection history. Don't change this file.\n"
350 ";; You can delete it, forcing Tramp to reapply the checks.\n\n"
351 (with-output-to-string
352 (pp (read (format "(%s)" (tramp-cache-print cache))))))
353 (write-region
354 (point-min) (point-max) tramp-persistency-file-name))))))
00d6fd04 355
845fc5e5
JB
356(unless noninteractive
357 (add-hook 'kill-emacs-hook 'tramp-dump-connection-properties))
00d6fd04 358(add-hook 'tramp-cache-unload-hook
4f91a816 359 (lambda ()
00d6fd04
MA
360 (remove-hook 'kill-emacs-hook
361 'tramp-dump-connection-properties)))
362
8fca3921 363;;;###tramp-autoload
00d6fd04
MA
364(defun tramp-parse-connection-properties (method)
365 "Return a list of (user host) tuples allowed to access for METHOD.
366This function is added always in `tramp-get-completion-function'
06207091 367for all methods. Resulting data are derived from connection history."
00d6fd04
MA
368 (let (res)
369 (maphash
4f91a816 370 (lambda (key value)
00d6fd04
MA
371 (if (and (vectorp key)
372 (string-equal method (tramp-file-name-method key))
373 (not (tramp-file-name-localname key)))
374 (push (list (tramp-file-name-user key)
375 (tramp-file-name-host key))
376 res)))
377 tramp-cache-data)
378 res))
379
27e813fe 380;; Read persistent connection history.
8a4438b6
MA
381(when (and (stringp tramp-persistency-file-name)
382 (zerop (hash-table-count tramp-cache-data)))
00d6fd04
MA
383 (condition-case err
384 (with-temp-buffer
385 (insert-file-contents tramp-persistency-file-name)
386 (let ((list (read (current-buffer)))
387 element key item)
388 (while (setq element (pop list))
389 (setq key (pop element))
390 (while (setq item (pop element))
7c3404ec
MA
391 (tramp-set-connection-property key (pop item) (car item)))))
392 (setq tramp-cache-data-changed nil))
00d6fd04
MA
393 (file-error
394 ;; Most likely because the file doesn't exist yet. No message.
395 (clrhash tramp-cache-data))
396 (error
397 ;; File is corrupted.
8a4438b6
MA
398 (message "Tramp persistency file '%s' is corrupted: %s"
399 tramp-persistency-file-name (error-message-string err))
00d6fd04
MA
400 (clrhash tramp-cache-data))))
401
0f34aa77
MA
402(add-hook 'tramp-unload-hook
403 (lambda ()
404 (unload-feature 'tramp-cache 'force)))
405
00d6fd04
MA
406(provide 'tramp-cache)
407
408;;; tramp-cache.el ends here