1 ;;; auto-save.el -- Safer autosaving for EFS and tmp.
3 ;; Copyright (C) 1997 Free Software Foundation, Inc.
4 ;; Copyright (C) 1992 by Sebastian Kremer <sk@thp.uni-koeln.de>
5 ;; Copyright (C) 2001 Ben Wing.
7 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
8 ;; Maintainer: XEmacs Development Team
9 ;; Keywords: extensions, dumped
12 ;; XEmacs is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 1, or (at your option)
17 ;; XEmacs is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with XEmacs; see the file COPYING. If not, write to the Free
24 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 ;;; Synched up with: Not in FSF
31 ;; This file is dumped with XEmacs.
33 ;; Combines autosaving for efs (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
38 ;; AUTOSAVE/#=2Fusr=2Ffoo=2Fbar=2Fbaz.txt#"
39 ;; assuming AUTOSAVE is the non-nil value of the variable
40 ;; `auto-save-directory'.
42 ;; Autosaves even if the current directory is not writable.
44 ;; Can limit autosave names to 14 characters using a hash function,
45 ;; see `auto-save-hash-p'.
47 ;; See `auto-save-directory' and `make-auto-save-file-name' and
48 ;; references therein for complete documentation.
50 ;; `M-x recover-all-files' will effectively do recover-file on all
51 ;; files whose autosave file is newer (one of the benefits of having
52 ;; all autosave files in the same place).
54 ;; This file is dumped with XEmacs.
56 ;; If you want to autosave in the fixed directory /tmp/USER-autosave/
57 ;; (setq auto-save-directory
58 ;; (concat "/tmp/" (user-login-name) "-autosave/"))
60 ;; If you don't want to save in /tmp (e.g., because it is swap
61 ;; mounted) but rather in ~/.autosave/
62 ;; (setq auto-save-directory (expand-file-name "~/.autosave/"))
64 ;; If you want to save each file in its own directory (the default)
65 ;; (setq auto-save-directory nil)
66 ;; You still can take advantage of autosaving efs remote files
67 ;; in a fixed local directory, `auto-save-directory-fallback' will
70 ;; If you want to use 14 character hashed autosave filenames
71 ;; (setq auto-save-hash-p t)
73 ;; Finally, put this line after the others in your ~/.emacs:
74 ;; (require 'auto-save)
79 ;; This code is loosely derived from autosave-in-tmp.el by Jamie
80 ;; Zawinski <jwz@jwz.org> (the version I had was last modified 22
81 ;; dec 90 jwz) and code submitted to ange-ftp-lovers on Sun, 5 Apr
82 ;; 92 23:20:47 EDT by drw@BOURBAKI.MIT.EDU (Dale R. Worley).
83 ;; auto-save.el tries to cover the functionality of those two
86 ;; Valuable comments and help from Dale Worley, Andy Norman, Jamie
87 ;; Zawinski and Sandy Rutherford are gratefully acknowledged.
89 (defconst auto-save-version "1.26"
90 "Version number of auto-save.")
97 (defgroup auto-save nil
98 "Autosaving with support for efs and /tmp."
101 (put 'auto-save-interval 'custom-type 'integer)
102 (put 'auto-save-interval 'factory-value '(300))
103 (custom-add-to-group 'auto-save 'auto-save-interval 'custom-variable)
105 (defcustom auto-save-directory nil
107 ;; Don't make this user-variable-p, it should be set in .emacs and
108 ;; left at that. In particular, it should remain constant across
109 ;; several Emacs session to make recover-all-files work.
111 ;; However, it's OK for it to be customizable, as most of the
112 ;; customizable variables are set at the time `.emacs' is read.
115 "If non-nil, fixed directory for autosaving: all autosave files go
116 there. If this directory does not yet exist at load time, it is
117 created and its mode is set to 0700 so that nobody else can read your
120 If nil, each autosave files goes into the same directory as its
121 corresponding visited file.
123 A non-nil `auto-save-directory' could be on a local disk such as in
124 /tmp, then auto-saves will always be fast, even if NFS or the
125 automounter is slow. In the usual case of /tmp being locally mounted,
126 note that if you run emacs on two different machines, they will not
127 see each other's auto-save files.
129 The value \(expand-file-name \"~/.autosave/\"\) might be better if /tmp
130 is mounted from swap (possible in SunOS, type `df /tmp' to find out)
131 and thus vanishes after a reboot, or if your system is particularly
132 thorough when cleaning up /tmp, clearing even non-empty subdirectories.
134 It should never be an efs remote filename because that would
135 defeat `efs-auto-save-remotely'.
137 Unless you set `auto-save-hash-p', you shouldn't set this to a
138 directory in a filesystem that does not support long filenames, since
141 /home/sk/lib/emacs/lisp/auto-save.el
143 will have a longish filename like
145 AUTO-SAVE-DIRECTORY/#=2Fhome=2Fsk=2Flib=2Femacs=2Flisp=2Fauto-save.el#
149 See also variables `auto-save-directory-fallback',
150 `efs-auto-save' and `efs-auto-save-remotely'."
151 :type '(choice (const :tag "Same as file" nil)
156 (defcustom auto-save-hash-p nil
157 "If non-nil, hashed autosave names of length 14 are used.
158 This is to avoid autosave filenames longer than 14 characters.
159 The directory used is `auto-save-hash-directory' regardless of
160 `auto-save-directory'.
161 Hashing defeats `recover-all-files', you have to recover files
162 individually by doing `recover-file'."
166 ;;; This defvar is in efs.el now, but doesn't hurt to give it here as
167 ;;; well so that loading first auto-save.el does not abort.
169 ;; #### Now that `auto-save' is dumped, this is looks obnoxious.
170 (or (boundp 'efs-auto-save) (defvar efs-auto-save 0))
171 (or (boundp 'efs-auto-save-remotely) (defvar efs-auto-save-remotely nil))
173 (defcustom auto-save-offer-delete nil
174 "*If non-nil, `recover-all-files' offers to delete autosave files
175 that are out of date or were dismissed for recovering.
176 Special value 'always deletes those files silently."
177 :type '(choice (const :tag "on" t)
178 (const :tag "off" nil)
179 (const :tag "Delete silently" always))
182 ;;;; end of customization
185 ;;; Preparations to be done at load time
187 ;; Do not call expand-file-name! This is evaluated at dump time now!
188 (defvar auto-save-directory-fallback "~/.autosave/"
189 ;; not user-variable-p, see above
190 "Directory used for local autosaving of remote files if
191 both `auto-save-directory' and `efs-auto-save-remotely' are nil.
192 Also used if a working directory to be used for autosaving is not writable.
193 This *must* always be the name of directory that exists or can be
194 created by you, never nil.")
196 (defvar auto-save-hash-directory
197 (expand-file-name "hash/" (or auto-save-directory
198 auto-save-directory-fallback))
199 "If non-nil, directory used for hashed autosave filenames.")
201 (defun auto-save-checked-directory (dir)
202 "Make sure the directory DIR exists and return it expanded if non-nil."
204 (setq dir (expand-file-name dir))
205 ;; Make sure directory exists
206 (unless (file-directory-p dir)
207 ;; Else we create and chmod 0700 the directory
208 (setq dir (directory-file-name dir)) ; some systems need this
210 (set-file-modes dir #o700))
213 ;; This make no sense at dump time
214 ;; (mapc #'auto-save-check-directory
215 ; '(auto-save-directory auto-save-directory-fallback))
217 ;(and auto-save-hash-p
218 ; (auto-save-check-directory 'auto-save-hash-directory))
221 ;;; Computing an autosave name for a file and vice versa
223 (defun make-auto-save-file-name (&optional file-name)
224 "Return file name to use for auto-saves of current buffer.
225 Does not consider `auto-save-visited-file-name'; that is checked
226 before calling this function.
228 Offers to autosave all files in the same `auto-save-directory'. All
229 autosave files can then be recovered at once with function
232 Takes care to make autosave files for files accessed through efs
233 be local files if variable `efs-auto-save-remotely' is nil.
235 Takes care of slashes in buffer names to prevent autosave errors.
237 Takes care that autosave files for buffers not visiting any file (such
238 as `*mail*') from two simultaneous Emacses don't collide by prepending
241 Uses 14 character autosave names if `auto-save-hash-p' is true.
243 Autosaves even if the current directory is not writable, using
244 directory `auto-save-directory-fallback'.
246 You can redefine this for customization (he he :-).
247 See also function `auto-save-file-name-p'."
249 ;; We have to be very careful about not signalling an error in this
250 ;; function since files.el does not provide for this (e.g. find-file
251 ;; would fail for each new file).
253 (setq file-name (or file-name
255 (and buffer-file-name
256 (expand-file-name buffer-file-name))))
257 (condition-case error-data
259 ;; So autosavename looks like #%...#, roughly as with the
260 ;; old make-auto-save-file-name function. The
261 ;; make-temp-name inserts the pid of this Emacs: this
262 ;; avoids autosaving from two Emacses into the same file.
263 ;; It cannot be recovered automatically then because in
264 ;; the next Emacs session (the one after the crash) the
265 ;; pid will be different, but file-less buffers like
266 ;; *mail* must be recovered manually anyway.
268 ;; jwz: putting the emacs PID in the auto-save file name is bad
269 ;; news, because that defeats auto-save-recovery of *mail*
270 ;; buffers -- the (sensible) code in sendmail.el calls
271 ;; (make-auto-save-file-name) to determine whether there is
272 ;; unsent, auto-saved mail to recover. If that mail came from a
273 ;; previous emacs process (far and away the most likely case)
274 ;; then this can never succeed as the pid differs.
275 ;;(name-prefix (if file-name nil (make-temp-name "#%")))
276 (name-prefix (if file-name nil "#%"))
278 (save-name (or file-name
279 ;; Prevent autosave errors. Buffername
280 ;; (to become non-dir part of filename) will
281 ;; be escaped twice. Don't care.
282 (auto-save-escape-name (buffer-name))))
283 (remote-p (and (stringp file-name)
284 (fboundp 'efs-ftp-path)
285 (efs-ftp-path file-name))))
286 ;; Return the appropriate auto save file name:
287 (expand-file-name;; a buffername needs this, a filename not
289 (if efs-auto-save-remotely
290 (auto-save-name-in-same-directory save-name)
291 ;; We have to use the `fixed-directory' now since the
292 ;; `same-directory' would be remote.
293 ;; It will use the fallback if needed.
294 (auto-save-name-in-fixed-directory save-name)))
295 ;; Else it is a local file (or a buffer without a file,
296 ;; hence the name-prefix).
297 ((or auto-save-directory auto-save-hash-p)
298 ;; Hashed files always go into the special hash dir,
299 ;; never in the same directory, to make recognizing
301 (auto-save-name-in-fixed-directory save-name name-prefix))
303 (auto-save-name-in-same-directory save-name name-prefix)))))
305 ;; If any error occurs in the above code, return what the old
306 ;; version of this function would have done. It is not ok to
307 ;; return nil, e.g., when after-find-file tests
308 ;; file-newer-than-file-p, nil would bomb.
310 (error (warn "Error caught in `make-auto-save-file-name':\n%s"
311 (error-message-string error-data))
314 (concat (file-name-directory file-name)
316 (file-name-nondirectory file-name)
319 (concat "#%" (auto-save-escape-name (buffer-name))
321 (if (or (file-writable-p fname)
322 (file-exists-p fname))
324 (expand-file-name (concat "~/"
325 (file-name-nondirectory fname))))))))
327 (defun auto-save-file-name-p (filename)
328 "Return non-nil if FILENAME can be yielded by `make-auto-save-file-name'.
329 FILENAME should lack slashes.
330 You can redefine this for customization."
331 (string-match "\\`#.*#\\'" filename))
333 (defun auto-save-original-name (savename)
334 "Reverse of `make-auto-save-file-name'.
335 Returns nil if SAVENAME was not associated with a file (e.g., it came
336 from an autosaved `*mail*' buffer) or does not appear to be an
337 autosave file at all.
338 Hashed files are not understood, see `auto-save-hash-p'."
339 (let ((basename (file-name-nondirectory savename))
340 (savedir (file-name-directory savename)))
341 (cond ((or (not (auto-save-file-name-p basename))
342 (string-match "^#%" basename))
344 ;; now we know it looks like #...# thus substring is safe to use
346 (and auto-save-directory
347 (expand-file-name auto-save-directory)))
350 (expand-file-name auto-save-directory-fallback)))
351 ;; it is of the `-fixed-directory' type
352 (auto-save-unescape-name (substring basename 1 -1)))
354 ;; else it is of `-same-directory' type
355 (concat savedir (substring basename 1 -1))))))
357 (defun auto-save-name-in-fixed-directory (filename &optional prefix)
358 ;; Escape and enclose the whole FILENAME in `#' to make an auto
359 ;; save file in the auto-save-directory, or if that is nil, in
360 ;; auto-save-directory-fallback (which must be the name of an
361 ;; existing directory). If the results would be too long for 14
362 ;; character filenames, and `auto-save-hash-p' is set, hash FILENAME
363 ;; into a shorter name.
364 ;; Optional PREFIX is string to use instead of "#" to prefix name.
365 (let ((base-name (concat (or prefix "#")
366 (auto-save-escape-name filename)
368 (if (and auto-save-hash-p
369 auto-save-hash-directory
370 (> (length base-name) 14))
371 (expand-file-name (auto-save-cyclic-hash-14 filename)
372 (auto-save-checked-directory auto-save-hash-directory))
373 (expand-file-name base-name
374 (auto-save-checked-directory
375 (or auto-save-directory
376 auto-save-directory-fallback))))))
378 (defun auto-save-name-in-same-directory (filename &optional prefix)
379 ;; Enclose the non-directory part of FILENAME in `#' to make an auto
380 ;; save file in the same directory as FILENAME. But if this
381 ;; directory is not writable, use auto-save-directory-fallback.
382 ;; FILENAME is assumed to be in non-directory form (no trailing slash).
383 ;; It may be a name without a directory part (presumably it really
384 ;; comes from a buffer name then), the fallback is used then.
385 ;; Optional PREFIX is string to use instead of "#" to prefix name.
386 (let ((directory (file-name-directory filename)))
388 (file-writable-p directory)
389 (setq directory (auto-save-checked-directory
390 auto-save-directory-fallback)))
391 (concat directory ; (concat nil) is ""
393 (file-name-nondirectory filename)
396 (defconst auto-save-reserved-chars
398 ?\0 ?\1 ?\2 ?\3 ?\4 ?\5 ?\6 ?\7 ?\10 ?\11 ?\12 ?\13 ?\14 ?\15 ?\16
399 ?\17 ?\20 ?\21 ?\22 ?\23 ?\24 ?\25 ?\26 ?\27 ?\30 ?\31 ?\32 ?\33
400 ?\34 ?\35 ?\36 ?\37 ?\40 ?? ?* ?: ?< ?> ?| ?/ ?\\ ?& ?^ ?% ?= ?\")
401 "List of characters disallowed (or potentially disallowed) in filenames.
402 Includes everything that can get us into trouble under MS Windows or Unix.")
404 ;; This code based on code in Bill Perry's url.el.
406 (defun auto-save-escape-name (str)
407 "Escape any evil nasty characters in a potential filename.
408 Uses quoted-printable-style escaping -- e.g. the dreaded =3D.
409 Does not use URL escaping (with %) because filenames beginning with #% are
410 a special signal for non-file buffers."
414 (if (memq char auto-save-reserved-chars)
416 (upcase (format "=0%x" char))
417 (upcase (format "=%x" char)))
418 (char-to-string char))))
421 (defun auto-save-unhex (x)
428 (defun auto-save-unescape-name (str)
429 "Undo any escaping of evil nasty characters in a file name.
430 See `auto-save-escape-name'."
431 (setq str (or str ""))
433 (case-fold-search t))
434 (while (string-match "=[0-9a-f][0-9a-f]" str)
435 (let* ((start (match-beginning 0))
436 (ch1 (auto-save-unhex (elt str (+ start 1))))
438 (auto-save-unhex (elt str (+ start 2))))))
439 (setq tmp (concat tmp (substring str 0 start)
440 (char-to-string code))
441 str (substring str (match-end 0)))))
442 (setq tmp (concat tmp str))
445 ;; The old versions are below.
447 ;(defun auto-save-escape-name (s)
448 ; ;; "Quote any slashes in string S by replacing them with the two
449 ; ;;characters `\\!'.
450 ; ;;Also, replace any backslash by double backslash, to make it one-to-one."
452 ; (while (string-match "[/\\]" s limit)
453 ; (setq s (concat (substring s 0 (match-beginning 0))
454 ; (if (string= (substring s
455 ; (match-beginning 0)
460 ; (substring s (match-end 0))))
461 ; (setq limit (1+ (match-end 0)))))
464 ;(defun auto-save-unescape-name (s)
465 ; ;;"Reverse of `auto-save-escape-name'."
467 ; (while (setq pos (string-match "\\\\[\\!]" s pos))
468 ; (setq s (concat (substring s 0 pos)
469 ; (if (eq ?! (aref s (1+ pos))) "/" "\\")
470 ; (substring s (+ pos 2)))
475 ;;; Hashing for autosave names
477 ;;; Hashing function contributed by Andy Norman <ange@hplb.hpl.hp.com>
478 ;;; based upon C code from pot@fly.cnuce.cnr.IT (Francesco Potorti`).
480 (defun auto-save-cyclic-hash-14 (s)
481 ;; "Hash string S into a string of length 14.
482 ;; A 7-bytes cyclic code for burst correction is calculated on a
483 ;; byte-by-byte basis. The polynomial used is D^7 + D^6 + D^3 +1.
484 ;; The resulting string consists of hexadecimal digits [0-9a-f].
485 ;; In particular, it contains no slash, so it can be used as autosave name."
486 (let ((crc (make-vector 7 ?\0)))
489 (setq new (+ new (aref crc 6)))
490 (aset crc 6 (+ (aref crc 5) new))
491 (aset crc 5 (aref crc 4))
492 (aset crc 4 (aref crc 3))
493 (aset crc 3 (+ (aref crc 2) new))
494 (aset crc 2 (aref crc 1))
495 (aset crc 1 (aref crc 0))
498 (format "%02x%02x%02x%02x%02x%02x%02x"
499 (logand 255 (aref crc 0))
500 (logand 255 (aref crc 1))
501 (logand 255 (aref crc 2))
502 (logand 255 (aref crc 3))
503 (logand 255 (aref crc 4))
504 (logand 255 (aref crc 5))
505 (logand 255 (aref crc 6)))))
507 ;; #### It is unclear to me how the following function is useful. It
508 ;; should be used in `auto-save-name-in-same-directory', if anywhere.
511 ;; This leaves two characters that could be used to wrap it in `#' or
512 ;; make two filenames from it: one for autosaving, and another for a
513 ;; file containing the name of the autosaved file, to make hashing
515 ;(defun auto-save-cyclic-hash-12 (s)
516 ; "Outputs the 12-characters ascii hex representation of a 6-bytes
517 ;cyclic code for burst correction calculated on STRING on a
518 ;byte-by-byte basis. The used polynomial is D^6 + D^5 + D^4 + D^3 +1."
519 ; (let ((crc (make-string 6 0)))
522 ; (setq new (+ new (aref crc 5)))
523 ; (aset crc 5 (+ (aref crc 4) new))
524 ; (aset crc 4 (+ (aref crc 3) new))
525 ; (aset crc 3 (+ (aref crc 2) new))
526 ; (aset crc 2 (aref crc 1))
527 ; (aset crc 1 (aref crc 0))
530 ; (format "%02x%02x%02x%02x%02x%02x"
542 (defun recover-all-files (&optional silent)
543 "Do recover-file for all autosave files which are current.
544 Only works if you have a non-nil `auto-save-directory'.
546 Optional prefix argument SILENT means to be silent about non-current
547 autosave files. This is useful if invoked automatically at Emacs
550 If `auto-save-offer-delete' is t, this function will offer to delete
551 old or rejected autosave files.
553 Hashed files (see `auto-save-hash-p') are not understood, use
554 `recover-file' to recover them individually."
556 (let ((savefiles (directory-files auto-save-directory
558 afile ; the auto save file
559 file ; its original file
560 (total 0) ; # of files offered to recover
561 (count 0)) ; # of files actually recovered
562 (or (equal (expand-file-name auto-save-directory)
563 (expand-file-name auto-save-directory-fallback))
566 (directory-files auto-save-directory-fallback
569 (setq afile (car savefiles)
570 file (auto-save-original-name afile)
571 savefiles (cdr savefiles))
572 (cond ((and file (not (file-newer-than-file-p afile file)))
573 (warn "Autosave file \"%s\" is not current." afile))
576 (with-output-to-temp-buffer "*Directory*"
577 (buffer-disable-undo standard-output)
579 (set-buffer "*Directory*")
580 (setq default-directory (file-name-directory afile))
581 (insert-directory afile "-l")
583 (setq default-directory (file-name-directory file))
584 (insert-directory file "-l"))))
585 (if (yes-or-no-p (format "Recover %s from auto save file? "
586 (or file "non-file buffer")))
587 (let* ((obuf (current-buffer)))
589 (find-file-noselect file t)
590 (generate-new-buffer "*recovered*")))
591 (setq buffer-read-only nil)
593 (insert-file-contents afile nil)
595 (after-find-file nil))
596 (setq buffer-auto-save-file-name nil)
599 Auto-save off in buffer \"%s\" till you do M-x auto-save-mode."
603 ;; If not used for recovering, offer to delete
605 (and auto-save-offer-delete
606 (or (eq 'always auto-save-offer-delete)
608 (format "Delete autosave file for `%s'? " file)))
609 (delete-file afile))))))
611 (or silent (message "Nothing to recover."))
612 (message "%d/%d file%s recovered." count total (if (= count 1) "" "s"))))
613 (and (get-buffer "*Directory*")
614 (kill-buffer "*Directory*")))
616 ;;; auto-save.el ends here