Spelling fixes.
[bpt/emacs.git] / lisp / files.el
index 43b31cb..40b6e7d 100644 (file)
@@ -151,6 +151,7 @@ Automatically local in all buffers."
   :type 'boolean
   :group 'backup)
 (make-variable-buffer-local 'buffer-offer-save)
+(put 'buffer-offer-save 'permanent-local t)
 
 (defcustom find-file-existing-other-name t
   "Non-nil means find a file under alternative names, in existing buffers.
@@ -278,7 +279,7 @@ The value `never' means do not make them."
   :group 'backup
   :group 'vc)
 (put 'version-control 'safe-local-variable
-     '(lambda (x) (or (booleanp x) (equal x 'never))))
+     (lambda (x) (or (booleanp x) (equal x 'never))))
 
 (defcustom dired-kept-versions 2
   "When cleaning directory, number of versions to keep."
@@ -635,22 +636,22 @@ the value of `default-directory'."
   "Value of the CDPATH environment variable, as a list.
 Not actually set up until the first time you use it.")
 
-(defun parse-colon-path (cd-path)
+(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 cd-path
+  (and search-path
        (let (cd-list (cd-start 0) cd-colon)
-        (setq cd-path (concat cd-path path-separator))
-        (while (setq cd-colon (string-match path-separator cd-path cd-start))
+        (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 cd-path cd-start cd-colon)))))))
+                                 (substring search-path cd-start cd-colon)))))))
           (setq cd-start (+ cd-colon 1)))
         cd-list)))
 
@@ -681,26 +682,37 @@ that list of directories (separated by occurrences of
 `path-separator') when resolving a relative directory name.
 The path separator is colon in GNU and GNU-like systems."
   (interactive
-   (list (read-directory-name "Change default directory: "
-                        default-directory default-directory
-                        (and (member cd-path '(nil ("./")))
-                             (null (getenv "CDPATH"))))))
-  (if (file-name-absolute-p dir)
-      (cd-absolute (expand-file-name dir))
-    (if (null cd-path)
-       (let ((trypath (parse-colon-path (getenv "CDPATH"))))
-         (setq cd-path (or trypath (list "./")))))
-    (if (not (catch 'found
-              (mapc
-               (function (lambda (x)
-                           (let ((f (expand-file-name (concat x dir))))
-                             (if (file-directory-p f)
-                                 (progn
-                                   (cd-absolute f)
-                                   (throw 'found t))))))
-               cd-path)
-              nil))
-       (error "No such directory found via CDPATH environment variable"))))
+   (list
+    ;; FIXME: There's a subtle bug in the completion below.  Seems linked
+    ;; to a fundamental difficulty of implementing `predicate' correctly.
+    ;; The manifestation is that TAB may list non-directories in the case where
+    ;; those files also correspond to valid directories (if your cd-path is (A/
+    ;; B/) and you have A/a a file and B/a a directory, then both `a' and `a/'
+    ;; will be listed as valid completions).
+    ;; This is because `a' (listed because of A/a) is indeed a valid choice
+    ;; (which will lead to the use of B/a).
+    (minibuffer-with-setup-hook
+        (lambda ()
+          (setq minibuffer-completion-table
+                (apply-partially #'locate-file-completion-table
+                                 cd-path nil))
+          (setq minibuffer-completion-predicate
+                (lambda (dir)
+                  (locate-file dir cd-path nil
+                               (lambda (f) (and (file-directory-p f) 'dir-ok))))))
+      (unless cd-path
+        (setq cd-path (or (parse-colon-path (getenv "CDPATH"))
+                          (list "./"))))
+      (read-directory-name "Change default directory: "
+                           default-directory default-directory
+                           t))))
+  (unless cd-path
+    (setq cd-path (or (parse-colon-path (getenv "CDPATH"))
+                      (list "./"))))
+  (cd-absolute
+   (or (locate-file dir cd-path nil
+                    (lambda (f) (and (file-directory-p f) 'dir-ok)))
+       (error "No such directory found via CDPATH environment variable"))))
 
 (defun load-file (file)
   "Load the Lisp file named FILE."
@@ -720,9 +732,12 @@ If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.  If SUFFIXES is nil, it is equivalent to '(\"\").
 Use '(\"/\") to disable PATH search, but still try the suffixes in SUFFIXES.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
+
+This function will normally skip directories, so if you want it to find
+directories, make sure the PREDICATE function returns `dir-ok' for them.
+
 PREDICATE can also be an integer to pass to the `access' system call,
 in which case file-name handlers are ignored.  This usage is deprecated.
-
 For compatibility, PREDICATE can also be one of the symbols
 `executable', `readable', `writable', or `exists', or a list of
 one or more of those symbols."
@@ -852,11 +867,10 @@ and return the directory.  Return nil if not found."
   ;; `name' in /home or in /.
   (setq file (abbreviate-file-name file))
   (let ((root nil)
-        (prev-file file)
         ;; `user' is not initialized outside the loop because
         ;; `file' may not exist, so we may have to walk up part of the
-        ;; hierarchy before we find the "initial UID".
-        (user nil)
+        ;; hierarchy before we find the "initial UID".  Note: currently unused
+        ;; (user nil)
         try)
     (while (not (or root
                     (null file)
@@ -873,8 +887,7 @@ and return the directory.  Return nil if not found."
                     (string-match locate-dominating-stop-dir-regexp file)))
       (setq try (file-exists-p (expand-file-name name file)))
       (cond (try (setq root file))
-            ((equal file (setq prev-file file
-                               file (file-name-directory
+            ((equal file (setq file (file-name-directory
                                      (directory-file-name file))))
              (setq file nil))))
     root))
@@ -944,10 +957,10 @@ reasonable to let-bind this variable to a value less then the
 time period between two checks.
 Example:
 
-  \(defun display-time-file-nonempty-p \(file)
-    \(let \(\(remote-file-name-inhibit-cache \(- display-time-interval 5)))
-      \(and \(file-exists-p file)
-           \(< 0 \(nth 7 \(file-attributes \(file-chase-links file)))))))"
+  (defun display-time-file-nonempty-p (file)
+    (let ((remote-file-name-inhibit-cache (- display-time-interval 5)))
+      (and (file-exists-p file)
+           (< 0 (nth 7 (file-attributes (file-chase-links file)))))))"
   :group 'files
   :version "24.1"
   :type `(choice
@@ -969,7 +982,8 @@ accessible."
       nil)))
 
 (defun file-truename (filename &optional counter prev-dirs)
-  "Return the truename of FILENAME, which should be absolute.
+  "Return the truename of FILENAME.
+If FILENAME is not absolute, first expands it against `default-directory'.
 The truename of a file name is found by chasing symbolic links
 both at the level of the file and at the level of the directories
 containing it, until no links are left at any level.
@@ -1125,6 +1139,37 @@ it means chase no more than that many links and then stop."
        (setq count (1+ count))))
     newname))
 
+;; A handy function to display file sizes in human-readable form.
+;; See http://en.wikipedia.org/wiki/Kibibyte for the reference.
+(defun file-size-human-readable (file-size &optional flavor)
+  "Produce a string showing FILE-SIZE in human-readable form.
+
+Optional second argument FLAVOR controls the units and the display format:
+
+ If FLAVOR is nil or omitted, each kilobyte is 1024 bytes and the produced
+    suffixes are \"k\", \"M\", \"G\", \"T\", etc.
+ If FLAVOR is `si', each kilobyte is 1000 bytes and the produced suffixes
+    are \"k\", \"M\", \"G\", \"T\", etc.
+ If FLAVOR is `iec', each kilobyte is 1024 bytes and the produced suffixes
+    are \"KiB\", \"MiB\", \"GiB\", \"TiB\", etc."
+  (let ((power (if (or (null flavor) (eq flavor 'iec))
+                  1024.0
+                1000.0))
+       (post-fixes
+        ;; none, kilo, mega, giga, tera, peta, exa, zetta, yotta
+        (list "" "k" "M" "G" "T" "P" "E" "Z" "Y")))
+    (while (and (>= file-size power) (cdr post-fixes))
+      (setq file-size (/ file-size power)
+           post-fixes (cdr post-fixes)))
+    (format (if (> (mod file-size 1.0) 0.05)
+               "%.1f%s%s"
+             "%.0f%s%s")
+           file-size
+           (if (and (eq flavor 'iec) (string= (car post-fixes) "k"))
+               "K"
+             (car post-fixes))
+           (if (eq flavor 'iec) "iB" ""))))
+
 (defun make-temp-file (prefix &optional dir-flag suffix)
   "Create a temporary file.
 The returned file name (created by appending some random characters at the end
@@ -1244,100 +1289,6 @@ return value, which may be passed as the REQUIRE-MATCH arg to
         'confirm)
        (t nil)))
 
-(defun read-buffer-to-switch (prompt)
-  "Read the name of a buffer to switch to and return as a string.
-It is intended for `switch-to-buffer' family of commands since they
-need to omit the name of current buffer from the list of completions
-and default values."
-  (let ((rbts-completion-table (internal-complete-buffer-except)))
-    (minibuffer-with-setup-hook
-        (lambda ()
-          (setq minibuffer-completion-table rbts-completion-table)
-          ;; Since rbts-completion-table is built dynamically, we
-          ;; can't just add it to the default value of
-          ;; icomplete-with-completion-tables, so we add it
-          ;; here manually.
-          (if (and (boundp 'icomplete-with-completion-tables)
-                   (listp icomplete-with-completion-tables))
-              (set (make-local-variable 'icomplete-with-completion-tables)
-                   (cons rbts-completion-table
-                         icomplete-with-completion-tables))))
-      (read-buffer prompt (other-buffer (current-buffer))
-                   (confirm-nonexistent-file-or-buffer)))))
-
-(defun switch-to-buffer-other-window (buffer-or-name &optional norecord)
-  "Select the buffer specified by BUFFER-OR-NAME in another window.
-BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or
-nil.  Return the buffer switched to.
-
-If called interactively, prompt for the buffer name using the
-minibuffer.  The variable `confirm-nonexistent-file-or-buffer'
-determines whether to request confirmation before creating a new
-buffer.
-
-If BUFFER-OR-NAME is a string and does not identify an existing
-buffer, create a new buffer with that name.  If BUFFER-OR-NAME is
-nil, switch to the buffer returned by `other-buffer'.
-
-Optional second argument NORECORD non-nil means do not put this
-buffer at the front of the list of recently selected ones.
-
-This uses the function `display-buffer' as a subroutine; see its
-documentation for additional customization information."
-  (interactive
-   (list (read-buffer-to-switch "Switch to buffer in other window: ")))
-  (let ((pop-up-windows t)
-       same-window-buffer-names same-window-regexps)
-    (pop-to-buffer buffer-or-name t norecord)))
-
-(defun switch-to-buffer-other-frame (buffer-or-name &optional norecord)
-  "Switch to buffer BUFFER-OR-NAME in another frame.
-BUFFER-OR-NAME may be a buffer, a string \(a buffer name), or
-nil.  Return the buffer switched to.
-
-If called interactively, prompt for the buffer name using the
-minibuffer.  The variable `confirm-nonexistent-file-or-buffer'
-determines whether to request confirmation before creating a new
-buffer.
-
-If BUFFER-OR-NAME is a string and does not identify an existing
-buffer, create a new buffer with that name.  If BUFFER-OR-NAME is
-nil, switch to the buffer returned by `other-buffer'.
-
-Optional second arg NORECORD non-nil means do not put this
-buffer at the front of the list of recently selected ones.
-
-This uses the function `display-buffer' as a subroutine; see its
-documentation for additional customization information."
-  (interactive
-   (list (read-buffer-to-switch "Switch to buffer in other frame: ")))
-  (let ((pop-up-frames t)
-       same-window-buffer-names same-window-regexps)
-    (pop-to-buffer buffer-or-name t norecord)))
-
-(defun display-buffer-other-frame (buffer)
-  "Display buffer BUFFER in another frame.
-This uses the function `display-buffer' as a subroutine; see
-its documentation for additional customization information."
-  (interactive "BDisplay buffer in other frame: ")
-  (let ((pop-up-frames t)
-       same-window-buffer-names same-window-regexps
-        (old-window (selected-window))
-       new-window)
-    (setq new-window (display-buffer buffer t))
-    ;; This may have been here in order to prevent the new frame from hiding
-    ;; the old frame.  But it does more harm than good.
-    ;; Maybe we should call `raise-window' on the old-frame instead?  --Stef
-    ;;(lower-frame (window-frame new-window))
-
-    ;; This may have been here in order to make sure the old-frame gets the
-    ;; focus.  But not only can it cause an annoying flicker, with some
-    ;; window-managers it just makes the window invisible, with no easy
-    ;; way to recover it.  --Stef
-    ;;(make-frame-invisible (window-frame old-window))
-    ;;(make-frame-visible (window-frame old-window))
-    ))
-
 (defmacro minibuffer-with-setup-hook (fun &rest body)
   "Temporarily add FUN to `minibuffer-setup-hook' while executing BODY.
 BODY should use the minibuffer at most once.
@@ -1529,6 +1480,8 @@ expand wildcards (if any) and replace the file with multiple files."
       (other-window 1)
       (find-alternate-file filename wildcards))))
 
