Auto-commit of loaddefs files.
[bpt/emacs.git] / lisp / net / tramp-cache.el
CommitLineData
00d6fd04
MA
1;;; tramp-cache.el --- file information caching for Tramp
2
acaf905b 3;; Copyright (C) 2000, 2005-2012 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."
d0c8fc8a
MA
165 ;; Remove file property of symlinks.
166 (let ((truename (tramp-get-file-property vec file "file-truename" nil)))
167 (when (and (stringp truename)
168 (not (string-equal file truename)))
169 (tramp-flush-file-property vec truename)))
00d6fd04
MA
170 ;; Unify localname.
171 (setq vec (copy-sequence vec))
87bdd2c7 172 (aset vec 3 (tramp-run-real-handler 'directory-file-name (list file)))
00d6fd04
MA
173 (tramp-message vec 8 "%s" file)
174 (remhash vec tramp-cache-data))
175
0f34aa77 176;;;###tramp-autoload
00d6fd04
MA
177(defun tramp-flush-directory-property (vec directory)
178 "Remove all properties of DIRECTORY in the cache context of VEC.
179Remove also properties of all files in subdirectories."
87bdd2c7
MA
180 (let ((directory (tramp-run-real-handler
181 'directory-file-name (list directory))))
00d6fd04
MA
182 (tramp-message vec 8 "%s" directory)
183 (maphash
4f91a816 184 (lambda (key value)
065ec2c7
MA
185 (when (and (stringp (tramp-file-name-localname key))
186 (string-match directory (tramp-file-name-localname key)))
187 (remhash key tramp-cache-data)))
00d6fd04
MA
188 tramp-cache-data)))
189
00d6fd04 190;; Reverting or killing a buffer should also flush file properties.
a7580c1c
MA
191;; They could have been changed outside Tramp. In eshell, "ls" would
192;; not show proper directory contents when a file has been copied or
193;; deleted before.
00d6fd04 194(defun tramp-flush-file-function ()
06207091 195 "Flush all Tramp cache properties from `buffer-file-name'."
a7580c1c
MA
196 (let ((bfn (if (stringp (buffer-file-name))
197 (buffer-file-name)
198 default-directory)))
199 (when (tramp-tramp-file-p bfn)
0f34aa77 200 (with-parsed-tramp-file-name bfn nil
00d6fd04
MA
201 (tramp-flush-file-property v localname)))))
202
203(add-hook 'before-revert-hook 'tramp-flush-file-function)
a7580c1c 204(add-hook 'eshell-pre-command-hook 'tramp-flush-file-function)
00d6fd04
MA
205(add-hook 'kill-buffer-hook 'tramp-flush-file-function)
206(add-hook 'tramp-cache-unload-hook
4f91a816 207 (lambda ()
065ec2c7
MA
208 (remove-hook 'before-revert-hook
209 'tramp-flush-file-function)
210 (remove-hook 'eshell-pre-command-hook
211 'tramp-flush-file-function)
212 (remove-hook 'kill-buffer-hook
213 'tramp-flush-file-function)))
00d6fd04
MA
214
215;;; -- Properties --
216
0f34aa77 217;;;###tramp-autoload
00d6fd04
MA
218(defun tramp-get-connection-property (key property default)
219 "Get the named PROPERTY for the connection.
220KEY identifies the connection, it is either a process or a vector.
221If the value is not set for the connection, returns DEFAULT."
222 ;; Unify key by removing localname from vector. Work with a copy in
223 ;; order to avoid side effects.
224 (when (vectorp key)
225 (setq key (copy-sequence key))
226 (aset key 3 nil))
227 (let* ((hash (gethash key tramp-cache-data))
228 (value (if (hash-table-p hash)
afae3a37
MA
229 (gethash property hash default)
230 default)))
00d6fd04
MA
231 (tramp-message key 7 "%s %s" property value)
232 value))
233
0f34aa77 234;;;###tramp-autoload
00d6fd04
MA
235(defun tramp-set-connection-property (key property value)
236 "Set the named PROPERTY of a connection to VALUE.
237KEY identifies the connection, it is either a process or a vector.
238PROPERTY is set persistent when KEY is a vector."
239 ;; Unify key by removing localname from vector. Work with a copy in
240 ;; order to avoid side effects.
241 (when (vectorp key)
242 (setq key (copy-sequence key))
243 (aset key 3 nil))
244 (let ((hash (or (gethash key tramp-cache-data)
245 (puthash key (make-hash-table :test 'equal)
a5509865 246 tramp-cache-data))))
00d6fd04 247 (puthash property value hash)
7c3404ec 248 (setq tramp-cache-data-changed t)
03c1ad43 249 (tramp-message key 7 "%s %s" property value)
00d6fd04
MA
250 value))
251
0f34aa77
MA
252;;;###tramp-autoload
253(defmacro with-connection-property (key property &rest body)
254 "Check in Tramp for property PROPERTY, otherwise executes BODY and set."
255 `(let ((value (tramp-get-connection-property ,key ,property 'undef)))
256 (when (eq value 'undef)
257 ;; We cannot pass ,@body as parameter to
258 ;; `tramp-set-connection-property' because it mangles our debug
259 ;; messages.
260 (setq value (progn ,@body))
261 (tramp-set-connection-property ,key ,property value))
262 value))
263
03c1ad43 264;;;###tramp-autoload
0f34aa77
MA
265(put 'with-connection-property 'lisp-indent-function 2)
266(put 'with-connection-property 'edebug-form-spec t)
6139f995
MA
267(tramp-compat-font-lock-add-keywords
268 'emacs-lisp-mode '("\\<with-connection-property\\>"))
0f34aa77
MA
269
270;;;###tramp-autoload
1a0b96d3 271(defun tramp-flush-connection-property (key)
00d6fd04 272 "Remove all properties identified by KEY.
1a0b96d3 273KEY identifies the connection, it is either a process or a vector."
00d6fd04
MA
274 ;; Unify key by removing localname from vector. Work with a copy in
275 ;; order to avoid side effects.
276 (when (vectorp key)
277 (setq key (copy-sequence key))
278 (aset key 3 nil))
e946faaf
MA
279 (tramp-message
280 key 7 "%s %s" key
674a9263
MA
281 (let ((hash (gethash key tramp-cache-data))
282 properties)
283 (if (hash-table-p hash)
284 (maphash
285 (lambda (x y) (add-to-list 'properties x 'append))
286 (gethash key tramp-cache-data)))
e946faaf 287 properties))
7c3404ec 288 (setq tramp-cache-data-changed t)
00d6fd04
MA
289 (remhash key tramp-cache-data))
290
0f34aa77 291;;;###tramp-autoload
726f0272 292(defun tramp-cache-print (table)
b08104a0 293 "Print hash table TABLE."
726f0272
MA
294 (when (hash-table-p table)
295 (let (result)
296 (maphash
4f91a816 297 (lambda (key value)
065ec2c7
MA
298 (let ((tmp (format
299 "(%s %s)"
300 (if (processp key)
301 (prin1-to-string (prin1-to-string key))
302 (prin1-to-string key))
303 (if (hash-table-p value)
304 (tramp-cache-print value)
305 (if (bufferp value)
306 (prin1-to-string (prin1-to-string value))
307 (prin1-to-string value))))))
308 (setq result (if result (concat result " " tmp) tmp))))
726f0272
MA
309 table)
310 result)))
311
0f34aa77 312;;;###tramp-autoload
b08104a0
MA
313(defun tramp-list-connections ()
314 "Return a list of all known connection vectors according to `tramp-cache'."
726f0272
MA
315 (let (result)
316 (maphash
4f91a816 317 (lambda (key value)
065ec2c7
MA
318 (when (and (vectorp key) (null (aref key 3)))
319 (add-to-list 'result key)))
726f0272
MA
320 tramp-cache-data)
321 result))
322
00d6fd04 323(defun tramp-dump-connection-properties ()
b08104a0 324 "Write persistent connection properties into file `tramp-persistency-file-name'."
00d6fd04 325 ;; We shouldn't fail, otherwise (X)Emacs might not be able to be closed.
03c1ad43
MA
326 (ignore-errors
327 (when (and (hash-table-p tramp-cache-data)
328 (not (zerop (hash-table-count tramp-cache-data)))
329 tramp-cache-data-changed
330 (stringp tramp-persistency-file-name))
331 (let ((cache (copy-hash-table tramp-cache-data)))
a5509865
MA
332 ;; Remove temporary data. If there is the key "login-as", we
333 ;; don't save either, because all other properties might
334 ;; depend on the login name, and we want to give the
335 ;; possibility to use another login name later on.
03c1ad43 336 (maphash
4f91a816 337 (lambda (key value)
a5509865
MA
338 (if (and (vectorp key)
339 (not (tramp-file-name-localname key))
340 (not (gethash "login-as" value)))
065ec2c7
MA
341 (progn
342 (remhash "process-name" value)
343 (remhash "process-buffer" value)
344 (remhash "first-password-request" value))
345 (remhash key cache)))
03c1ad43
MA
346 cache)
347 ;; Dump it.
348 (with-temp-buffer
349 (insert
350 ";; -*- emacs-lisp -*-"
351 ;; `time-stamp-string' might not exist in all (X)Emacs flavors.
352 (condition-case nil
353 (progn
354 (format
355 " <%s %s>\n"
356 (time-stamp-string "%02y/%02m/%02d %02H:%02M:%02S")
357 tramp-persistency-file-name))
358 (error "\n"))
359 ";; Tramp connection history. Don't change this file.\n"
360 ";; You can delete it, forcing Tramp to reapply the checks.\n\n"
361 (with-output-to-string
362 (pp (read (format "(%s)" (tramp-cache-print cache))))))
363 (write-region
364 (point-min) (point-max) tramp-persistency-file-name))))))
00d6fd04 365
845fc5e5
JB
366(unless noninteractive
367 (add-hook 'kill-emacs-hook 'tramp-dump-connection-properties))
00d6fd04 368(add-hook 'tramp-cache-unload-hook
4f91a816 369 (lambda ()
065ec2c7
MA
370 (remove-hook 'kill-emacs-hook
371 'tramp-dump-connection-properties)))
00d6fd04 372
8fca3921 373;;;###tramp-autoload
00d6fd04
MA
374(defun tramp-parse-connection-properties (method)
375 "Return a list of (user host) tuples allowed to access for METHOD.
376This function is added always in `tramp-get-completion-function'
06207091 377for all methods. Resulting data are derived from connection history."
00d6fd04
MA
378 (let (res)
379 (maphash
4f91a816 380 (lambda (key value)
065ec2c7
MA
381 (if (and (vectorp key)
382 (string-equal method (tramp-file-name-method key))
383 (not (tramp-file-name-localname key)))
384 (push (list (tramp-file-name-user key)
385 (tramp-file-name-host key))
386 res)))
00d6fd04
MA
387 tramp-cache-data)
388 res))
389
27e813fe 390;; Read persistent connection history.
8a4438b6 391(when (and (stringp tramp-persistency-file-name)
065ec2c7
MA
392 (zerop (hash-table-count tramp-cache-data))
393 ;; When "emacs -Q" has been called, both variables are nil.
394 ;; We do not load the persistency file then, in order to
395 ;; have a clean test environment.
09388e76
MA
396 (or (and (boundp 'init-file-user) (symbol-value 'init-file-user))
397 (and (boundp 'site-run-file) (symbol-value 'site-run-file))))
00d6fd04
MA
398 (condition-case err
399 (with-temp-buffer
400 (insert-file-contents tramp-persistency-file-name)
401 (let ((list (read (current-buffer)))
402 element key item)
403 (while (setq element (pop list))
404 (setq key (pop element))
405 (while (setq item (pop element))
7c3404ec
MA
406 (tramp-set-connection-property key (pop item) (car item)))))
407 (setq tramp-cache-data-changed nil))
00d6fd04
MA
408 (file-error
409 ;; Most likely because the file doesn't exist yet. No message.
410 (clrhash tramp-cache-data))
411 (error
412 ;; File is corrupted.
8a4438b6
MA
413 (message "Tramp persistency file '%s' is corrupted: %s"
414 tramp-persistency-file-name (error-message-string err))
00d6fd04
MA
415 (clrhash tramp-cache-data))))
416
0f34aa77
MA
417(add-hook 'tramp-unload-hook
418 (lambda ()
419 (unload-feature 'tramp-cache 'force)))
420
00d6fd04
MA
421(provide 'tramp-cache)
422
423;;; tramp-cache.el ends here