]> code.delx.au - gnu-emacs/blobdiff - lisp/files.el
(substitute-key-definition): Add comment describing
[gnu-emacs] / lisp / files.el
index 8873fadb2fc2b555d45e3a0328db4fd5ba1e0d13..daf1341357d28a4c5e39e8bba75f473839f6b608 100644 (file)
@@ -134,11 +134,23 @@ This variable is relevant only if `backup-by-copying' and
   :group 'backup)
 
 (defvar backup-enable-predicate
-  '(lambda (name)
-     (or (< (length name) 5)
-        (not (string-equal "/tmp/" (substring name 0 5)))))
+  (lambda (name)
+    (not (or (let ((comp (compare-strings temporary-file-directory 0 nil
+                                     name 0 nil)))
+              ;; Directory is under temporary-file-directory.
+              (and (not (eq comp t))
+                   (< comp -1)))
+            (if small-temporary-file-directory
+                (let ((comp (compare-strings small-temporary-file-directory
+                                             0 nil
+                                             name 0 nil)))
+                  ;; Directory is under small-temporary-file-directory.
+                  (and (not (eq comp t))
+                       (< comp -1)))))))
   "Predicate that looks at a file name and decides whether to make backups.
-Called with an absolute file name as argument, it returns t to enable backup.")
+Called with an absolute file name as argument, it returns t to enable backup.
+The default version checks for files in `temporary-file-directory' or
+`small-temporary-file-directory'.")
 
 (defcustom buffer-offer-save nil
   "*Non-nil in a buffer means always offer to save buffer on exit.
@@ -267,6 +279,23 @@ Normally auto-save files are written under other names."
   :type 'boolean
   :group 'auto-save)
 
+(defcustom auto-save-file-name-transforms
+  '(("\\`/[^/]*:\\(.+/\\)*\\(.*\\)" "/tmp/\\2"))
+  "*Transforms to apply to buffer file name before making auto-save file name.
+Each transform is a list (REGEXP REPLACEMENT):
+REGEXP is a regular expression to match against the file name.
+If it matches, `replace-match' is used to replace the
+matching part with REPLACEMENT.
+All the transforms in the list are tried, in the order they are listed.
+When one transform applies, its result is final;
+no further transforms are tried.
+
+The default value is set up to put the auto-save file into `/tmp'
+for editing a remote file."
+  :group 'auto-save
+  :type '(repeat (list (string :tag "Regexp") (string :tag "Replacement")))
+  :version "21.1")
+
 (defcustom save-abbrevs nil
   "*Non-nil means save word abbrevs too when files are saved.
 Loading an abbrev file sets this to t."
@@ -724,7 +753,7 @@ expand wildcards (if any) and visit multiple files."
 
 (defun find-file-read-only (filename &optional wildcards)
   "Edit file FILENAME but don't allow changes.
-Like \\[find-file] but marks buffer as read-only.
+Like `find-file' but marks buffer as read-only.
 Use \\[toggle-read-only] to permit editing."
   (interactive "fFind file read-only: \np")
   (find-file filename wildcards)
@@ -1405,7 +1434,9 @@ in that case, this function acts as if `enable-local-variables' were t."
     ("\\.sm2$" . snmpv2-mode)
     ("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode)
     ("\\.[eE]?[pP][sS]$" . ps-mode)
-    ("configure\\.in\\'" . autoconf-mode))
+    ("configure\\.in\\'" . autoconf-mode)
+    ("BROWSE\\'" . ebrowse-tree-mode)
+    ("\\.ebrowse\\'" . ebrowse-tree-mode))
   "\
 Alist of filename patterns vs corresponding major mode functions.
 Each element looks like (REGEXP . FUNCTION) or (REGEXP FUNCTION NON-NIL).
@@ -1571,10 +1602,9 @@ and we don't even do that unless it would come from the file name."
                  (if (string-match (car (car alist)) name)
                      (if (and (consp (cdr (car alist)))
                               (nth 2 (car alist)))
-                         (progn
-                           (setq mode (car (cdr (car alist)))
-                                 name (substring name 0 (match-beginning 0))
-                                 keep-going t))
+                         (setq mode (car (cdr (car alist)))
+                               name (substring name 0 (match-beginning 0))
+                               keep-going t)
                        (setq mode (cdr (car alist))
                              keep-going nil)))
                  (setq alist (cdr alist))))
