(defconst file-precious-flag nil
"*Non-nil means protect against I/O errors while saving files.
-Some modes set this non-nil in particular buffers.")
+Some modes set this non-nil in particular buffers.
+
+This feature works by writing the new contents into a temporary file
+and then renaming the temporary file to replace the original.
+In this way, any I/O error in writing leaves the original untouched,
+and there is never any instant where the file is nonexistent.
+
+Note that this feature forces backups to be made by copying.
+Yet, at the same time, saving a precious file
+breaks any hard links between it and other files.")
(defvar version-control nil
"*Control use of version numbers for backup files.
"Value of the CDPATH environment variable, as a list.
Not actually set up until the first time you you use it.")
+(defvar path-separator ":"
+ "Character used to separate concatenated paths.")
+
(defun parse-colon-path (cd-path)
"Explode a colon-separated list of paths into a string list."
(and cd-path
(let (cd-prefix cd-list (cd-start 0) cd-colon)
- (setq cd-path (concat cd-path ":"))
- (while (setq cd-colon (string-match ":" cd-path cd-start))
+ (setq cd-path (concat cd-path path-separator))
+ (while (setq cd-colon (string-match path-separator cd-path cd-start))
(setq cd-list
(nconc cd-list
(list (if (= cd-start cd-colon)
;; to chase before getting an error.
;; PREV-DIRS can be a cons cell whose car is an alist
;; of truenames we've just recently computed.
- (if (or (string= filename "~")
+
+ ;; The last test looks dubious, maybe `+' is meant here? --simon.
+ (if (or (string= filename "") (string= filename "~")
(and (string= (substring filename 0 1) "~")
(string-match "~[^/]*" filename)))
(progn
(cond ((not (file-exists-p filename))
(error "File %s no longer exists!" filename))
((yes-or-no-p
- (format
- (if (buffer-modified-p buf)
- "File %s changed on disk. Discard your edits? "
- "File %s changed on disk. Reread from disk? ")
- (file-name-nondirectory filename)))
+ (if (string= (file-name-nondirectory filename)
+ (buffer-name buf))
+ (format
+ (if (buffer-modified-p buf)
+ "File %s changed on disk. Discard your edits? "
+ "File %s changed on disk. Reread from disk? ")
+ (file-name-nondirectory filename))
+ (format
+ (if (buffer-modified-p buf)
+ "File %s changed on disk. Discard your edits in %s? "
+ "File %s changed on disk. Reread from disk into %s? ")
+ (file-name-nondirectory filename)
+ (buffer-name buf))))
(save-excursion
(set-buffer buf)
(revert-buffer t t)))))
("\\.hh\\'" . c++-mode)
("\\.C\\'" . c++-mode)
("\\.H\\'" . c++-mode)
+ ("\\.cpp\\'" . c++-mode)
+ ("\\.cxx\\'" . c++-mode)
+ ("\\.hxx\\'" . c++-mode)
+ ("\\.c\\+\\+\\'" . c++-mode)
+ ("\\.h\\+\\+\\'" . c++-mode)
;;; ("\\.mk\\'" . makefile-mode)
;;; ("[Mm]akefile" . makefile-mode)
;;; Less common extensions come here
the name of the interpreter specified in the first line.
If it matches, mode MODE is selected.")
-(defconst inhibit-first-line-modes-regexps '("\\.tar$")
+(defconst inhibit-first-line-modes-regexps '("\\.tar\\'")
"List of regexps; if one matches a file name, don't look for `-*-'.")
+(defconst inhibit-first-line-modes-suffixes nil
+ "List of regexps for what to ignore, for `inhibit-first-line-modes-regexps'.
+When checking `inhibit-first-line-modes-regexps', we first discard
+from the end of the file name anything that matches one of these regexps.")
+
(defvar user-init-file
"" ; set by command-line
"File name including directory of user's initialization file.")
;; Don't look for -*- if this file name matches any
;; of the regexps in inhibit-first-line-modes-regexps.
(let ((temp inhibit-first-line-modes-regexps)
- (name (file-name-sans-versions buffer-file-name)))
+ (name (if buffer-file-name
+ (file-name-sans-versions buffer-file-name)
+ (buffer-name))))
+ (while (let ((sufs inhibit-first-line-modes-suffixes))
+ (while (and sufs (not (string-match (car sufs) name)))
+ (setq sufs (cdr sufs)))
+ sufs)
+ (setq name (substring name 0 (match-beginning 0))))
(while (and temp
- (not (string-match (car temp)
- name)))
+ (not (string-match (car temp) name)))
(setq temp (cdr temp)))
(not temp))
(search-forward "-*-" (save-excursion
(forward-char -1)
(goto-char end))
(skip-chars-backward " \t")
- (funcall (intern (concat (downcase (buffer-substring beg (point))) "-mode"))))
+ (funcall (intern (concat (downcase (buffer-substring beg (point))) "-mode")))
+ (setq done t))
;; Simple -*-MODE-*- case.
- (funcall (intern (concat (downcase (buffer-substring beg end)) "-mode"))))
- (setq done t)))
+ (funcall (intern (concat (downcase (buffer-substring beg end)) "-mode")))
+ (setq done t))))
;; If we didn't find a mode from a -*- line, try using the file name.
(if (and (not done) buffer-file-name)
(let ((name buffer-file-name)
(let ((alist auto-mode-alist)
(mode nil))
;; Find first matching alist entry.
- (let ((case-fold-search (eq system-type 'vax-vms)))
+ (let ((case-fold-search
+ (memq system-type '(vax-vms windows-nt))))
(while (and (not mode) alist)
(if (string-match (car (car alist)) name)
(if (and (consp (cdr (car alist)))
(or (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
(error "malformed -*- line"))
(goto-char (match-end 0))
- (let ((key (intern (downcase (buffer-substring
- (match-beginning 1)
- (match-end 1)))))
+ ;; There used to be a downcase here,
+ ;; but the manual didn't say so,
+ ;; and people want to set var names that aren't all lc.
+ (let ((key (intern (buffer-substring
+ (match-beginning 1)
+ (match-end 1))))
(val (save-restriction
(narrow-to-region (point) end)
(read (current-buffer)))))
(if buffer-file-name
(set-buffer-modified-p t)))
-(defun write-file (filename)
+(defun write-file (filename &optional confirm)
"Write current buffer into file FILENAME.
Makes buffer visit that file, and marks it not modified.
If the buffer is already visiting a file, you can specify
a directory name as FILENAME, to write a file of the same
-old name in that directory."
+old name in that directory.
+If optional second arg CONFIRM is non-nil,
+ask for confirmation for overwriting an existing file."
;; (interactive "FWrite file: ")
(interactive
(list (if buffer-file-name
(read-file-name "Write file: "
(cdr (assq 'default-directory
(buffer-local-variables)))
- nil nil (buffer-name)))))
+ nil nil (buffer-name)))
+ t))
(or (null filename) (string-equal filename "")
(progn
;; If arg is just a directory,
(if (and (file-directory-p filename) buffer-file-name)
(setq filename (concat (file-name-as-directory filename)
(file-name-nondirectory buffer-file-name))))
+ (and confirm
+ (file-exists-p filename)
+ (or (y-or-n-p (format "File `%s' exists; overwrite? " filename))
+ (error "Canceled")))
(set-visited-file-name filename)))
(set-buffer-modified-p t)
(save-buffer))
(let ((handler (find-file-name-handler file 'file-ownership-preserved-p)))
(if handler
(funcall handler 'file-ownership-preserved-p file)
- (= (nth 2 (file-attributes file)) (user-uid)))))
+ (let ((attributes (file-attributes file)))
+ ;; Return t if the file doesn't exist, since it's true that no
+ ;; information would be lost by an (attempted) delete and create.
+ (or (null attributes)
+ (= (nth 2 attributes) (user-uid)))))))
(defun file-name-sans-extension (filename)
"Return FILENAME sans final \"extension\".
(if (null arg)
(not buffer-read-only)
(> (prefix-numeric-value arg) 0)))
- ;; Force mode-line redisplay
- (set-buffer-modified-p (buffer-modified-p)))
+ (force-mode-line-update))
(defun insert-file (filename)
"Insert contents of file FILENAME into buffer after point.
This function is useful for creating multiple shell process buffers
or multiple mail buffers, etc."
(interactive)
- (let* ((new-buf (generate-new-buffer (buffer-name)))
- (name (buffer-name new-buf)))
- (kill-buffer new-buf)
- (rename-buffer name)
- (set-buffer-modified-p (buffer-modified-p)))) ; force mode line update
+ (save-match-data
+ (let* ((base-name (if (and (string-match "<[0-9]+>\\'" (buffer-name))
+ (not (and buffer-file-name
+ (string= (buffer-name)
+ (file-name-nondirectory
+ buffer-file-name)))))
+ ;; If the existing buffer name has a <NNN>,
+ ;; which isn't part of the file name (if any),
+ ;; then get rid of that.
+ (substring (buffer-name) 0 (match-beginning 0))
+ (buffer-name)))
+ (new-buf (generate-new-buffer base-name))
+ (name (buffer-name new-buf)))
+ (kill-buffer new-buf)
+ (rename-buffer name)
+ (force-mode-line-update))))
(defun make-directory (dir &optional parents)
"Create the directory DIR and any nonexistent parent dirs.
Gets two args, first the nominal file name to use,
and second, t if reading the auto-save file.")
+(defvar before-revert-hook nil
+ "Normal hook for `revert-buffer' to run before reverting.
+If `revert-buffer-function' is used to override the normal revert
+mechanism, this hook is not used.")
+
+(defvar after-revert-hook nil
+ "Normal hook for `revert-buffer' to run after reverting.
+Note that the hook value that it runs is the value that was in effect
+before reverting; that makes a difference if you have buffer-local
+hook functions.
+
+If `revert-buffer-function' is used to override the normal revert
+mechanism, this hook is not used.")
+
(defun revert-buffer (&optional ignore-auto noconfirm)
"Replace the buffer text with the text of the visited file on disk.
This undoes all changes since the file was visited or saved.
With a prefix argument, offer to revert from latest auto-save file, if
that is more recent than the visited file.
-When called from lisp, The first argument is IGNORE-AUTO; only offer
+When called from Lisp, the first argument is IGNORE-AUTO; only offer
to revert from the auto-save file when this is nil. Note that the
sense of this argument is the reverse of the prefix argument, for the
sake of backward compatibility. IGNORE-AUTO is optional, defaulting
;; there's no straightforward way to encourage authors to notice a
;; reversal of the argument sense. So I'm just changing the user
;; interface, but leaving the programmatic interface the same.
- (interactive (list (not prefix-arg)))
+ (interactive (list (not current-prefix-arg)))
(if revert-buffer-function
(funcall revert-buffer-function ignore-auto noconfirm)
(let* ((opoint (point))
;; Get rid of all undo records for this buffer.
(or (eq buffer-undo-list t)
(setq buffer-undo-list nil))
- (let ((buffer-read-only nil)
- ;; Don't make undo records for the reversion.
- (buffer-undo-list t))
- (if revert-buffer-insert-file-contents-function
- (funcall revert-buffer-insert-file-contents-function
- file-name auto-save-p)
- (if (not (file-exists-p file-name))
- (error "File %s no longer exists!" file-name))
- ;; Bind buffer-file-name to nil
- ;; so that we don't try to lock the file.
- (let ((buffer-file-name nil))
- (or auto-save-p
- (unlock-buffer)))
- (widen)
- (insert-file-contents file-name (not auto-save-p)
- nil nil t)))
- (goto-char (min opoint (point-max)))
- ;; Recompute the truename in case changes in symlinks
- ;; have changed the truename.
- (setq buffer-file-truename
- (abbreviate-file-name (file-truename buffer-file-name)))
- (after-find-file nil nil t t)
- (run-hooks 'after-revert-hook)
+ ;; Effectively copy the after-revert-hook status,
+ ;; since after-find-file will clobber it.
+ (let ((global-hook (default-value 'after-revert-hook))
+ (local-hook-p (local-variable-p 'after-revert-hook))
+ (local-hook (and (local-variable-p 'after-revert-hook)
+ after-revert-hook)))
+ (let (buffer-read-only
+ ;; Don't make undo records for the reversion.
+ (buffer-undo-list t))
+ (if revert-buffer-insert-file-contents-function
+ (funcall revert-buffer-insert-file-contents-function
+ file-name auto-save-p)
+ (if (not (file-exists-p file-name))
+ (error "File %s no longer exists!" file-name))
+ ;; Bind buffer-file-name to nil
+ ;; so that we don't try to lock the file.
+ (let ((buffer-file-name nil))
+ (or auto-save-p
+ (unlock-buffer)))
+ (widen)
+ (insert-file-contents file-name (not auto-save-p)
+ nil nil t)))
+ (goto-char (min opoint (point-max)))
+ ;; Recompute the truename in case changes in symlinks
+ ;; have changed the truename.
+ (setq buffer-file-truename
+ (abbreviate-file-name (file-truename buffer-file-name)))
+ (after-find-file nil nil t t)
+ ;; Run after-revert-hook as it was before we reverted.
+ (setq-default revert-buffer-internal-hook global-hook)
+ (if local-hook-p
+ (progn
+ (make-local-variable 'revert-buffer-internal-hook)
+ (setq revert-buffer-internal-hook local-hook))
+ (kill-local-variable 'revert-buffer-internal-hook))
+ (run-hooks 'revert-buffer-internal-hook))
t)))))
(defun recover-file (file)
"Visit file FILE, but get contents from its last auto-save file."
- (interactive
- (let ((prompt-file buffer-file-name)
- (file-name nil)
- (file-dir nil))
- (and prompt-file
- (setq file-name (file-name-nondirectory prompt-file)
- file-dir (file-name-directory prompt-file)))
- (list (read-file-name "Recover file: "
- file-dir nil nil file-name))))
+ ;; Actually putting the file name in the minibuffer should be used
+ ;; only rarely.
+ ;; Not just because users often use the default.
+ (interactive "fRecover file: ")
(setq file (expand-file-name file))
(if (auto-save-file-name-p (file-name-nondirectory file))
(error "%s is an auto-save file" file))
(after-find-file nil nil t))
(t (error "Recover-file cancelled.")))))
+(defun recover-session ()
+ "Recover auto save files from a previous Emacs session.
+This command first displays a Dired buffer showing you the
+previous sessions that you could recover from.
+To choose one, move point to the proper line and then type C-c C-c.
+Then you'll be asked about a number of files to recover."
+ (interactive)
+ (dired "~/.save*")
+ (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")))
+ (use-local-map (nconc (make-sparse-keymap) (current-local-map)))
+ (define-key (current-local-map) "\C-c\C-c" 'recover-session-finish))
+
+(defun recover-session-finish ()
+ "Choose one saved session to recover auto-save files from.
+This command is used in the special Dired buffer created by
+\\[recover-session]."
+ (interactive)
+ ;; Get the name of the session file to recover from.
+ (let ((file (dired-get-filename))
+ (buffer (get-buffer-create " *recover*")))
+ (unwind-protect
+ (save-excursion
+ ;; Read in the auto-save-list file.
+ (set-buffer buffer)
+ (erase-buffer)
+ (insert-file-contents file)
+ (map-y-or-n-p "Recover %s? "
+ (lambda (file) (save-excursion (recover-file file)))
+ (lambda ()
+ (if (eobp)
+ nil
+ (prog1
+ (buffer-substring-no-properties
+ (point) (progn (end-of-line) (point)))
+ (while (and (eolp) (not (eobp)))
+ (forward-line 2)))))
+ '("file" "files" "recover")))
+ (kill-buffer buffer))))
+
(defun kill-some-buffers ()
"For each buffer, ask whether to kill it."
(interactive)
(substring pattern (match-beginning 0)))
beg (1+ (match-end 0))))
(call-process shell-file-name nil t nil
- "-c" (concat insert-directory-program
+ "-c" (concat "\\" ;; Disregard shell aliases!
+ insert-directory-program
" -d "
(if (stringp switches)
switches
(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.")
+If any of these functions returns nil, killing Emacs is cancelled.
+`save-buffers-kill-emacs' (\\[save-buffers-kill-emacs]) calls these functions,
+but `kill-emacs', the low level primitive, does not.
+See also `kill-emacs-hook'.")
(defun save-buffers-kill-emacs (&optional arg)
"Offer to save each buffer, then kill this Emacs process.