(undigestify-rmail-message): Better error messages.
[bpt/emacs.git] / lisp / files.el
index fd5268a..fc38769 100644 (file)
@@ -113,13 +113,6 @@ under another name, you get the existing buffer instead of a new buffer.")
 The truename of a file is found by chasing all links
 both at the file level and at the levels of the containing directories.")
 
-(defvar buffer-file-truename nil
-  "The abbreviated truename of the file visited in the current buffer.
-That is, (abbreviate-file-name (file-truename buffer-file-name)).
-This variable is automatically local in all buffers, when non-nil.")
-(make-variable-buffer-local 'buffer-file-truename)
-(put 'buffer-file-truename 'permanent-local t)
-
 (defvar buffer-file-number nil
   "The device number and file number of the file visited in the current buffer.
 The value is a list of the form (FILENUM DEVNUM).
@@ -305,7 +298,9 @@ Not actually set up until the first time you you use it.")
   "Make DIR become the current buffer's default directory.
 If your environment includes a `CDPATH' variable, try each one of that
 colon-separated list of directories when resolving a relative directory name."
-  (interactive "FChange default directory: ")
+  (interactive
+   (list (read-file-name "Change default directory: "
+                        default-directory default-directory)))
   (if (file-name-absolute-p dir)
       (cd-absolute (expand-file-name dir))
     (if (null cd-path)
@@ -540,6 +535,8 @@ If the current buffer now contains an empty file that you just visited
        (onum buffer-file-number)
        (otrue buffer-file-truename)
        (oname (buffer-name)))
+    (if (get-buffer " **lose**")
+       (kill-buffer " **lose**"))
     (rename-buffer " **lose**")
     (setq buffer-file-name nil)
     (setq buffer-file-number nil)
@@ -662,7 +659,28 @@ If there is no such live buffer, return nil."
                 (setq list (cdr list))))
          found))))
 
-(defun find-file-noselect (filename &optional nowarn)
+(defun insert-file-contents-literally (filename &optional visit beg end replace)
+  "Like `insert-file-contents', q.v., but only reads in the file.
+A buffer may be modified in several ways after reading into the buffer due
+to advanced Emacs features, such as file-name-handlers, format decoding,
+find-file-hooks, etc.
+  This function ensures that none of these modifications will take place."
+  (let ((file-name-handler-alist nil)
+       (format-alist nil)
+       (after-insert-file-functions nil)
+       (find-buffer-file-type-function 
+        (if (fboundp 'find-buffer-file-type)
+            (symbol-function 'find-buffer-file-type)
+          nil)))
+    (unwind-protect
+       (progn
+         (fset 'find-buffer-file-type (lambda (filename) t))
+         (insert-file-contents filename visit beg end replace))
+      (if find-buffer-file-type-function
+         (fset 'find-buffer-file-type find-buffer-file-type-function)
+       (fmakunbound 'find-buffer-file-type)))))
+
+(defun find-file-noselect (filename &optional nowarn rawfile)
   "Read file FILENAME into a buffer and return the buffer.
 If a buffer exists visiting FILENAME, return that one, but
 verify that the file has not changed since visited or saved.
@@ -724,13 +742,19 @@ The buffer is not selected, just returned to the caller."
          (set-buffer-major-mode buf)
          (set-buffer buf)
          (erase-buffer)
-         (condition-case ()
-             (insert-file-contents filename t)
-           (file-error
-            ;; Run find-file-not-found-hooks until one returns non-nil.
-            (or (run-hook-with-args-until-success 'find-file-not-found-hooks)
-                ;; If they fail too, set error.
-                (setq error t))))
+         (if rawfile
+             (condition-case ()
+                 (insert-file-contents-literally filename t)
+               (file-error
+                ;; Unconditionally set error
+                (setq error t)))
+           (condition-case ()
+               (insert-file-contents filename t)
+             (file-error
+              ;; Run find-file-not-found-hooks until one returns non-nil.
+              (or (run-hook-with-args-until-success 'find-file-not-found-hooks)
+                  ;; If they fail too, set error.
+                  (setq error t)))))
          ;; Find the file's truename, and maybe use that as visited name.
          (setq buffer-file-truename truename)
          (setq buffer-file-number number)
@@ -755,7 +779,9 @@ The buffer is not selected, just returned to the caller."
               (progn
                 (make-local-variable 'backup-inhibited)
                 (setq backup-inhibited t)))
-         (after-find-file error (not nowarn))))
+         (if rawfile
+             nil
+           (after-find-file error (not nowarn)))))
       buf)))
 \f
 (defvar after-find-file-from-revert-buffer nil)
@@ -849,9 +875,9 @@ run `normal-mode' explicitly."
                                  ("\\.p\\'" . pascal-mode)
                                  ("\\.pas\\'" . pascal-mode)
                                  ("\\.mss\\'" . scribe-mode)
-                                 ("\\.ada\\'" . ada-mode)
+                                 ("\\.ad[abs]\\'" . ada-mode)
                                  ("\\.icn\\'" . icon-mode)
-                                 ("\\.pl\\'" . prolog-mode)
+                                 ("\\.pl\\'" . perl-mode)
                                  ("\\.cc\\'" . c++-mode)
                                  ("\\.hh\\'" . c++-mode)
                                  ("\\.C\\'" . c++-mode)
@@ -861,8 +887,8 @@ run `normal-mode' explicitly."
                                  ("\\.hxx\\'" . c++-mode)
                                  ("\\.c\\+\\+\\'" . c++-mode)
                                  ("\\.h\\+\\+\\'" . c++-mode)
-;;;                              ("\\.mk\\'" . makefile-mode)
-;;;                              ("[Mm]akefile" . makefile-mode)
+                                 ("\\.mk\\'" . makefile-mode)
+                                 ("[Mm]akefile\\(.in\\)?\\'" . makefile-mode)
 ;;; Less common extensions come here
 ;;; so more common ones above are found faster.
                                  ("\\.texinfo\\'" . texinfo-mode)
@@ -873,8 +899,10 @@ run `normal-mode' explicitly."
                                  ("changelo\\'" . change-log-mode)
                                  ("ChangeLog.[0-9]+\\'" . change-log-mode)
                                  ("\\$CHANGE_LOG\\$\\.TXT" . change-log-mode)
-;; The following should come after the ChangeLog pattern
-;; for the sake of ChangeLog.1, etc.
+                                 ("\\.scm\\.[0-9]*\\'" . scheme-mode)
+;;; The following should come after the ChangeLog pattern
+;;; for the sake of ChangeLog.1, etc.
+;;; and after the .scm.[0-9] pattern too.
                                  ("\\.[12345678]\\'" . nroff-mode)
                                  ("\\.TeX\\'" . tex-mode)
                                  ("\\.sty\\'" . latex-mode)
@@ -884,10 +912,12 @@ run `normal-mode' explicitly."
                                  ("\\.article\\'" . text-mode)
                                  ("\\.letter\\'" . text-mode)
                                  ("\\.tcl\\'" . tcl-mode)
+                                 ("\\.f90\\'" . f90-mode)
                                  ("\\.lsp\\'" . lisp-mode)
                                  ("\\.awk\\'" . awk-mode)
                                  ("\\.prolog\\'" . prolog-mode)
                                  ("\\.tar\\'" . tar-mode)
+                                 ("\\.\\(arc\\|zip\\|lzh\\|zoo\\)\\'" . archive-mode)
                                  ;; Mailer puts message to be edited in
                                  ;; /tmp/Re.... or Message
                                  ("^/tmp/Re" . text-mode)
@@ -898,7 +928,6 @@ run `normal-mode' explicitly."
                                  ("\\.y\\'" . c-mode)
                                  ("\\.lex\\'" . c-mode)
                                  ("\\.oak\\'" . scheme-mode)
-                                 ("\\.scm.[0-9]*\\'" . scheme-mode)
                                  ("\\.sgm\\'" . sgml-mode)
                                  ("\\.sgml\\'" . sgml-mode)
                                  ("\\.dtd\\'" . sgml-mode)
@@ -1496,7 +1525,7 @@ we do not remove backup version numbers, only true file version numbers."
                         (length name))
                   (if keep-backup-version
                       (length name)
-                    (or (string-match "\\.~[0-9]+~\\'" name)
+                    (or (string-match "\\.~[0-9.]+~\\'" name)
                         (string-match "~\\'" name)
                         (length name))))))))
 
@@ -1665,8 +1694,12 @@ the last real save, but optional arg FORCE non-nil means delete anyway."
           (file-error nil))
         (set-buffer-auto-saved))))
 
+(defvar after-save-hook nil
+  "Normal hook that is run after a buffer is saved to its file.")
+
 (defun basic-save-buffer ()
-  "Save the current buffer in its visited file, if it has been modified."
+  "Save the current buffer in its visited file, if it has been modified.
+After saving the buffer, run `after-save-hook'."
   (interactive)
   (save-excursion
     ;; In an indirect buffer, save its base buffer instead.
@@ -1690,8 +1723,14 @@ the last real save, but optional arg FORCE non-nil means delete anyway."
                (rename-buffer buffer-new-name)))
          ;; If buffer has no file name, ask user for one.
          (or buffer-file-name
-             (set-visited-file-name
-              (expand-file-name (read-file-name "File to save in: ") nil)))
+             (let ((filename
+                    (expand-file-name
+                     (read-file-name "File to save in: ") nil)))
+               (and (file-exists-p filename)
+                    (or (y-or-n-p (format "File `%s' exists; overwrite? "
+                                          filename))
+                        (error "Canceled")))
+               (set-visited-file-name filename)))
          (or (verify-visited-file-modtime (current-buffer))
              (not (file-exists-p buffer-file-name))
              (yes-or-no-p
@@ -1726,6 +1765,8 @@ the last real save, but optional arg FORCE non-nil means delete anyway."
          ;; If the auto-save file was recent before this command,
          ;; delete it now.
          (delete-auto-save-file-if-necessary recent-save)
+         ;; Support VC `implicit' locking.
+         (vc-after-save)
          (run-hooks 'after-save-hook))
       (message "(No changes need to be saved)"))))
 
@@ -1768,7 +1809,8 @@ the last real save, but optional arg FORCE non-nil means delete anyway."
            (unwind-protect
                (progn (clear-visited-file-modtime)
                       (write-region (point-min) (point-max)
-                                    tempname nil realname)
+                                    tempname nil realname
+                                    buffer-file-truename)
                       (setq succeed t))
              ;; If writing the temp file fails,
              ;; delete the temp file.
@@ -1792,7 +1834,7 @@ the last real save, but optional arg FORCE non-nil means delete anyway."
               (setq setmodes (file-modes buffer-file-name))
               (set-file-modes buffer-file-name 511)))
        (write-region (point-min) (point-max)
-                     buffer-file-name nil t)))
+                     buffer-file-name nil t buffer-file-truename)))
     setmodes))
 
 (defun save-some-buffers (&optional arg exiting)
@@ -2082,7 +2124,9 @@ beginning and `after-revert-hook' at the end."
       (error "%s is an auto-save file" file))
   (let ((file-name (let ((buffer-file-name file))
                     (make-auto-save-file-name))))
-    (cond ((not (file-newer-than-file-p file-name file))
+    (cond ((if (file-exists-p file)
+              (not (file-newer-than-file-p file-name file))
+            (not (file-exists-p file-name)))
           (error "Auto-save file %s not current" file-name))
          ((save-window-excursion
             (if (not (eq system-type 'vax-vms))
@@ -2110,8 +2154,10 @@ Then you'll be asked about a number of files to recover."
   (goto-char (point-min))
   (or (looking-at "Move to the session you want to recover,")
       (let ((inhibit-read-only t))
-       (insert "Move to the session you want to recover,\n")
-       (insert "then type C-c C-c to select it.\n\n")))
+       (insert "Move to the session you want to recover,\n"
+               "then type C-c C-c to select it.\n\n"
+               "You can also delete some of these files;\n"
+               "type d on a line to mark that file for deletion.\n\n")))
   (use-local-map (nconc (make-sparse-keymap) (current-local-map)))
   (define-key (current-local-map) "\C-c\C-c" 'recover-session-finish))
 
@@ -2123,20 +2169,52 @@ This command is used in the special Dired buffer created by
   ;; Get the name of the session file to recover from.
   (let ((file (dired-get-filename))
        (buffer (get-buffer-create " *recover*")))
+    (dired-do-flagged-delete t)
     (unwind-protect
        (save-excursion
          ;; Read in the auto-save-list file.
          (set-buffer buffer)
          (erase-buffer)
          (insert-file-contents file)
+         ;; The file contains a pair of line for each auto-saved buffer.
+         ;; The first line of the pair contains the visited file name
+         ;; or is empty if the buffer was not visiting a file.
+         ;; The second line is the auto-save file name.
          (map-y-or-n-p  "Recover %s? "
-                        (lambda (file) (save-excursion (recover-file file)))
+                        (lambda (file)
+                          (condition-case nil
+                              (save-excursion (recover-file file))
+                            (error 
+                             "Failed to recover `%s'" file)))
                         (lambda ()
                           (if (eobp)
                               nil
                             (prog1
-                                (buffer-substring-no-properties
-                                 (point) (progn (end-of-line) (point)))
+                                (if (eolp)
+                                    ;; If the first line of the pair is empty,
+                                    ;; it means this was a non-file buffer
+                                    ;; that was autosaved.
+                                    ;; Make a file name from 
+                                    ;; the auto-save file name.
+                                    (let ((autofile
+                                           (buffer-substring-no-properties
+                                            (save-excursion
+                                              (forward-line 1)
+                                              (point))
+                                            (save-excursion
+                                              (forward-line 1)
+                                              (end-of-line)
+                                              (point)))))
+                                      (expand-file-name
+                                       (concat "temp"
+                                               (substring
+                                                (file-name-nondirectory autofile)
+                                                1 -1))
+                                       (file-name-directory autofile)))
+                                  ;; This pair of lines is a file-visiting
+                                  ;; buffer.  Use the visited file name.
+                                  (buffer-substring-no-properties
+                                   (point) (progn (end-of-line) (point))))
                               (while (and (eolp) (not (eobp)))
                                 (forward-line 2)))))
                         '("file" "files" "recover")))
@@ -2266,6 +2344,7 @@ and `list-directory-verbose-switches'."
       (terpri)
       (save-excursion
        (set-buffer "*Directory*")
+       (setq default-directory (file-name-directory dirname))
        (let ((wildcard (not (file-directory-p dirname))))
          (insert-directory dirname switches wildcard (not wildcard)))))))