@@ -1593,9 +1623,9 @@ and we don't even do that unless it would come from the file name."
                (let ((interpreter
                       (save-excursion
                         (goto-char (point-min))
-                        (if (looking-at "#![ \t]?\\([^ \t\n]*/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
-                            (buffer-substring (match-beginning 2)
-                                              (match-end 2))
+                        (if (looking-at "#![ \t]?\\([^ \t\n]*\
+/bin/env[ \t]\\)?\\([^ \t\n]+\\)")
+                            (match-string 2)
                           "")))
                      elt)
                  ;; Map interpreter name to a mode.
@@ -2173,19 +2203,94 @@ the value is \"\"."
         (if period
             "")))))
 
+(defcustom make-backup-file-name-function nil
+  "A function to use instead of the default `make-backup-file-name'.
+A value of nil gives the default `make-backup-file-name' behaviour.
+
+This could be buffer-local to do something special for for specific
+files.  If you define it, you may need to change `backup-file-name-p'
+and `file-name-sans-versions' too.
+
+See also `backup-directory-alist'."
+  :group 'backup
+  :type '(choice (const :tag "Default" nil)
+                (function :tag "Your function")))
+
+(defcustom backup-directory-alist nil
+  "Alist of filename patterns and backup directory names.
+Each element looks like (REGEXP . DIRECTORY).  Backups of files with
+names matching REGEXP will be made in DIRECTORY.  DIRECTORY may be
+relative or absolute.  If it is absolute, so that all matching files
+are backed up into the same directory, the file names in this
+directory will be the full name of the file backed up with all
+directory separators changed to `|' to prevent clashes.  This will not
+work correctly if your filesystem truncates the resulting name.
+
+For the common case of all backups going into one directory, the alist
+should contain a single element pairing \".\" with the appropriate
+directory name.
+
+If this variable is nil, or it fails to match a filename, the backup
+is made in the original file's directory.
+
+On MS-DOS filesystems without long names this variable is always
+ignored."
+  :group 'backup
+  :type '(repeat (cons (regexp :tag "Regexp macthing filename")
+                      (directory :tag "Backup directory name"))))
+
 (defun make-backup-file-name (file)
   "Create the non-numeric backup file name for FILE.
-This is a separate function so you can redefine it for customization."
-  (if (and (eq system-type 'ms-dos)
-          (not (msdos-long-file-names)))
-      (let ((fn (file-name-nondirectory file)))
-       (concat (file-name-directory file)
-               (or
-                (and (string-match "\\`[^.]+\\'" fn)
-                     (concat (match-string 0 fn) ".~"))
-                (and (string-match "\\`[^.]+\\.\\(..?\\)?" fn)
-                     (concat (match-string 0 fn) "~")))))
-    (concat file "~")))
+Normally this will just be the file's name with `~' appended.
+Customization hooks are provided as follows.
+
+If the variable `make-backup-file-name-function' is non-nil, its value
+should be a function which will be called with FILE as its argument;
+the resulting name is used.
+
+Otherwise a match for FILE is sought in `backup-directory-alist'; see
+the documentation of that variable.  If the directory for the backup
+doesn't exist, it is created."
+  (if make-backup-file-name-function
+      (funcall make-backup-file-name-function file)
+    (if (and (eq system-type 'ms-dos)
+            (not (msdos-long-file-names)))
+       (let ((fn (file-name-nondirectory file)))
+         (concat (file-name-directory file)
+                 (or (and (string-match "\\`[^.]+\\'" fn)
+                          (concat (match-string 0 fn) ".~"))
+                     (and (string-match "\\`[^.]+\\.\\(..?\\)?" fn)
+                          (concat (match-string 0 fn) "~")))))
+      (concat (make-backup-file-name-1 file) "~"))))
+
+(defun make-backup-file-name-1 (file)
+  "Subroutine of `make-backup-file-name' and `find-backup-file-name'."
+  (let ((alist backup-directory-alist)
+       elt backup-directory)
+    (while alist
+      (setq elt (pop alist))
+      (if (string-match (car elt) file)
+         (setq backup-directory (cdr elt)
+               alist nil)))
+    (if (null backup-directory)
+       file
+      (unless (file-exists-p backup-directory)
+       (condition-case nil
+           (make-directory backup-directory 'parents)
+         (file-error file)))
+      (if (file-name-absolute-p backup-directory)
+         ;; Make the name unique by substituting directory
+         ;; separators.  It may not really be worth bothering about
+         ;; doubling `|'s in the original name...
+         (expand-file-name
+          (subst-char-in-string
+           directory-sep-char ?|
+           (replace-regexp-in-string "|" "||" file))
+          backup-directory)
+       (expand-file-name (file-name-nondirectory file)
+                         (file-name-as-directory
+                          (expand-file-name backup-directory
+                                            (file-name-directory file))))))))
 
 (defun backup-file-name-p (file)
   "Return non-nil if FILE is a backup file name (numeric or not).
@@ -2212,45 +2317,47 @@ the index in the name where the version number begins."
 (defun find-backup-file-name (fn)
   "Find a file name for a backup file FN, and suggestions for deletions.
 Value is a list whose car is the name for the backup file
- and whose cdr is a list of old versions to consider deleting now.
-If the value is nil, don't make a backup."
+and whose cdr is a list of old versions to consider deleting now.
+If the value is nil, don't make a backup.
+Uses `backup-directory-alist' in the same way as does
+`make-backup-file-name'."
   (let ((handler (find-file-name-handler fn 'find-backup-file-name)))
     ;; Run a handler for this function so that ange-ftp can refuse to do it.
     (if handler
        (funcall handler 'find-backup-file-name fn)
       (if (eq version-control 'never)
          (list (make-backup-file-name fn))
-       (let* ((base-versions (concat (file-name-nondirectory fn) ".~"))
+       (let* ((basic-name (make-backup-file-name-1 fn))
+              (base-versions (concat (file-name-nondirectory basic-name)
+                                     ".~"))
               (backup-extract-version-start (length base-versions))
-              possibilities
-              (versions nil)
               (high-water-mark 0)
-              (deserve-versions-p nil)
-              (number-to-delete 0))
+              (number-to-delete 0)
+              possibilities deserve-versions-p versions)
          (condition-case ()
              (setq possibilities (file-name-all-completions
                                   base-versions
-                                  (file-name-directory fn))
-                   versions (sort (mapcar
-                                   (function backup-extract-version)
-                                   possibilities)
-                                  '<)
+                                  (file-name-directory basic-name))
+                   versions (sort (mapcar #'backup-extract-version
+                                          possibilities)
+                                  #'<)
                    high-water-mark (apply 'max 0 versions)
                    deserve-versions-p (or version-control
                                           (> high-water-mark 0))
                    number-to-delete (- (length versions)
-                                       kept-old-versions kept-new-versions -1))
-           (file-error
-            (setq possibilities nil)))
+                                       kept-old-versions
+                                       kept-new-versions
+                                       -1))
+           (file-error (setq possibilities nil)))
          (if (not deserve-versions-p)
-             (list (make-backup-file-name fn))
-           (cons (concat fn ".~" (int-to-string (1+ high-water-mark)) "~")
+             (list (concat basic-name "~"))
+           (cons (format "%s.~%d~" basic-name (1+ high-water-mark))
                  (if (and (> number-to-delete 0)
                           ;; Delete nothing if there is overflow
                           ;; in the number of versions to keep.
                           (>= (+ kept-new-versions kept-old-versions -1) 0))
-                     (mapcar (function (lambda (n)
-                                         (concat fn ".~" (int-to-string n) "~")))
+                     (mapcar (lambda (n)
+                               (format "%s.~%d~" basic-name n))
                              (let ((v (nthcdr kept-old-versions versions)))
                                (rplacd (nthcdr (1- number-to-delete) v) ())
                                v))))))))))
@@ -2500,7 +2607,9 @@ After saving the buffer, this function runs `after-save-hook'."
                              (if (and (eq system-type 'ms-dos)
                                       (not (msdos-long-file-names)))
                                  "%s#%d.tm#" ; MSDOS limits files to 8+3
-                               "%s#tmp#%d")
+                               (if (memq system-type '(vax-vms axp-vms))
+                                   "%s$tmp$%d"
+                                 "%s#tmp#%d"))
                              dir i))
              (setq nogood (file-exists-p tempname))
              (setq i (1+ i)))
@@ -2651,15 +2760,18 @@ saying what text to write."
 
 (defun file-newest-backup (filename)
   "Return most recent backup file for FILENAME or nil if no backups exist."
-  (let* ((filename (expand-file-name filename))
+  ;; `make-backup-file-name' will get us the right directory for
+  ;; ordinary or numeric backups.  It might create a directory for
+  ;; backups as a side-effect, according to `backup-directory-alist'.
+  (let* ((filename (file-name-sans-versions
+                   (make-backup-file-name filename)))
         (file (file-name-nondirectory filename))
         (dir  (file-name-directory    filename))
         (comp (file-name-all-completions file dir))
          (newest nil)
          tem)
     (while comp
-      (setq tem (car comp)
-           comp (cdr comp))
+      (setq tem (pop comp))
       (cond ((and (backup-file-name-p tem)
                   (string= (file-name-sans-versions tem) file))
              (setq tem (concat dir tem))
@@ -2899,6 +3011,9 @@ Then you'll be asked about a number of files to recover."
   (interactive)
   (if (null auto-save-list-file-prefix)
       (error "You set `auto-save-list-file-prefix' to disable making session files"))
+  (let ((dir (file-name-directory auto-save-list-file-prefix)))
+    (unless (file-directory-p dir)
+      (make-directory dir t)))
   (let ((ls-lisp-support-shell-wildcards t))
     (dired (concat auto-save-list-file-prefix "*")
           (concat dired-listing-switches "t")))
@@ -3048,17 +3163,29 @@ Does not consider `auto-save-visited-file-name' as that variable is checked
 before calling this function.  You can redefine this for customization.
 See also `auto-save-file-name-p'."
   (if buffer-file-name
-      (if (and (eq system-type 'ms-dos)
-              (not (msdos-long-file-names)))
-         (let ((fn (file-name-nondirectory buffer-file-name)))
-           (string-match "\\`\\([^.]+\\)\\(\\.\\(..?\\)?.?\\|\\)\\'" fn)
-           (concat (file-name-directory buffer-file-name)
-                   "#" (match-string 1 fn)
-                   "." (match-string 3 fn) "#"))
-       (concat (file-name-directory buffer-file-name)
-               "#"
-               (file-name-nondirectory buffer-file-name)
-               "#"))
+      (let ((list auto-save-file-name-transforms)
+           (filename buffer-file-name)
+           result)
+       ;; Apply user-specified translations
+       ;; to the file name.
+       (while (and list (not result))
+         (if (string-match (car (car list)) filename)
+             (setq result (replace-match (cadr (car list)) t nil
+                                         filename)))
+         (setq list (cdr list)))
+       (if result (setq filename result))
+
+       (if (and (eq system-type 'ms-dos)
+                (not (msdos-long-file-names)))
+           (let ((fn (file-name-nondirectory buffer-file-name)))
+             (string-match "\\`\\([^.]+\\)\\(\\.\\(..?\\)?.?\\|\\)\\'" fn)
+             (concat (file-name-directory buffer-file-name)
+                     "#" (match-string 1 fn) 
+                     "." (match-string 3 fn) "#"))
+         (concat (file-name-directory filename)
+                 "#"
+                 (file-name-nondirectory filename)
+                 "#")))
 
     ;; Deal with buffers that don't have any associated files.  (Mail
     ;; mode tends to create a good number of these.)
@@ -3351,8 +3478,26 @@ If WILDCARD, it also runs the shell specified by `shell-file-name'."
          (if (/= result 0)
              ;; We get here if ls failed.
              ;; Access the file to get a suitable error.
-             (access-file file "Reading directory")))))))
-
+             (access-file file "Reading directory")
+           ;; Replace "total" with "used", to avoid confusion.
+           ;; Add in the amount of free space.
+           (save-excursion
+             (goto-char (point-min))
+             (when (re-search-forward "^total" nil t)
+               (replace-match "used")
+               (end-of-line)
+               (let (available)
+                 (with-temp-buffer
+                   (call-process "df" nil t nil ".")
+                   (goto-char (point-min))
+                   (forward-line 1)
+                   (skip-chars-forward "^ \t")
+                   (forward-word 3)
+                   (let ((end (point)))
+                     (forward-word -1)
+                     (setq available (buffer-substring (point) end))))
+                 (insert " available " available))))))))))
+                   
 (defvar kill-emacs-query-functions nil
   "Functions to call with no arguments to query about killing Emacs.
 If any of these functions returns nil, killing Emacs is cancelled.