;;; files.el --- file input and output commands for Emacs -*- lexical-binding:t -*-
-;; Copyright (C) 1985-1987, 1992-2015 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992-2016 Free Software Foundation, Inc.
;; Maintainer: emacs-devel@gnu.org
;; Package: emacs
\f
(defun insert-file-contents-literally (filename &optional visit beg end replace)
"Like `insert-file-contents', but only reads in the file literally.
+See `insert-file-contents' for an explanation of the parameters.
A buffer may be modified in several ways after reading into the buffer,
due to Emacs features such as format decoding, character code
conversion, `find-file-hook', automatic uncompression, etc.
or from Lisp without specifying the optional argument FIND-FILE;
in that case, this function acts as if `enable-local-variables' were t."
(interactive)
- (fundamental-mode)
+ (kill-all-local-variables)
+ (unless delay-mode-hooks
+ (run-hooks 'change-major-mode-after-body-hook
+ 'after-change-major-mode-hook))
(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:".
(with-demoted-errors "File mode specification error: %s"
(set-auto-mode))
- (with-demoted-errors "File local-variables error: %s"
- (hack-local-variables)))
+ ;; `delay-mode-hooks' being non-nil will have prevented the major
+ ;; mode's call to `run-mode-hooks' from calling
+ ;; `hack-local-variables'. In that case, call it now.
+ (when delay-mode-hooks
+ (with-demoted-errors "File local-variables error: %s"
+ (hack-local-variables 'no-mode))))
;; Turn font lock off and on, to make sure it takes account of
;; whatever file local variables are relevant to it.
(when (and font-lock-mode
;; TODO? Warn once per file rather than once per session?
(defvar hack-local-variables--warned-lexical nil)
-(defun hack-local-variables (&optional mode-only)
+(defun hack-local-variables (&optional handle-mode)
"Parse and put into effect this buffer's local variables spec.
Uses `hack-local-variables-apply' to apply the variables.
-If MODE-ONLY is non-nil, all we do is check whether a \"mode:\"
+If HANDLE-MODE is nil, we apply all the specified local
+variables. If HANDLE-MODE is neither nil nor t, we do the same,
+except that any settings of `mode' are ignored.
+
+If HANDLE-MODE is t, all we do is check whether a \"mode:\"
is specified, and return the corresponding mode symbol, or nil.
In this case, we try to ignore minor-modes, and only return a
major-mode.
(let ((enable-local-variables
(and local-enable-local-variables enable-local-variables))
result)
- (unless mode-only
+ (unless (eq handle-mode t)
(setq file-local-variables-alist nil)
(with-demoted-errors "Directory-local variables error: %s"
;; Note this is a no-op if enable-local-variables is nil.
;; This entire function is basically a no-op if enable-local-variables
;; is nil. All it does is set file-local-variables-alist to nil.
(when enable-local-variables
- ;; This part used to ignore enable-local-variables when mode-only
- ;; was non-nil. That was inappropriate, eg consider the
+ ;; This part used to ignore enable-local-variables when handle-mode
+ ;; was t. That was inappropriate, eg consider the
;; (artificial) example of:
;; (setq local-enable-local-variables nil)
;; Open a file foo.txt that contains "mode: sh".
;; It correctly opens in text-mode.
;; M-x set-visited-file name foo.c, and it incorrectly stays in text-mode.
(unless (or (inhibit-local-variables-p)
- ;; If MODE-ONLY is non-nil, and the prop line specifies a
+ ;; If HANDLE-MODE is t, and the prop line specifies a
;; mode, then we're done, and have no need to scan further.
- (and (setq result (hack-local-variables-prop-line mode-only))
- mode-only))
+ (and (setq result (hack-local-variables-prop-line
+ (eq handle-mode t)))
+ (eq handle-mode t)))
;; Look for "Local variables:" line in last page.
(save-excursion
(goto-char (point-max))
(goto-char (point-min))
(while (not (or (eobp)
- (and mode-only result)))
+ (and (eq handle-mode t) result)))
;; Find the variable name;
(unless (looking-at hack-local-variable-regexp)
(error "Malformed local variable line: %S"
(forward-char 1)
(let ((read-circle nil))
(setq val (read (current-buffer))))
- (if mode-only
+ (if (eq handle-mode t)
(and (eq var 'mode)
;; Specifying minor-modes via mode: is
;; deprecated, but try to reject them anyway.
(format-message
"%s: `lexical-binding' at end of file unreliable"
(file-name-nondirectory
- (or buffer-file-name ""))))))
+ ;; We are called from
+ ;; 'with-temp-buffer', so we need
+ ;; to use 'thisbuf's name in the
+ ;; warning message.
+ (or (buffer-file-name thisbuf) ""))))))
+ ((and (eq var 'mode) handle-mode))
(t
(ignore-errors
(push (cons (if (eq var 'eval)
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.
- (if mode-only result
+ ;; If HANDLE-MODE is t, return whether the mode was specified.
+ (if (eq handle-mode t) result
;; Otherwise, set the variables.
(hack-local-variables-filter result nil)
(hack-local-variables-apply)))))
applied by recursively following these rules."
(setf (alist-get class dir-locals-class-alist) variables))
-(defconst dir-locals-file ".dir-locals*.el"
- "Pattern for files that contain directory-local variables.
+(defconst dir-locals-file ".dir-locals.el"
+ "File that contains directory-local variables.
It has to be constant to enforce uniform values across different
environments and users.
+See also `dir-locals-file-2', whose values override this one's.
+See Info node `(elisp)Directory Local Variables' for details.")
-Multiple dir-locals files in the same directory are loaded in
-`string<' order.
+(defconst dir-locals-file-2 ".dir-locals-2.el"
+ "File that contains directory-local variables.
+This essentially a second file that can be used like
+`dir-locals-file', so that users can have specify their personal
+dir-local variables even if the current directory already has a
+`dir-locals-file' that is shared with other users (such as in a
+git repository).
See Info node `(elisp)Directory Local Variables' for details.")
-(defun dir-locals--all-files (file-or-dir)
- "Return a list of all readable dir-locals files matching FILE-OR-DIR.
-If FILE-OR-DIR is a file pattern, expand wildcards in it and
-return a sorted list of the results. If it is a directory name,
-return a sorted list of all files matching `dir-locals-file' in
-this directory.
-The returned list is sorted by `string<' order."
- (require 'seq)
- (let ((default-directory (if (file-directory-p file-or-dir)
- file-or-dir
- default-directory)))
- (seq-filter (lambda (f) (and (file-readable-p f)
- (file-regular-p f)))
- (file-expand-wildcards
- (cond ((not (file-directory-p file-or-dir)) file-or-dir)
- ((eq system-type 'ms-dos) (dosified-file-name dir-locals-file))
- (t dir-locals-file))
- 'full))))
+(defun dir-locals--all-files (directory)
+ "Return a list of all readable dir-locals files in DIRECTORY.
+The returned list is sorted by increasing priority. That is,
+values specified in the last file should take precedence over
+those in the first."
+ (when (file-readable-p directory)
+ (let* ((file-1 (expand-file-name (if (eq system-type 'ms-dos)
+ (dosified-file-name dir-locals-file)
+ dir-locals-file)
+ directory))
+ (file-2 (when (string-match "\\.el\\'" file-1)
+ (replace-match "-2.el" t nil file-1)))
+ (out nil))
+ ;; The order here is important.
+ (dolist (f (list file-2 file-1))
+ (when (and f
+ (file-readable-p f)
+ (file-regular-p f)
+ (not (file-directory-p f)))
+ (push f out)))
+ out)))
(defun dir-locals-find-file (file)
"Find the directory-local variables for FILE.
time stored in the cache matches the current file modification time.
If not, the cache entry is cleared so that the file will be re-read.
-This function returns either nil (no directory local variables found),
-or the matching entry from `dir-locals-directory-cache' (a list),
-or the full path to the `dir-locals-file' (a string) in the case
-of no valid cache entry. If `dir-locals-file' contains
-wildcards, then the return value is not a proper filename, it is
-an absolute version of `dir-locals-file' which is guaranteed to
-expand to at least one file."
+This function returns either:
+ - nil (no directory local variables found),
+ - the matching entry from `dir-locals-directory-cache' (a list),
+ - or the full path to the directory (a string) containing at
+ least one `dir-locals-file' in the case of no valid cache
+ entry."
(setq file (expand-file-name file))
(let* ((locals-dir (locate-dominating-file (file-name-directory file)
#'dir-locals--all-files))
- locals-file dir-elt)
+ dir-elt)
;; `locate-dominating-file' may have abbreviated the name.
(when locals-dir
- (setq locals-dir (expand-file-name locals-dir))
- (setq locals-file (expand-file-name (if (eq system-type 'ms-dos)
- (dosified-file-name dir-locals-file)
- dir-locals-file)
- locals-dir)))
+ (setq locals-dir (expand-file-name locals-dir)))
;; Find the best cached value in `dir-locals-directory-cache'.
(dolist (elt dir-locals-directory-cache)
(when (and (string-prefix-p (car elt) file
;; The entry MTIME should match the most recent
;; MTIME among matching files.
(and cached-files
- (= (time-to-seconds (nth 2 dir-elt))
- (apply #'max (mapcar (lambda (f) (time-to-seconds (nth 5 (file-attributes f))))
+ (= (float-time (nth 2 dir-elt))
+ (apply #'max (mapcar (lambda (f)
+ (float-time
+ (nth 5 (file-attributes f))))
cached-files))))))
;; This cache entry is OK.
dir-elt
(delq dir-elt dir-locals-directory-cache))
;; Return the first existing dir-locals file. Might be the same
;; as dir-elt's, might not (eg latter might have been deleted).
- locals-file)
+ locals-dir)
;; No cache entry.
- locals-file)))
-
-(defun dir-locals-read-from-file (file)
- "Load a variables FILE and register a new class and instance.
-FILE is the absolute name of the file holding the variables to
-apply. It may contain wildcards.
-The new class name is the same as the directory in which FILE
-is found. Returns the new class name."
+ locals-dir)))
+
+(defun dir-locals-read-from-dir (dir)
+ "Load all variables files in DIR and register a new class and instance.
+DIR is the absolute name of a directory which must contain at
+least one dir-local file (which is a file holding variables to
+apply).
+Return the new class name, which is a symbol named DIR."
(require 'map)
- (let* ((dir-name (file-name-directory file))
- (class-name (intern dir-name))
- (files (dir-locals--all-files file))
+ (let* ((class-name (intern dir))
+ (files (dir-locals--all-files dir))
(read-circle nil)
(success nil)
(variables))
(setq success t))
(dir-locals-set-class-variables class-name variables)
(dir-locals-set-directory-class
- dir-name class-name
+ dir class-name
(seconds-to-time
(if success
(apply #'max (mapcar (lambda (file)
- (time-to-seconds (nth 5 (file-attributes file))))
+ (float-time (nth 5 (file-attributes file))))
files))
;; If there was a problem, use the values we could get but
;; don't let the cache prevent future reads.
0)))
class-name))
+(define-obsolete-function-alias 'dir-locals-read-from-file
+ 'dir-locals-read-from-dir "25.1")
+
(defcustom enable-remote-dir-locals nil
"Non-nil means dir-local variables will be applied to remote files."
:version "24.3"
(not (file-remote-p (or (buffer-file-name)
default-directory)))))
;; Find the variables file.
- (let ((file-pattern-or-cache (dir-locals-find-file
- (or (buffer-file-name) default-directory)))
+ (let ((dir-or-cache (dir-locals-find-file
+ (or (buffer-file-name) default-directory)))
(class nil)
(dir-name nil))
(cond
- ((stringp file-pattern-or-cache)
- (setq dir-name (file-name-directory file-pattern-or-cache)
- class (dir-locals-read-from-file file-pattern-or-cache)))
- ((consp file-pattern-or-cache)
- (setq dir-name (nth 0 file-pattern-or-cache))
- (setq class (nth 1 file-pattern-or-cache))))
+ ((stringp dir-or-cache)
+ (setq dir-name dir-or-cache
+ class (dir-locals-read-from-dir dir-or-cache)))
+ ((consp dir-or-cache)
+ (setq dir-name (nth 0 dir-or-cache))
+ (setq class (nth 1 dir-or-cache))))
(when class
(let ((variables
(dir-locals-collect-variables
(defun file-name-sans-extension (filename)
"Return FILENAME sans final \"extension\".
-The extension, in a file name, is the part that follows the last `.',
-except that a leading `.', if any, doesn't count."
+The extension, in a file name, is the part that begins with the last `.',
+except that a leading `.' of the file name, if there is one, doesn't count."
(save-match-data
(let ((file (file-name-sans-versions (file-name-nondirectory filename)))
directory)
(defun file-name-extension (filename &optional period)
"Return FILENAME's final \"extension\".
-The extension, in a file name, is the part that follows the last `.',
-excluding version numbers and backup suffixes,
-except that a leading `.', if any, doesn't count.
+The extension, in a file name, is the part that begins with the last `.',
+excluding version numbers and backup suffixes, except that a leading `.'
+of the file name, if there is one, doesn't count.
Return nil for extensionless file names such as `foo'.
Return the empty string for file names such as `foo.'.
-If PERIOD is non-nil, then the returned value includes the period
-that delimits the extension, and if FILENAME has no extension,
-the value is \"\"."
+By default, the returned value excludes the period that starts the
+extension, but if the optional argument PERIOD is non-nil, the period
+is included in the value, and in that case, if FILENAME has no
+extension, the value is \"\"."
(save-match-data
(let ((file (file-name-sans-versions (file-name-nondirectory filename))))
(if (and (string-match "\\.[^.]*\\'" file)
\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."
+Next attempt to save will not complain of a discrepancy."
(set-visited-file-modtime 0))
(defun not-modified (&optional arg)
PATTERN is assumed to represent a file-name wildcard suitable for the
underlying filesystem. For Unix and GNU/Linux, each character from the
-set [ \\t\\n;<>&|()`'\"#$] is quoted with a backslash; for DOS/Windows, all
+set [ \\t\\n;<>&|()\\=`\\='\"#$] is quoted with a backslash; for DOS/Windows, all
the parts of the pattern which don't include wildcard characters are
quoted with double quotes.
(setq error-lines (nreverse error-lines))
;; Now read the numeric positions of file names.
(goto-char linebeg)
- (forward-word 1)
+ (forward-word-strictly 1)
(forward-char 3)
(while (< (point) end)
(let ((start (insert-directory-adj-pos
;; Simulate the message printed by `ls'.
(insert (format "%s: No such file or directory\n" file))))
-(defvar kill-emacs-query-functions nil
+(defcustom 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 canceled.
`save-buffers-kill-emacs' calls these functions, but `kill-emacs',
-the low level primitive, does not. See also `kill-emacs-hook'.")
+the low level primitive, does not. See also `kill-emacs-hook'."
+ :type 'hook
+ :version "25.2"
+ :group 'convenience)
(defcustom confirm-kill-emacs nil
"How to ask for confirmation when leaving Emacs.
(defun save-buffers-kill-terminal (&optional arg)
"Offer to save each buffer, then kill the current connection.
-If the current frame has no client, kill Emacs itself.
+If the current frame has no client, kill Emacs itself using
+`save-buffers-kill-emacs'.
With prefix ARG, silently save all file-visiting buffers, then kill.
(cond ((= char ?r) #o0444)
((= char ?w) #o0222)
((= char ?x) #o0111)
- ((= char ?s) #o1000)
- ((= char ?t) #o6000)
+ ((= char ?s) #o6000)
+ ((= char ?t) #o1000)
;; Rights relative to the previous file modes.
((= char ?X) (if (= (logand from #o111) 0) 0 #o0111))
((= char ?u) (let ((uright (logand #o4700 from)))
(mapcar 'file-modes-char-to-who
(match-string 1 modes)))))
(when (= num-who 0)
- (setq num-who (default-file-modes)))
+ (setq num-who (logior #o7000 (default-file-modes))))
(setq num-modes
(file-modes-rights-to-number (substring modes (match-end 1))
num-who num-modes)
(let ((delete-by-moving-to-trash nil))
(rename-file fn new-fn)))))))))
+(defsubst file-attribute-type (attributes)
+ "The type field in ATTRIBUTES returned by `file-attribute'.
+The value is either t for directory, string (name linked to) for
+symbolic link, or nil."
+ (nth 0 attributes))
+
+(defsubst file-attribute-link-number (attributes)
+ "Return the number of links in ATTRIBUTES returned by `file-attribute'."
+ (nth 1 attributes))
+
+(defsubst file-attribute-user-id (attributes)
+ "The UID field in ATTRIBUTES returned by `file-attribute'.
+This is either a string or a number. If a string value cannot be
+looked up, a numeric value, either an integer or a float, is
+returned."
+ (nth 2 attributes))
+
+(defsubst file-attribute-group-id (attributes)
+ "The GID field in ATTRIBUTES returned by `file-attribute'.
+This is either a string or a number. If a string value cannot be
+looked up, a numeric value, either an integer or a float, is
+returned."
+ (nth 3 attributes))
+
+(defsubst file-attribute-access-time (attributes)
+ "The last access time in ATTRIBUTES returned by `file-attribute'.
+This a list of integers (HIGH LOW USEC PSEC) in the same style
+as (current-time)."
+ (nth 4 attributes))
+
+(defsubst file-attribute-modification-time (attributes)
+ "The modification time in ATTRIBUTES returned by `file-attribute'.
+This is the time of the last change to the file's contents, and
+is a list of integers (HIGH LOW USEC PSEC) in the same style
+as (current-time)."
+ (nth 5 attributes))
+
+(defsubst file-attribute-status-change-time (attributes)
+ "The status modification time in ATTRIBUTES returned by `file-attribute'.
+This is the time of last change to the file's attributes: owner
+and group, access mode bits, etc, and is a list of integers (HIGH
+LOW USEC PSEC) in the same style as (current-time)."
+ (nth 6 attributes))
+
+(defsubst file-attribute-size (attributes)
+ "The size (in bytes) in ATTRIBUTES returned by `file-attribute'.
+This is a floating point number if the size is too large for an integer."
+ (nth 7 attributes))
+
+(defsubst file-attribute-modes (attributes)
+ "The file modes in ATTRIBUTES returned by `file-attribute'.
+This is a string of ten letters or dashes as in ls -l."
+ (nth 8 attributes))
+
+(defsubst file-attribute-inode-number (attributes)
+ "The inode number in ATTRIBUTES returned by `file-attribute'.
+If it is larger than what an Emacs integer can hold, this is of
+the form (HIGH . LOW): first the high bits, then the low 16 bits.
+If even HIGH is too large for an Emacs integer, this is instead
+of the form (HIGH MIDDLE . LOW): first the high bits, then the
+middle 24 bits, and finally the low 16 bits."
+ (nth 10 attributes))
+
+(defsubst file-attribute-device-number (attributes)
+ "The file system device number in ATTRIBUTES returned by `file-attribute'.
+If it is larger than what an Emacs integer can hold, this is of
+the form (HIGH . LOW): first the high bits, then the low 16 bits.
+If even HIGH is too large for an Emacs integer, this is instead
+of the form (HIGH MIDDLE . LOW): first the high bits, then the
+middle 24 bits, and finally the low 16 bits."
+ (nth 11 attributes))
+
\f
(define-key ctl-x-map "\C-f" 'find-file)
(define-key ctl-x-map "\C-r" 'find-file-read-only)