Mercurial > hg > xemacs-beta
comparison lisp/packages/auto-save.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 0293115a14e9 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;;; auto-save.el --- safer auto saving with support for ange-ftp and /tmp | |
2 | |
3 (defconst auto-save-version "cvs ate me") | |
4 | |
5 ;;;; Copyright (C) 1992, 1993, 1994 by Sebastian Kremer <sk@thp.uni-koeln.de> | |
6 ;;;; Modified by jwz | |
7 | |
8 ;; This file is part of XEmacs. | |
9 | |
10 ;; XEmacs is free software; you can redistribute it and/or modify it | |
11 ;; under the terms of the GNU General Public License as published by | |
12 ;; the Free Software Foundation; either version 2, or (at your option) | |
13 ;; any later version. | |
14 | |
15 ;; XEmacs is distributed in the hope that it will be useful, but | |
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 ;; General Public License for more details. | |
19 | |
20 ;; You should have received a copy of the GNU General Public License | |
21 ;; along with XEmacs; see the file COPYING. If not, write to the Free | |
22 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | |
24 ;;; Synched up with: Not in FSF. | |
25 | |
26 ;;;; LISPDIR ENTRY for the Elisp Archive =============================== | |
27 ;;;; LCD Archive Entry: | |
28 ;;;; auto-save|Sebastian Kremer|sk@thp.uni-koeln.de | |
29 ;;;; |safer auto saving with support for ange-ftp and /tmp | |
30 | |
31 ;;;; OVERVIEW ========================================================== | |
32 | |
33 ;;;; Combines autosaving for ange-ftp (to a local or remote directory) | |
34 ;;;; with the ability to do autosaves to a fixed directory on a local | |
35 ;;;; disk, in case NFS is slow. The auto-save file used for | |
36 ;;;; /usr/foo/bar/baz.txt | |
37 ;;;; will be | |
38 ;;;; AUTOSAVE/#\!usr\!foo\!bar\!baz.txt# | |
39 ;;;; assuming AUTOSAVE is the non-nil value of the variable | |
40 ;;;; `auto-save-directory'. | |
41 | |
42 ;;;; Takes care that autosave files for non-file-buffers (e.g. *mail*) | |
43 ;;;; from two simultaneous Emacses don't collide. | |
44 | |
45 ;;;; Autosaves even if the current directory is not writable. | |
46 | |
47 ;;;; Can limit autosave names to 14 characters using a hash function, | |
48 ;;;; see `auto-save-hash-p'. | |
49 | |
50 ;;;; See `auto-save-directory' and `make-auto-save-file-name' and | |
51 ;;;; references therein for complete documentation. | |
52 | |
53 ;;;; Meta-x recover-all-files will effectively do recover-file on all | |
54 ;;;; files whose autosave file is newer (one of the benefits of having | |
55 ;;;; all autosave files in the same place). | |
56 | |
57 ;;;; INSTALLATION ====================================================== | |
58 | |
59 ;;;; Put this file into your load-path and the following in your ~/.emacs: | |
60 | |
61 ;;;; If you want to autosave in the fixed directory /tmp/USER-autosave/ | |
62 ;;;; (setq auto-save-directory | |
63 ;;;; (concat "/tmp/" (user-login-name) "-autosave/")) | |
64 | |
65 ;;;; If you don't want to save in /tmp (e.g., because it is swap | |
66 ;;;; mounted) but rather in ~/autosave/ | |
67 ;;;; (setq auto-save-directory (expand-file-name "~/autosave/")) | |
68 | |
69 ;;;; If you want to save each file in its own directory (the default) | |
70 ;;;; (setq auto-save-directory nil) | |
71 ;;;; You still can take advantage of autosaving ange-ftp remote files | |
72 ;;;; in a fixed local directory, `auto-save-directory-fallback' will | |
73 ;;;; be used. | |
74 | |
75 ;;;; If you want to use 14 character hashed autosave filenames | |
76 ;;;; (setq auto-save-hash-p t) | |
77 | |
78 ;;;; Finally, put this line after the others in your ~/.emacs: | |
79 ;;;; (require 'auto-save) | |
80 | |
81 | |
82 ;;;; ACKNOWLEDGEMENT =================================================== | |
83 | |
84 ;;;; This code is loosely derived from autosave-in-tmp.el by Jamie | |
85 ;;;; Zawinski <jwz@lucid.com> (the version I had was last modified 22 | |
86 ;;;; dec 90 jwz) and code submitted to ange-ftp-lovers on Sun, 5 Apr | |
87 ;;;; 92 23:20:47 EDT by drw@BOURBAKI.MIT.EDU (Dale R. Worley). | |
88 ;;;; auto-save.el tries to cover the functionality of those two | |
89 ;;;; packages. | |
90 | |
91 ;;;; Valuable comments and help from Dale Worley, Andy Norman, Jamie | |
92 ;;;; Zawinski and Sandy Rutherford are gratefully acknowledged. | |
93 | |
94 ;;;; CUSTOMIZATION ===================================================== | |
95 | |
96 (defvar auto-save-directory nil | |
97 | |
98 ;;; Don't make this user-variable-p, it should be set in .emacs and | |
99 ;;; left at that. In particular, it should remain constant across | |
100 ;;; several Emacs session to make recover-all-files work. | |
101 | |
102 "If non-nil, fixed directory for autosaving: all autosave files go | |
103 there. If this directory does not yet exist at load time, it is | |
104 created and its mode is set to 0700 so that nobody else can read your | |
105 autosave files. | |
106 | |
107 If nil, each autosave files goes into the same directory as its | |
108 corresponding visited file. | |
109 | |
110 A non-nil `auto-save-directory' could be on a local disk such as in | |
111 /tmp, then auto-saves will always be fast, even if NFS or the | |
112 automounter is slow. In the usual case of /tmp being locally mounted, | |
113 note that if you run emacs on two different machines, they will not | |
114 see each other's auto-save files. | |
115 | |
116 The value \(expand-file-name \"~/autosave/\"\) might be better if /tmp | |
117 is mounted from swap (possible in SunOS, type `df /tmp' to find out) | |
118 and thus vanishes after a reboot, or if your system is particularly | |
119 thorough when cleaning up /tmp, clearing even non-empty subdirectories. | |
120 | |
121 It should never be an ange-ftp remote filename because that would | |
122 defeat `ange-ftp-auto-save-remotely'. | |
123 | |
124 Unless you set `auto-save-hash-p', you shouldn't set this to a | |
125 directory in a filesystem that does not support long filenames, since | |
126 a file named | |
127 | |
128 /home/sk/lib/emacs/lisp/auto-save.el | |
129 | |
130 will have a longish filename like | |
131 | |
132 AUTO-SAVE-DIRECTORY/#\\!home\\!sk\\!lib\\!emacs\\!lisp\\!auto-save.el# | |
133 | |
134 as auto save file. | |
135 | |
136 See also variables `auto-save-directory-fallback', `auto-save-hash-p', | |
137 `ange-ftp-auto-save' and `ange-ftp-auto-save-remotely'.") | |
138 | |
139 (defvar auto-save-hash-p nil | |
140 "If non-nil, hashed autosave names of length 14 are used. | |
141 This is to avoid autosave filenames longer than 14 characters. | |
142 The directory used is `auto-save-hash-directory' regardless of | |
143 `auto-save-directory'. | |
144 Hashing defeats `recover-all-files', you have to recover files | |
145 individually by doing `recover-file'.") | |
146 | |
147 ;;; This defvar is in ange-ftp.el now, but for older versions it | |
148 ;;; doesn't hurt to give it here as well so that loading auto-save.el | |
149 ;;; does not abort. | |
150 (defvar ange-ftp-auto-save 0 | |
151 "If 1, allows ange-ftp files to be auto-saved. | |
152 If 0, suppresses auto-saving of ange-ftp files. | |
153 Don't use any other value.") | |
154 | |
155 (defvar ange-ftp-auto-save-remotely nil | |
156 "*If non-nil, causes the auto-save file for an ange-ftp file to be written in | |
157 the remote directory containing the file, rather than in a local directory. | |
158 | |
159 For remote files, this being true overrides a non-nil | |
160 `auto-save-directory'. Local files are unaffected. | |
161 | |
162 If you want to use this feature, you probably only want to set this | |
163 true in a few buffers, rather than globally. You might want to give | |
164 each buffer its own value using `make-variable-buffer-local'. | |
165 | |
166 See also variable `ange-ftp-auto-save'.") | |
167 | |
168 ;;;; end of customization | |
169 | |
170 | |
171 ;;; Preparations to be done at load time | |
172 | |
173 (defvar auto-save-directory-fallback (expand-file-name "~/autosave/") | |
174 ;; not user-variable-p, see above | |
175 "Directory used for local autosaving of remote files if | |
176 both `auto-save-directory' and `ange-ftp-auto-save-remotely' are nil. | |
177 Also used if a working directory to be used for autosaving is not writable. | |
178 This *must* always be the name of directory that exists or can be | |
179 created by you, never nil.") | |
180 | |
181 (defvar auto-save-hash-directory | |
182 (expand-file-name "hash/" (or auto-save-directory | |
183 auto-save-directory-fallback)) | |
184 "If non-nil, directory used for hashed autosave filenames.") | |
185 | |
186 (defun auto-save-check-directory (var) | |
187 (let ((dir (symbol-value var))) | |
188 (if (null dir) | |
189 nil | |
190 ;; Expand and store back into the variable | |
191 (set var (setq dir (expand-file-name dir))) | |
192 ;; Make sure directory exists | |
193 (if (file-directory-p dir) | |
194 nil | |
195 ;; Else we create and chmod 0700 the directory | |
196 (setq dir (directory-file-name dir)) ; some systems need this | |
197 (if (fboundp 'make-directory) ; V19 or tree dired | |
198 (make-directory dir) | |
199 (call-process "mkdir" nil nil nil dir)) | |
200 ;; This is 1300, aka "d-wx-----T" | |
201 ;; The sticky bit means that you can only delete your own files, | |
202 ;; even if you have write permission in the directory (which is | |
203 ;; moot, since the directory is only writable by owner.) | |
204 (set-file-modes dir (* 7 8 8)))))) | |
205 | |
206 (mapcar (function auto-save-check-directory) | |
207 '(auto-save-directory auto-save-directory-fallback)) | |
208 | |
209 (and auto-save-hash-p | |
210 (auto-save-check-directory 'auto-save-hash-directory)) | |
211 | |
212 | |
213 ;;; Computing an autosave name for a file and vice versa | |
214 | |
215 (defun make-auto-save-file-name ();; redefines files.el | |
216 ;; auto-save-file-name-p need not be redefined. | |
217 | |
218 "Return file name to use for auto-saves of current buffer. | |
219 Does not consider `auto-save-visited-file-name'; that is checked | |
220 before calling this function. | |
221 | |
222 Offers to autosave all files in the same `auto-save-directory'. All | |
223 autosave files can then be recovered at once with function | |
224 `recover-all-files'. | |
225 | |
226 Takes care to make autosave files for files accessed through ange-ftp | |
227 be local files if variable `ange-ftp-auto-save-remotely' is nil. | |
228 | |
229 Takes care of slashes in buffer names to prevent autosave errors. | |
230 | |
231 Uses 14 character autosave names if `auto-save-hash-p' is true. | |
232 | |
233 Autosaves even if the current directory is not writable, using | |
234 directory `auto-save-directory-fallback'. | |
235 | |
236 You can redefine this for customization (he he :-). | |
237 See also function `auto-save-file-name-p'." | |
238 | |
239 ;; We have to be very careful about not signalling an error in this | |
240 ;; function since files.el does not provide for this (e.g. find-file | |
241 ;; would fail for each new file). | |
242 | |
243 (condition-case error-data | |
244 (let* ((file-name (or (and (boundp 'buffer-file-truename) ; From jwz, | |
245 buffer-file-truename) ; for Emacs 19? | |
246 buffer-file-name)) | |
247 ;; So autosavename looks like #%...#, roughly as with the | |
248 ;; old make-auto-save-file-name function. The | |
249 ;; make-temp-name inserts the pid of this Emacs: this | |
250 ;; avoids autosaving from two Emacses into the same file. | |
251 ;; It cannot be recovered automatically then because in | |
252 ;; the next Emacs session (the one after the crash) the | |
253 ;; pid will be different, but file-less buffers like | |
254 ;; *mail* must be recovered manually anyway. | |
255 | |
256 ;; jwz: putting the emacs PID in the auto-save file name is bad | |
257 ;; news, because that defeats auto-save-recovery of *mail* | |
258 ;; buffers -- the (sensible) code in sendmail.el calls | |
259 ;; (make-auto-save-file-name) to determine whether there is | |
260 ;; unsent, auto-saved mail to recover. If that mail came from a | |
261 ;; previous emacs process (far and away the most likely case) | |
262 ;; then this can never succeed as the pid differs. | |
263 ;; (name-prefix (if file-name nil (make-temp-name "#%"))) | |
264 (name-prefix (if file-name nil "#%")) | |
265 | |
266 (save-name (or file-name | |
267 ;; Prevent autosave errors. Buffername | |
268 ;; (to become non-dir part of filename) will | |
269 ;; be unslashified twice. Don't care. | |
270 (auto-save-unslashify-name (buffer-name)))) | |
271 (remote-p (and (stringp file-name) | |
272 (fboundp 'ange-ftp-ftp-path) | |
273 (ange-ftp-ftp-path file-name)))) | |
274 ;; Return the appropriate auto save file name: | |
275 (expand-file-name;; a buffername needs this, a filename not | |
276 (if remote-p | |
277 (if ange-ftp-auto-save-remotely | |
278 (auto-save-name-in-same-directory save-name) | |
279 ;; We have to use the `fixed-directory' now since the | |
280 ;; `same-directory' would be remote. | |
281 ;; It will use the fallback if needed. | |
282 (auto-save-name-in-fixed-directory save-name)) | |
283 ;; Else it is a local file (or a buffer without a file, hence | |
284 ;; the name-prefix). | |
285 ;; Hashed files always go into the special hash dir, never | |
286 ;; in the same directory, to make recognizing reliable. | |
287 (if (or auto-save-directory auto-save-hash-p) | |
288 (auto-save-name-in-fixed-directory save-name name-prefix) | |
289 (auto-save-name-in-same-directory save-name name-prefix))))) | |
290 | |
291 ;; If any error occurs in the above code, return what the old | |
292 ;; version of this function would have done. It is not ok to | |
293 ;; return nil, e.g., when after-find-file tests | |
294 ;; file-newer-than-file-p, nil would bomb. | |
295 | |
296 (error (progn | |
297 (message "make-auto-save-file-name %s" error-data) | |
298 (sit-for 2) | |
299 (if buffer-file-name | |
300 (concat (file-name-directory buffer-file-name) | |
301 "#" | |
302 (file-name-nondirectory buffer-file-name) | |
303 "#") | |
304 (expand-file-name (concat "#%" (buffer-name) "#"))))))) | |
305 | |
306 (defun auto-save-original-name (savename) | |
307 "Reverse of `make-auto-save-file-name'. | |
308 Returns nil if SAVENAME was not associated with a file (e.g., it came | |
309 from an autosaved `*mail*' buffer) or does not appear to be an | |
310 autosave file at all. | |
311 Hashed files are not understood, see `auto-save-hash-p'." | |
312 (let ((basename (file-name-nondirectory savename)) | |
313 (savedir (file-name-directory savename))) | |
314 (cond ((or (not (auto-save-file-name-p basename)) | |
315 (string-match "^#%" basename)) | |
316 nil) | |
317 ;; now we know it looks like #...# thus substring is safe to use | |
318 ((or (equal savedir auto-save-directory) ; 2nd arg may be nil | |
319 (equal savedir auto-save-directory-fallback)) | |
320 ;; it is of the `-fixed-directory' type | |
321 (auto-save-slashify-name (substring basename 1 -1))) | |
322 (t | |
323 ;; else it is of `-same-directory' type | |
324 (concat savedir (substring basename 1 -1)))))) | |
325 | |
326 (defun auto-save-name-in-fixed-directory (filename &optional prefix) | |
327 ;; Unslashify and enclose the whole FILENAME in `#' to make an auto | |
328 ;; save file in the auto-save-directory, or if that is nil, in | |
329 ;; auto-save-directory-fallback (which must be the name of an | |
330 ;; existing directory). If the results would be too long for 14 | |
331 ;; character filenames, and `auto-save-hash-p' is set, hash FILENAME | |
332 ;; into a shorter name. | |
333 ;; Optional PREFIX is string to use instead of "#" to prefix name. | |
334 (let ((base-name (concat (or prefix "#") | |
335 (auto-save-unslashify-name filename) | |
336 "#"))) | |
337 (if (and auto-save-hash-p | |
338 auto-save-hash-directory | |
339 (> (length base-name) 14)) | |
340 (expand-file-name (auto-save-cyclic-hash-14 filename) | |
341 auto-save-hash-directory) | |
342 (expand-file-name base-name | |
343 (or auto-save-directory | |
344 auto-save-directory-fallback))))) | |
345 | |
346 (defun auto-save-name-in-same-directory (filename &optional prefix) | |
347 ;; Enclose the non-directory part of FILENAME in `#' to make an auto | |
348 ;; save file in the same directory as FILENAME. But if this | |
349 ;; directory is not writable, use auto-save-directory-fallback. | |
350 ;; FILENAME is assumed to be in non-directory form (no trailing slash). | |
351 ;; It may be a name without a directory part (pesumably it really | |
352 ;; comes from a buffer name then), the fallback is used then. | |
353 ;; Optional PREFIX is string to use instead of "#" to prefix name. | |
354 (let ((directory (file-name-directory filename))) | |
355 (or (null directory) | |
356 (file-writable-p directory) | |
357 (setq directory auto-save-directory-fallback)) | |
358 (concat directory ; (concat nil) is "" | |
359 (or prefix "#") | |
360 (file-name-nondirectory filename) | |
361 "#"))) | |
362 | |
363 (defun auto-save-unslashify-name (s) | |
364 ;; "Quote any slashes in string S by replacing them with the two | |
365 ;;characters `\\!'. | |
366 ;;Also, replace any backslash by double backslash, to make it one-to-one." | |
367 (let ((limit 0)) | |
368 (while (string-match "[/\\]" s limit) | |
369 (setq s (concat (substring s 0 (match-beginning 0)) | |
370 (if (string= (substring s | |
371 (match-beginning 0) | |
372 (match-end 0)) | |
373 "/") | |
374 "\\!" | |
375 "\\\\") | |
376 (substring s (match-end 0)))) | |
377 (setq limit (1+ (match-end 0))))) | |
378 s) | |
379 | |
380 (defun auto-save-slashify-name (s) | |
381 ;;"Reverse of `auto-save-unslashify-name'." | |
382 (let (pos) | |
383 (while (setq pos (string-match "\\\\[\\!]" s pos)) | |
384 (setq s (concat (substring s 0 pos) | |
385 (if (eq ?! (aref s (1+ pos))) "/" "\\") | |
386 (substring s (+ pos 2))) | |
387 pos (1+ pos)))) | |
388 s) | |
389 | |
390 | |
391 ;;; Hashing for autosave names | |
392 | |
393 ;;; Hashing function contributed by Andy Norman <ange@hplb.hpl.hp.com> | |
394 ;;; based upon C code from pot@fly.cnuce.cnr.IT (Francesco Potorti`). | |
395 | |
396 (defun auto-save-cyclic-hash-14 (s) | |
397 ;; "Hash string S into a string of length 14. | |
398 ;; The resulting string consists of hexadecimal digits [0-9a-f]. | |
399 ;; In particular, it contains no slash, so it can be used as autosave name." | |
400 (let ((crc (make-string 8 0)) | |
401 result) | |
402 (mapcar | |
403 (function | |
404 (lambda (new) | |
405 (setq new (+ new (aref crc 7))) | |
406 (aset crc 7 (aref crc 6)) | |
407 (aset crc 6 (+ (aref crc 5) new)) | |
408 (aset crc 5 (aref crc 4)) | |
409 (aset crc 4 (aref crc 3)) | |
410 (aset crc 3 (+ (aref crc 2) new)) | |
411 (aset crc 2 (aref crc 1)) | |
412 (aset crc 1 (aref crc 0)) | |
413 (aset crc 0 new))) | |
414 s) | |
415 (setq result (format "%02x%02x%02x%02x%02x%02x%02x" | |
416 (aref crc 0) | |
417 (aref crc 1) | |
418 (aref crc 2) | |
419 (aref crc 3) | |
420 (aref crc 4) | |
421 (aref crc 5) | |
422 (aref crc 6) | |
423 (aref crc 7))) | |
424 result)) | |
425 | |
426 | |
427 | |
428 ;;; Recovering files | |
429 | |
430 ;; jwz: changed this to also offer to recover auto-saved buffers which | |
431 ;; had no associated file name (such as sendmail buffers.) | |
432 (defun recover-all-files () | |
433 "Do recover-file for all autosave files which are current. | |
434 Only works if you have a non-nil `auto-save-directory'. | |
435 Hashed files (see `auto-save-hash-p') are not understood, use | |
436 `recover-file' to recover them individually." | |
437 (interactive) | |
438 (let ((savefiles (directory-files auto-save-directory t "^#")) | |
439 afile ; the auto save file | |
440 file ; its original file | |
441 (total 0) ; # of files offered to recover | |
442 (count 0)) ; # of files actually recovered | |
443 (or (equal auto-save-directory auto-save-directory-fallback) | |
444 (setq savefiles | |
445 (append savefiles | |
446 (directory-files auto-save-directory-fallback t "^#")))) | |
447 (while savefiles | |
448 (setq afile (car savefiles) | |
449 file (auto-save-original-name afile) | |
450 savefiles (cdr savefiles)) | |
451 (cond ((and file (not (file-newer-than-file-p afile file))) | |
452 (message "autosave file \"%s\" is not current." afile) | |
453 (sit-for 2)) | |
454 (t | |
455 (setq total (1+ total)) | |
456 (with-output-to-temp-buffer "*Directory*" | |
457 (apply 'call-process "ls" nil standard-output nil | |
458 "-l" afile (if file (list file)))) | |
459 (if (yes-or-no-p (format "Recover %s from auto save file? " | |
460 (or file "non-file buffer"))) | |
461 (let* ((obuf (current-buffer)) | |
462 (buf (set-buffer | |
463 (if file | |
464 (find-file-noselect file t) | |
465 (generate-new-buffer "*recovered*")))) | |
466 (buffer-read-only nil)) | |
467 (erase-buffer) | |
468 (insert-file-contents afile nil) | |
469 (condition-case () | |
470 (after-find-file nil) | |
471 (error nil)) | |
472 (setq buffer-auto-save-file-name nil) | |
473 (setq count (1+ count)) | |
474 (message "\ | |
475 Auto-save off in buffer \"%s\" till you do M-x auto-save-mode." | |
476 (buffer-name)) | |
477 (set-buffer obuf) | |
478 (sit-for 1)))))) | |
479 (if (zerop total) | |
480 (message "Nothing to recover.") | |
481 (message "%d/%d file%s recovered." count total (if (= count 1) "" "s")))) | |
482 (if (get-buffer "*Directory*") (kill-buffer "*Directory*"))) | |
483 | |
484 (provide 'auto-save) |