comparison lisp/auto-save.el @ 464:5aa1854ad537 r21-2-47

Import from CVS: tag r21-2-47
author cvs
date Mon, 13 Aug 2007 11:45:51 +0200
parents 8de8e3f6228a
children 943eaba38521
comparison
equal deleted inserted replaced
463:a158004111cd 464:5aa1854ad537
1 ;;; auto-save.el -- Safer autosaving for EFS and tmp. 1 ;;; auto-save.el -- Safer autosaving for EFS and tmp.
2 2
3 ;; Copyright (C) 1997 Free Software Foundation, Inc. 3 ;; Copyright (C) 1997 Free Software Foundation, Inc.
4 ;; Copyright (C) 1992 by Sebastian Kremer <sk@thp.uni-koeln.de> 4 ;; Copyright (C) 1992 by Sebastian Kremer <sk@thp.uni-koeln.de>
5 ;; Copyright (C) 2001 Ben Wing.
5 6
6 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de> 7 ;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>
7 ;; Maintainer: XEmacs Development Team 8 ;; Maintainer: XEmacs Development Team
8 ;; Keywords: extensions, dumped 9 ;; Keywords: extensions, dumped
9 ;; Version: 1.26 10 ;; Version: 1.26
32 ;; Combines autosaving for efs (to a local or remote directory) 33 ;; Combines autosaving for efs (to a local or remote directory)
33 ;; with the ability to do autosaves to a fixed directory on a local 34 ;; with the ability to do autosaves to a fixed directory on a local
34 ;; disk, in case NFS is slow. The auto-save file used for 35 ;; disk, in case NFS is slow. The auto-save file used for
35 ;; /usr/foo/bar/baz.txt 36 ;; /usr/foo/bar/baz.txt
36 ;; will be 37 ;; will be
37 ;; AUTOSAVE/#\!usr\!foo\!bar\!baz.txt# 38 ;; AUTOSAVE/#=2Fusr=2Ffoo=2Fbar=2Fbaz.txt#"
38 ;; assuming AUTOSAVE is the non-nil value of the variable 39 ;; assuming AUTOSAVE is the non-nil value of the variable
39 ;; `auto-save-directory'. 40 ;; `auto-save-directory'.
40
41 ;; Takes care that autosave files for non-file-buffers (e.g. *mail*)
42 ;; from two simultaneous Emacses don't collide.
43 41
44 ;; Autosaves even if the current directory is not writable. 42 ;; Autosaves even if the current directory is not writable.
45 43
46 ;; Can limit autosave names to 14 characters using a hash function, 44 ;; Can limit autosave names to 14 characters using a hash function,
47 ;; see `auto-save-hash-p'. 45 ;; see `auto-save-hash-p'.
58 ;; If you want to autosave in the fixed directory /tmp/USER-autosave/ 56 ;; If you want to autosave in the fixed directory /tmp/USER-autosave/
59 ;; (setq auto-save-directory 57 ;; (setq auto-save-directory
60 ;; (concat "/tmp/" (user-login-name) "-autosave/")) 58 ;; (concat "/tmp/" (user-login-name) "-autosave/"))
61 59
62 ;; If you don't want to save in /tmp (e.g., because it is swap 60 ;; If you don't want to save in /tmp (e.g., because it is swap
63 ;; mounted) but rather in ~/autosave/ 61 ;; mounted) but rather in ~/.autosave/
64 ;; (setq auto-save-directory (expand-file-name "~/.autosave/")) 62 ;; (setq auto-save-directory (expand-file-name "~/.autosave/"))
65 63
66 ;; If you want to save each file in its own directory (the default) 64 ;; If you want to save each file in its own directory (the default)
67 ;; (setq auto-save-directory nil) 65 ;; (setq auto-save-directory nil)
68 ;; You still can take advantage of autosaving efs remote files 66 ;; You still can take advantage of autosaving efs remote files
142 140
143 /home/sk/lib/emacs/lisp/auto-save.el 141 /home/sk/lib/emacs/lisp/auto-save.el
144 142
145 will have a longish filename like 143 will have a longish filename like
146 144
147 AUTO-SAVE-DIRECTORY/#\\!home\\!sk\\!lib\\!emacs\\!lisp\\!auto-save.el# 145 AUTO-SAVE-DIRECTORY/#=2Fhome=2Fsk=2Flib=2Femacs=2Flisp=2Fauto-save.el#
148 146
149 as auto save file. 147 as auto save file.
150 148
151 See also variables `auto-save-directory-fallback', 149 See also variables `auto-save-directory-fallback',
152 `efs-auto-save' and `efs-auto-save-remotely'." 150 `efs-auto-save' and `efs-auto-save-remotely'."
220 ; (auto-save-check-directory 'auto-save-hash-directory)) 218 ; (auto-save-check-directory 'auto-save-hash-directory))
221 219
222 220
223 ;;; Computing an autosave name for a file and vice versa 221 ;;; Computing an autosave name for a file and vice versa
224 222
225 ;; #### Now that this file is dumped, we should turn off the routine 223 (defun make-auto-save-file-name (&optional file-name)
226 ;; from files.el. But it would make it harder to remove it!
227
228 (defun make-auto-save-file-name (&optional file-name);; redefines files.el
229 ;; auto-save-file-name-p need not be redefined.
230
231 "Return file name to use for auto-saves of current buffer. 224 "Return file name to use for auto-saves of current buffer.
232 Does not consider `auto-save-visited-file-name'; that is checked 225 Does not consider `auto-save-visited-file-name'; that is checked
233 before calling this function. 226 before calling this function.
234 227
235 Offers to autosave all files in the same `auto-save-directory'. All 228 Offers to autosave all files in the same `auto-save-directory'. All
283 (name-prefix (if file-name nil "#%")) 276 (name-prefix (if file-name nil "#%"))
284 277
285 (save-name (or file-name 278 (save-name (or file-name
286 ;; Prevent autosave errors. Buffername 279 ;; Prevent autosave errors. Buffername
287 ;; (to become non-dir part of filename) will 280 ;; (to become non-dir part of filename) will
288 ;; be unslashified twice. Don't care. 281 ;; be escaped twice. Don't care.
289 (auto-save-unslashify-name (buffer-name)))) 282 (auto-save-escape-name (buffer-name))))
290 (remote-p (and (stringp file-name) 283 (remote-p (and (stringp file-name)
291 (fboundp 'efs-ftp-path) 284 (fboundp 'efs-ftp-path)
292 (efs-ftp-path file-name)))) 285 (efs-ftp-path file-name))))
293 ;; Return the appropriate auto save file name: 286 ;; Return the appropriate auto save file name:
294 (expand-file-name;; a buffername needs this, a filename not 287 (expand-file-name;; a buffername needs this, a filename not
314 ;; return nil, e.g., when after-find-file tests 307 ;; return nil, e.g., when after-find-file tests
315 ;; file-newer-than-file-p, nil would bomb. 308 ;; file-newer-than-file-p, nil would bomb.
316 309
317 (error (warn "Error caught in `make-auto-save-file-name':\n%s" 310 (error (warn "Error caught in `make-auto-save-file-name':\n%s"
318 (error-message-string error-data)) 311 (error-message-string error-data))
319 (if buffer-file-name 312 (let ((fname
320 (concat (file-name-directory buffer-file-name) 313 (if file-name
321 "#" 314 (concat (file-name-directory file-name)
322 (file-name-nondirectory buffer-file-name) 315 "#"
323 "#") 316 (file-name-nondirectory file-name)
324 (expand-file-name (concat "#%" (buffer-name) "#")))))) 317 "#")
318 (expand-file-name
319 (concat "#%" (auto-save-escape-name (buffer-name))
320 "#")))))
321 (if (or (file-writable-p fname)
322 (file-exists-p fname))
323 fname
324 (expand-file-name (concat "~/"
325 (file-name-nondirectory fname))))))))
326
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))
325 332
326 (defun auto-save-original-name (savename) 333 (defun auto-save-original-name (savename)
327 "Reverse of `make-auto-save-file-name'. 334 "Reverse of `make-auto-save-file-name'.
328 Returns nil if SAVENAME was not associated with a file (e.g., it came 335 Returns nil if SAVENAME was not associated with a file (e.g., it came
329 from an autosaved `*mail*' buffer) or does not appear to be an 336 from an autosaved `*mail*' buffer) or does not appear to be an
340 (expand-file-name auto-save-directory))) 347 (expand-file-name auto-save-directory)))
341 ; 2nd arg may be nil 348 ; 2nd arg may be nil
342 (equal savedir 349 (equal savedir
343 (expand-file-name auto-save-directory-fallback))) 350 (expand-file-name auto-save-directory-fallback)))
344 ;; it is of the `-fixed-directory' type 351 ;; it is of the `-fixed-directory' type
345 (auto-save-slashify-name (substring basename 1 -1))) 352 (auto-save-unescape-name (substring basename 1 -1)))
346 (t 353 (t
347 ;; else it is of `-same-directory' type 354 ;; else it is of `-same-directory' type
348 (concat savedir (substring basename 1 -1)))))) 355 (concat savedir (substring basename 1 -1))))))
349 356
350 (defun auto-save-name-in-fixed-directory (filename &optional prefix) 357 (defun auto-save-name-in-fixed-directory (filename &optional prefix)
351 ;; Unslashify and enclose the whole FILENAME in `#' to make an auto 358 ;; Escape and enclose the whole FILENAME in `#' to make an auto
352 ;; save file in the auto-save-directory, or if that is nil, in 359 ;; save file in the auto-save-directory, or if that is nil, in
353 ;; auto-save-directory-fallback (which must be the name of an 360 ;; auto-save-directory-fallback (which must be the name of an
354 ;; existing directory). If the results would be too long for 14 361 ;; existing directory). If the results would be too long for 14
355 ;; character filenames, and `auto-save-hash-p' is set, hash FILENAME 362 ;; character filenames, and `auto-save-hash-p' is set, hash FILENAME
356 ;; into a shorter name. 363 ;; into a shorter name.
357 ;; Optional PREFIX is string to use instead of "#" to prefix name. 364 ;; Optional PREFIX is string to use instead of "#" to prefix name.
358 (let ((base-name (concat (or prefix "#") 365 (let ((base-name (concat (or prefix "#")
359 (auto-save-unslashify-name filename) 366 (auto-save-escape-name filename)
360 "#"))) 367 "#")))
361 (if (and auto-save-hash-p 368 (if (and auto-save-hash-p
362 auto-save-hash-directory 369 auto-save-hash-directory
363 (> (length base-name) 14)) 370 (> (length base-name) 14))
364 (expand-file-name (auto-save-cyclic-hash-14 filename) 371 (expand-file-name (auto-save-cyclic-hash-14 filename)
384 (concat directory ; (concat nil) is "" 391 (concat directory ; (concat nil) is ""
385 (or prefix "#") 392 (or prefix "#")
386 (file-name-nondirectory filename) 393 (file-name-nondirectory filename)
387 "#"))) 394 "#")))
388 395
389 ;; #### The following two should probably use `replace-in-string'. 396 (defconst auto-save-reserved-chars
390 397 '(
391 (defun auto-save-unslashify-name (s) 398 ?\0 ?\1 ?\2 ?\3 ?\4 ?\5 ?\6 ?\7 ?\10 ?\11 ?\12 ?\13 ?\14 ?\15 ?\16
392 ;; "Quote any slashes in string S by replacing them with the two 399 ?\17 ?\20 ?\21 ?\22 ?\23 ?\24 ?\25 ?\26 ?\27 ?\30 ?\31 ?\32 ?\33
393 ;;characters `\\!'. 400 ?\34 ?\35 ?\36 ?\37 ?\40 ?? ?* ?: ?< ?> ?| ?/ ?\\ ?& ?^ ?% ?= ?\")
394 ;;Also, replace any backslash by double backslash, to make it one-to-one." 401 "List of characters disallowed (or potentially disallowed) in filenames.
395 (let ((limit 0)) 402 Includes everything that can get us into trouble under MS Windows or Unix.")
396 (while (string-match "[/\\]" s limit) 403
397 (setq s (concat (substring s 0 (match-beginning 0)) 404 ;; This code based on code in Bill Perry's url.el.
398 (if (string= (substring s 405
399 (match-beginning 0) 406 (defun auto-save-escape-name (str)
400 (match-end 0)) 407 "Escape any evil nasty characters in a potential filename.
401 "/") 408 Uses quoted-printable-style escaping -- e.g. the dreaded =3D.
402 "\\!" 409 Does not use URL escaping (with %) because filenames beginning with #% are
403 "\\\\") 410 a special signal for non-file buffers."
404 (substring s (match-end 0)))) 411 (mapconcat
405 (setq limit (1+ (match-end 0))))) 412 (function
406 s) 413 (lambda (char)
407 414 (if (memq char auto-save-reserved-chars)
408 (defun auto-save-slashify-name (s) 415 (if (< char 16)
409 ;;"Reverse of `auto-save-unslashify-name'." 416 (upcase (format "=0%x" char))
410 (let (pos) 417 (upcase (format "=%x" char)))
411 (while (setq pos (string-match "\\\\[\\!]" s pos)) 418 (char-to-string char))))
412 (setq s (concat (substring s 0 pos) 419 str ""))
413 (if (eq ?! (aref s (1+ pos))) "/" "\\") 420
414 (substring s (+ pos 2))) 421 (defun auto-save-unhex (x)
415 pos (1+ pos)))) 422 (if (> x ?9)
416 s) 423 (if (>= x ?a)
424 (+ 10 (- x ?a))
425 (+ 10 (- x ?A)))
426 (- x ?0)))
427
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 ""))
432 (let ((tmp "")
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))))
437 (code (+ (* 16 ch1)
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))
443 tmp))
444
445 ;; The old versions are below.
446
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."
451 ; (let ((limit 0))
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)
456 ; (match-end 0))
457 ; "/")
458 ; "\\!"
459 ; "\\\\")
460 ; (substring s (match-end 0))))
461 ; (setq limit (1+ (match-end 0)))))
462 ; s)
463
464 ;(defun auto-save-unescape-name (s)
465 ; ;;"Reverse of `auto-save-escape-name'."
466 ; (let (pos)
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)))
471 ; pos (1+ pos))))
472 ; s)
417 473
418 474
419 ;;; Hashing for autosave names 475 ;;; Hashing for autosave names
420 476
421 ;;; Hashing function contributed by Andy Norman <ange@hplb.hpl.hp.com> 477 ;;; Hashing function contributed by Andy Norman <ange@hplb.hpl.hp.com>
452 ;; should be used in `auto-save-name-in-same-directory', if anywhere. 508 ;; should be used in `auto-save-name-in-same-directory', if anywhere.
453 ;; -hniksic 509 ;; -hniksic
454 510
455 ;; This leaves two characters that could be used to wrap it in `#' or 511 ;; This leaves two characters that could be used to wrap it in `#' or
456 ;; make two filenames from it: one for autosaving, and another for a 512 ;; make two filenames from it: one for autosaving, and another for a
457 ;; file containing the name of the autosaved filed, to make hashing 513 ;; file containing the name of the autosaved file, to make hashing
458 ;; reversible. 514 ;; reversible.
459 ;(defun auto-save-cyclic-hash-12 (s) 515 ;(defun auto-save-cyclic-hash-12 (s)
460 ; "Outputs the 12-characters ascii hex representation of a 6-bytes 516 ; "Outputs the 12-characters ascii hex representation of a 6-bytes
461 ;cyclic code for burst correction calculated on STRING on a 517 ;cyclic code for burst correction calculated on STRING on a
462 ;byte-by-byte basis. The used polynomial is D^6 + D^5 + D^4 + D^3 +1." 518 ;byte-by-byte basis. The used polynomial is D^6 + D^5 + D^4 + D^3 +1."
516 (cond ((and file (not (file-newer-than-file-p afile file))) 572 (cond ((and file (not (file-newer-than-file-p afile file)))
517 (warn "Autosave file \"%s\" is not current." afile)) 573 (warn "Autosave file \"%s\" is not current." afile))
518 (t 574 (t
519 (incf total) 575 (incf total)
520 (with-output-to-temp-buffer "*Directory*" 576 (with-output-to-temp-buffer "*Directory*"
521 (apply 'call-process "ls" nil standard-output nil 577 (buffer-disable-undo standard-output)
522 "-l" afile (if file (list file)))) 578 (save-excursion
579 (set-buffer "*Directory*")
580 (setq default-directory (file-name-directory afile))
581 (insert-directory afile "-l")
582 (when file
583 (setq default-directory (file-name-directory file))
584 (insert-directory file "-l"))))
523 (if (yes-or-no-p (format "Recover %s from auto save file? " 585 (if (yes-or-no-p (format "Recover %s from auto save file? "
524 (or file "non-file buffer"))) 586 (or file "non-file buffer")))
525 (let* ((obuf (current-buffer))) 587 (let* ((obuf (current-buffer)))
526 (set-buffer (if file 588 (set-buffer (if file
527 (find-file-noselect file t) 589 (find-file-noselect file t)