-;;; files.el --- file input and output commands for Emacs
+;;; files.el --- file input and output commands for Emacs -*- lexical-binding:t -*-
-;; Copyright (C) 1985-1987, 1992-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992-2014 Free Software Foundation, Inc.
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Package: emacs
;; This file is part of GNU Emacs.
;;; Commentary:
-;; Defines most of Emacs'ss file- and directory-handling functions,
+;; Defines most of Emacs's file- and directory-handling functions,
;; including basic file visiting, backup generation, link handling,
;; ITS-id version control, load- and write-hook handling, and the like.
;; Do this so that local variables based on the file name
;; are not overridden by the major mode.
(defvar backup-inhibited nil
- "Non-nil means don't make a backup, regardless of the other parameters.
-This variable is intended for use by making it local to a buffer.
-But it is local only if you make it local.")
+ "If non-nil, backups will be inhibited.
+This variable is intended for use by making it local to a buffer,
+but it is not an automatically buffer-local variable.")
(put 'backup-inhibited 'permanent-local t)
(defcustom backup-by-copying nil
:group 'find-file)
(defcustom find-file-visit-truename nil
- "Non-nil means visit a file under its truename.
-The truename of a file is found by chasing all links
-both at the file level and at the levels of the containing directories."
+ "Non-nil means visiting a file uses its truename as the visited-file name.
+That is, the buffer visiting the file has the truename as the
+value of `buffer-file-name'. The truename of a file is found by
+chasing all links both at the file level and at the levels of the
+containing directories."
:type 'boolean
:group 'find-file)
(put 'find-file-visit-truename 'safe-local-variable 'booleanp)
(declare-function msdos-long-file-names "msdos.c")
(declare-function w32-long-file-name "w32proc.c")
(declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep))
-(declare-function dired-unmark "dired" (arg))
+(declare-function dired-unmark "dired" (arg &optional interactive))
(declare-function dired-do-flagged-delete "dired" (&optional nomessage))
(declare-function dos-8+3-filename "dos-fns" (filename))
-(declare-function view-mode-disable "view" ())
(declare-function dosified-file-name "dos-fns" (file-name))
(defvar file-name-invalid-regexp
:group 'backup)
(defcustom break-hardlink-on-save nil
- "Non-nil means when saving a file that exists under several names
-\(i.e., has multiple hardlinks), break the hardlink associated with
-`buffer-file-name' and write to a new file, so that the other
-instances of the file are not affected by the save.
+ "Whether to allow breaking hardlinks when saving files.
+If non-nil, then when saving a file that exists under several
+names \(i.e., has multiple hardlinks), break the hardlink
+associated with `buffer-file-name' and write to a new file, so
+that the other instances of the file are not affected by the
+save.
If `buffer-file-name' refers to a symlink, do not break the symlink.
Certain major modes set this locally to the value obtained
from `mode-require-final-newline'."
+ :safe #'symbolp
:type '(choice (const :tag "When visiting" visit)
(const :tag "When saving" t)
(const :tag "When visiting or saving" visit-save)
(other :tag "Query" other))
:group 'find-file)
+(defvar enable-dir-local-variables t
+ "Non-nil means enable use of directory-local variables.
+Some modes may wish to set this to nil to prevent directory-local
+settings being applied, but still respect file-local ones.")
+
;; This is an odd variable IMO.
;; You might wonder why it is needed, when we could just do:
;; (set (make-local-variable 'enable-local-variables) nil)
(defun parse-colon-path (search-path)
"Explode a search path into a list of directory names.
-Directories are separated by occurrences of `path-separator'
-\(which is colon in GNU and GNU-like systems)."
- ;; We could use split-string here.
- (and search-path
- (let (cd-list (cd-start 0) cd-colon)
- (setq search-path (concat search-path path-separator))
- (while (setq cd-colon (string-match path-separator search-path cd-start))
- (setq cd-list
- (nconc cd-list
- (list (if (= cd-start cd-colon)
- nil
- (substitute-in-file-name
- (file-name-as-directory
- (substring search-path cd-start cd-colon)))))))
- (setq cd-start (+ cd-colon 1)))
- cd-list)))
+Directories are separated by `path-separator' (which is colon in
+GNU and Unix systems). Substitute environment variables into the
+resulting list of directory names. For an empty path element (i.e.,
+a leading or trailing separator, or two adjacent separators), return
+nil (meaning `default-directory') as the associated list element."
+ (when (stringp search-path)
+ (mapcar (lambda (f)
+ (if (equal "" f) nil
+ (substitute-in-file-name (file-name-as-directory f))))
+ (split-string search-path path-separator))))
(defun cd-absolute (dir)
"Change current directory to given absolute file name DIR."
;; This is a case where .elc makes a lot of sense.
(interactive (list (let ((completion-ignored-extensions
(remove ".elc" completion-ignored-extensions)))
- (read-file-name "Load file: "))))
+ (read-file-name "Load file: " nil nil 'lambda))))
(load (expand-file-name file) nil nil t))
(defun locate-file (filename path &optional suffixes predicate)
"Search for FILENAME through PATH.
-If found, return the absolute file name of FILENAME, with its suffixes;
-otherwise return nil.
+If found, return the absolute file name of FILENAME; otherwise
+return nil.
PATH should be a list of directories to look in, like the lists in
`exec-path' or `load-path'.
If SUFFIXES is non-nil, it should be a list of suffixes to append to
(defun locate-file-completion (string path-and-suffixes action)
"Do completion for file names passed to `locate-file'.
PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
+ (declare (obsolete locate-file-completion-table "23.1"))
(locate-file-completion-table (car path-and-suffixes)
(cdr path-and-suffixes)
string nil action))
-(make-obsolete 'locate-file-completion 'locate-file-completion-table "23.1")
(defvar locate-dominating-stop-dir-regexp
(purecopy "\\`\\(?:[\\/][\\/][^\\/]+[\\/]\\|/\\(?:net\\|afs\\|\\.\\.\\.\\)/\\)\\'")
;; nil)))
(defun locate-dominating-file (file name)
- "Look up the directory hierarchy from FILE for a file named NAME.
+ "Look up the directory hierarchy from FILE for a directory containing NAME.
Stop at the first parent directory containing a file NAME,
and return the directory. Return nil if not found.
-
-This function only tests if FILE exists. If you care about whether
-it is readable, regular, etc., you should test the result."
+Instead of a string, NAME can also be a predicate taking one argument
+\(a directory) and returning a non-nil value if that directory is the one for
+which we're looking."
;; We used to use the above locate-dominating-files code, but the
;; directory-files call is very costly, so we're much better off doing
;; multiple calls using the code in here.
;; (setq user (nth 2 (file-attributes file)))
;; (and prev-user (not (equal user prev-user))))
(string-match locate-dominating-stop-dir-regexp file)))
- ;; FIXME? maybe this function should (optionally?)
- ;; use file-readable-p instead. In many cases, an unreadable
- ;; FILE is no better than a non-existent one.
- ;; See eg dir-locals-find-file.
- (setq try (file-exists-p (expand-file-name name file)))
+ (setq try (if (stringp name)
+ (file-exists-p (expand-file-name name file))
+ (funcall name file)))
(cond (try (setq root file))
((equal file (setq file (file-name-directory
(directory-file-name file))))
(setq file nil))))
- root))
+ (if root (file-name-as-directory root))))
+
+(defcustom user-emacs-directory-warning t
+ "Non-nil means warn if cannot access `user-emacs-directory'.
+Set this to nil at your own risk..."
+ :type 'boolean
+ :group 'initialization
+ :version "24.4")
+
+(defun locate-user-emacs-file (new-name &optional old-name)
+ "Return an absolute per-user Emacs-specific file name.
+If NEW-NAME exists in `user-emacs-directory', return it.
+Else if OLD-NAME is non-nil and ~/OLD-NAME exists, return ~/OLD-NAME.
+Else return NEW-NAME in `user-emacs-directory', creating the
+directory if it does not exist."
+ (convert-standard-filename
+ (let* ((home (concat "~" (or init-file-user "")))
+ (at-home (and old-name (expand-file-name old-name home)))
+ (bestname (abbreviate-file-name
+ (expand-file-name new-name user-emacs-directory))))
+ (if (and at-home (not (file-readable-p bestname))
+ (file-readable-p at-home))
+ at-home
+ ;; Make sure `user-emacs-directory' exists,
+ ;; unless we're in batch mode or dumping Emacs.
+ (or noninteractive
+ purify-flag
+ (let (errtype)
+ (if (file-directory-p user-emacs-directory)
+ (or (file-accessible-directory-p user-emacs-directory)
+ (setq errtype "access"))
+ (let ((umask (default-file-modes)))
+ (unwind-protect
+ (progn
+ (set-default-file-modes ?\700)
+ (condition-case nil
+ (make-directory user-emacs-directory)
+ (error (setq errtype "create"))))
+ (set-default-file-modes umask))))
+ (when (and errtype
+ user-emacs-directory-warning
+ (not (get 'user-emacs-directory-warning 'this-session)))
+ ;; Only warn once per Emacs session.
+ (put 'user-emacs-directory-warning 'this-session t)
+ (display-warning 'initialization
+ (format "\
+Unable to %s `user-emacs-directory' (%s).
+Any data that would normally be written there may be lost!
+If you never want to see this message again,
+customize the variable `user-emacs-directory-warning'."
+ errtype user-emacs-directory)))))
+ bestname))))
(defun executable-find (command)
(setq list (cdr list))))
(or (car list) "ssh")))
"Program to use to execute commands on a remote host (e.g. ssh or rsh)."
- :version "24.2" ; ssh rather than rsh, etc
+ :version "24.3" ; ssh rather than rsh, etc
:initialize 'custom-initialize-delay
:group 'environment
:type 'file)
(delq (rassq 'ange-ftp-completion-hook-function tem) tem)))))
(or prev-dirs (setq prev-dirs (list nil)))
- ;; andrewi@harlequin.co.uk - none of the following code (except for
- ;; invoking the file-name handler) currently applies on Windows
- ;; (ie. there are no native symlinks), but there is an issue with
+ ;; andrewi@harlequin.co.uk - on Windows, there is an issue with
;; case differences being ignored by the OS, and short "8.3 DOS"
;; name aliases existing for all files. (The short names are not
;; reported by directory-files, but can be used to refer to files.)
;; it is stored on disk (expanding short name aliases with the full
;; name in the process).
(if (eq system-type 'windows-nt)
- (let ((handler (find-file-name-handler filename 'file-truename)))
- ;; For file name that has a special handler, call handler.
- ;; This is so that ange-ftp can save time by doing a no-op.
- (if handler
- (setq filename (funcall handler 'file-truename filename))
- ;; If filename contains a wildcard, newname will be the old name.
- (unless (string-match "[[*?]" filename)
- ;; If filename exists, use the long name. If it doesn't exist,
- ;; drill down until we find a directory that exists, and use
- ;; the long name of that, with the extra non-existent path
- ;; components concatenated.
- (let ((longname (w32-long-file-name filename))
- missing rest)
- (if longname
- (setq filename longname)
- ;; Include the preceding directory separator in the missing
- ;; part so subsequent recursion on the rest works.
- (setq missing (concat "/" (file-name-nondirectory filename)))
- (let ((length (length missing)))
- (setq rest
- (if (> length (length filename))
- ""
- (substring filename 0 (- length)))))
- (setq filename (concat (file-truename rest) missing))))))
- (setq done t)))
+ (unless (string-match "[[*?]" filename)
+ ;; If filename exists, use its long name. If it doesn't
+ ;; exist, the recursion below on the directory of filename
+ ;; will drill down until we find a directory that exists,
+ ;; and use the long name of that, with the extra
+ ;; non-existent path components concatenated.
+ (let ((longname (w32-long-file-name filename)))
+ (if longname
+ (setq filename longname)))))
;; If this file directly leads to a link, process that iteratively
;; so that we don't use lots of stack.
(setq dirfile (directory-file-name dir))
;; If these are equal, we have the (or a) root directory.
(or (string= dir dirfile)
+ (and (memq system-type '(windows-nt ms-dos cygwin))
+ (eq (compare-strings dir 0 nil dirfile 0 nil t) t))
;; If this is the same dir we last got the truename for,
;; save time--don't recalculate.
(if (assoc dir (car prev-dirs))
like /ssh:SOME_REMOTE_MACHINE:FILE for the file name. You can
also visit local files as a different user by specifying
/sudo::FILE for the file name.
-See the Info node `(tramp)Filename Syntax' in the Tramp Info
+See the Info node `(tramp)File name Syntax' in the Tramp Info
manual, for more about this.
Interactively, or if WILDCARDS is non-nil in a call from Lisp,
(find-file filename)
(current-buffer)))
-(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.
-Use \\[toggle-read-only] to permit editing."
- (interactive
- (find-file-read-args "Find file read-only: "
- (confirm-nonexistent-file-or-buffer)))
+(defun find-file--read-only (fun filename wildcards)
(unless (or (and wildcards find-file-wildcards
(not (string-match "\\`/:" filename))
(string-match "[[*?]" filename))
(file-exists-p filename))
(error "%s does not exist" filename))
- (let ((value (find-file filename wildcards)))
- (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
+ (let ((value (funcall fun filename wildcards)))
+ (mapc (lambda (b) (with-current-buffer b (read-only-mode 1)))
(if (listp value) value (list value)))
value))
+(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.
+Use \\[toggle-read-only] to permit editing."
+ (interactive
+ (find-file-read-args "Find file read-only: "
+ (confirm-nonexistent-file-or-buffer)))
+ (find-file--read-only #'find-file filename wildcards))
+
(defun find-file-read-only-other-window (filename &optional wildcards)
"Edit file FILENAME in another window but don't allow changes.
Like \\[find-file-other-window], but marks buffer as read-only.
(interactive
(find-file-read-args "Find file read-only other window: "
(confirm-nonexistent-file-or-buffer)))
- (unless (or (and wildcards find-file-wildcards
- (not (string-match "\\`/:" filename))
- (string-match "[[*?]" filename))
- (file-exists-p filename))
- (error "%s does not exist" filename))
- (let ((value (find-file-other-window filename wildcards)))
- (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
- (if (listp value) value (list value)))
- value))
+ (find-file--read-only #'find-file-other-window filename wildcards))
(defun find-file-read-only-other-frame (filename &optional wildcards)
"Edit file FILENAME in another frame but don't allow changes.
(interactive
(find-file-read-args "Find file read-only other frame: "
(confirm-nonexistent-file-or-buffer)))
- (unless (or (and wildcards find-file-wildcards
- (not (string-match "\\`/:" filename))
- (string-match "[[*?]" filename))
- (file-exists-p filename))
- (error "%s does not exist" filename))
- (let ((value (find-file-other-frame filename wildcards)))
- (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
- (if (listp value) value (list value)))
- value))
+ (find-file--read-only #'find-file-other-frame filename wildcards))
(defun find-alternate-file-other-window (filename &optional wildcards)
"Find file FILENAME as a replacement for the file in the next window.
(other-window 1)
(find-alternate-file filename wildcards))))
-(defvar kill-buffer-hook) ; from buffer.c
+;; Defined and used in buffer.c, but not as a DEFVAR_LISP.
+(defvar kill-buffer-hook nil
+ "Hook run when a buffer is killed.
+The buffer being killed is current while the hook is running.
+See `kill-buffer'.
+
+Note: Be careful with let-binding this hook considering it is
+frequently used for cleanup.")
(defun find-alternate-file (filename &optional wildcards)
"Find file FILENAME, select its buffer, kill previous buffer.
t)))
(unless (run-hook-with-args-until-failure 'kill-buffer-query-functions)
(error "Aborted"))
- (when (and (buffer-modified-p) buffer-file-name)
- (if (yes-or-no-p (format "Buffer %s is modified; save it first? "
- (buffer-name)))
- (save-buffer)
- (unless (yes-or-no-p "Kill and replace the buffer without saving it? ")
- (error "Aborted"))))
+ (and (buffer-modified-p) buffer-file-name
+ (not (yes-or-no-p "Kill and replace the buffer without saving it? "))
+ (error "Aborted"))
(let ((obuf (current-buffer))
(ofile buffer-file-name)
(onum buffer-file-number)
"Create a suitably named buffer for visiting FILENAME, and return it.
FILENAME (sans directory) is used unchanged if that name is free;
otherwise a string <2> or <3> or ... is appended to get an unused name.
-Spaces at the start of FILENAME (sans directory) are removed."
+
+Emacs treats buffers whose names begin with a space as internal buffers.
+To avoid confusion when visiting a file whose name begins with a space,
+this function prepends a \"|\" to the final result if necessary."
(let ((lastname (file-name-nondirectory filename)))
(if (string= lastname "")
(setq lastname filename))
- (save-match-data
- (string-match "^ *\\(.*\\)" lastname)
- (generate-new-buffer (match-string 1 lastname)))))
+ (generate-new-buffer (if (string-match-p "\\` " lastname)
+ (concat "|" lastname)
+ lastname))))
(defun generate-new-buffer (name)
"Create and return a buffer with a name based on NAME.
"Regexp to match the automounter prefix in a directory name."
:group 'files
:type 'regexp)
-(make-obsolete-variable 'automount-dir-prefix 'directory-abbrev-alist "24.2")
+(make-obsolete-variable 'automount-dir-prefix 'directory-abbrev-alist "24.3")
(defvar abbreviated-home-dir nil
"The user's homedir abbreviated according to `directory-abbrev-alist'.")
(eq read-only buffer-file-read-only)
(eq read-only buffer-read-only))
(when (or nowarn
- (let ((question
- (format "File %s is %s on disk. Change buffer mode? "
- buffer-file-name
- (if read-only "read-only" "writable"))))
+ (let* ((new-status
+ (if read-only "read-only" "writable"))
+ (question
+ (format "File %s is %s on disk. Make buffer %s, too? "
+ buffer-file-name
+ new-status new-status)))
(y-or-n-p question)))
(setq buffer-read-only read-only)))
(setq buffer-file-read-only read-only))
- (when (and (not (eq (not (null rawfile))
- (not (null find-file-literally))))
- (not nonexistent)
- ;; It is confusing to ask whether to visit
- ;; non-literally if they have the file in
- ;; hexl-mode or image-mode.
- (not (memq major-mode '(hexl-mode image-mode))))
+ (unless (or (eq (null rawfile) (null find-file-literally))
+ nonexistent
+ ;; It is confusing to ask whether to visit
+ ;; non-literally if they have the file in
+ ;; hexl-mode or image-mode.
+ (memq major-mode '(hexl-mode image-mode)))
(if (buffer-modified-p)
(if (y-or-n-p
(format
(set-buffer-multibyte nil)
(setq buffer-file-coding-system 'no-conversion)
(set-buffer-major-mode buf)
- (make-local-variable 'find-file-literally)
- (setq find-file-literally t))
+ (setq-local find-file-literally t))
(after-find-file error (not nowarn)))
(current-buffer))))
\f
(after-insert-file-functions nil)
(coding-system-for-read 'no-conversion)
(coding-system-for-write 'no-conversion)
- (find-buffer-file-type-function
- (if (fboundp 'find-buffer-file-type)
- (symbol-function 'find-buffer-file-type)
- nil))
(inhibit-file-name-handlers
+ ;; FIXME: Yuck!! We should turn insert-file-contents-literally
+ ;; into a file operation instead!
(append '(jka-compr-handler image-file-handler epa-file-handler)
inhibit-file-name-handlers))
(inhibit-file-name-operation 'insert-file-contents))
- (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)))))
+ (insert-file-contents filename visit beg end replace)))
(defun insert-file-1 (filename insert-func)
(if (file-directory-p filename)
\(Its calling sequence is different; see its documentation)."
(interactive "*fInsert file literally: ")
(insert-file-1 filename #'insert-file-contents-literally))
+(put 'insert-file-literally 'interactive-only 'insert-file-contents-literally)
(defvar find-file-literally nil
"Non-nil if this buffer was made by `find-file-literally' or equivalent.
(setq buffer-read-only t))
(unless nomodes
(when (and view-read-only view-mode)
- (view-mode-disable))
+ (view-mode -1))
(normal-mode t)
;; If requested, add a newline at the end of the file.
(and (memq require-final-newline '(visit visit-save))
(not buffer-read-only)
(save-excursion
(goto-char (point-max))
- (insert "\n")))
+ (ignore-errors (insert "\n"))))
(when (and buffer-read-only
view-read-only
(not (eq (get major-mode 'mode-class) 'special)))
or from Lisp without specifying the optional argument FIND-FILE;
in that case, this function acts as if `enable-local-variables' were t."
(interactive)
- (funcall (or (default-value 'major-mode) 'fundamental-mode))
+ (fundamental-mode)
(let ((enable-local-variables (or (not find-file) enable-local-variables)))
;; FIXME this is less efficient than it could be, since both
;; s-a-m and h-l-v may parse the same regions, looking for "mode:".
;; .PROCESSORNAME-gdbinit so that the host and target gdbinit files
;; don't interfere with each other.
("/\\.[a-z0-9-]*gdbinit" . gdb-script-mode)
+ ;; GDB 7.5 introduced OBJFILE-gdb.gdb script files; e.g. a file
+ ;; named 'emacs-gdb.gdb', if it exists, will be automatically
+ ;; loaded when GDB reads an objfile called 'emacs'.
+ ("-gdb\\.gdb" . gdb-script-mode)
("[cC]hange\\.?[lL]og?\\'" . change-log-mode)
("[cC]hange[lL]og[-.][0-9]+\\'" . change-log-mode)
("\\$CHANGE_LOG\\$\\.TXT" . change-log-mode)
("\\.scm\\.[0-9]*\\'" . scheme-mode)
("\\.[ck]?sh\\'\\|\\.shar\\'\\|/\\.z?profile\\'" . sh-mode)
("\\.bash\\'" . sh-mode)
- ("\\(/\\|\\`\\)\\.\\(bash_profile\\|z?login\\|bash_login\\|z?logout\\)\\'" . sh-mode)
- ("\\(/\\|\\`\\)\\.\\(bash_logout\\|shrc\\|[kz]shrc\\|bashrc\\|t?cshrc\\|esrc\\)\\'" . sh-mode)
+ ("\\(/\\|\\`\\)\\.\\(bash_\\(profile\\|history\\|log\\(in\\|out\\)\\)\\|z?log\\(in\\|out\\)\\)\\'" . sh-mode)
+ ("\\(/\\|\\`\\)\\.\\(shrc\\|[kz]shrc\\|bashrc\\|t?cshrc\\|esrc\\)\\'" . sh-mode)
("\\(/\\|\\`\\)\\.\\([kz]shenv\\|xinitrc\\|startxrc\\|xsession\\)\\'" . sh-mode)
("\\.m?spec\\'" . sh-mode)
("\\.m[mes]\\'" . nroff-mode)
("\\.\\(\
arc\\|zip\\|lzh\\|lha\\|zoo\\|[jew]ar\\|xpi\\|rar\\|7z\\|\
ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
- ("\\.\\(sx[dmicw]\\|od[fgpst]\\|oxt\\)\\'" . archive-mode) ;OpenOffice.org
+ ("\\.oxt\\'" . archive-mode) ;(Open|Libre)Office extensions.
("\\.\\(deb\\|[oi]pk\\)\\'" . archive-mode) ; Debian/Opkg packages.
;; Mailer puts message to be edited in
;; /tmp/Re.... or Message
("\\.js\\'" . javascript-mode)
("\\.json\\'" . javascript-mode)
("\\.[ds]?vh?\\'" . verilog-mode)
+ ("\\.by\\'" . bovine-grammar-mode)
+ ("\\.wy\\'" . wisent-grammar-mode)
;; .emacs or .gnus or .viper following a directory delimiter in
;; Unix, MSDOG or VMS syntax.
("[]>:/\\]\\..*\\(emacs\\|gnus\\|viper\\)\\'" . emacs-lisp-mode)
("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode)
("\\.\\(dif\\|pat\\)\\'" . diff-mode) ; for MSDOG
("\\.[eE]?[pP][sS]\\'" . ps-mode)
- ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|dvi\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe)
+ ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|djvu\\|dvi\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe)
("configure\\.\\(ac\\|in\\)\\'" . autoconf-mode)
("\\.s\\(v\\|iv\\|ieve\\)\\'" . sieve-mode)
("BROWSE\\'" . ebrowse-tree-mode)
(mapcar
(lambda (l)
(cons (purecopy (car l)) (cdr l)))
- '(("perl" . perl-mode)
- ("perl5" . perl-mode)
- ("miniperl" . perl-mode)
- ("wish" . tcl-mode)
- ("wishx" . tcl-mode)
- ("tcl" . tcl-mode)
- ("tclsh" . tcl-mode)
+ '(("\\(mini\\)?perl5?" . perl-mode)
+ ("wishx?" . tcl-mode)
+ ("tcl\\(sh\\)?" . tcl-mode)
+ ("expect" . tcl-mode)
+ ("octave" . octave-mode)
("scm" . scheme-mode)
- ("ash" . sh-mode)
- ("bash" . sh-mode)
- ("bash2" . sh-mode)
- ("csh" . sh-mode)
- ("dtksh" . sh-mode)
+ ("[acjkwz]sh" . sh-mode)
+ ("r?bash2?" . sh-mode)
+ ("dash" . sh-mode)
+ ("\\(dt\\|pd\\|w\\)ksh" . sh-mode)
("es" . sh-mode)
- ("itcsh" . sh-mode)
- ("jsh" . sh-mode)
- ("ksh" . sh-mode)
+ ("i?tcsh" . sh-mode)
("oash" . sh-mode)
- ("pdksh" . sh-mode)
- ("rbash" . sh-mode)
("rc" . sh-mode)
("rpm" . sh-mode)
- ("sh" . sh-mode)
- ("sh5" . sh-mode)
- ("tcsh" . sh-mode)
- ("wksh" . sh-mode)
- ("wsh" . sh-mode)
- ("zsh" . sh-mode)
+ ("sh5?" . sh-mode)
("tail" . text-mode)
("more" . text-mode)
("less" . text-mode)
("emacs" . emacs-lisp-mode)))
"Alist mapping interpreter names to major modes.
This is used for files whose first lines match `auto-mode-interpreter-regexp'.
-Each element looks like (INTERPRETER . MODE).
-If INTERPRETER matches the name of the interpreter specified in the first line
-of a script, mode MODE is enabled.
+Each element looks like (REGEXP . MODE).
+If \\\\`REGEXP\\\\' matches the name (minus any directory part) of
+the interpreter specified in the first line of a script, enable
+major mode MODE.
See also `auto-mode-alist'.")
"\\.zoo\\'" "\\.[jew]ar\\'" "\\.xpi\\'" "\\.rar\\'"
"\\.7z\\'"
"\\.sx[dmicw]\\'" "\\.odt\\'"
+ "\\.diff\\'" "\\.patch\\'"
"\\.tiff?\\'" "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'"))
"List of regexps matching file names in which to ignore local variables.
This includes `-*-' lines as well as trailing \"Local Variables\" sections.
specifications, but are not really, or they may be containers for
member files with their own local variable sections, which are
not appropriate for the containing file.
-See also `inhibit-local-variables-suffixes'.")
+The function `inhibit-local-variables-p' uses this.")
(define-obsolete-variable-alias 'inhibit-first-line-modes-suffixes
'inhibit-local-variables-suffixes "24.1")
(defvar inhibit-local-variables-suffixes nil
"List of regexps matching suffixes to remove from file names.
-When checking `inhibit-local-variables-regexps', we first discard
-from the end of the file name anything that matches one of these regexps.")
+The function `inhibit-local-variables-p' uses this: when checking
+a file name, it first discards from the end of the name anything that
+matches one of these regexps.")
+
+;; Can't think of any situation in which you'd want this to be nil...
+(defvar inhibit-local-variables-ignore-case t
+ "Non-nil means `inhibit-local-variables-p' ignores case.")
-;; TODO explicitly add case-fold-search t?
(defun inhibit-local-variables-p ()
"Return non-nil if file local variables should be ignored.
This checks the file (or buffer) name against `inhibit-local-variables-regexps'
-and `inhibit-local-variables-suffixes'."
+and `inhibit-local-variables-suffixes'. If
+`inhibit-local-variables-ignore-case' is non-nil, this ignores case."
(let ((temp inhibit-local-variables-regexps)
(name (if buffer-file-name
(file-name-sans-versions buffer-file-name)
- (buffer-name))))
+ (buffer-name)))
+ (case-fold-search inhibit-local-variables-ignore-case))
(while (let ((sufs inhibit-local-variables-suffixes))
(while (and sufs (not (string-match (car sufs) name)))
(setq sufs (cdr sufs)))
;; If we didn't, look for an interpreter specified in the first line.
;; As a special case, allow for things like "#!/bin/env perl", which
;; finds the interpreter anywhere in $PATH.
- (unless done
- (setq mode (save-excursion
- (goto-char (point-min))
- (if (looking-at auto-mode-interpreter-regexp)
- (match-string 2)
- ""))
- ;; Map interpreter name to a mode, signaling we're done at the
- ;; same time.
- done (assoc (file-name-nondirectory mode)
- interpreter-mode-alist))
- ;; If we found an interpreter mode to use, invoke it now.
- (if done
- (set-auto-mode-0 (cdr done) keep-mode-if-same)))
+ (and (not done)
+ (setq mode (save-excursion
+ (goto-char (point-min))
+ (if (looking-at auto-mode-interpreter-regexp)
+ (match-string 2))))
+ ;; Map interpreter name to a mode, signaling we're done at the
+ ;; same time.
+ (setq done (assoc-default
+ (file-name-nondirectory mode)
+ (mapcar (lambda (e)
+ (cons
+ (format "\\`%s\\'" (car e))
+ (cdr e)))
+ interpreter-mode-alist)
+ #'string-match-p))
+ ;; If we found an interpreter mode to use, invoke it now.
+ (set-auto-mode-0 done keep-mode-if-same))
;; Next try matching the buffer beginning against magic-mode-alist.
(unless done
(if (setq done (save-excursion
(cadr mode))
(setq mode (car mode)
name (substring name 0 (match-beginning 0)))
- (setq name))
+ (setq name nil))
(when mode
(set-auto-mode-0 mode keep-mode-if-same)
(setq done t))))))
(if (functionp re)
(funcall re)
(looking-at re)))))))
- (set-auto-mode-0 done keep-mode-if-same)))))
+ (set-auto-mode-0 done keep-mode-if-same)))
+ (unless done
+ (set-buffer-major-mode (current-buffer)))))
;; When `keep-mode-if-same' is set, we are working on behalf of
;; set-visited-file-name. In that case, if the major mode specified is the
;; This should be here at least as long as Emacs supports write-file-hooks.
'((add-hook 'write-file-hooks 'time-stamp)
(add-hook 'write-file-functions 'time-stamp)
- (add-hook 'before-save-hook 'time-stamp))
+ (add-hook 'before-save-hook 'time-stamp nil t)
+ (add-hook 'before-save-hook 'delete-trailing-whitespace nil t))
"Expressions that are considered safe in an `eval:' local variable.
Add expressions to this list if you want Emacs to evaluate them, when
they appear in an `eval' local variable specification, without first
RISKY-VARS is the list of those that are marked as risky.
If these settings come from directory-local variables, then
DIR-NAME is the name of the associated directory. Otherwise it is nil."
- (if noninteractive
- nil
- (save-window-excursion
- (let* ((name (or dir-name
- (if buffer-file-name
- (file-name-nondirectory buffer-file-name)
- (concat "buffer " (buffer-name)))))
- (offer-save (and (eq enable-local-variables t)
- unsafe-vars))
- (exit-chars
- (if offer-save '(?! ?y ?n ?\s ?\C-g) '(?y ?n ?\s ?\C-g)))
- (buf (pop-to-buffer "*Local Variables*"))
- prompt char)
- (set (make-local-variable 'cursor-type) nil)
+ (unless noninteractive
+ (let ((name (cond (dir-name)
+ (buffer-file-name
+ (file-name-nondirectory buffer-file-name))
+ ((concat "buffer " (buffer-name)))))
+ (offer-save (and (eq enable-local-variables t)
+ unsafe-vars))
+ (buf (get-buffer-create "*Local Variables*")))
+ ;; Set up the contents of the *Local Variables* buffer.
+ (with-current-buffer buf
(erase-buffer)
(cond
(unsafe-vars
(let ((print-escape-newlines t))
(prin1 (cdr elt) buf))
(insert "\n"))
- (setq prompt
- (format "Please type %s%s: "
- (if offer-save "y, n, or !" "y or n")
- (if (< (line-number-at-pos) (window-body-height))
- ""
- (push ?\C-v exit-chars)
- ", or C-v to scroll")))
- (goto-char (point-min))
- (while (null char)
- (setq char (read-char-choice prompt exit-chars t))
- (when (eq char ?\C-v)
- (condition-case nil
- (scroll-up)
- (error (goto-char (point-min))))
- (setq char nil)))
- (kill-buffer buf)
- (when (and offer-save (= char ?!) unsafe-vars)
- (customize-push-and-save 'safe-local-variable-values unsafe-vars))
- (memq char '(?! ?\s ?y))))))
+ (set (make-local-variable 'cursor-type) nil)
+ (set-buffer-modified-p nil)
+ (goto-char (point-min)))
+
+ ;; Display the buffer and read a choice.
+ (save-window-excursion
+ (pop-to-buffer buf)
+ (let* ((exit-chars '(?y ?n ?\s ?\C-g ?\C-v))
+ (prompt (format "Please type %s%s: "
+ (if offer-save "y, n, or !" "y or n")
+ (if (< (line-number-at-pos (point-max))
+ (window-body-height))
+ ""
+ (push ?\C-v exit-chars)
+ ", or C-v to scroll")))
+ char)
+ (if offer-save (push ?! exit-chars))
+ (while (null char)
+ (setq char (read-char-choice prompt exit-chars t))
+ (when (eq char ?\C-v)
+ (condition-case nil
+ (scroll-up)
+ (error (goto-char (point-min))
+ (recenter 1)))
+ (setq char nil)))
+ (when (and offer-save (= char ?!) unsafe-vars)
+ (customize-push-and-save 'safe-local-variable-values unsafe-vars))
+ (prog1 (memq char '(?! ?\s ?y))
+ (quit-window t)))))))
+
+(defconst hack-local-variable-regexp
+ "[ \t]*\\([^][;\"'?()\\ \t\n]+\\)[ \t]*:[ \t]*")
(defun hack-local-variables-prop-line (&optional mode-only)
"Return local variables specified in the -*- line.
;; (last ";" is optional).
;; If MODE-ONLY, just check for `mode'.
;; Otherwise, parse the -*- line into the RESULT alist.
- (while (and (or (not mode-only)
- (not result))
- (< (point) end))
- (unless (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
- (message "Malformed mode-line")
+ (while (not (or (and mode-only result)
+ (>= (point) end)))
+ (unless (looking-at hack-local-variable-regexp)
+ (message "Malformed mode-line: %S"
+ (buffer-substring-no-properties (point) end))
(throw 'malformed-line nil))
(goto-char (match-end 0))
;; There used to be a downcase here,
;; Obey `enable-local-eval'.
((eq var 'eval)
(when enable-local-eval
- (push elt all-vars)
- (or (eq enable-local-eval t)
- (hack-one-local-variable-eval-safep (eval (quote val)))
- (safe-local-variable-p var val)
- (push elt unsafe-vars))))
+ (let ((safe (or (hack-one-local-variable-eval-safep val)
+ ;; In case previously marked safe (bug#5636).
+ (safe-local-variable-p var val))))
+ ;; If not safe and e-l-v = :safe, ignore totally.
+ (when (or safe (not (eq enable-local-variables :safe)))
+ (push elt all-vars)
+ (or (eq enable-local-eval t)
+ safe
+ (push elt unsafe-vars))))))
;; Ignore duplicates (except `mode') in the present list.
((and (assq var all-vars) (not (eq var 'mode))) nil)
;; Accept known-safe variables.
(assq-delete-all (car elt) file-local-variables-alist)))
(push elt file-local-variables-alist)))))
+;; TODO? Warn once per file rather than once per session?
+(defvar hack-local-variables--warned-lexical nil)
+
(defun hack-local-variables (&optional mode-only)
"Parse and put into effect this buffer's local variables spec.
Uses `hack-local-variables-apply' to apply the variables.
(prefix
(concat "^" (regexp-quote
(buffer-substring (line-beginning-position)
- (match-beginning 0)))))
- beg)
+ (match-beginning 0))))))
(forward-line 1)
(let ((startpos (point))
(forward-line 1))
(goto-char (point-min))
- (while (and (not (eobp))
- (or (not mode-only)
- (not result)))
- ;; Find the variable name; strip whitespace.
- (skip-chars-forward " \t")
- (setq beg (point))
- (skip-chars-forward "^:\n")
- (if (eolp) (error "Missing colon in local variables entry"))
- (skip-chars-backward " \t")
- (let* ((str (buffer-substring beg (point)))
- (var (let ((read-circle nil))
- (read str)))
+ (while (not (or (eobp)
+ (and mode-only result)))
+ ;; Find the variable name;
+ (unless (looking-at hack-local-variable-regexp)
+ (error "Malformed local variable line: %S"
+ (buffer-substring-no-properties
+ (point) (line-end-position))))
+ (goto-char (match-end 1))
+ (let* ((str (match-string 1))
+ (var (intern str))
val val2)
(and (equal (downcase (symbol-name var)) "mode")
(setq var 'mode))
"-minor\\'"
(setq val2 (downcase (symbol-name val)))))
(setq result (intern (concat val2 "-mode"))))
- (unless (eq var 'coding)
- (condition-case nil
- (push (cons (if (eq var 'eval)
- 'eval
- (indirect-variable var))
- val) result)
- (error nil)))))
+ (cond ((eq var 'coding))
+ ((eq var 'lexical-binding)
+ (unless hack-local-variables--warned-lexical
+ (setq hack-local-variables--warned-lexical t)
+ (display-warning
+ :warning
+ (format "%s: `lexical-binding' at end of file unreliable"
+ (file-name-nondirectory
+ (or buffer-file-name ""))))))
+ (t
+ (ignore-errors
+ (push (cons (if (eq var 'eval)
+ 'eval
+ (indirect-variable var))
+ val) result))))))
(forward-line 1))))))))
;; Now we've read all the local variables.
;; If MODE-ONLY is non-nil, return whether the mode was specified.
(setq ok t)))
ok))))))))
+(defun hack-one-local-variable--obsolete (var)
+ (let ((o (get var 'byte-obsolete-variable)))
+ (when o
+ (let ((instead (nth 0 o))
+ (since (nth 2 o)))
+ (message "%s is obsolete%s; %s"
+ var (if since (format " (since %s)" since))
+ (if (stringp instead) instead
+ (format "use `%s' instead" instead)))))))
+
(defun hack-one-local-variable (var val)
"Set local variable VAR with value VAL.
If VAR is `mode', call `VAL-mode' as a function unless it's
already the major mode."
- (cond ((eq var 'mode)
- (let ((mode (intern (concat (downcase (symbol-name val))
- "-mode"))))
- (unless (eq (indirect-function mode)
- (indirect-function major-mode))
- (if (memq mode minor-mode-list)
- ;; A minor mode must be passed an argument.
- ;; Otherwise, if the user enables the minor mode in a
- ;; major mode hook, this would toggle it off.
- (funcall mode 1)
- (funcall mode)))))
- ((eq var 'eval)
- (save-excursion (eval val)))
- (t
- ;; Make sure the string has no text properties.
- ;; Some text properties can get evaluated in various ways,
- ;; so it is risky to put them on with a local variable list.
- (if (stringp val)
- (set-text-properties 0 (length val) nil val))
- (set (make-local-variable var) val))))
+ (pcase var
+ (`mode
+ (let ((mode (intern (concat (downcase (symbol-name val))
+ "-mode"))))
+ (unless (eq (indirect-function mode)
+ (indirect-function major-mode))
+ (funcall mode))))
+ (`eval
+ (pcase val
+ (`(add-hook ',hook . ,_) (hack-one-local-variable--obsolete hook)))
+ (save-excursion (eval val)))
+ (_
+ (hack-one-local-variable--obsolete var)
+ ;; Make sure the string has no text properties.
+ ;; Some text properties can get evaluated in various ways,
+ ;; so it is risky to put them on with a local variable list.
+ (if (stringp val)
+ (set-text-properties 0 (length val) nil val))
+ (set (make-local-variable var) val))))
\f
;;; Handling directory-local variables, aka project settings.
CLASS is the name of a variable class (a symbol).
MTIME is the recorded modification time of the directory-local
variables file associated with this entry. This time is a list
-of two integers (the same format as `file-attributes'), and is
+of integers (the same format as `file-attributes'), and is
used to test whether the cache entry is still valid.
Alternatively, MTIME can be nil, which means the entry is always
considered valid.")
The new class name is the same as the directory in which FILE
is found. Returns the new class name."
(with-temp-buffer
- ;; This is with-demoted-errors, but we want to mention dir-locals
- ;; in any error message.
- (let (err)
- (condition-case err
- (progn
- (insert-file-contents file)
- (let* ((dir-name (file-name-directory file))
- (class-name (intern dir-name))
- (variables (let ((read-circle nil))
- (read (current-buffer)))))
- (dir-locals-set-class-variables class-name variables)
- (dir-locals-set-directory-class dir-name class-name
- (nth 5 (file-attributes file)))
- class-name))
- (error (message "Error reading dir-locals: %S" err) nil)))))
+ (with-demoted-errors "Error reading dir-locals: %S"
+ (insert-file-contents file)
+ (unless (zerop (buffer-size))
+ (let* ((dir-name (file-name-directory file))
+ (class-name (intern dir-name))
+ (variables (let ((read-circle nil))
+ (read (current-buffer)))))
+ (dir-locals-set-class-variables class-name variables)
+ (dir-locals-set-directory-class dir-name class-name
+ (nth 5 (file-attributes file)))
+ class-name)))))
(defcustom enable-remote-dir-locals nil
"Non-nil means dir-local variables will be applied to remote files."
- :version "24.2"
+ :version "24.3"
:type 'boolean
:group 'find-file)
+(defvar hack-dir-local-variables--warned-coding nil)
+
(defun hack-dir-local-variables ()
"Read per-directory local variables for the current buffer.
Store the directory-local variables in `dir-local-variables-alist'
-and `file-local-variables-alist', without applying them."
+and `file-local-variables-alist', without applying them.
+
+This does nothing if either `enable-local-variables' or
+`enable-dir-local-variables' are nil."
(when (and enable-local-variables
+ enable-dir-local-variables
(or enable-remote-dir-locals
(not (file-remote-p (or (buffer-file-name)
default-directory)))))
(dir-locals-get-class-variables class) dir-name nil)))
(when variables
(dolist (elt variables)
- (unless (memq (car elt) '(eval mode))
- (setq dir-local-variables-alist
- (assq-delete-all (car elt) dir-local-variables-alist)))
- (push elt dir-local-variables-alist))
+ (if (eq (car elt) 'coding)
+ (unless hack-dir-local-variables--warned-coding
+ (setq hack-dir-local-variables--warned-coding t)
+ (display-warning :warning
+ "Coding cannot be specified by dir-locals"))
+ (unless (memq (car elt) '(eval mode))
+ (setq dir-local-variables-alist
+ (assq-delete-all (car elt) dir-local-variables-alist)))
+ (push elt dir-local-variables-alist)))
(hack-local-variables-filter variables dir-name)))))))
(defun hack-dir-local-variables-non-file-buffer ()
(or buffer-file-name (buffer-name))))))
(and confirm
(file-exists-p filename)
+ ;; NS does its own confirm dialog.
+ (not (and (eq (framep-on-display) 'ns)
+ (listp last-nonmenu-event)
+ use-dialog-box))
(or (y-or-n-p (format "File `%s' exists; overwrite? " filename))
(error "Canceled")))
(set-visited-file-name filename (not confirm))))
;; the one at the old location.
(vc-find-file-hook))
\f
+(defun file-extended-attributes (filename)
+ "Return an alist of extended attributes of file FILENAME.
+
+Extended attributes are platform-specific metadata about the file,
+such as SELinux context, list of ACL entries, etc."
+ `((acl . ,(file-acl filename))
+ (selinux-context . ,(file-selinux-context filename))))
+
+(defun set-file-extended-attributes (filename attributes)
+ "Set extended attributes of file FILENAME to ATTRIBUTES.
+
+ATTRIBUTES must be an alist of file attributes as returned by
+`file-extended-attributes'."
+ (dolist (elt attributes)
+ (let ((attr (car elt))
+ (val (cdr elt)))
+ (cond ((eq attr 'acl)
+ (set-file-acl filename val))
+ ((eq attr 'selinux-context)
+ (set-file-selinux-context filename val))))))
+\f
(defun backup-buffer ()
"Make a backup of the disk file visited by the current buffer, if appropriate.
This is normally done before saving the buffer the first time.
no longer accessible under its old name.
The value is non-nil after a backup was made by renaming.
-It has the form (MODES SELINUXCONTEXT BACKUPNAME).
+It has the form (MODES EXTENDED-ATTRIBUTES BACKUPNAME).
MODES is the result of `file-modes' on the original
file; this means that the caller, after saving the buffer, should change
the modes of the new file to agree with the old modes.
-SELINUXCONTEXT is the result of `file-selinux-context' on the original
-file; this means that the caller, after saving the buffer, should change
-the SELinux context of the new file to agree with the old context.
+EXTENDED-ATTRIBUTES is the result of `file-extended-attributes'
+on the original file; this means that the caller, after saving
+the buffer, should change the extended attributes of the new file
+to agree with the old attributes.
BACKUPNAME is the backup file name, which is the old file renamed."
(if (and make-backup-files (not backup-inhibited)
(not buffer-backed-up)
(y-or-n-p (format "Delete excess backup versions of %s? "
real-file-name)))))
(modes (file-modes buffer-file-name))
- (context (file-selinux-context buffer-file-name)))
+ (extended-attributes
+ (file-extended-attributes buffer-file-name)))
;; Actually write the back up file.
(condition-case ()
(if (or file-precious-flag
(and (integerp (nth 2 attr))
(integerp backup-by-copying-when-privileged-mismatch)
(<= (nth 2 attr) backup-by-copying-when-privileged-mismatch)))
- (or (nth 9 attr)
- (not (file-ownership-preserved-p real-file-name)))))))
- (backup-buffer-copy real-file-name backupname modes context)
+ (not (file-ownership-preserved-p
+ real-file-name t))))))
+ (backup-buffer-copy real-file-name
+ backupname modes
+ extended-attributes)
;; rename-file should delete old backup.
(rename-file real-file-name backupname t)
- (setq setmodes (list modes context backupname)))
+ (setq setmodes (list modes extended-attributes
+ backupname)))
(file-error
;; If trouble writing the backup, write it in
;; .emacs.d/%backup%.
(message "Cannot write backup file; backing up in %s"
backupname)
(sleep-for 1)
- (backup-buffer-copy real-file-name backupname modes context)))
+ (backup-buffer-copy real-file-name backupname
+ modes extended-attributes)))
(setq buffer-backed-up t)
;; Now delete the old versions, if desired.
(if delete-old-versions
setmodes)
(file-error nil))))))
-(defun backup-buffer-copy (from-name to-name modes context)
+(defun backup-buffer-copy (from-name to-name modes extended-attributes)
(let ((umask (default-file-modes)))
(unwind-protect
(progn
nil)))
;; Reset the umask.
(set-default-file-modes umask)))
- (and modes
- (set-file-modes to-name (logand modes #o1777)))
- (and context
- (set-file-selinux-context to-name context)))
+ ;; If set-file-extended-attributes fails, fall back on set-file-modes.
+ (unless (and extended-attributes
+ (with-demoted-errors
+ (set-file-extended-attributes to-name extended-attributes)))
+ (and modes
+ (set-file-modes to-name (logand modes #o1777)))))
(defvar file-name-version-regexp
"\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)"
(string-match (concat file-name-version-regexp "\\'")
name))))))
-(defun file-ownership-preserved-p (file)
- "Return t if deleting FILE and rewriting it would preserve the owner."
+(defun file-ownership-preserved-p (file &optional group)
+ "Return t if deleting FILE and rewriting it would preserve the owner.
+Return nil if FILE does not exist, or if deleting and recreating it
+might not preserve the owner. If GROUP is non-nil, check whether
+the group would be preserved too."
(let ((handler (find-file-name-handler file 'file-ownership-preserved-p)))
(if handler
- (funcall handler 'file-ownership-preserved-p file)
+ (funcall handler 'file-ownership-preserved-p file group)
(let ((attributes (file-attributes file 'integer)))
;; 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))
- ;; Files created on Windows by Administrator (RID=500)
- ;; have the Administrators group (RID=544) recorded as
- ;; their owner. Rewriting them will still preserve the
- ;; owner.
- (and (eq system-type 'windows-nt)
- (= (user-uid) 500) (= (nth 2 attributes) 544)))))))
+ (and (or (= (nth 2 attributes) (user-uid))
+ ;; Files created on Windows by Administrator (RID=500)
+ ;; have the Administrators group (RID=544) recorded as
+ ;; their owner. Rewriting them will still preserve the
+ ;; owner.
+ (and (eq system-type 'windows-nt)
+ (= (user-uid) 500) (= (nth 2 attributes) 544)))
+ (or (not group)
+ ;; On BSD-derived systems files always inherit the parent
+ ;; directory's group, so skip the group-gid test.
+ (memq system-type '(berkeley-unix darwin gnu/kfreebsd))
+ (= (nth 3 attributes) (group-gid)))
+ (let* ((parent (or (file-name-directory file) "."))
+ (parent-attributes (file-attributes parent 'integer)))
+ (and parent-attributes
+ ;; On some systems, a file created in a setuid directory
+ ;; inherits that directory's owner.
+ (or
+ (= (nth 2 parent-attributes) (user-uid))
+ (string-match "^...[^sS]" (nth 8 parent-attributes)))
+ ;; On many systems, a file created in a setgid directory
+ ;; inherits that directory's group. On some systems
+ ;; this happens even if the setgid bit is not set.
+ (or (not group)
+ (= (nth 3 parent-attributes)
+ (nth 3 attributes)))))))))))
(defun file-name-sans-extension (filename)
"Return FILENAME sans final \"extension\".
(if period
"")))))
-(defcustom make-backup-file-name-function nil
+(defun file-name-base (&optional filename)
+ "Return the base name of the FILENAME: no directory, no extension.
+FILENAME defaults to `buffer-file-name'."
+ (file-name-sans-extension
+ (file-name-nondirectory (or filename (buffer-file-name)))))
+
+(defcustom make-backup-file-name-function
+ #'make-backup-file-name--default-function
"A function to use instead of the default `make-backup-file-name'.
-A value of nil gives the default `make-backup-file-name' behavior.
This could be buffer-local to do something special for specific
files. If you define it, you may need to change `backup-file-name-p'
See also `backup-directory-alist'."
:group 'backup
- :type '(choice (const :tag "Default" nil)
- (function :tag "Your function")))
+ :type '(function :tag "Your function"))
(defcustom backup-directory-alist nil
"Alist of filename patterns and backup directory names.
"Default `backup-enable-predicate' function.
Checks for files in `temporary-file-directory',
`small-temporary-file-directory', and /tmp."
- (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 (- (length temporary-file-directory)))))
- (let ((comp (compare-strings "/tmp" 0 nil
- name 0 nil)))
- ;; Directory is under /tmp.
- (and (not (eq comp t))
- (< comp (- (length "/tmp")))))
- (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 (- (length small-temporary-file-directory)))))))))
+ (let ((temporary-file-directory temporary-file-directory)
+ caseless)
+ ;; On MS-Windows, file-truename will convert short 8+3 aliases to
+ ;; their long file-name equivalents, so compare-strings does TRT.
+ (if (memq system-type '(ms-dos windows-nt))
+ (setq temporary-file-directory (file-truename temporary-file-directory)
+ name (file-truename name)
+ caseless t))
+ (not (or (let ((comp (compare-strings temporary-file-directory 0 nil
+ name 0 nil caseless)))
+ ;; Directory is under temporary-file-directory.
+ (and (not (eq comp t))
+ (< comp (- (length temporary-file-directory)))))
+ (let ((comp (compare-strings "/tmp" 0 nil
+ name 0 nil)))
+ ;; Directory is under /tmp.
+ (and (not (eq comp t))
+ (< comp (- (length "/tmp")))))
+ (if small-temporary-file-directory
+ (let ((comp (compare-strings small-temporary-file-directory
+ 0 nil
+ name 0 nil caseless)))
+ ;; Directory is under small-temporary-file-directory.
+ (and (not (eq comp t))
+ (< comp (- (length small-temporary-file-directory))))))))))
(defun make-backup-file-name (file)
"Create the non-numeric backup file name for 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.
+The value of `make-backup-file-name-function' 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
+By default, 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) "~"))))
+ (funcall (or make-backup-file-name-function
+ #'make-backup-file-name--default-function)
+ file))
+
+(defun make-backup-file-name--default-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'."
"Convert FILENAME to be relative to DIRECTORY (default: `default-directory').
This function returns a relative file name which is equivalent to FILENAME
when used with that default directory as the default.
+If FILENAME is a relative file name, it will be interpreted as existing in
+`default-directory'.
If FILENAME and DIRECTORY lie on different machines or on different drives
on a DOS/Windows machine, it returns FILENAME in expanded form."
(save-match-data
default-directory))))
(setq filename (expand-file-name filename))
(let ((fremote (file-remote-p filename))
- (dremote (file-remote-p directory)))
+ (dremote (file-remote-p directory))
+ (fold-case (or (memq system-type '(ms-dos cygwin windows-nt))
+ read-file-name-completion-ignore-case)))
(if ;; Conditions for separate trees
(or
;; Test for different filesystems on DOS/Windows
(memq system-type '(ms-dos cygwin windows-nt))
(or
;; Test for different drive letters
- (not (eq t (compare-strings filename 0 2 directory 0 2)))
+ (not (eq t (compare-strings filename 0 2 directory 0 2 fold-case)))
;; Test for UNCs on different servers
(not (eq t (compare-strings
(progn
(while (not
(or
(eq t (compare-strings filename-dir nil (length directory)
- directory nil nil case-fold-search))
+ directory nil nil fold-case))
(eq t (compare-strings filename nil (length directory)
- directory nil nil case-fold-search))))
+ directory nil nil fold-case))))
(setq directory (file-name-directory (substring directory 0 -1))
ancestor (if (equal ancestor ".")
".."
(concat "../" ancestor))))
;; Now ancestor is empty, or .., or ../.., etc.
(if (eq t (compare-strings filename nil (length directory)
- directory nil nil case-fold-search))
+ directory nil nil fold-case))
;; We matched within FILENAME's directory part.
;; Add the rest of FILENAME onto ANCESTOR.
(let ((rest (substring filename (length directory))))
;; We matched FILENAME's directory equivalent.
ancestor))))))
\f
-(defun save-buffer (&optional args)
+(defun save-buffer (&optional arg)
"Save current buffer in visited file if modified.
Variations are described below.
to become a backup when the next save is done,
and unconditionally makes the previous version into a backup file.
-With a numeric argument of 0, never make the previous version
+With a numeric prefix argument of 0, never make the previous version
into a backup file.
If a file's name is FOO, the names of its numbered backup versions are
See the subroutine `basic-save-buffer' for more information."
(interactive "p")
(let ((modp (buffer-modified-p))
- (make-backup-files (or (and make-backup-files (not (eq args 0)))
- (memq args '(16 64)))))
- (and modp (memq args '(16 64)) (setq buffer-backed-up nil))
+ (make-backup-files (or (and make-backup-files (not (eq arg 0)))
+ (memq arg '(16 64)))))
+ (and modp (memq arg '(16 64)) (setq buffer-backed-up nil))
;; We used to display the message below only for files > 50KB, but
;; then Rmail-mbox never displays it due to buffer swapping. If
;; the test is ever re-introduced, be sure to handle saving of
(if (and modp (buffer-file-name))
(message "Saving file %s..." (buffer-file-name)))
(basic-save-buffer)
- (and modp (memq args '(4 64)) (setq buffer-backed-up nil))))
+ (and modp (memq arg '(4 64)) (setq buffer-backed-up nil))))
(defun delete-auto-save-file-if-necessary (&optional force)
"Delete auto-save file for current buffer if `delete-auto-save-files' is t.
(not (file-exists-p buffer-file-name))))
(let ((recent-save (recent-auto-save-p))
setmodes)
- ;; If buffer has no file name, ask user for one.
+ ;; If buffer has no file name, ask user for one.
(or buffer-file-name
- (let ((filename
- (expand-file-name
- (read-file-name "File to save in: ") nil)))
- (if (file-exists-p filename)
- (if (file-directory-p filename)
- ;; Signal an error if the user specified the name of an
- ;; existing directory.
- (error "%s is a directory" filename)
- (unless (y-or-n-p (format "File `%s' exists; overwrite? "
- filename))
- (error "Canceled")))
- ;; Signal an error if the specified name refers to a
- ;; non-existing directory.
- (let ((dir (file-name-directory filename)))
- (unless (file-directory-p dir)
- (if (file-exists-p dir)
- (error "%s is not a directory" dir)
- (error "%s: no such directory" dir)))))
- (set-visited-file-name filename)))
+ (let ((filename
+ (expand-file-name
+ (read-file-name "File to save in: "
+ nil (expand-file-name (buffer-name))))))
+ (if (file-exists-p filename)
+ (if (file-directory-p filename)
+ ;; Signal an error if the user specified the name of an
+ ;; existing directory.
+ (error "%s is a directory" filename)
+ (unless (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
(insert ?\n))))
;; Support VC version backups.
(vc-before-save)
- (run-hooks 'before-save-hook)
+ ;; Don't let errors prevent saving the buffer.
+ (with-demoted-errors (run-hooks 'before-save-hook))
(or (run-hook-with-args-until-success 'write-contents-functions)
(run-hook-with-args-until-success 'local-write-file-hooks)
(run-hook-with-args-until-success 'write-file-functions)
;; If a hook returned t, file is already "written".
;; Otherwise, write it the usual way now.
- (setq setmodes (basic-save-buffer-1)))
+ (let ((dir (file-name-directory
+ (expand-file-name buffer-file-name))))
+ (unless (file-exists-p dir)
+ (if (y-or-n-p
+ (format "Directory `%s' does not exist; create? " dir))
+ (make-directory dir t)
+ (error "Canceled")))
+ (setq setmodes (basic-save-buffer-1))))
;; Now we have saved the current buffer. Let's make sure
;; that buffer-file-coding-system is fixed to what
;; actually used for saving by binding it locally.
(if setmodes
(condition-case ()
(progn
- (set-file-modes buffer-file-name (car setmodes))
- (set-file-selinux-context buffer-file-name (nth 1 setmodes)))
+ (unless
+ (with-demoted-errors
+ (set-file-modes buffer-file-name (car setmodes)))
+ (set-file-extended-attributes buffer-file-name
+ (nth 1 setmodes))))
(error nil))))
;; If the auto-save file was recent before this command,
;; delete it now.
;; This does the "real job" of writing a buffer into its visited file
;; and making a backup file. This is what is normally done
;; but inhibited if one of write-file-functions returns non-nil.
-;; It returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; It returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
(defun basic-save-buffer-1 ()
(prog1
(if save-buffer-coding-system
(basic-save-buffer-2))
(basic-save-buffer-2))
(if buffer-file-coding-system-explicit
- (setcar buffer-file-coding-system-explicit last-coding-system-used)
- (setq buffer-file-coding-system-explicit
- (cons last-coding-system-used nil)))))
+ (setcar buffer-file-coding-system-explicit last-coding-system-used))))
-;; This returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; This returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
(defun basic-save-buffer-2 ()
- (let (tempsetmodes setmodes)
+ (let (tempsetmodes setmodes writecoding)
(if (not (file-writable-p buffer-file-name))
(let ((dir (file-name-directory buffer-file-name)))
(if (not (file-directory-p dir))
buffer-file-name)))
(setq tempsetmodes t)
(error "Attempt to save to a file which you aren't allowed to write"))))))
+ ;; This may involve prompting, so do it now before backing up the file.
+ ;; Otherwise there can be a delay while the user answers the
+ ;; prompt during which the original file has been renamed. (Bug#13522)
+ (setq writecoding
+ ;; Args here should match write-region call below around
+ ;; which we use writecoding.
+ (choose-write-coding-system nil nil buffer-file-name nil t
+ buffer-file-truename))
(or buffer-backed-up
(setq setmodes (backup-buffer)))
(let* ((dir (file-name-directory buffer-file-name))
(setq setmodes (or setmodes
(list (or (file-modes buffer-file-name)
(logand ?\666 umask))
- (file-selinux-context buffer-file-name)
+ (file-extended-attributes buffer-file-name)
buffer-file-name)))
;; We succeeded in writing the temp file,
;; so rename it.
(cond ((and tempsetmodes (not setmodes))
;; Change the mode back, after writing.
(setq setmodes (list (file-modes buffer-file-name)
- (file-selinux-context buffer-file-name)
+ (file-extended-attributes buffer-file-name)
buffer-file-name))
- (set-file-modes buffer-file-name (logior (car setmodes) 128))
- (set-file-selinux-context buffer-file-name (nth 1 setmodes)))))
+ ;; If set-file-extended-attributes fails, fall back on
+ ;; set-file-modes.
+ (unless
+ (with-demoted-errors
+ (set-file-extended-attributes buffer-file-name
+ (nth 1 setmodes)))
+ (set-file-modes buffer-file-name
+ (logior (car setmodes) 128))))))
(let (success)
(unwind-protect
- (progn
;; Pass in nil&nil rather than point-min&max to indicate
;; we're saving the buffer rather than just a region.
;; write-region-annotate-functions may make us of it.
+ (let ((coding-system-for-write writecoding)
+ (coding-system-require-warning nil))
(write-region nil nil
buffer-file-name nil t buffer-file-truename)
(setq success t))
(length autosaved-buffers)
(mapconcat 'identity autosaved-buffers ", "))))))))
\f
+(defun clear-visited-file-modtime ()
+ "Clear out records of last mod time of visited file.
+Next attempt to save will certainly not complain of a discrepancy."
+ (set-visited-file-modtime 0))
+
(defun not-modified (&optional arg)
"Mark current buffer as unmodified, not needing to be saved.
With prefix ARG, mark buffer as modified, so \\[save-buffer] will save.
"Modification-flag cleared"))
(set-buffer-modified-p arg))
-(defun toggle-read-only (&optional arg)
- "Change whether this buffer is read-only.
-With prefix argument ARG, make the buffer read-only if ARG is
-positive, otherwise make it writable. If buffer is read-only
-and `view-read-only' is non-nil, enter view mode.
-
-This function is usually the wrong thing to use in a Lisp program.
-It can have side-effects beyond changing the read-only status of a buffer
-\(e.g., enabling view mode), and does not affect read-only regions that
-are caused by text properties. To make a buffer read-only in Lisp code,
-set `buffer-read-only'. To ignore read-only status (whether due to text
-properties or buffer state) and make changes, temporarily bind
-`inhibit-read-only'."
- (interactive "P")
- (if (and arg
- (if (> (prefix-numeric-value arg) 0) buffer-read-only
- (not buffer-read-only))) ; If buffer-read-only is set correctly,
- nil ; do nothing.
- ;; Toggle.
- (progn
- (cond
- ((and buffer-read-only view-mode)
- (View-exit-and-edit)
- (make-local-variable 'view-read-only)
- (setq view-read-only t)) ; Must leave view mode.
- ((and (not buffer-read-only) view-read-only
- ;; If view-mode is already active, `view-mode-enter' is a nop.
- (not view-mode)
- (not (eq (get major-mode 'mode-class) 'special)))
- (view-mode-enter))
- (t (setq buffer-read-only (not buffer-read-only))
- (force-mode-line-update))))
- (message "Read-only %s for this buffer"
- (if buffer-read-only "enabled" "disabled"))))
+(defun toggle-read-only (&optional arg interactive)
+ (declare (obsolete read-only-mode "24.3"))
+ (interactive (list current-prefix-arg t))
+ (if interactive
+ (call-interactively 'read-only-mode)
+ (read-only-mode (or arg 'toggle))))
(defun insert-file (filename)
"Insert contents of file FILENAME into buffer after point.
\(Its calling sequence is different; see its documentation)."
(interactive "*fInsert file: ")
(insert-file-1 filename #'insert-file-contents))
+(put 'insert-file 'interactive-only 'insert-file-contents)
(defun append-to-file (start end filename)
"Append the contents of the region to the end of file FILENAME.
\f
(put 'revert-buffer-function 'permanent-local t)
-(defvar revert-buffer-function nil
+(defvar revert-buffer-function #'revert-buffer--default
"Function to use to revert this buffer, or nil to do the default.
The function receives two arguments IGNORE-AUTO and NOCONFIRM,
-which are the arguments that `revert-buffer' received.")
+which are the arguments that `revert-buffer' received.
+It also has access to the `preserve-modes' argument of `revert-buffer'
+via the `revert-buffer-preserve-modes' dynamic variable.")
(put 'revert-buffer-insert-file-contents-function 'permanent-local t)
-(defvar revert-buffer-insert-file-contents-function nil
+(defvar revert-buffer-insert-file-contents-function
+ #'revert-buffer-insert-file-contents--default-function
"Function to use to insert contents when reverting this buffer.
Gets two args, first the nominal file name to use,
and second, t if reading the auto-save file.
The function you specify is responsible for updating (or preserving) point.")
-(defvar buffer-stale-function nil
+(defun buffer-stale--default-function (&optional _noconfirm)
+ (and buffer-file-name
+ (file-readable-p buffer-file-name)
+ (not (verify-visited-file-modtime (current-buffer)))))
+
+(defvar buffer-stale-function #'buffer-stale--default-function
"Function to check whether a non-file buffer needs reverting.
This should be a function with one optional argument NOCONFIRM.
Auto Revert Mode passes t for NOCONFIRM. The function should return
(defvar revert-buffer-internal-hook)
+;; `revert-buffer-function' was defined long ago to be a function of only
+;; 2 arguments, so we have to use a dynbind variable to pass the
+;; `preserve-modes' argument of `revert-buffer'.
+(defvar revert-buffer-preserve-modes)
+
(defun revert-buffer (&optional ignore-auto noconfirm preserve-modes)
"Replace current buffer text with the text of the visited file on disk.
This undoes all changes since the file was visited or saved.
;; reversal of the argument sense. So I'm just changing the user
;; interface, but leaving the programmatic interface the same.
(interactive (list (not current-prefix-arg)))
- (if revert-buffer-function
- (let ((revert-buffer-in-progress-p t))
- (funcall revert-buffer-function ignore-auto noconfirm))
- (with-current-buffer (or (buffer-base-buffer (current-buffer))
- (current-buffer))
- (let* ((revert-buffer-in-progress-p t)
- (auto-save-p (and (not ignore-auto)
- (recent-auto-save-p)
- buffer-auto-save-file-name
- (file-readable-p buffer-auto-save-file-name)
- (y-or-n-p
- "Buffer has been auto-saved recently. Revert from auto-save file? ")))
- (file-name (if auto-save-p
- buffer-auto-save-file-name
- buffer-file-name)))
- (cond ((null file-name)
- (error "Buffer does not seem to be associated with any file"))
- ((or noconfirm
- (and (not (buffer-modified-p))
- (catch 'found
- (dolist (regexp revert-without-query)
- (when (string-match regexp file-name)
- (throw 'found t)))))
- (yes-or-no-p (format "Revert buffer from file %s? "
- file-name)))
- (run-hooks 'before-revert-hook)
- ;; If file was backed up but has changed since,
- ;; we should make another backup.
- (and (not auto-save-p)
- (not (verify-visited-file-modtime (current-buffer)))
- (setq buffer-backed-up nil))
- ;; Effectively copy the after-revert-hook status,
- ;; since after-find-file will clobber it.
- (let ((global-hook (default-value 'after-revert-hook))
- (local-hook (when (local-variable-p 'after-revert-hook)
- after-revert-hook))
- (inhibit-read-only t))
- (cond
- (revert-buffer-insert-file-contents-function
- (unless (eq buffer-undo-list t)
- ;; Get rid of all undo records for this buffer.
- (setq buffer-undo-list nil))
- ;; Don't make undo records for the reversion.
- (let ((buffer-undo-list t))
- (funcall revert-buffer-insert-file-contents-function
- file-name auto-save-p)))
- ((not (file-exists-p file-name))
- (error (if buffer-file-number
- "File %s no longer exists!"
- "Cannot revert nonexistent file %s")
- file-name))
- ((not (file-readable-p file-name))
- (error (if buffer-file-number
- "File %s no longer readable!"
- "Cannot revert unreadable file %s")
- file-name))
- (t
- ;; 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)
- (let ((coding-system-for-read
- ;; Auto-saved file should be read by Emacs's
- ;; internal coding.
- (if auto-save-p 'auto-save-coding
- (or coding-system-for-read
- (and
- buffer-file-coding-system-explicit
- (car buffer-file-coding-system-explicit))))))
- (if (and (not enable-multibyte-characters)
- coding-system-for-read
- (not (memq (coding-system-base
- coding-system-for-read)
- '(no-conversion raw-text))))
- ;; As a coding system suitable for multibyte
- ;; buffer is specified, make the current
- ;; buffer multibyte.
- (set-buffer-multibyte t))
-
- ;; This force after-insert-file-set-coding
- ;; (called from insert-file-contents) to set
- ;; buffer-file-coding-system to a proper value.
- (kill-local-variable 'buffer-file-coding-system)
-
- ;; Note that this preserves point in an intelligent way.
- (if preserve-modes
- (let ((buffer-file-format buffer-file-format))
- (insert-file-contents file-name (not auto-save-p)
- nil nil t))
- (insert-file-contents file-name (not auto-save-p)
- nil nil t)))))
- ;; 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 nil preserve-modes)
- ;; Run after-revert-hook as it was before we reverted.
- (setq-default revert-buffer-internal-hook global-hook)
- (if local-hook
- (set (make-local-variable 'revert-buffer-internal-hook)
- local-hook)
- (kill-local-variable 'revert-buffer-internal-hook))
- (run-hooks 'revert-buffer-internal-hook))
- t))))))
+ (let ((revert-buffer-in-progress-p t)
+ (revert-buffer-preserve-modes preserve-modes))
+ (funcall (or revert-buffer-function #'revert-buffer--default)
+ ignore-auto noconfirm)))
+(defun revert-buffer--default (ignore-auto noconfirm)
+ (with-current-buffer (or (buffer-base-buffer (current-buffer))
+ (current-buffer))
+ (let* ((auto-save-p (and (not ignore-auto)
+ (recent-auto-save-p)
+ buffer-auto-save-file-name
+ (file-readable-p buffer-auto-save-file-name)
+ (y-or-n-p
+ "Buffer has been auto-saved recently. Revert from auto-save file? ")))
+ (file-name (if auto-save-p
+ buffer-auto-save-file-name
+ buffer-file-name)))
+ (cond ((null file-name)
+ (error "Buffer does not seem to be associated with any file"))
+ ((or noconfirm
+ (and (not (buffer-modified-p))
+ (catch 'found
+ (dolist (regexp revert-without-query)
+ (when (string-match regexp file-name)
+ (throw 'found t)))))
+ (yes-or-no-p (format "Revert buffer from file %s? "
+ file-name)))
+ (run-hooks 'before-revert-hook)
+ ;; If file was backed up but has changed since,
+ ;; we should make another backup.
+ (and (not auto-save-p)
+ (not (verify-visited-file-modtime (current-buffer)))
+ (setq buffer-backed-up nil))
+ ;; Effectively copy the after-revert-hook status,
+ ;; since after-find-file will clobber it.
+ (let ((global-hook (default-value 'after-revert-hook))
+ (local-hook (when (local-variable-p 'after-revert-hook)
+ after-revert-hook))
+ (inhibit-read-only t))
+ ;; FIXME: Throw away undo-log when preserve-modes is nil?
+ (funcall
+ (or revert-buffer-insert-file-contents-function
+ #'revert-buffer-insert-file-contents--default-function)
+ file-name auto-save-p)
+ ;; 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 nil revert-buffer-preserve-modes)
+ ;; Run after-revert-hook as it was before we reverted.
+ (setq-default revert-buffer-internal-hook global-hook)
+ (if local-hook
+ (set (make-local-variable 'revert-buffer-internal-hook)
+ local-hook)
+ (kill-local-variable 'revert-buffer-internal-hook))
+ (run-hooks 'revert-buffer-internal-hook))
+ t)))))
+
+(defun revert-buffer-insert-file-contents--default-function (file-name auto-save-p)
+ (cond
+ ((not (file-exists-p file-name))
+ (error (if buffer-file-number
+ "File %s no longer exists!"
+ "Cannot revert nonexistent file %s")
+ file-name))
+ ((not (file-readable-p file-name))
+ (error (if buffer-file-number
+ "File %s no longer readable!"
+ "Cannot revert unreadable file %s")
+ file-name))
+ (t
+ ;; 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)
+ (let ((coding-system-for-read
+ ;; Auto-saved file should be read by Emacs's
+ ;; internal coding.
+ (if auto-save-p 'auto-save-coding
+ (or coding-system-for-read
+ (and
+ buffer-file-coding-system-explicit
+ (car buffer-file-coding-system-explicit))))))
+ (if (and (not enable-multibyte-characters)
+ coding-system-for-read
+ (not (memq (coding-system-base
+ coding-system-for-read)
+ '(no-conversion raw-text))))
+ ;; As a coding system suitable for multibyte
+ ;; buffer is specified, make the current
+ ;; buffer multibyte.
+ (set-buffer-multibyte t))
+
+ ;; This force after-insert-file-set-coding
+ ;; (called from insert-file-contents) to set
+ ;; buffer-file-coding-system to a proper value.
+ (kill-local-variable 'buffer-file-coding-system)
+
+ ;; Note that this preserves point in an intelligent way.
+ (if revert-buffer-preserve-modes
+ (let ((buffer-file-format buffer-file-format))
+ (insert-file-contents file-name (not auto-save-p)
+ nil nil t))
+ (insert-file-contents file-name (not auto-save-p)
+ nil nil t))))))
(defun recover-this-file ()
"Recover the visited file--get contents from its last auto-save file."
(not (file-exists-p file-name)))
(error "Auto-save file %s not current"
(abbreviate-file-name file-name)))
- ((save-window-excursion
- (with-output-to-temp-buffer "*Directory*"
- (buffer-disable-undo standard-output)
- (save-excursion
- (let ((switches dired-listing-switches))
- (if (file-symlink-p file)
- (setq switches (concat switches " -L")))
- (set-buffer standard-output)
- ;; Use insert-directory-safely, not insert-directory,
- ;; because these files might not exist. In particular,
- ;; FILE might not exist if the auto-save file was for
- ;; a buffer that didn't visit a file, such as "*mail*".
- ;; The code in v20.x called `ls' directly, so we need
- ;; to emulate what `ls' did in that case.
- (insert-directory-safely file switches)
- (insert-directory-safely file-name switches))))
- (yes-or-no-p (format "Recover auto save file %s? " file-name)))
+ ((with-temp-buffer-window
+ "*Directory*" nil
+ #'(lambda (window _value)
+ (with-selected-window window
+ (unwind-protect
+ (yes-or-no-p (format "Recover auto save file %s? " file-name))
+ (when (window-live-p window)
+ (quit-restore-window window 'kill)))))
+ (with-current-buffer standard-output
+ (let ((switches dired-listing-switches))
+ (if (file-symlink-p file)
+ (setq switches (concat switches " -L")))
+ ;; Use insert-directory-safely, not insert-directory,
+ ;; because these files might not exist. In particular,
+ ;; FILE might not exist if the auto-save file was for
+ ;; a buffer that didn't visit a file, such as "*mail*".
+ ;; The code in v20.x called `ls' directly, so we need
+ ;; to emulate what `ls' did in that case.
+ (insert-directory-safely file switches)
+ (insert-directory-safely file-name switches))))
(switch-to-buffer (find-file-noselect file t))
(let ((inhibit-read-only t)
;; Keep the current buffer-file-coding-system.
(insert-file-contents file-name nil)
(set-buffer-file-coding-system coding-system))
(after-find-file nil nil t))
- (t (user-error "Recover-file cancelled")))))
+ (t (user-error "Recover-file canceled")))))
(defun recover-session ()
"Recover auto save files from a previous Emacs session.
(let ((ls-lisp-support-shell-wildcards t))
(dired (concat auto-save-list-file-prefix "*")
(concat dired-listing-switches " -t")))
+ (use-local-map (nconc (make-sparse-keymap) (current-local-map)))
+ (define-key (current-local-map) "\C-c\C-c" 'recover-session-finish)
(save-excursion
(goto-char (point-min))
(or (looking-at " Move to the session you want to recover,")
(let ((inhibit-read-only t))
;; Each line starts with a space
;; so that Font Lock mode won't highlight the first character.
- (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))
+ (insert " To recover a session, move to it and type C-c C-c.\n"
+ (substitute-command-keys
+ " To delete a session file, type \
+\\[dired-flag-file-deletion] on its line to flag
+ the file for deletion, then \\[dired-do-flagged-delete] to \
+delete flagged files.\n\n"))))))
(defun recover-session-finish ()
"Choose one saved session to recover auto-save files from.
(defun auto-save-file-name-p (filename)
"Return non-nil if FILENAME can be yielded by `make-auto-save-file-name'.
FILENAME should lack slashes. You can redefine this for customization."
- (string-match "^#.*#$" filename))
+ (string-match "\\`#.*#\\'" filename))
\f
(defun wildcard-to-regexp (wildcard)
"Given a shell file name pattern WILDCARD, return an equivalent regexp.
(when (and directory-free-space-program
;; Avoid failure if the default directory does
;; not exist (Bug#2631, Bug#3911).
- (let ((default-directory "/"))
- (eq (call-process directory-free-space-program
+ (let ((default-directory
+ (locate-dominating-file dir 'file-directory-p)))
+ (eq (process-file directory-free-space-program
nil t nil
directory-free-space-args
- dir)
+ (file-relative-name dir))
0)))
;; Assume that the "available" column is before the
;; "capacity" column. Find the "%" and scan backward.
;; directory if FILE is a symbolic link.
(unless full-directory-p
(setq switches
- (if (stringp switches)
- (concat switches " -d")
- (add-to-list 'switches "-d" 'append))))
+ (cond
+ ((stringp switches) (concat switches " -d"))
+ ((member "-d" switches) switches)
+ (t (append switches '("-d"))))))
(apply 'call-process
insert-directory-program nil t nil
(append
(defcustom confirm-kill-emacs nil
"How to ask for confirmation when leaving Emacs.
If nil, the default, don't ask at all. If the value is non-nil, it should
-be a predicate function such as `yes-or-no-p'."
+be a predicate function; for example `yes-or-no-p'."
:type '(choice (const :tag "Ask with yes-or-no-p" yes-or-no-p)
(const :tag "Ask with y-or-n-p" y-or-n-p)
- (const :tag "Don't confirm" nil))
+ (const :tag "Don't confirm" nil)
+ (function :tag "Predicate function"))
:group 'convenience
:version "21.1")
(setq active t))
(setq processes (cdr processes)))
(or (not active)
- (progn (list-processes t)
- (yes-or-no-p "Active processes exist; kill them and exit anyway? ")))))
+ (with-current-buffer-window
+ (get-buffer-create "*Process List*") nil
+ #'(lambda (window _value)
+ (with-selected-window window
+ (unwind-protect
+ (yes-or-no-p "Active processes exist; kill them and exit anyway? ")
+ (when (window-live-p window)
+ (quit-restore-window window 'kill)))))
+ (list-processes t)))))
;; Query the user for other things, perhaps.
(run-hook-with-args-until-failure 'kill-emacs-query-functions)
(or (null confirm-kill-emacs)
If emacsclient was started with a list of filenames to edit, then
only these files will be asked to be saved."
(interactive "P")
- (if (frame-parameter (selected-frame) 'client)
+ (if (frame-parameter nil 'client)
(server-save-buffers-kill-terminal arg)
(save-buffers-kill-emacs arg)))
\f
(string-to-number value 8)
(file-modes-symbolic-to-number value modes)))))
-\f
+(define-obsolete-variable-alias 'cache-long-line-scans
+ 'cache-long-scans "24.4")
+
;; Trashcan handling.
(defcustom trash-directory nil
"Directory for `move-file-to-trash' to move files and directories to.
(define-key esc-map "~" 'not-modified)
(define-key ctl-x-map "\C-d" 'list-directory)
(define-key ctl-x-map "\C-c" 'save-buffers-kill-terminal)
-(define-key ctl-x-map "\C-q" 'toggle-read-only)
+(define-key ctl-x-map "\C-q" 'read-only-mode)
(define-key ctl-x-4-map "f" 'find-file-other-window)
(define-key ctl-x-4-map "r" 'find-file-read-only-other-window)