+(defvar kill-buffer-hook)  ; from buffer.c
+
 (defun find-alternate-file (filename &optional wildcards)
   "Find file FILENAME, select its buffer, kill previous buffer.
 If the current buffer now contains an empty file that you just visited
@@ -1686,11 +1639,6 @@ home directory is a root directory) and removes automounter prefixes
                        (substring filename (match-end 0)))))
       filename)))
 
-(defcustom find-file-not-true-dirname-list nil
-  "List of logical names for which visiting shouldn't save the true dirname."
-  :type '(repeat (string :tag "Name"))
-  :group 'find-file)
-
 (defun find-buffer-visiting (filename &optional predicate)
   "Return the buffer visiting file FILENAME (a string).
 This is like `get-file-buffer', except that it checks for any buffer
@@ -1879,8 +1827,8 @@ the various files."
                           (not nonexistent)
                           ;; It is confusing to ask whether to visit
                           ;; non-literally if they have the file in
-                          ;; hexl-mode.
-                          (not (eq major-mode 'hexl-mode)))
+                          ;; hexl-mode or image-mode.
+                          (not (memq major-mode '(hexl-mode image-mode))))
                  (if (buffer-modified-p)
                      (if (y-or-n-p
                           (format
@@ -2027,7 +1975,7 @@ This function ensures that none of these modifications will take place."
         (inhibit-file-name-operation 'insert-file-contents))
     (unwind-protect
          (progn
-           (fset 'find-buffer-file-type (lambda (filename) t))
+           (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)
@@ -2058,7 +2006,8 @@ Don't call it from programs!  Use `insert-file-contents-literally' instead.
 
 (defvar find-file-literally nil
   "Non-nil if this buffer was made by `find-file-literally' or equivalent.
-This is a permanent local.")
+This has the `permanent-local' property, which takes effect if you
+make the variable buffer-local.")
 (put 'find-file-literally 'permanent-local t)
 
 (defun find-file-literally (filename)
@@ -2084,10 +2033,8 @@ the file contents into it using `insert-file-contents-literally'."
          (confirm-nonexistent-file-or-buffer))))
   (switch-to-buffer (find-file-noselect filename nil t)))
 \f
-(defvar after-find-file-from-revert-buffer nil)
-
 (defun after-find-file (&optional error warn noauto
-                                 after-find-file-from-revert-buffer
+                                 _after-find-file-from-revert-buffer
                                  nomodes)
   "Called after finding a file and by the default revert function.
 Sets buffer mode, parses local variables.
@@ -2095,8 +2042,8 @@ Optional args ERROR, WARN, and NOAUTO: ERROR non-nil means there was an
 error in reading the file.  WARN non-nil means warn if there
 exists an auto-save file more recent than the visited file.
 NOAUTO means don't mess with auto-save mode.
-Fourth arg AFTER-FIND-FILE-FROM-REVERT-BUFFER non-nil
- means this call was from `revert-buffer'.
+Fourth arg AFTER-FIND-FILE-FROM-REVERT-BUFFER is ignored
+\(see `revert-buffer-in-progress-p' for similar functionality).
 Fifth arg NOMODES non-nil means don't alter the file's modes.
 Finishes by calling the functions in `find-file-hook'
 unless NOMODES is non-nil."
@@ -2109,7 +2056,11 @@ unless NOMODES is non-nil."
             ((not warn) nil)
             ((and error (file-attributes buffer-file-name))
              (setq buffer-read-only t)
-             "File exists, but cannot be read")
+             (if (and (file-symlink-p buffer-file-name)
+                      (not (file-exists-p
+                            (file-chase-links buffer-file-name))))
+                 "Symbolic link that points to nonexistent file"
+               "File exists, but cannot be read"))
             ((not buffer-read-only)
              (if (and warn
                       ;; No need to warn if buffer is auto-saved
@@ -2137,7 +2088,7 @@ unless NOMODES is non-nil."
        (message "%s" msg)
        (or not-serious (sit-for 1 t))))
     (when (and auto-save-default (not noauto))
-      (auto-save-mode t)))
+      (auto-save-mode 1)))
   ;; Make people do a little extra work (C-x C-q)
   ;; before altering a backup file.
   (when (backup-file-name-p buffer-file-name)
@@ -2196,6 +2147,8 @@ in that case, this function acts as if `enable-local-variables' were t."
   (interactive)
   (funcall (or (default-value 'major-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:".
     (report-errors "File mode specification error: %s"
       (set-auto-mode))
     (report-errors "File local-variables error: %s"
@@ -2315,7 +2268,12 @@ since only a single case-insensitive search through the alist is made."
      ("\\.icn\\'" . icon-mode)
      ("\\.sim\\'" . simula-mode)
      ("\\.mss\\'" . scribe-mode)
+     ;; The Fortran standard does not say anything about file extensions.
+     ;; .f90 was widely used for F90, now we seem to be trapped into
+     ;; using a different extension for each language revision.
+     ;; Anyway, the following extensions are supported by gfortran.
      ("\\.f9[05]\\'" . f90-mode)
+     ("\\.f0[38]\\'" . f90-mode)
      ("\\.indent\\.pro\\'" . fundamental-mode) ; to avoid idlwave-mode
      ("\\.\\(pro\\|PRO\\)\\'" . idlwave-mode)
      ("\\.srt\\'" . srecode-template-mode)
@@ -2341,6 +2299,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ("\\.dtd\\'" . sgml-mode)
      ("\\.ds\\(ss\\)?l\\'" . dsssl-mode)
      ("\\.js\\'" . js-mode)            ; javascript-mode would be better
+     ("\\.json\\'" . js-mode)
      ("\\.[ds]?vh?\\'" . verilog-mode)
      ;; .emacs or .gnus or .viper following a directory delimiter in
      ;; Unix, MSDOG or VMS syntax.
@@ -2379,6 +2338,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ("\\.ebrowse\\'" . ebrowse-tree-mode)
      ("#\\*mail\\*" . mail-mode)
      ("\\.g\\'" . antlr-mode)
+     ("\\.mod\\'" . m2-mode)
      ("\\.ses\\'" . ses-mode)
      ("\\.docbook\\'" . sgml-mode)
      ("\\.com\\'" . dcl-mode)
@@ -2389,8 +2349,6 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ("\\.ppd\\'" . conf-ppd-mode)
      ("java.+\\.conf\\'" . conf-javaprop-mode)
      ("\\.properties\\(?:\\.[a-zA-Z0-9._-]+\\)?\\'" . conf-javaprop-mode)
-     ;; *.cf, *.cfg, *.conf, *.config[.local|.de_DE.UTF8|...], */config
-     ("[/.]c\\(?:on\\)?f\\(?:i?g\\)?\\(?:\\.[a-zA-Z0-9._-]+\\)?\\'" . conf-mode-maybe)
      ("\\`/etc/\\(?:DIR_COLORS\\|ethers\\|.?fstab\\|.*hosts\\|lesskey\\|login\\.?de\\(?:fs\\|vperm\\)\\|magic\\|mtab\\|pam\\.d/.*\\|permissions\\(?:\\.d/.+\\)?\\|protocols\\|rpc\\|services\\)\\'" . conf-space-mode)
      ("\\`/etc/\\(?:acpid?/.+\\|aliases\\(?:\\.d/.+\\)?\\|default/.+\\|group-?\\|hosts\\..+\\|inittab\\|ksysguarddrc\\|opera6rc\\|passwd-?\\|shadow-?\\|sysconfig/.+\\)\\'" . conf-mode)
      ;; ChangeLog.old etc.  Other change-log-mode entries are above;
@@ -2412,11 +2370,14 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ;; Using mode nil rather than `ignore' would let the search continue
      ;; through this list (with the shortened name) rather than start over.
      ("\\.~?[0-9]+\\.[0-9][-.0-9]*~?\\'" nil t)
+     ("\\.\\(?:orig\\|in\\|[bB][aA][kK]\\)\\'" nil t)
+     ;; This should come after "in" stripping (e.g. config.h.in).
+     ;; *.cf, *.cfg, *.conf, *.config[.local|.de_DE.UTF8|...], */config
+     ("[/.]c\\(?:on\\)?f\\(?:i?g\\)?\\(?:\\.[a-zA-Z0-9._-]+\\)?\\'" . conf-mode-maybe)
      ;; The following should come after the ChangeLog pattern
      ;; for the sake of ChangeLog.1, etc.
      ;; and after the .scm.[0-9] and CVS' <file>.<rev> patterns too.
-     ("\\.[1-9]\\'" . nroff-mode)
-     ("\\.\\(?:orig\\|in\\|[bB][aA][kK]\\)\\'" nil t)))
+     ("\\.[1-9]\\'" . nroff-mode)))
   "Alist of filename patterns vs corresponding major mode functions.
 Each element looks like (REGEXP . FUNCTION) or (REGEXP FUNCTION NON-NIL).
 \(NON-NIL stands for anything that is not nil; the value does not matter.)
@@ -2475,6 +2436,7 @@ and `magic-mode-alist', which determines modes based on file contents.")
      ("ksh" . sh-mode)
      ("oash" . sh-mode)
      ("pdksh" . sh-mode)
+     ("rbash" . sh-mode)
      ("rc" . sh-mode)
      ("rpm" . sh-mode)
      ("sh" . sh-mode)
@@ -2499,7 +2461,9 @@ of a script, mode MODE is enabled.
 
 See also `auto-mode-alist'.")
 
-(defvar inhibit-first-line-modes-regexps (mapcar 'purecopy '("\\.tar\\'" "\\.tgz\\'"))
+(defvar inhibit-first-line-modes-regexps
+  (mapcar 'purecopy '("\\.tar\\'" "\\.tgz\\'" "\\.tiff?\\'"
+                     "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'"))
   "List of regexps; if one matches a file name, don't look for `-*-'.")
 
 (defvar inhibit-first-line-modes-suffixes nil
@@ -2570,23 +2534,24 @@ Also applies to `magic-fallback-mode-alist'.")
   "Select major mode appropriate for current buffer.
 
 To find the right major mode, this function checks for a -*- mode tag,
+checks for a `mode:' entry in the Local Variables section of the file,
 checks if it uses an interpreter listed in `interpreter-mode-alist',
 matches the buffer beginning against `magic-mode-alist',
 compares the filename against the entries in `auto-mode-alist',
 then matches the buffer beginning against `magic-fallback-mode-alist'.
 
-It does not check for the `mode:' local variable in the
-Local Variables section of the file; for that, use `hack-local-variables'.
-
-If `enable-local-variables' is nil, this function does not check for a
--*- mode tag.
+If `enable-local-variables' is nil, this function does not check for
+any mode: tag anywhere in the file.
 
 If the optional argument KEEP-MODE-IF-SAME is non-nil, then we
 set the major mode only if that would change it.  In other words
 we don't actually set it to the same mode the buffer already has."
   ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*-
   (let (end done mode modes)
-    ;; Find a -*- mode tag
+    ;; Once we drop the deprecated feature where mode: is also allowed to
+    ;; specify minor-modes (ie, there can be more than one "mode:"), we can
+    ;; remove this section and just let (hack-local-variables t) handle it.
+    ;; Find a -*- mode tag.
     (save-excursion
       (goto-char (point-min))
       (skip-chars-forward " \t\n")
@@ -2621,6 +2586,14 @@ we don't actually set it to the same mode the buffer already has."
              (or (set-auto-mode-0 mode keep-mode-if-same)
                  ;; continuing would call minor modes again, toggling them off
                  (throw 'nop nil))))))
+    (and (not done)
+        enable-local-variables
+        (setq mode (hack-local-variables t))
+        (not (memq mode modes))        ; already tried and failed
+        (if (not (functionp mode))
+            (message "Ignoring unknown mode `%s'" mode)
+          (setq done t)
+          (set-auto-mode-0 mode keep-mode-if-same)))
     ;; 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.
@@ -2630,7 +2603,7 @@ we don't actually set it to the same mode the buffer already has."
                   (if (looking-at auto-mode-interpreter-regexp)
                       (match-string 2)
                     ""))
-           ;; Map interpreter name to a mode, signalling we're done at the
+           ;; Map interpreter name to a mode, signaling we're done at the
            ;; same time.
            done (assoc (file-name-nondirectory mode)
                        interpreter-mode-alist))
@@ -2646,7 +2619,7 @@ we don't actually set it to the same mode the buffer already has."
                                           (min (point-max)
                                                (+ (point-min) magic-mode-regexp-match-limit)))
                         (assoc-default nil magic-mode-alist
-                                       (lambda (re dummy)
+                                       (lambda (re _dummy)
                                          (if (functionp re)
                                              (funcall re)
                                            (looking-at re)))))))
@@ -2656,12 +2629,12 @@ we don't actually set it to the same mode the buffer already has."
       (if buffer-file-name
          (let ((name buffer-file-name)
                (remote-id (file-remote-p buffer-file-name)))
+           ;; Remove backup-suffixes from file name.
+           (setq name (file-name-sans-versions name))
            ;; Remove remote file name identification.
            (when (and (stringp remote-id)
                       (string-match (regexp-quote remote-id) name))
              (setq name (substring name (match-end 0))))
-           ;; Remove backup-suffixes from file name.
-           (setq name (file-name-sans-versions name))
            (while name
              ;; Find first matching alist entry.
              (setq mode
@@ -2699,7 +2672,7 @@ we don't actually set it to the same mode the buffer already has."
                                           (min (point-max)
                                                (+ (point-min) magic-mode-regexp-match-limit)))
                         (assoc-default nil magic-fallback-mode-alist
-                                       (lambda (re dummy)
+                                       (lambda (re _dummy)
                                          (if (functionp re)
                                              (funcall re)
                                            (looking-at re)))))))
@@ -2791,7 +2764,9 @@ symbol and VAL is a value that is considered safe."
   :type 'alist)
 
 (defcustom safe-local-eval-forms
-  '((add-hook 'write-file-functions 'time-stamp)
+  ;; 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))
   "Expressions that are considered safe in an `eval:' local variable.
 Add expressions to this list if you want Emacs to evaluate them, when
@@ -2799,7 +2774,7 @@ they appear in an `eval' local variable specification, without first
 asking you for confirmation."
   :risky t
   :group 'find-file
-  :version "22.2"
+  :version "24.1"                      ; added write-file-hooks
   :type '(repeat sexp))
 
 ;; Risky local variables:
@@ -2851,18 +2826,19 @@ asking you for confirmation."
 ;;
 ;; For variables defined in the C source code the declaration should go here:
 
-(mapc (lambda (pair)
-       (put (car pair) 'safe-local-variable (cdr pair)))
-      '((buffer-read-only        . booleanp)   ;; C source code
-       (default-directory       . stringp)    ;; C source code
-       (fill-column             . integerp)   ;; C source code
-       (indent-tabs-mode        . booleanp)   ;; C source code
-       (left-margin             . integerp)   ;; C source code
-       (no-update-autoloads     . booleanp)
-       (tab-width               . integerp)   ;; C source code
-       (truncate-lines          . booleanp)   ;; C source code
-       (word-wrap               . booleanp) ;; C source code
-       (bidi-display-reordering . booleanp))) ;; C source code
+(dolist (pair
+        '((buffer-read-only        . booleanp) ;; C source code
+          (default-directory       . stringp)  ;; C source code
+          (fill-column             . integerp) ;; C source code
+          (indent-tabs-mode        . booleanp) ;; C source code
+          (left-margin             . integerp) ;; C source code
+          (no-update-autoloads     . booleanp)
+          (lexical-binding      . booleanp)      ;; C source code
+          (tab-width               . integerp)   ;; C source code
+          (truncate-lines          . booleanp)   ;; C source code
+          (word-wrap               . booleanp)   ;; C source code
+          (bidi-display-reordering . booleanp))) ;; C source code
+  (put (car pair) 'safe-local-variable (cdr pair)))
 
 (put 'bidi-paragraph-direction 'safe-local-variable
      (lambda (v) (memq v '(nil right-to-left left-to-right))))
@@ -2902,8 +2878,8 @@ variable to set.")
 ALL-VARS is the list of all variables to be set up.
 UNSAFE-VARS is the list of those that aren't marked as safe or risky.
 RISKY-VARS is the list of those that are marked as risky.
-DIR-NAME is a directory name if these settings come from
-directory-local variables, or nil otherwise."
+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
@@ -2969,74 +2945,71 @@ n  -- to ignore the local variables list.")
            (setq char nil)))
        (kill-buffer buf)
        (when (and offer-save (= char ?!) unsafe-vars)
-         (dolist (elt unsafe-vars)
-           (add-to-list 'safe-local-variable-values elt))
-         ;; When this is called from desktop-restore-file-buffer,
-         ;; coding-system-for-read may be non-nil.  Reset it before
-         ;; writing to .emacs.
-         (if (or custom-file user-init-file)
-             (let ((coding-system-for-read nil))
-               (customize-save-variable
-                'safe-local-variable-values
-                safe-local-variable-values))))
+         (customize-push-and-save 'safe-local-variable-values unsafe-vars))
        (memq char '(?! ?\s ?y))))))
 
 (defun hack-local-variables-prop-line (&optional mode-only)
   "Return local variables specified in the -*- line.
-Ignore any specification for `mode:' and `coding:';
-`set-auto-mode' should already have handled `mode:',
-`set-auto-coding' should already have handled `coding:'.
-
-If MODE-ONLY is non-nil, all we do is check whether the major
-mode is specified, returning t if it is specified.  Otherwise,
-return an alist of elements (VAR . VAL), where VAR is a variable
-and VAL is the specified value."
-  (save-excursion
-    (goto-char (point-min))
-    (let ((end (set-auto-mode-1))
-         result mode-specified)
-      ;; Parse the -*- line into the RESULT alist.
-      ;; Also set MODE-SPECIFIED if we see a spec or `mode'.
-      (cond ((not end)
-            nil)
-           ((looking-at "[ \t]*\\([^ \t\n\r:;]+\\)\\([ \t]*-\\*-\\)")
-            ;; Simple form: "-*- MODENAME -*-".  Already handled.
-            (setq mode-specified t)
-            nil)
-           (t
-            ;; Hairy form: '-*-' [ <variable> ':' <value> ';' ]* '-*-'
-            ;; (last ";" is optional).
-            (while (< (point) end)
-              (or (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
-                  (error "Malformed -*- line"))
-              (goto-char (match-end 0))
-              ;; There used to be a downcase here,
-              ;; but the manual didn't say so,
-              ;; and people want to set var names that aren't all lc.
-              (let ((key (intern (match-string 1)))
-                    (val (save-restriction
-                           (narrow-to-region (point) end)
-                           (let ((read-circle nil))
-                             (read (current-buffer))))))
-                ;; It is traditional to ignore
-                ;; case when checking for `mode' in set-auto-mode,
-                ;; so we must do that here as well.
-                ;; That is inconsistent, but we're stuck with it.
-                ;; The same can be said for `coding' in set-auto-coding.
-                (or (and (equal (downcase (symbol-name key)) "mode")
-                         (setq mode-specified t))
-                    (equal (downcase (symbol-name key)) "coding")
-                    (condition-case nil
-                        (push (cons (if (eq key 'eval)
-                                        'eval
-                                      (indirect-variable key))
-                                    val) result)
-                      (error nil)))
-                (skip-chars-forward " \t;")))))
-
-      (if mode-only
-         mode-specified
-       result))))
+Returns an alist of elements (VAR . VAL), where VAR is a variable
+and VAL is the specified value.  Ignores any specification for
+`mode:' and `coding:' (which should have already been handled
+by `set-auto-mode' and `set-auto-coding', respectively).
+Return nil if the -*- line is malformed.
+
+If MODE-ONLY is non-nil, just returns the symbol specifying the
+mode, if there is one, otherwise nil."
+  (catch 'malformed-line
+    (save-excursion
+      (goto-char (point-min))
+      (let ((end (set-auto-mode-1))
+           result)
+       (cond ((not end)
+              nil)
+             ((looking-at "[ \t]*\\([^ \t\n\r:;]+\\)\\([ \t]*-\\*-\\)")
+              ;; Simple form: "-*- MODENAME -*-".
+              (if mode-only
+                  (intern (concat (match-string 1) "-mode"))))
+             (t
+              ;; Hairy form: '-*-' [ <variable> ':' <value> ';' ]* '-*-'
+              ;; (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")
+                  (throw 'malformed-line nil))
+                (goto-char (match-end 0))
+                ;; There used to be a downcase here,
+                ;; but the manual didn't say so,
+                ;; and people want to set var names that aren't all lc.
+                (let* ((key (intern (match-string 1)))
+                       (val (save-restriction
+                              (narrow-to-region (point) end)
+                              (let ((read-circle nil))
+                                (read (current-buffer)))))
+                       ;; It is traditional to ignore
+                       ;; case when checking for `mode' in set-auto-mode,
+                       ;; so we must do that here as well.
+                       ;; That is inconsistent, but we're stuck with it.
+                       ;; The same can be said for `coding' in set-auto-coding.
+                       (keyname (downcase (symbol-name key))))
+                  (if mode-only
+                      (and (equal keyname "mode")
+                           (setq result
+                                 (intern (concat (downcase (symbol-name val))
+                                                 "-mode"))))
+                    (or (equal keyname "coding")
+                        (condition-case nil
+                            (push (cons (cond ((eq key 'eval) 'eval)
+                                              ;; Downcase "Mode:".
+                                              ((equal keyname "mode") 'mode)
+                                              (t (indirect-variable key)))
+                                        val) result)
+                          (error nil))))
+                  (skip-chars-forward " \t;")))
+              result))))))
 
 (defun hack-local-variables-filter (variables dir-name)
   "Filter local variable settings, querying the user if necessary.
@@ -3045,8 +3018,8 @@ VARIABLES is the alist of variable-value settings.  This alist is
  `enable-local-eval', `enable-local-variables', and (if necessary)
  user interaction.  The results are added to
  `file-local-variables-alist', without applying them.
-DIR-NAME is a directory name if these settings come from
- directory-local variables, or nil otherwise."
+If these settings come from directory-local variables, then
+DIR-NAME is the name of the associated directory.  Otherwise it is nil."
   ;; Find those variables that we may want to save to
   ;; `safe-local-variable-values'.
   (let (all-vars risky-vars unsafe-vars)
@@ -3095,8 +3068,12 @@ DIR-NAME is a directory name if these settings come from
 
 (defun hack-local-variables (&optional mode-only)
   "Parse and put into effect this buffer's local variables spec.
-If MODE-ONLY is non-nil, all we do is check whether the major mode
-is specified, returning t if it is specified."
+Uses `hack-local-variables-apply' to apply the variables.
+
+If MODE-ONLY is non-nil, 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)
@@ -3105,88 +3082,100 @@ is specified, returning t if it is specified."
       (report-errors "Directory-local variables error: %s"
        (hack-dir-local-variables)))
     (when (or mode-only enable-local-variables)
-      (setq result (hack-local-variables-prop-line mode-only))
-      ;; Look for "Local variables:" line in last page.
-      (save-excursion
-       (goto-char (point-max))
-       (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
-                        'move)
-       (when (let ((case-fold-search t))
-               (search-forward "Local Variables:" nil t))
-         (skip-chars-forward " \t")
-         ;; suffix is what comes after "local variables:" in its line.
-         ;; prefix is what comes before "local variables:" in its line.
-         (let ((suffix
-                (concat
-                 (regexp-quote (buffer-substring (point)
-                                                 (line-end-position)))
-                 "$"))
-               (prefix
-                (concat "^" (regexp-quote
-                             (buffer-substring (line-beginning-position)
-                                               (match-beginning 0)))))
-               beg)
-
-           (forward-line 1)
-           (let ((startpos (point))
-                 endpos
-                 (thisbuf (current-buffer)))
-             (save-excursion
-               (unless (let ((case-fold-search t))
-                         (re-search-forward
-                          (concat prefix "[ \t]*End:[ \t]*" suffix)
-                          nil t))
-                  ;; This used to be an error, but really all it means is
-                  ;; that this may simply not be a local-variables section,
-                  ;; so just ignore it.
-                 (message "Local variables list is not properly terminated"))
-               (beginning-of-line)
-               (setq endpos (point)))
-
-             (with-temp-buffer
-               (insert-buffer-substring thisbuf startpos endpos)
-               (goto-char (point-min))
-               (subst-char-in-region (point) (point-max) ?\^m ?\n)
-               (while (not (eobp))
-                 ;; Discard the prefix.
-                 (if (looking-at prefix)
-                     (delete-region (point) (match-end 0))
-                   (error "Local variables entry is missing the prefix"))
-                 (end-of-line)
-                 ;; Discard the suffix.
-                 (if (looking-back suffix)
-                     (delete-region (match-beginning 0) (point))
-                   (error "Local variables entry is missing the suffix"))
-                 (forward-line 1))
-               (goto-char (point-min))
-
-               (while (not (eobp))
-                 ;; 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)))
-                        val)
-                   ;; Read the variable value.
-                   (skip-chars-forward "^:")
-                   (forward-char 1)
-                   (let ((read-circle nil))
-                     (setq val (read (current-buffer))))
-                   (if mode-only
-                       (if (eq var 'mode)
-                           (setq result t))
-                     (unless (eq var 'coding)
-                       (condition-case nil
-                           (push (cons (if (eq var 'eval)
-                                           'eval
-                                         (indirect-variable var))
-                                       val) result)
-                         (error nil)))))
-                 (forward-line 1))))))))
+      ;; If MODE-ONLY is non-nil, and the prop line specifies a mode,
+      ;; then we're done, and have no need to scan further.
+      (unless (and (setq result (hack-local-variables-prop-line mode-only))
+                  mode-only)
+       ;; Look for "Local variables:" line in last page.
+       (save-excursion
+         (goto-char (point-max))
+         (search-backward "\n\^L" (max (- (point-max) 3000) (point-min))
+                          'move)
+         (when (let ((case-fold-search t))
+                 (search-forward "Local Variables:" nil t))
+           (skip-chars-forward " \t")
+           ;; suffix is what comes after "local variables:" in its line.
+           ;; prefix is what comes before "local variables:" in its line.
+           (let ((suffix
+                  (concat
+                   (regexp-quote (buffer-substring (point)
+                                                   (line-end-position)))
+                   "$"))
+                 (prefix
+                  (concat "^" (regexp-quote
+                               (buffer-substring (line-beginning-position)
+                                                 (match-beginning 0)))))
+                 beg)
+
+             (forward-line 1)
+             (let ((startpos (point))
+                   endpos
+                   (thisbuf (current-buffer)))
+               (save-excursion
+                 (unless (let ((case-fold-search t))
+                           (re-search-forward
+                            (concat prefix "[ \t]*End:[ \t]*" suffix)
+                            nil t))
+                   ;; This used to be an error, but really all it means is
+                   ;; that this may simply not be a local-variables section,
+                   ;; so just ignore it.
+                   (message "Local variables list is not properly terminated"))
+                 (beginning-of-line)
+                 (setq endpos (point)))
+
+               (with-temp-buffer
+                 (insert-buffer-substring thisbuf startpos endpos)
+                 (goto-char (point-min))
+                 (subst-char-in-region (point) (point-max) ?\^m ?\n)
+                 (while (not (eobp))
+                   ;; Discard the prefix.
+                   (if (looking-at prefix)
+                       (delete-region (point) (match-end 0))
+                     (error "Local variables entry is missing the prefix"))
+                   (end-of-line)
+                   ;; Discard the suffix.
+                   (if (looking-back suffix)
+                       (delete-region (match-beginning 0) (point))
+                     (error "Local variables entry is missing the suffix"))
+                   (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)))
+                          val val2)
+                     (and (equal (downcase (symbol-name var)) "mode")
+                          (setq var 'mode))
+                     ;; Read the variable value.
+                     (skip-chars-forward "^:")
+                     (forward-char 1)
+                     (let ((read-circle nil))
+                       (setq val (read (current-buffer))))
+                     (if mode-only
+                         (and (eq var 'mode)
+                              ;; Specifying minor-modes via mode: is
+                              ;; deprecated, but try to reject them anyway.
+                              (not (string-match
+                                    "-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)))))
+                   (forward-line 1)))))))))
     ;; Now we've read all the local variables.
     ;; If MODE-ONLY is non-nil, return whether the mode was specified.
     (cond (mode-only result)
@@ -3196,6 +3185,14 @@ is specified, returning t if it is specified."
           (hack-local-variables-apply)))))
 
 (defun hack-local-variables-apply ()
+  "Apply the elements of `file-local-variables-alist'.
+If there are any elements, runs `before-hack-local-variables-hook',
+then calls `hack-one-local-variable' to apply the alist elements one by one.
+Finishes by running `hack-local-variables-hook', regardless of whether
+the alist is empty or not.
+
+Note that this function ignores a `mode' entry if it specifies the same
+major mode as the buffer already has."
   (when file-local-variables-alist
     ;; Any 'evals must run in the Right sequence.
     (setq file-local-variables-alist
@@ -3221,7 +3218,7 @@ It is safe if any of these conditions are met:
              ;; can't assure us that the value is safe.
              (with-demoted-errors (funcall safep val))))))
 
-(defun risky-local-variable-p (sym &optional ignored)
+(defun risky-local-variable-p (sym &optional _ignored)
   "Non-nil if SYM could be dangerous as a file-local variable.
 It is dangerous if either of these conditions are met:
 
@@ -3278,21 +3275,25 @@ It is dangerous if either of these conditions are met:
       ;; Certain functions can be allowed with safe arguments
       ;; or can specify verification functions to try.
       (and (symbolp (car exp))
-          (let ((prop (get (car exp) 'safe-local-eval-function)))
-            (cond ((eq prop t)
-                   (let ((ok t))
-                     (dolist (arg (cdr exp))
-                       (unless (hack-one-local-variable-constantp arg)
-                         (setq ok nil)))
-                     ok))
-                  ((functionp prop)
-                   (funcall prop exp))
-                  ((listp prop)
-                   (let ((ok nil))
-                     (dolist (function prop)
-                       (if (funcall function exp)
-                           (setq ok t)))
-                     ok)))))))
+          ;; Allow (minor)-modes calls with no arguments.
+          ;; This obsoletes the use of "mode:" for such things.  (Bug#8613)
+          (or (and (member (cdr exp) '(nil (1) (-1)))
+                   (string-match "-mode\\'" (symbol-name (car exp))))
+              (let ((prop (get (car exp) 'safe-local-eval-function)))
+                (cond ((eq prop t)
+                       (let ((ok t))
+                         (dolist (arg (cdr exp))
+                           (unless (hack-one-local-variable-constantp arg)
+                             (setq ok nil)))
+                         ok))
+                      ((functionp prop)
+                       (funcall prop exp))
+                      ((listp prop)
+                       (let ((ok nil))
+                         (dolist (function prop)
+                           (if (funcall function exp)
+                               (setq ok t)))
+                         ok))))))))
 
 (defun hack-one-local-variable (var val)
   "Set local variable VAR with value VAL.
@@ -3330,11 +3331,11 @@ Each element in this list has the form (DIR CLASS MTIME).
 DIR is the name of the directory.
 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
- used to test whether the cache entry is still valid.
- Alternatively, MTIME can be nil, which means the entry is always
- considered valid.")
+variables file associated with this entry.  This time is a list
+of two 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.")
 
 (defsubst dir-locals-get-class-variables (class)
   "Return the variable list for CLASS."
@@ -3377,8 +3378,19 @@ Return the new variables list."
                                  (cdr entry) root variables))))
              ((or (not key)
                   (derived-mode-p key))
-              (setq variables (dir-locals-collect-mode-variables
-                               (cdr entry) variables))))))
+              (let* ((alist (cdr entry))
+                     (subdirs (assq 'subdirs alist)))
+                (if (or (not subdirs)
+                        (progn
+                          (setq alist (delq subdirs alist))
+                          (cdr-safe subdirs))
+                        ;; TODO someone might want to extend this to allow
+                        ;; integer values for subdir, where N means
+                        ;; variables apply to this directory and N levels
+                        ;; below it (0 == nil).
+                        (equal root default-directory))
+                    (setq variables (dir-locals-collect-mode-variables
+                                     alist variables))))))))
       (error
        ;; The file's content might be invalid (e.g. have a merge conflict), but
        ;; that shouldn't prevent the user from opening the file.
@@ -3443,13 +3455,20 @@ across different environments and users.")
 (defun dir-locals-find-file (file)
   "Find the directory-local variables for FILE.
 This searches upward in the directory tree from FILE.
-If the directory root of FILE has been registered in
- `dir-locals-directory-cache' and the directory-local variables
- file has not been modified, return the matching entry in
- `dir-locals-directory-cache'.
-Otherwise, if a directory-local variables file is found, return
- the file name.
-Otherwise, return nil."
+It stops at the first directory that has been registered in
+`dir-locals-directory-cache' or contains a `dir-locals-file'.
+If it finds an entry in the cache, it checks that it is valid.
+A cache entry with no modification time element (normally, one that
+has been assigned directly using `dir-locals-set-directory-class', not
+set from a file) is always valid.
+A cache entry based on a `dir-locals-file' is valid if the modification
+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."
   (setq file (expand-file-name file))
   (let* ((dir-locals-file-name
          (if (eq system-type 'ms-dos)
@@ -3458,8 +3477,8 @@ Otherwise, return nil."
         (locals-file (locate-dominating-file file dir-locals-file-name))
         (dir-elt nil))
     ;; `locate-dominating-file' may have abbreviated the name.
-    (when locals-file
-      (setq locals-file (expand-file-name dir-locals-file-name locals-file)))
+    (if locals-file
+       (setq locals-file (expand-file-name dir-locals-file-name locals-file)))
     ;; Find the best cached value in `dir-locals-directory-cache'.
     (dolist (elt dir-locals-directory-cache)
       (when (and (eq t (compare-strings file nil (length (car elt))
@@ -3468,23 +3487,32 @@ Otherwise, return nil."
                                              '(windows-nt cygwin ms-dos))))
                 (> (length (car elt)) (length (car dir-elt))))
        (setq dir-elt elt)))
-    (let ((use-cache (and dir-elt
-                         (or (null locals-file)
-                             (<= (length (file-name-directory locals-file))
-                                 (length (car dir-elt)))))))
-      (if use-cache
-         ;; Check the validity of the cache.
-         (if (and (file-readable-p (car dir-elt))
-                  (or (null  (nth 2 dir-elt))
+    (if (and dir-elt
+            (or (null locals-file)
+                (<= (length (file-name-directory locals-file))
+                    (length (car dir-elt)))))
+       ;; Found a potential cache entry.  Check validity.
+       ;; A cache entry with no MTIME is assumed to always be valid
+       ;; (ie, set directly, not from a dir-locals file).
+       ;; Note, we don't bother to check that there is a matching class
+       ;; element in dir-locals-class-alist, since that's done by
+       ;; dir-locals-set-directory-class.
+       (if (or (null (nth 2 dir-elt))
+               (let ((cached-file (expand-file-name dir-locals-file-name
+                                                    (car dir-elt))))
+                 (and (file-readable-p cached-file)
                       (equal (nth 2 dir-elt)
-                             (nth 5 (file-attributes (car dir-elt))))))
-             ;; This cache entry is OK.
-             dir-elt
-           ;; This cache entry is invalid; clear it.
-           (setq dir-locals-directory-cache
-                 (delq dir-elt dir-locals-directory-cache))
-           locals-file)
-       locals-file))))
+                             (nth 5 (file-attributes cached-file))))))
+           ;; This cache entry is OK.
+           dir-elt
+         ;; This cache entry is invalid; clear it.
+         (setq dir-locals-directory-cache
+               (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)
+      ;; No cache entry.
+      locals-file)))
 
 (defun dir-locals-read-from-file (file)
   "Load a variables FILE and register a new class and instance.
@@ -3514,10 +3542,8 @@ and `file-local-variables-alist', without applying them."
          (dir-name nil))
       (cond
        ((stringp variables-file)
-       (setq dir-name (if (buffer-file-name)
-                           (file-name-directory (buffer-file-name))
-                         default-directory))
-       (setq class (dir-locals-read-from-file variables-file)))
+       (setq dir-name (file-name-directory variables-file)
+             class (dir-locals-read-from-file variables-file)))
        ((consp variables-file)
        (setq dir-name (nth 0 variables-file))
        (setq class (nth 1 variables-file))))
@@ -3656,7 +3682,11 @@ the old visited file has been renamed to the new name FILENAME."
          (get major-mode 'mode-class)
          ;; Don't change the mode if the local variable list specifies it.
          (hack-local-variables t)
-         (set-auto-mode t))
+         ;; TODO consider making normal-mode handle this case.
+         (let ((old major-mode))
+           (set-auto-mode t)
+           (or (eq old major-mode)
+               (hack-local-variables))))
     (error nil)))
 
 (defun write-file (filename &optional confirm)
@@ -3826,7 +3856,9 @@ BACKUPNAME is the backup file name, which is the old file renamed."
        (set-file-selinux-context to-name context)))
 
 (defvar file-name-version-regexp
-  "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+~\\)"
+  "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)"
+  ;; The last ~[[:digit]]+ matches relative versions in git,
+  ;; e.g. `foo.js.~HEAD~1~'.
   "Regular expression matching the backup/version part of a file name.
 Used by `file-name-sans-versions'.")
 
@@ -3850,11 +3882,17 @@ See also `file-name-version-regexp'."
   (let ((handler (find-file-name-handler file 'file-ownership-preserved-p)))
     (if handler
        (funcall handler 'file-ownership-preserved-p file)
-      (let ((attributes (file-attributes file)))
+      (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)))))))
+           (= (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)))))))
 
 (defun file-name-sans-extension (filename)
   "Return FILENAME sans final \"extension\".
@@ -4431,6 +4469,7 @@ Before and after saving the buffer, this function runs
            (dir-writable (file-writable-p dir)))
       (if (or (and file-precious-flag dir-writable)
               (and break-hardlink-on-save
+                   (file-exists-p buffer-file-name)
                    (> (file-nlinks buffer-file-name) 1)
                    (or dir-writable
                        (error (concat (format
@@ -4555,6 +4594,9 @@ You can answer `y' to save, `n' not to save, `C-r' to look at the
 buffer in question with `view-buffer' before deciding or `d' to
 view the differences using `diff-buffer-with-file'.
 
+This command first saves any buffers where `buffer-save-without-query' is
+non-nil, without asking.
+
 Optional argument (the prefix) non-nil means save all with no questions.
 Optional second argument PRED determines which buffers are considered:
 If PRED is nil, all the file-visiting buffers are considered.
@@ -4566,14 +4608,14 @@ See `save-some-buffers-action-alist' if you want to
 change the additional actions you can take on files."
   (interactive "P")
   (save-window-excursion
-    (let* (queried some-automatic
+    (let* (queried autosaved-buffers
           files-done abbrevs-done)
       (dolist (buffer (buffer-list))
        ;; First save any buffers that we're supposed to save unconditionally.
        ;; That way the following code won't ask about them.
        (with-current-buffer buffer
          (when (and buffer-save-without-query (buffer-modified-p))
-           (setq some-automatic t)
+           (push (buffer-name) autosaved-buffers)
            (save-buffer))))
       ;; Ask about those buffers that merit it,
       ;; and record the number thus saved.
@@ -4619,9 +4661,15 @@ change the additional actions you can take on files."
             (setq abbrevs-changed nil)
             (setq abbrevs-done t)))
       (or queried (> files-done 0) abbrevs-done
-         (message (if some-automatic
-                      "(Some special files were saved without asking)"
-                    "(No files need saving)"))))))
+         (cond
+          ((null autosaved-buffers)
+           (message "(No files need saving)"))
+          ((= (length autosaved-buffers) 1)
+           (message "(Saved %s)" (car autosaved-buffers)))
+          (t
+           (message "(Saved %d files: %s)"
+                    (length autosaved-buffers)
+                    (mapconcat 'identity autosaved-buffers ", "))))))))
 \f
 (defun not-modified (&optional arg)
   "Mark current buffer as unmodified, not needing to be saved.
@@ -4638,7 +4686,15 @@ prints a message in the minibuffer.  Instead, use `set-buffer-modified-p'."
   "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."
+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
@@ -4656,11 +4712,7 @@ and `view-read-only' is non-nil, enter 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)))
-    (if (vc-backend buffer-file-name)
-        (message "%s" (substitute-command-keys
-                  (concat "File is under version-control; "
-                          "use \\[vc-next-action] to check in/out"))))))
+        (force-mode-line-update)))))
 
 (defun insert-file (filename)
   "Insert contents of file FILENAME into buffer after point.
@@ -4737,7 +4789,10 @@ visited a file in a nonexistent directory.
 
 Noninteractively, the second (optional) argument PARENTS, if
 non-nil, says whether to create parent directories that don't
-exist.  Interactively, this happens by default."
+exist.  Interactively, this happens by default.
+
+If creating the directory or directories fail, an error will be
+raised."
   (interactive
    (list (read-file-name "Make directory: " default-directory default-directory
                         nil nil)
@@ -4780,7 +4835,7 @@ given.  With a prefix argument, TRASH is nil."
    (let* ((trashing (and delete-by-moving-to-trash
                         (null current-prefix-arg)))
          (dir (expand-file-name
-               (read-file-name
+               (read-directory-name
                 (if trashing
                     "Move directory to trash: "
                   "Delete directory: ")
@@ -4811,7 +4866,7 @@ given.  With a prefix argument, TRASH is nil."
                directory 'full directory-files-no-dot-files-regexp))
          (error "Directory is not empty, not moving to trash")
        (move-file-to-trash directory)))
-     ;; Otherwise, call outselves recursively if needed.
+     ;; Otherwise, call ourselves recursively if needed.
      (t
       (if (and recursive (not (file-symlink-p directory)))
          (mapc (lambda (file)
@@ -4826,10 +4881,8 @@ given.  With a prefix argument, TRASH is nil."
                 directory 'full directory-files-no-dot-files-regexp)))
       (delete-directory-internal directory)))))
 
-(defun copy-directory (directory newname &optional keep-time parents)
+(defun copy-directory (directory newname &optional keep-time parents copy-contents)
   "Copy DIRECTORY to NEWNAME.  Both args must be strings.
-If NEWNAME names an existing directory, copy DIRECTORY as subdirectory there.
-
 This function always sets the file modes of the output files to match
 the corresponding input file.
 
@@ -4840,15 +4893,20 @@ A prefix arg makes KEEP-TIME non-nil.
 
 Noninteractively, the last argument PARENTS says whether to
 create parent directories if they don't exist.  Interactively,
-this happens by default."
+this happens by default.
+
+If NEWNAME names an existing directory, copy DIRECTORY as a
+subdirectory there.  However, if called from Lisp with a non-nil
+optional argument COPY-CONTENTS, copy the contents of DIRECTORY
+directly into NEWNAME instead."
   (interactive
    (let ((dir (read-directory-name
               "Copy directory: " default-directory default-directory t nil)))
      (list dir
-          (read-file-name
+          (read-directory-name
            (format "Copy directory %s to: " dir)
            default-directory default-directory nil nil)
-          current-prefix-arg t)))
+          current-prefix-arg t nil)))
   ;; If default-directory is a remote directory, make sure we find its
   ;; copy-directory handler.
   (let ((handler (or (find-file-name-handler directory 'copy-directory)
@@ -4860,21 +4918,22 @@ this happens by default."
       (setq directory (directory-file-name (expand-file-name directory))
            newname   (directory-file-name (expand-file-name newname)))
 
-      (if (not (file-directory-p newname))
-         ;; If NEWNAME is not an existing directory, create it; that
-         ;; is where we will copy the files of DIRECTORY.
-         (make-directory newname parents)
-       ;; If NEWNAME is an existing directory, we will copy into
-       ;; NEWNAME/[DIRECTORY-BASENAME].
-       (setq newname (expand-file-name
-                      (file-name-nondirectory
-                       (directory-file-name directory))
-                      newname))
-       (and (file-exists-p newname)
-            (not (file-directory-p newname))
-            (error "Cannot overwrite non-directory %s with a directory"
-                   newname))
-       (make-directory newname t))
+      (cond ((not (file-directory-p newname))
+            ;; If NEWNAME is not an existing directory, create it;
+            ;; that is where we will copy the files of DIRECTORY.
+            (make-directory newname parents))
+           ;; If NEWNAME is an existing directory and COPY-CONTENTS
+           ;; is nil, copy into NEWNAME/[DIRECTORY-BASENAME].
+           ((not copy-contents)
+            (setq newname (expand-file-name
+                           (file-name-nondirectory
+                            (directory-file-name directory))
+                           newname))
+            (and (file-exists-p newname)
+                 (not (file-directory-p newname))
+                 (error "Cannot overwrite non-directory %s with a directory"
+                        newname))
+            (make-directory newname t)))
 
       ;; Copy recursively.
       (dolist (file
@@ -4890,9 +4949,10 @@ this happens by default."
              (copy-file file target t keep-time)))))
 
       ;; Set directory attributes.
-      (set-file-modes newname (file-modes directory))
-      (if keep-time
-         (set-file-times newname (nth 5 (file-attributes directory)))))))
+      (let ((modes (file-modes directory))
+           (times (and keep-time (nth 5 (file-attributes directory)))))
+       (if modes (set-file-modes newname modes))
+       (if times (set-file-times newname times))))))
 \f
 (put 'revert-buffer-function 'permanent-local t)
 (defvar revert-buffer-function nil
@@ -4939,6 +4999,10 @@ hook functions.
 If `revert-buffer-function' is used to override the normal revert
 mechanism, this hook is not used.")
 
+(defvar revert-buffer-in-progress-p nil
+  "Non-nil if a `revert-buffer' operation is in progress, nil otherwise.
+This is true even if a `revert-buffer-function' is being used.")
+
 (defvar revert-buffer-internal-hook)
 
 (defun revert-buffer (&optional ignore-auto noconfirm preserve-modes)
@@ -4961,7 +5025,7 @@ sake of backward compatibility.  IGNORE-AUTO is optional, defaulting
 to nil.
 
 Optional second argument NOCONFIRM means don't ask for confirmation
-at all.  \(The variable `revert-without-query' offers another way to
+at all.  (The variable `revert-without-query' offers another way to
 revert buffers without querying for confirmation.)
 
 Optional third argument PRESERVE-MODES non-nil means don't alter
@@ -4981,10 +5045,12 @@ non-nil, it is called instead of rereading visited file contents."
   ;; interface, but leaving the programmatic interface the same.
   (interactive (list (not current-prefix-arg)))
   (if revert-buffer-function
-      (funcall revert-buffer-function ignore-auto noconfirm)
+      (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* ((auto-save-p (and (not ignore-auto)
+      (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)
@@ -5075,7 +5141,7 @@ non-nil, it is called instead of rereading visited file contents."
                 ;; have changed the truename.
                 (setq buffer-file-truename
                       (abbreviate-file-name (file-truename buffer-file-name)))
-                (after-find-file nil nil t t preserve-modes)
+                (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
@@ -5112,7 +5178,7 @@ non-nil, it is called instead of rereading visited file contents."
               (save-excursion
                 (let ((switches dired-listing-switches))
                   (if (file-symlink-p file)
-                      (setq switches (concat switches "L")))
+                      (setq switches (concat switches " -L")))
                   (set-buffer standard-output)
                   ;; Use insert-directory-safely, not insert-directory,
                   ;; because these files might not exist.  In particular,
@@ -5155,7 +5221,7 @@ Then you'll be asked about a number of files to recover."
       (error "No previous sessions to recover")))
   (let ((ls-lisp-support-shell-wildcards t))
     (dired (concat auto-save-list-file-prefix "*")
-          (concat dired-listing-switches "t")))
+          (concat dired-listing-switches " -t")))
   (save-excursion
     (goto-char (point-min))
     (or (looking-at " Move to the session you want to recover,")
@@ -5513,7 +5579,8 @@ default directory.  However, if FULL is non-nil, they are absolute."
           contents)
       (while dirs
        (when (or (null (car dirs))     ; Possible if DIRPART is not wild.
-                 (file-directory-p (directory-file-name (car dirs))))
+                 (and (file-directory-p (directory-file-name (car dirs)))
+                      (file-readable-p (car dirs))))
          (let ((this-dir-contents
                 ;; Filter out "." and ".."
                 (delq nil
@@ -5543,7 +5610,7 @@ Prefix arg (second arg if noninteractive) means supply -l switch to `ls'.
 Actions controlled by variables `list-directory-brief-switches'
 and `list-directory-verbose-switches'."
   (interactive (let ((pfx current-prefix-arg))
-                (list (read-file-name (if pfx "List directory (verbose): "
+                (list (read-directory-name (if pfx "List directory (verbose): "
                                         "List directory (brief): ")
                                       nil default-directory nil)
                       pfx)))
@@ -5723,7 +5790,7 @@ returns nil."
 
          ;; vc dired listings provide the state or blanks between file
          ;; permissions and date.  The state is always surrounded by
-         ;; parantheses:
+         ;; parentheses:
          ;; -rw-r--r-- (modified) 2005-10-22 21:25 files.el
          ;; This is not supported yet.
     (purecopy (concat "\\([0-9][BkKMGTPEZY]? " iso
@@ -5802,6 +5869,9 @@ normally equivalent short `-D' option is just passed on to
                                  (file-name-directory file)
                                (file-name-directory (expand-file-name file))))
                            (pattern (file-name-nondirectory file)))
+                       ;; NB since switches is passed to the shell, be
+                       ;; careful of malicious values, eg "-l;reboot".
+                       ;; See eg dired-safe-switches-p.
                        (call-process
                         shell-file-name nil t nil
                         "-c"
@@ -6040,7 +6110,7 @@ message to that effect instead of signaling an error."
 
 (defvar kill-emacs-query-functions nil
   "Functions to call with no arguments to query about killing Emacs.
-If any of these functions returns nil, killing Emacs is cancelled.
+If any of these functions returns nil, killing Emacs is canceled.
 `save-buffers-kill-emacs' calls these functions, but `kill-emacs',
 the low level primitive, does not.  See also `kill-emacs-hook'.")
 
@@ -6074,8 +6144,8 @@ With prefix ARG, silently save all file-visiting buffers, then kill."
                    (setq active t))
               (setq processes (cdr processes)))
             (or (not active)
-                (list-processes t)
-                (yes-or-no-p "Active processes exist; kill them and exit anyway? "))))
+                (progn (list-processes t)
+                       (yes-or-no-p "Active processes exist; kill them and exit anyway? ")))))
        ;; Query the user for other things, perhaps.
        (run-hook-with-args-until-failure 'kill-emacs-query-functions)
        (or (null confirm-kill-emacs)
@@ -6244,7 +6314,7 @@ as in \"og+rX-w\"."
   "Convert symbolic file modes to numeric file modes.
 MODES is the string to convert, it should match
 \"[ugoa]*([+-=][rwxXstugo]*)+,...\".
-See (info \"(coreutils)File permissions\") for more information on this
+See Info node `(coreutils)File permissions' for more information on this
 notation.
 FROM (or 0 if nil) gives the mode bits on which to base permissions if
 MODES request to add, remove, or set permissions based on existing ones,
@@ -6430,7 +6500,7 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
                                 (setq tries 0 success t))
                             (file-already-exists nil))
                     (setq tries (1- tries))
-                    ;; Uniqify new-fn.  (Some file managers do not
+                    ;; Uniquify new-fn.  (Some file managers do not
                     ;; like Emacs-style backup file names---e.g. bug
                     ;; 170956 in Konqueror bug tracker.)
                     (setq new-fn (make-temp-name (concat base-fn "_")))))