Replace version 24.2 with 24.3 where appropriate (hopefully)
[bpt/emacs.git] / lisp / files.el
index 588fda4..5caa468 100644 (file)
 
 ;;; Commentary:
 
-;; Defines most of Emacs's file- and directory-handling functions,
+;; Defines most of Emacs'ss file- and directory-handling functions,
 ;; including basic file visiting, backup generation, link handling,
 ;; ITS-id version control, load- and write-hook handling, and the like.
 
 ;;; Code:
 
-(eval-when-compile (require 'cl))
-
 (defvar font-lock-keywords)
 
 (defgroup backup nil
@@ -415,13 +413,13 @@ location of point in the current buffer."
 
 ;;;It is not useful to make this a local variable.
 ;;;(put 'find-file-not-found-hooks 'permanent-local t)
+(define-obsolete-variable-alias 'find-file-not-found-hooks
+    'find-file-not-found-functions "22.1")
 (defvar find-file-not-found-functions nil
   "List of functions to be called for `find-file' on nonexistent file.
 These functions are called as soon as the error is detected.
 Variable `buffer-file-name' is already set up.
 The functions are called in the order given until one of them returns non-nil.")
-(define-obsolete-variable-alias 'find-file-not-found-hooks
-    'find-file-not-found-functions "22.1")
 
 ;;;It is not useful to make this a local variable.
 ;;;(put 'find-file-hooks 'permanent-local t)
@@ -435,6 +433,7 @@ functions are called."
   :options '(auto-insert)
   :version "22.1")
 
+(define-obsolete-variable-alias 'write-file-hooks 'write-file-functions "22.1")
 (defvar write-file-functions nil
   "List of functions to be called before writing out a buffer to a file.
 If one of them returns non-nil, the file is considered already written
@@ -451,13 +450,14 @@ coding system and setting mode bits.  (See Info
 node `(elisp)Saving Buffers'.)  To perform various checks or
 updates before the buffer is saved, use `before-save-hook'.")
 (put 'write-file-functions 'permanent-local t)
-(define-obsolete-variable-alias 'write-file-hooks 'write-file-functions "22.1")
 
 (defvar local-write-file-hooks nil)
 (make-variable-buffer-local 'local-write-file-hooks)
 (put 'local-write-file-hooks 'permanent-local t)
 (make-obsolete-variable 'local-write-file-hooks 'write-file-functions "22.1")
 
+(define-obsolete-variable-alias 'write-contents-hooks
+    'write-contents-functions "22.1")
 (defvar write-contents-functions nil
   "List of functions to be called before writing out a buffer to a file.
 If one of them returns non-nil, the file is considered already written
@@ -475,8 +475,6 @@ For hooks that _do_ pertain to the particular visited file, use
 To perform various checks or updates before the buffer is saved,
 use `before-save-hook'.")
 (make-variable-buffer-local 'write-contents-functions)
-(define-obsolete-variable-alias 'write-contents-hooks
-    'write-contents-functions "22.1")
 
 (defcustom enable-local-variables t
   "Control use of local variables in files you visit.
@@ -510,14 +508,36 @@ and ignores this variable."
                 (other :tag "Query" other))
   :group 'find-file)
 
+;; This is an odd variable IMO.
+;; You might wonder why it is needed, when we could just do:
+;; (set (make-local-variable 'enable-local-variables) nil)
+;; These two are not precisely the same.
+;; Setting this variable does not cause -*- mode settings to be
+;; ignored, whereas setting enable-local-variables does.
+;; Only three places in Emacs use this variable: tar and arc modes,
+;; and rmail.  The first two don't need it.  They already use
+;; inhibit-local-variables-regexps, which is probably enough, and
+;; could also just set enable-local-variables locally to nil.
+;; Them setting it has the side-effect that dir-locals cannot apply to
+;; eg tar files (?).  FIXME Is this appropriate?
+;; AFAICS, rmail is the only thing that needs this, and the only
+;; reason it uses it is for BABYL files (which are obsolete).
+;; These contain "-*- rmail -*-" in the first line, which rmail wants
+;; to respect, so that find-file on a BABYL file will switch to
+;; rmail-mode automatically (this is nice, but hardly essential,
+;; since most people are used to explicitly running a command to
+;; access their mail; M-x gnus etc).  Rmail files may happen to
+;; contain Local Variables sections in messages, which Rmail wants to
+;; ignore.  So AFAICS the only reason this variable exists is for a
+;; minor convenience feature for handling of an obsolete Rmail file format.
 (defvar local-enable-local-variables t
   "Like `enable-local-variables' but meant for buffer-local bindings.
 The meaningful values are nil and non-nil.  The default is non-nil.
 If a major mode sets this to nil, buffer-locally, then any local
-variables list in the file will be ignored.
+variables list in a file visited in that mode will be ignored.
 
-This variable does not affect the use of major modes
-specified in a -*- line.")
+This variable does not affect the use of major modes specified
+in a -*- line.")
 
 (defcustom enable-local-eval 'maybe
   "Control processing of the \"variable\" `eval' in a file's local variables.
@@ -760,10 +780,10 @@ one or more of those symbols."
     (read-file-name-internal string pred action))
    ((eq (car-safe action) 'boundaries)
     (let ((suffix (cdr action)))
-      (list* 'boundaries
-             (length (file-name-directory string))
-             (let ((x (file-name-directory suffix)))
-               (if x (1- (length x)) (length suffix))))))
+      `(boundaries
+        ,(length (file-name-directory string))
+        ,@(let ((x (file-name-directory suffix)))
+            (if x (1- (length x)) (length suffix))))))
    (t
     (let ((names '())
           ;; If we have files like "foo.el" and "foo.elc", we could load one of
@@ -856,9 +876,12 @@ or mount points potentially requiring authentication as a different user.")
 ;;       nil)))
 
 (defun locate-dominating-file (file name)
-  "Look up the directory hierarchy from FILE for a file named NAME.
+  "Look up the directory hierarchy from FILE for a directory containing NAME.
 Stop at the first parent directory containing a file NAME,
-and return the directory.  Return nil if not found."
+and return the directory.  Return nil if not found.
+Instead of a string, NAME can also be a predicate taking one argument
+\(a directory) and returning a non-nil value if that directory is the one for
+which we're looking."
   ;; We used to use the above locate-dominating-files code, but the
   ;; directory-files call is very costly, so we're much better off doing
   ;; multiple calls using the code in here.
@@ -885,12 +908,14 @@ and return the directory.  Return nil if not found."
                     ;;   (setq user (nth 2 (file-attributes file)))
                     ;;   (and prev-user (not (equal user prev-user))))
                     (string-match locate-dominating-stop-dir-regexp file)))
-      (setq try (file-exists-p (expand-file-name name file)))
+      (setq try (if (stringp name)
+                    (file-exists-p (expand-file-name name file))
+                  (funcall name file)))
       (cond (try (setq root file))
             ((equal file (setq file (file-name-directory
                                      (directory-file-name file))))
              (setq file nil))))
-    root))
+    (if root (file-name-as-directory root))))
 
 
 (defun executable-find (command)
@@ -957,22 +982,43 @@ Tip: You can use this expansion of remote identifier components
        (funcall handler 'file-remote-p file identification connected)
       nil)))
 
+;; Probably this entire variable should be obsolete now, in favor of
+;; something Tramp-related (?).  It is not used in many places.
+;; It's not clear what the best file for this to be in is, but given
+;; it uses custom-initialize-delay, it is easier if it is preloaded
+;; rather than autoloaded.
+(defcustom remote-shell-program
+  ;; This used to try various hard-coded places for remsh, rsh, and
+  ;; rcmd, trying to guess based on location whether "rsh" was
+  ;; "restricted shell" or "remote shell", but I don't see the point
+  ;; in this day and age.  Almost everyone will use ssh, and have
+  ;; whatever command they want to use in PATH.
+  (purecopy
+   (let ((list '("ssh" "remsh" "rcmd" "rsh")))
+     (while (and list
+                (not (executable-find (car list)))
+                (setq list (cdr list))))
+     (or (car list) "ssh")))
+  "Program to use to execute commands on a remote host (e.g. ssh or rsh)."
+  :version "24.3"                      ; ssh rather than rsh, etc
+  :initialize 'custom-initialize-delay
+  :group 'environment
+  :type 'file)
+
 (defcustom remote-file-name-inhibit-cache 10
   "Whether to use the remote file-name cache for read access.
+When `nil', never expire cached values (caution)
+When `t', never use the cache (safe, but may be slow)
+A number means use cached values for that amount of seconds since caching.
 
-When `nil', always use the cached values.
-When `t', never use them.
-A number means use them for that amount of seconds since they were
-cached.
-
-File attributes of remote files are cached for better performance.
-If they are changed out of Emacs' control, the cached values
-become invalid, and must be invalidated.
+The attributes of remote files are cached for better performance.
+If they are changed outside of Emacs's control, the cached values
+become invalid, and must be reread.  If you are sure that nothing
+other than Emacs changes the files, you can set this variable to `nil'.
 
-In case a remote file is checked regularly, it might be
-reasonable to let-bind this variable to a value less then the
-time period between two checks.
-Example:
+If a remote file is checked regularly, it might be a good idea to
+let-bind this variable to a value less than the interval between
+consecutive checks.  For example:
 
   (defun display-time-file-nonempty-p (file)
     (let ((remote-file-name-inhibit-cache (- display-time-interval 5)))
@@ -1033,9 +1079,7 @@ containing it, until no links are left at any level.
             (delq (rassq 'ange-ftp-completion-hook-function tem) tem)))))
     (or prev-dirs (setq prev-dirs (list nil)))
 
-    ;; andrewi@harlequin.co.uk - none of the following code (except for
-    ;; invoking the file-name handler) currently applies on Windows
-    ;; (ie. there are no native symlinks), but there is an issue with
+    ;; andrewi@harlequin.co.uk - on Windows, there is an issue with
     ;; case differences being ignored by the OS, and short "8.3 DOS"
     ;; name aliases existing for all files.  (The short names are not
     ;; reported by directory-files, but can be used to refer to files.)
@@ -1045,31 +1089,15 @@ containing it, until no links are left at any level.
     ;; it is stored on disk (expanding short name aliases with the full
     ;; name in the process).
     (if (eq system-type 'windows-nt)
-      (let ((handler (find-file-name-handler filename 'file-truename)))
-       ;; For file name that has a special handler, call handler.
-       ;; This is so that ange-ftp can save time by doing a no-op.
-       (if handler
-           (setq filename (funcall handler 'file-truename filename))
-         ;; If filename contains a wildcard, newname will be the old name.
-         (unless (string-match "[[*?]" filename)
-           ;; If filename exists, use the long name.  If it doesn't exist,
-            ;; drill down until we find a directory that exists, and use
-            ;; the long name of that, with the extra non-existent path
-            ;; components concatenated.
-            (let ((longname (w32-long-file-name filename))
-                  missing rest)
-              (if longname
-                  (setq filename longname)
-                ;; Include the preceding directory separator in the missing
-                ;; part so subsequent recursion on the rest works.
-                (setq missing (concat "/" (file-name-nondirectory filename)))
-               (let ((length (length missing)))
-                 (setq rest
-                       (if (> length (length filename))
-                           ""
-                         (substring filename 0 (- length)))))
-                (setq filename (concat (file-truename rest) missing))))))
-       (setq done t)))
+       (unless (string-match "[[*?]" filename)
+         ;; If filename exists, use its long name.  If it doesn't
+         ;; exist, the recursion below on the directory of filename
+         ;; will drill down until we find a directory that exists,
+         ;; and use the long name of that, with the extra
+         ;; non-existent path components concatenated.
+         (let ((longname (w32-long-file-name filename)))
+           (if longname
+               (setq filename longname)))))
 
     ;; If this file directly leads to a link, process that iteratively
     ;; so that we don't use lots of stack.
@@ -1089,6 +1117,8 @@ containing it, until no links are left at any level.
            (setq dirfile (directory-file-name dir))
            ;; If these are equal, we have the (or a) root directory.
            (or (string= dir dirfile)
+               (and (memq system-type '(windows-nt ms-dos cygwin))
+                    (eq (compare-strings dir 0 nil dirfile 0 nil t) t))
                ;; If this is the same dir we last got the truename for,
                ;; save time--don't recalculate.
                (if (assoc dir (car prev-dirs))
@@ -1419,23 +1449,26 @@ file names with wildcards."
      (find-file filename)
      (current-buffer)))
 
-(defun find-file-read-only (filename &optional wildcards)
-  "Edit file FILENAME but don't allow changes.
-Like \\[find-file], but marks buffer as read-only.
-Use \\[toggle-read-only] to permit editing."
-  (interactive
-   (find-file-read-args "Find file read-only: "
-                        (confirm-nonexistent-file-or-buffer)))
+(defun find-file--read-only (fun filename wildcards)
   (unless (or (and wildcards find-file-wildcards
                   (not (string-match "\\`/:" filename))
                   (string-match "[[*?]" filename))
              (file-exists-p filename))
     (error "%s does not exist" filename))
-  (let ((value (find-file filename wildcards)))
+  (let ((value (funcall fun filename wildcards)))
     (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
          (if (listp value) value (list value)))
     value))
 
+(defun find-file-read-only (filename &optional wildcards)
+  "Edit file FILENAME but don't allow changes.
+Like \\[find-file], but marks buffer as read-only.
+Use \\[toggle-read-only] to permit editing."
+  (interactive
+   (find-file-read-args "Find file read-only: "
+                        (confirm-nonexistent-file-or-buffer)))
+  (find-file--read-only #'find-file filename wildcards))
+
 (defun find-file-read-only-other-window (filename &optional wildcards)
   "Edit file FILENAME in another window but don't allow changes.
 Like \\[find-file-other-window], but marks buffer as read-only.
@@ -1443,15 +1476,7 @@ Use \\[toggle-read-only] to permit editing."
   (interactive
    (find-file-read-args "Find file read-only other window: "
                         (confirm-nonexistent-file-or-buffer)))
-  (unless (or (and wildcards find-file-wildcards
-                  (not (string-match "\\`/:" filename))
-                  (string-match "[[*?]" filename))
-             (file-exists-p filename))
-    (error "%s does not exist" filename))
-  (let ((value (find-file-other-window filename wildcards)))
-    (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
-         (if (listp value) value (list value)))
-    value))
+  (find-file--read-only #'find-file-other-window filename wildcards))
 
 (defun find-file-read-only-other-frame (filename &optional wildcards)
   "Edit file FILENAME in another frame but don't allow changes.
@@ -1460,15 +1485,7 @@ Use \\[toggle-read-only] to permit editing."
   (interactive
    (find-file-read-args "Find file read-only other frame: "
                         (confirm-nonexistent-file-or-buffer)))
-  (unless (or (and wildcards find-file-wildcards
-                  (not (string-match "\\`/:" filename))
-                  (string-match "[[*?]" filename))
-             (file-exists-p filename))
-    (error "%s does not exist" filename))
-  (let ((value (find-file-other-frame filename wildcards)))
-    (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
-         (if (listp value) value (list value)))
-    value))
+  (find-file--read-only #'find-file-other-frame filename wildcards))
 
 (defun find-alternate-file-other-window (filename &optional wildcards)
   "Find file FILENAME as a replacement for the file in the next window.
@@ -1497,7 +1514,11 @@ 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
+;; Defined and used in buffer.c, but not as a DEFVAR_LISP.
+(defvar kill-buffer-hook nil
+  "Hook run when a buffer is killed.
+The buffer being killed is current while the hook is running.
+See `kill-buffer'.")
 
 (defun find-alternate-file (filename &optional wildcards)
   "Find file FILENAME, select its buffer, kill previous buffer.
@@ -1600,6 +1621,7 @@ Choose the buffer's name using `generate-new-buffer-name'."
   "Regexp to match the automounter prefix in a directory name."
   :group 'files
   :type 'regexp)
+(make-obsolete-variable 'automount-dir-prefix 'directory-abbrev-alist "24.3")
 
 (defvar abbreviated-home-dir nil
   "The user's homedir abbreviated according to `directory-abbrev-alist'.")
@@ -1725,9 +1747,9 @@ When nil, never request confirmation."
 OP-TYPE specifies the file operation being performed (for message to user)."
   (when (and large-file-warning-threshold size
             (> size large-file-warning-threshold)
-            (not (y-or-n-p (format "File %s is large (%dMB), really %s? "
+            (not (y-or-n-p (format "File %s is large (%s), really %s? "
                                    (file-name-nondirectory filename)
-                                   (/ size 1048576) op-type))))
+                                   (file-size-human-readable size) op-type))))
     (error "Aborted")))
 
 (defun find-file-noselect (filename &optional nowarn rawfile wildcards)
@@ -1971,6 +1993,8 @@ Do you want to revisit the file normally now? ")
        (after-find-file error (not nowarn)))
       (current-buffer))))
 \f
+(defvar file-name-buffer-file-type-alist) ;From dos-w32.el.
+
 (defun insert-file-contents-literally (filename &optional visit beg end replace)
   "Like `insert-file-contents', but only reads in the file literally.
 A buffer may be modified in several ways after reading into the buffer,
@@ -1982,21 +2006,14 @@ This function ensures that none of these modifications will take place."
        (after-insert-file-functions nil)
        (coding-system-for-read 'no-conversion)
        (coding-system-for-write 'no-conversion)
-       (find-buffer-file-type-function
-         (if (fboundp 'find-buffer-file-type)
-             (symbol-function 'find-buffer-file-type)
-           nil))
+       (file-name-buffer-file-type-alist '(("" . t)))
         (inhibit-file-name-handlers
+         ;; FIXME: Yuck!!  We should turn insert-file-contents-literally
+         ;; into a file operation instead!
          (append '(jka-compr-handler image-file-handler epa-file-handler)
                  inhibit-file-name-handlers))
         (inhibit-file-name-operation 'insert-file-contents))
-    (unwind-protect
-         (progn
-           (fset 'find-buffer-file-type (lambda (_filename) t))
-           (insert-file-contents filename visit beg end replace))
-      (if find-buffer-file-type-function
-         (fset 'find-buffer-file-type find-buffer-file-type-function)
-       (fmakunbound 'find-buffer-file-type)))))
+    (insert-file-contents filename visit beg end replace)))
 
 (defun insert-file-1 (filename insert-func)
   (if (file-directory-p filename)
@@ -2125,6 +2142,7 @@ unless NOMODES is non-nil."
         (/= (char-after (1- (point-max))) ?\n)
         (not (and (eq selective-display t)
                   (= (char-after (1- (point-max))) ?\r)))
+        (not buffer-read-only)
         (save-excursion
           (goto-char (point-max))
           (insert "\n")))
@@ -2178,10 +2196,7 @@ in that case, this function acts as if `enable-local-variables' were t."
              (boundp 'font-lock-keywords)
              (eq (car font-lock-keywords) t))
     (setq font-lock-keywords (cadr font-lock-keywords))
-    (font-lock-mode 1))
-
-  (if (fboundp 'ucs-set-table-for-input) ; don't lose when building
-      (ucs-set-table-for-input)))
+    (font-lock-mode 1)))
 
 (defcustom auto-mode-case-fold t
   "Non-nil means to try second pass through `auto-mode-alist'.
@@ -2236,9 +2251,11 @@ since only a single case-insensitive search through the alist is made."
      ("\\.makepp\\'" . makefile-makepp-mode)
      ,@(if (memq system-type '(berkeley-unix darwin))
           '(("\\.mk\\'" . makefile-bsdmake-mode)
+            ("\\.make\\'" . makefile-bsdmake-mode)
             ("GNUmakefile\\'" . makefile-gmake-mode)
             ("[Mm]akefile\\'" . makefile-bsdmake-mode))
         '(("\\.mk\\'" . makefile-gmake-mode)   ; Might be any make, give Gnu the host advantage
+          ("\\.make\\'" . makefile-gmake-mode)
           ("[Mm]akefile\\'" . makefile-gmake-mode)))
      ("\\.am\\'" . makefile-automake-mode)
      ;; Less common extensions come here
@@ -2315,8 +2332,8 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ("\\.dbk\\'" . xml-mode)
      ("\\.dtd\\'" . sgml-mode)
      ("\\.ds\\(ss\\)?l\\'" . dsssl-mode)
-     ("\\.js\\'" . js-mode)            ; javascript-mode would be better
-     ("\\.json\\'" . js-mode)
+     ("\\.js\\'" . javascript-mode)
+     ("\\.json\\'" . javascript-mode)
      ("\\.[ds]?vh?\\'" . verilog-mode)
      ;; .emacs or .gnus or .viper following a directory delimiter in
      ;; Unix, MSDOG or VMS syntax.
@@ -2405,9 +2422,6 @@ If the element has the form (REGEXP FUNCTION NON-NIL), then after
 calling FUNCTION (if it's not nil), we delete the suffix that matched
 REGEXP and search the list again for another match.
 
-If the file name matches `inhibit-first-line-modes-regexps',
-then `auto-mode-alist' is not processed.
-
 The extensions whose FUNCTION is `archive-mode' should also
 appear in `auto-coding-alist' with `no-conversion' coding system.
 
@@ -2478,16 +2492,55 @@ of a script, mode MODE is enabled.
 
 See also `auto-mode-alist'.")
 
-(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
-  "List of regexps for what to ignore, for `inhibit-first-line-modes-regexps'.
-When checking `inhibit-first-line-modes-regexps', we first discard
+(define-obsolete-variable-alias 'inhibit-first-line-modes-regexps
+  'inhibit-file-local-variables-regexps "24.1")
+
+;; TODO really this should be a list of modes (eg tar-mode), not regexps,
+;; because we are duplicating info from auto-mode-alist.
+;; TODO many elements of this list are also in auto-coding-alist.
+(defvar inhibit-local-variables-regexps
+  (mapcar 'purecopy '("\\.tar\\'" "\\.t[bg]z\\'"
+                     "\\.arc\\'" "\\.zip\\'" "\\.lzh\\'" "\\.lha\\'"
+                     "\\.zoo\\'" "\\.[jew]ar\\'" "\\.xpi\\'" "\\.rar\\'"
+                     "\\.7z\\'"
+                     "\\.sx[dmicw]\\'" "\\.odt\\'"
+                     "\\.tiff?\\'" "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'"))
+  "List of regexps matching file names in which to ignore local variables.
+This includes `-*-' lines as well as trailing \"Local Variables\" sections.
+Files matching this list are typically binary file formats.
+They may happen to contain sequences that look like local variable
+specifications, but are not really, or they may be containers for
+member files with their own local variable sections, which are
+not appropriate for the containing file.
+See also `inhibit-local-variables-suffixes'.")
+
+(define-obsolete-variable-alias 'inhibit-first-line-modes-suffixes
+  'inhibit-local-variables-suffixes "24.1")
+
+(defvar inhibit-local-variables-suffixes nil
+  "List of regexps matching suffixes to remove from file names.
+When checking `inhibit-local-variables-regexps', we first discard
 from the end of the file name anything that matches one of these regexps.")
 
+;; TODO explicitly add case-fold-search t?
+(defun inhibit-local-variables-p ()
+  "Return non-nil if file local variables should be ignored.
+This checks the file (or buffer) name against `inhibit-local-variables-regexps'
+and `inhibit-local-variables-suffixes'."
+  (let ((temp inhibit-local-variables-regexps)
+       (name (if buffer-file-name
+                 (file-name-sans-versions buffer-file-name)
+               (buffer-name))))
+    (while (let ((sufs inhibit-local-variables-suffixes))
+            (while (and sufs (not (string-match (car sufs) name)))
+              (setq sufs (cdr sufs)))
+            sufs)
+      (setq name (substring name 0 (match-beginning 0))))
+    (while (and temp
+               (not (string-match (car temp) name)))
+      (setq temp (cdr temp)))
+    temp))
+
 (defvar auto-mode-interpreter-regexp
   (purecopy "#![ \t]?\\([^ \t\n]*\
 /bin/env[ \t]\\)?\\([^ \t\n]+\\)")
@@ -2550,21 +2603,24 @@ Also applies to `magic-fallback-mode-alist'.")
 (defun set-auto-mode (&optional keep-mode-if-same)
   "Select major mode appropriate for current buffer.
 
-To find the right major mode, this function checks for a -*- mode tag,
+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'.
 
-If `enable-local-variables' is nil, this function does not check for
-any mode: tag anywhere in the file.
+If `enable-local-variables' is nil, or if the file name matches
+`inhibit-local-variables-regexps', this function does not check
+for any mode: tag anywhere in the file.  If `local-enable-local-variables'
+is nil, then the only mode: tag that can be relevant is a -*- one.
 
 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)
+  (let ((try-locals (not (inhibit-local-variables-p)))
+       end done mode modes)
     ;; 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.
@@ -2572,7 +2628,9 @@ we don't actually set it to the same mode the buffer already has."
     (save-excursion
       (goto-char (point-min))
       (skip-chars-forward " \t\n")
+      ;; Note by design local-enable-local-variables does not matter here.
       (and enable-local-variables
+          try-locals
           (setq end (set-auto-mode-1))
           (if (save-excursion (search-forward ":" end t))
               ;; Find all specifications for the `mode:' variable
@@ -2603,8 +2661,12 @@ 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))))))
+    ;; hack-local-variables checks local-enable-local-variables etc, but
+    ;; we might as well be explicit here for the sake of clarity.
     (and (not done)
         enable-local-variables
+        local-enable-local-variables
+        try-locals
         (setq mode (hack-local-variables t))
         (not (memq mode modes))        ; already tried and failed
         (if (not (functionp mode))
@@ -2676,7 +2738,7 @@ we don't actually set it to the same mode the buffer already has."
                       (cadr mode))
                  (setq mode (car mode)
                        name (substring name 0 (match-beginning 0)))
-               (setq name))
+               (setq name nil))
              (when mode
                (set-auto-mode-0 mode keep-mode-if-same)
                (setq done t))))))
@@ -2711,43 +2773,34 @@ same, do nothing and return nil."
       (funcall mode)
       mode)))
 
+(defvar file-auto-mode-skip "^\\(#!\\|'\\\\\"\\)"
+  "Regexp of lines to skip when looking for file-local settings.
+If the first line matches this regular expression, then the -*-...-*- file-
+local settings will be consulted on the second line instead of the first.")
+
 (defun set-auto-mode-1 ()
   "Find the -*- spec in the buffer.
 Call with point at the place to start searching from.
-If one is found, set point to the beginning
-and return the position of the end.
-Otherwise, return nil; point may be changed."
+If one is found, set point to the beginning and return the position
+of the end.  Otherwise, return nil; may change point.
+The variable `inhibit-local-variables-regexps' can cause a -*- spec to
+be ignored; but `enable-local-variables' and `local-enable-local-variables'
+have no effect."
   (let (beg end)
     (and
      ;; Don't look for -*- if this file name matches any
-     ;; of the regexps in inhibit-first-line-modes-regexps.
-     (let ((temp inhibit-first-line-modes-regexps)
-          (name (if buffer-file-name
-                    (file-name-sans-versions buffer-file-name)
-                  (buffer-name))))
-       (while (let ((sufs inhibit-first-line-modes-suffixes))
-               (while (and sufs (not (string-match (car sufs) name)))
-                 (setq sufs (cdr sufs)))
-               sufs)
-        (setq name (substring name 0 (match-beginning 0))))
-       (while (and temp
-                  (not (string-match (car temp) name)))
-        (setq temp (cdr temp)))
-       (not temp))
-
+     ;; of the regexps in inhibit-local-variables-regexps.
+     (not (inhibit-local-variables-p))
      (search-forward "-*-" (line-end-position
-                            ;; If the file begins with "#!"
-                            ;; (exec interpreter magic), look
-                            ;; for mode frobs in the first two
-                            ;; lines.  You cannot necessarily
-                            ;; put them in the first line of
-                            ;; such a file without screwing up
-                            ;; the interpreter invocation.
-                            ;; The same holds for
-                            ;;   '\"
-                            ;; in man pages (preprocessor
+                            ;; If the file begins with "#!"  (exec
+                            ;; interpreter magic), look for mode frobs
+                            ;; in the first two lines.  You cannot
+                            ;; necessarily put them in the first line
+                            ;; of such a file without screwing up the
+                            ;; interpreter invocation.  The same holds
+                            ;; for '\" in man pages (preprocessor
                             ;; magic for the `man' program).
-                            (and (looking-at "^\\(#!\\|'\\\\\"\\)") 2)) t)
+                            (and (looking-at file-auto-mode-skip) 2)) t)
      (progn
        (skip-chars-forward " \t")
        (setq beg (point))
@@ -3049,11 +3102,15 @@ DIR-NAME is the name of the associated directory.  Otherwise it is nil."
              ;; Obey `enable-local-eval'.
              ((eq var 'eval)
               (when enable-local-eval
-                (push elt all-vars)
-                (or (eq enable-local-eval t)
-                    (hack-one-local-variable-eval-safep (eval (quote val)))
-                    (safe-local-variable-p var val)
-                    (push elt unsafe-vars))))
+                (let ((safe (or (hack-one-local-variable-eval-safep val)
+                                ;; In case previously marked safe (bug#5636).
+                                (safe-local-variable-p var val))))
+                  ;; If not safe and e-l-v = :safe, ignore totally.
+                  (when (or safe (not (eq enable-local-variables :safe)))
+                    (push elt all-vars)
+                    (or (eq enable-local-eval t)
+                        safe
+                        (push elt unsafe-vars))))))
              ;; Ignore duplicates (except `mode') in the present list.
              ((and (assq var all-vars) (not (eq var 'mode))) nil)
              ;; Accept known-safe variables.
@@ -3090,19 +3147,41 @@ 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."
+major-mode.
+
+If `enable-local-variables' or `local-enable-local-variables' is nil,
+this function does nothing.  If `inhibit-local-variables-regexps'
+applies to the file in question, the file is not scanned for
+local variables, but directory-local variables may still be applied."
+  ;; We don't let inhibit-local-variables-p influence the value of
+  ;; enable-local-variables, because then it would affect dir-local
+  ;; variables.  We don't want to search eg tar files for file local
+  ;; variable sections, but there is no reason dir-locals cannot apply
+  ;; to them.  The real meaning of inhibit-local-variables-p is "do
+  ;; not scan this file for local variables".
   (let ((enable-local-variables
         (and local-enable-local-variables enable-local-variables))
        result)
     (unless mode-only
       (setq file-local-variables-alist nil)
       (report-errors "Directory-local variables error: %s"
+       ;; Note this is a no-op if enable-local-variables is nil.
        (hack-dir-local-variables)))
-    (when (or mode-only enable-local-variables)
-      ;; 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)
+    ;; 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
+      ;; (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
+                 ;; mode, then we're done, and have no need to scan further.
+                 (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))
@@ -3192,14 +3271,13 @@ major-mode."
                                            (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)
-         ;; Otherwise, set the variables.
-         (enable-local-variables
-          (hack-local-variables-filter result nil)
-          (hack-local-variables-apply)))))
+                   (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
+       ;; Otherwise, set the variables.
+       (hack-local-variables-filter result nil)
+       (hack-local-variables-apply)))))
 
 (defun hack-local-variables-apply ()
   "Apply the elements of `file-local-variables-alist'.
@@ -3294,7 +3372,7 @@ It is dangerous if either of these conditions are met:
       (and (symbolp (car exp))
           ;; 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)))
+          (or (and (member (cdr exp) '(nil (1) (0) (-1)))
                    (string-match "-mode\\'" (symbol-name (car exp))))
               (let ((prop (get (car exp) 'safe-local-eval-function)))
                 (cond ((eq prop t)
@@ -3447,7 +3525,7 @@ LIST is a list of the form accepted by the function.
 When a file is visited, the file's class is found.  A directory
 may be assigned a class using `dir-locals-set-directory-class'.
 Then variables are set in the file's buffer according to the
-class' LIST.  The list is processed in order.
+VARIABLES list of the class.  The list is processed in order.
 
 * If the element is of the form (MAJOR-MODE . ALIST), and the
   buffer's major mode is derived from MAJOR-MODE (as determined
@@ -3494,8 +3572,15 @@ of no valid cache entry."
         (locals-file (locate-dominating-file file dir-locals-file-name))
         (dir-elt nil))
     ;; `locate-dominating-file' may have abbreviated the name.
-    (if locals-file
-       (setq locals-file (expand-file-name dir-locals-file-name locals-file)))
+    (and locals-file
+        (setq locals-file (expand-file-name dir-locals-file-name locals-file)))
+        ;; Let dir-locals-read-from-file inform us via demoted-errors
+        ;; about unreadable files, etc.
+        ;; Maybe we'd want to keep searching though - that is
+        ;; a locate-dominating-file issue.
+;;;     (or (not (file-readable-p locals-file))
+;;;         (not (file-regular-p locals-file)))
+;;;     (setq locals-file nil))
     ;; 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))
@@ -3537,24 +3622,39 @@ FILE is the name of the file holding the variables to apply.
 The new class name is the same as the directory in which FILE
 is found.  Returns the new class name."
   (with-temp-buffer
-    (insert-file-contents file)
-    (let* ((dir-name (file-name-directory file))
-          (class-name (intern dir-name))
-          (variables (let ((read-circle nil))
-                       (read (current-buffer)))))
-      (dir-locals-set-class-variables class-name variables)
-      (dir-locals-set-directory-class dir-name class-name
-                                     (nth 5 (file-attributes file)))
-      class-name)))
+    ;; This is with-demoted-errors, but we want to mention dir-locals
+    ;; in any error message.
+    (let (err)
+      (condition-case err
+         (progn
+           (insert-file-contents file)
+           (let* ((dir-name (file-name-directory file))
+                  (class-name (intern dir-name))
+                  (variables (let ((read-circle nil))
+                               (read (current-buffer)))))
+             (dir-locals-set-class-variables class-name variables)
+             (dir-locals-set-directory-class dir-name class-name
+                                             (nth 5 (file-attributes file)))
+             class-name))
+       (error (message "Error reading dir-locals: %S" err) nil)))))
+
+(defcustom enable-remote-dir-locals nil
+  "Non-nil means dir-local variables will be applied to remote files."
+  :version "24.3"
+  :type 'boolean
+  :group 'find-file)
 
 (defun hack-dir-local-variables ()
   "Read per-directory local variables for the current buffer.
 Store the directory-local variables in `dir-local-variables-alist'
 and `file-local-variables-alist', without applying them."
   (when (and enable-local-variables
-            (not (file-remote-p (or (buffer-file-name) default-directory))))
+            (or enable-remote-dir-locals
+                (not (file-remote-p (or (buffer-file-name)
+                                        default-directory)))))
     ;; Find the variables file.
-    (let ((variables-file (dir-locals-find-file (or (buffer-file-name) default-directory)))
+    (let ((variables-file (dir-locals-find-file
+                           (or (buffer-file-name) default-directory)))
          (class nil)
          (dir-name nil))
       (cond
@@ -3577,6 +3677,10 @@ and `file-local-variables-alist', without applying them."
            (hack-local-variables-filter variables dir-name)))))))
 
 (defun hack-dir-local-variables-non-file-buffer ()
+  "Apply directory-local variables to a non-file buffer.
+For non-file buffers, such as Dired buffers, directory-local
+variables are looked for in `default-directory' and its parent
+directories."
   (hack-dir-local-variables)
   (hack-local-variables-apply))
 
@@ -3607,7 +3711,7 @@ the old visited file has been renamed to the new name FILENAME."
   (interactive "FSet visited file name: ")
   (if (buffer-base-buffer)
       (error "An indirect buffer cannot visit a file"))
-  (let (truename)
+  (let (truename old-try-locals)
     (if filename
        (setq filename
              (if (string-equal filename "")
@@ -3632,7 +3736,8 @@ the old visited file has been renamed to the new name FILENAME."
        (progn
          (and filename (lock-buffer filename))
          (unlock-buffer)))
-    (setq buffer-file-name filename)
+    (setq old-try-locals (not (inhibit-local-variables-p))
+         buffer-file-name filename)
     (if filename                       ; make buffer name reflect filename.
        (let ((new-name (file-name-nondirectory buffer-file-name)))
          (setq default-directory (file-name-directory buffer-file-name))
@@ -3652,59 +3757,63 @@ the old visited file has been renamed to the new name FILENAME."
     (setq buffer-file-number
          (if filename
              (nthcdr 10 (file-attributes buffer-file-name))
-             nil)))
-  ;; write-file-functions is normally used for things like ftp-find-file
-  ;; that visit things that are not local files as if they were files.
-  ;; Changing to visit an ordinary local file instead should flush the hook.
-  (kill-local-variable 'write-file-functions)
-  (kill-local-variable 'local-write-file-hooks)
-  (kill-local-variable 'revert-buffer-function)
-  (kill-local-variable 'backup-inhibited)
-  ;; If buffer was read-only because of version control,
-  ;; that reason is gone now, so make it writable.
-  (if vc-mode
-      (setq buffer-read-only nil))
-  (kill-local-variable 'vc-mode)
-  ;; Turn off backup files for certain file names.
-  ;; Since this is a permanent local, the major mode won't eliminate it.
-  (and buffer-file-name
-       backup-enable-predicate
-       (not (funcall backup-enable-predicate buffer-file-name))
-       (progn
-        (make-local-variable 'backup-inhibited)
-        (setq backup-inhibited t)))
-  (let ((oauto buffer-auto-save-file-name))
-    ;; If auto-save was not already on, turn it on if appropriate.
-    (if (not buffer-auto-save-file-name)
-       (and buffer-file-name auto-save-default
-            (auto-save-mode t))
-      ;; If auto save is on, start using a new name.
-      ;; We deliberately don't rename or delete the old auto save
-      ;; for the old visited file name.  This is because perhaps
-      ;; the user wants to save the new state and then compare with the
-      ;; previous state from the auto save file.
-      (setq buffer-auto-save-file-name
-           (make-auto-save-file-name)))
-    ;; Rename the old auto save file if any.
-    (and oauto buffer-auto-save-file-name
-        (file-exists-p oauto)
-        (rename-file oauto buffer-auto-save-file-name t)))
-  (and buffer-file-name
-       (not along-with-file)
-       (set-buffer-modified-p t))
-  ;; Update the major mode, if the file name determines it.
-  (condition-case nil
-      ;; Don't change the mode if it is special.
-      (or (not change-major-mode-with-file-name)
-         (get major-mode 'mode-class)
-         ;; Don't change the mode if the local variable list specifies it.
-         (hack-local-variables 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)))
+           nil))
+    ;; write-file-functions is normally used for things like ftp-find-file
+    ;; that visit things that are not local files as if they were files.
+    ;; Changing to visit an ordinary local file instead should flush the hook.
+    (kill-local-variable 'write-file-functions)
+    (kill-local-variable 'local-write-file-hooks)
+    (kill-local-variable 'revert-buffer-function)
+    (kill-local-variable 'backup-inhibited)
+    ;; If buffer was read-only because of version control,
+    ;; that reason is gone now, so make it writable.
+    (if vc-mode
+       (setq buffer-read-only nil))
+    (kill-local-variable 'vc-mode)
+    ;; Turn off backup files for certain file names.
+    ;; Since this is a permanent local, the major mode won't eliminate it.
+    (and buffer-file-name
+        backup-enable-predicate
+        (not (funcall backup-enable-predicate buffer-file-name))
+        (progn
+          (make-local-variable 'backup-inhibited)
+          (setq backup-inhibited t)))
+    (let ((oauto buffer-auto-save-file-name))
+      ;; If auto-save was not already on, turn it on if appropriate.
+      (if (not buffer-auto-save-file-name)
+         (and buffer-file-name auto-save-default
+              (auto-save-mode t))
+       ;; If auto save is on, start using a new name.
+       ;; We deliberately don't rename or delete the old auto save
+       ;; for the old visited file name.  This is because perhaps
+       ;; the user wants to save the new state and then compare with the
+       ;; previous state from the auto save file.
+       (setq buffer-auto-save-file-name
+             (make-auto-save-file-name)))
+      ;; Rename the old auto save file if any.
+      (and oauto buffer-auto-save-file-name
+          (file-exists-p oauto)
+          (rename-file oauto buffer-auto-save-file-name t)))
+    (and buffer-file-name
+        (not along-with-file)
+        (set-buffer-modified-p t))
+    ;; Update the major mode, if the file name determines it.
+    (condition-case nil
+       ;; Don't change the mode if it is special.
+       (or (not change-major-mode-with-file-name)
+           (get major-mode 'mode-class)
+           ;; Don't change the mode if the local variable list specifies it.
+           ;; The file name can influence whether the local variables apply.
+           (and old-try-locals
+                ;; h-l-v also checks it, but might as well be explicit.
+                (not (inhibit-local-variables-p))
+                (hack-local-variables 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)
   "Write current buffer into file FILENAME.
@@ -3946,6 +4055,12 @@ the value is \"\"."
         (if period
             "")))))
 
+(defun file-name-base (&optional filename)
+  "Return the base name of the FILENAME: no directory, no extension.
+FILENAME defaults to `buffer-file-name'."
+  (file-name-sans-extension
+   (file-name-nondirectory (or filename (buffer-file-name)))))
+
 (defcustom make-backup-file-name-function nil
   "A function to use instead of the default `make-backup-file-name'.
 A value of nil gives the default `make-backup-file-name' behavior.
@@ -4209,7 +4324,9 @@ on a DOS/Windows machine, it returns FILENAME in expanded form."
                                                        default-directory))))
     (setq filename (expand-file-name filename))
     (let ((fremote (file-remote-p filename))
-          (dremote (file-remote-p directory)))
+         (dremote (file-remote-p directory))
+         (fold-case (or (memq system-type '(ms-dos cygwin windows-nt))
+                        read-file-name-completion-ignore-case)))
       (if ;; Conditions for separate trees
          (or
           ;; Test for different filesystems on DOS/Windows
@@ -4218,7 +4335,7 @@ on a DOS/Windows machine, it returns FILENAME in expanded form."
            (memq system-type '(ms-dos cygwin windows-nt))
            (or
             ;; Test for different drive letters
-            (not (eq t (compare-strings filename 0 2 directory 0 2)))
+            (not (eq t (compare-strings filename 0 2 directory 0 2 fold-case)))
             ;; Test for UNCs on different servers
             (not (eq t (compare-strings
                         (progn
@@ -4243,16 +4360,16 @@ on a DOS/Windows machine, it returns FILENAME in expanded form."
           (while (not
                  (or
                   (eq t (compare-strings filename-dir nil (length directory)
-                                         directory nil nil case-fold-search))
+                                         directory nil nil fold-case))
                   (eq t (compare-strings filename nil (length directory)
-                                         directory nil nil case-fold-search))))
+                                         directory nil nil fold-case))))
             (setq directory (file-name-directory (substring directory 0 -1))
                  ancestor (if (equal ancestor ".")
                               ".."
                             (concat "../" ancestor))))
           ;; Now ancestor is empty, or .., or ../.., etc.
           (if (eq t (compare-strings filename nil (length directory)
-                                    directory nil nil case-fold-search))
+                                    directory nil nil fold-case))
              ;; We matched within FILENAME's directory part.
              ;; Add the rest of FILENAME onto ANCESTOR.
              (let ((rest (substring filename (length directory))))
@@ -4375,7 +4492,8 @@ Before and after saving the buffer, this function runs
          (or buffer-file-name
              (let ((filename
                     (expand-file-name
-                     (read-file-name "File to save in: ") nil)))
+                     (read-file-name "File to save in: "
+                                     nil (expand-file-name (buffer-name))))))
                (if (file-exists-p filename)
                    (if (file-directory-p filename)
                        ;; Signal an error if the user specified the name of an
@@ -4398,7 +4516,7 @@ Before and after saving the buffer, this function runs
               (format
                "%s has changed since visited or saved.  Save anyway? "
                (file-name-nondirectory buffer-file-name)))
-             (error "Save not confirmed"))
+             (user-error "Save not confirmed"))
          (save-restriction
            (widen)
            (save-excursion
@@ -4699,37 +4817,51 @@ prints a message in the minibuffer.  Instead, use `set-buffer-modified-p'."
               "Modification-flag cleared"))
   (set-buffer-modified-p arg))
 
-(defun toggle-read-only (&optional arg)
-  "Change whether this buffer is read-only.
+(defun toggle-read-only (&optional arg message)
+  "Toggle the read-only state of the current buffer.
 With prefix argument ARG, make the buffer read-only if ARG is
-positive, otherwise make it writable.  If buffer is read-only
-and `view-read-only' is non-nil, enter view mode.
-
-This function is usually the wrong thing to use in a Lisp program.
-It can have side-effects beyond changing the read-only status of a buffer
-\(e.g., enabling view mode), and does not affect read-only regions that
-are caused by text properties.  To make a buffer read-only in Lisp code,
-set `buffer-read-only'.  To ignore read-only status (whether due to text
-properties or buffer state) and make changes, temporarily bind
-`inhibit-read-only'."
+positive; otherwise make it writable.
+
+When making the buffer read-only, enable View mode if
+`view-read-only' is non-nil.  When making the buffer writable,
+disable View mode if View mode is enabled.
+
+If called interactively, or if called from Lisp with MESSAGE
+non-nil, print a message reporting the buffer's new read-only
+status.
+
+Do not call this from a Lisp program unless you really intend to
+do the same thing as the \\[toggle-read-only] command, including
+possibly enabling or disabling View mode.  Also, note that this
+command works by setting the variable `buffer-read-only', which
+does not affect read-only regions caused by text properties.  To
+ignore read-only status in a Lisp program (whether due to text
+properties or buffer state), bind `inhibit-read-only' temporarily
+to a non-nil value."
   (interactive "P")
-  (if (and arg
-           (if (> (prefix-numeric-value arg) 0) buffer-read-only
-             (not buffer-read-only)))  ; If buffer-read-only is set correctly,
-      nil                             ; do nothing.
-    ;; Toggle.
-    (cond
-     ((and buffer-read-only view-mode)
-      (View-exit-and-edit)
-      (make-local-variable 'view-read-only)
-      (setq view-read-only t))         ; Must leave view mode.
-     ((and (not buffer-read-only) view-read-only
-          ;; If view-mode is already active, `view-mode-enter' is a nop.
-          (not view-mode)
-           (not (eq (get major-mode 'mode-class) 'special)))
-      (view-mode-enter))
-     (t (setq buffer-read-only (not buffer-read-only))
-        (force-mode-line-update)))))
+  (cond
+   ;; Do nothing if `buffer-read-only' already matches the state
+   ;; specified by ARG.
+   ((and arg
+        (if (> (prefix-numeric-value arg) 0)
+            buffer-read-only
+          (not buffer-read-only))))
+   ;; If View mode is enabled, exit it.
+   ((and buffer-read-only view-mode)
+    (View-exit-and-edit)
+    (set (make-local-variable 'view-read-only) t))
+   ;; If `view-read-only' is non-nil, enable View mode.
+   ((and view-read-only
+        (not buffer-read-only)
+        (not view-mode)
+        (not (eq (get major-mode 'mode-class) 'special)))
+    (view-mode-enter))
+   ;; The usual action: flip `buffer-read-only'.
+   (t (setq buffer-read-only (not buffer-read-only))
+      (force-mode-line-update)))
+  (if (or message (called-interactively-p 'interactive))
+      (message "Read-only %s for this buffer"
+              (if buffer-read-only "enabled" "disabled"))))
 
 (defun insert-file (filename)
   "Insert contents of file FILENAME into buffer after point.
@@ -4780,7 +4912,13 @@ like `write-region' does."
 (defun rename-uniquely ()
   "Rename current buffer to a similar name not already taken.
 This function is useful for creating multiple shell process buffers
-or multiple mail buffers, etc."
+or multiple mail buffers, etc.
+
+Note that some commands, in particular those based on `compilation-mode'
+\(`compile', `grep', etc.) will reuse the current buffer if it has the
+appropriate mode even if it has been renamed.  So as well as renaming
+the buffer, you also need to switch buffers before running another
+instance of such commands."
   (interactive)
   (save-match-data
     (let ((base-name (buffer-name)))
@@ -4898,6 +5036,42 @@ given.  With a prefix argument, TRASH is nil."
                 directory 'full directory-files-no-dot-files-regexp)))
       (delete-directory-internal directory)))))
 
+(defun file-equal-p (file1 file2)
+  "Return non-nil if files FILE1 and FILE2 name the same file.
+If FILE1 or FILE2 does not exist, the return value is unspecified."
+  (let ((handler (or (find-file-name-handler file1 'file-equal-p)
+                     (find-file-name-handler file2 'file-equal-p))))
+    (if handler
+        (funcall handler 'file-equal-p file1 file2)
+      (let (f1-attr f2-attr)
+        (and (setq f1-attr (file-attributes (file-truename file1)))
+            (setq f2-attr (file-attributes (file-truename file2)))
+            (equal f1-attr f2-attr))))))
+
+(defun file-in-directory-p (file dir)
+  "Return non-nil if FILE is in DIR or a subdirectory of DIR.
+A directory is considered to be \"in\" itself.
+Return nil if DIR is not an existing directory."
+  (let ((handler (or (find-file-name-handler file 'file-in-directory-p)
+                     (find-file-name-handler dir  'file-in-directory-p))))
+    (if handler
+        (funcall handler 'file-in-directory-p file dir)
+      (when (file-directory-p dir) ; DIR must exist.
+       (setq file (file-truename file)
+             dir  (file-truename dir))
+       (let ((ls1 (split-string file "/" t))
+             (ls2 (split-string dir  "/" t))
+             (root (if (string-match "\\`/" file) "/" ""))
+             (mismatch nil))
+         (while (and ls1 ls2 (not mismatch))
+           (if (string-equal (car ls1) (car ls2))
+               (setq root (concat root (car ls1) "/"))
+             (setq mismatch t))
+           (setq ls1 (cdr ls1)
+                 ls2 (cdr ls2)))
+         (unless mismatch
+           (file-equal-p root dir)))))))
+
 (defun copy-directory (directory newname &optional keep-time parents copy-contents)
   "Copy DIRECTORY to NEWNAME.  Both args must be strings.
 This function always sets the file modes of the output files to match
@@ -4924,12 +5098,16 @@ directly into NEWNAME instead."
            (format "Copy directory %s to: " dir)
            default-directory default-directory nil nil)
           current-prefix-arg t nil)))
+  (when (file-in-directory-p newname directory)
+    (error "Cannot copy `%s' into its subdirectory `%s'"
+           directory newname))
   ;; 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)
                     (find-file-name-handler newname 'copy-directory))))
     (if handler
-       (funcall handler 'copy-directory directory newname keep-time parents)
+       (funcall handler 'copy-directory directory
+                 newname keep-time parents copy-contents)
 
       ;; Compute target name.
       (setq directory (directory-file-name (expand-file-name directory))
@@ -4957,19 +5135,38 @@ directly into NEWNAME instead."
               ;; We do not want to copy "." and "..".
               (directory-files directory 'full
                                directory-files-no-dot-files-regexp))
-       (if (file-directory-p file)
-           (copy-directory file newname keep-time parents)
-         (let ((target (expand-file-name (file-name-nondirectory file) newname))
-               (attrs (file-attributes file)))
-           (if (stringp (car attrs)) ; Symbolic link
-               (make-symbolic-link (car attrs) target t)
-             (copy-file file target t keep-time)))))
+       (let ((target (expand-file-name (file-name-nondirectory file) newname))
+             (filetype (car (file-attributes file))))
+         (cond
+          ((eq filetype t)       ; Directory but not a symlink.
+           (copy-directory file newname keep-time parents))
+          ((stringp filetype)    ; Symbolic link
+           (make-symbolic-link filetype target t))
+          ((copy-file file target t keep-time)))))
 
       ;; Set directory attributes.
       (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
+;; At time of writing, only info uses this.
+(defun prune-directory-list (dirs &optional keep reject)
+  "Return a copy of DIRS with all non-existent directories removed.
+The optional argument KEEP is a list of directories to retain even if
+they don't exist, and REJECT is a list of directories to remove from
+DIRS, even if they exist; REJECT takes precedence over KEEP.
+
+Note that membership in REJECT and KEEP is checked using simple string
+comparison."
+  (apply #'nconc
+        (mapcar (lambda (dir)
+                  (and (not (member dir reject))
+                       (or (member dir keep) (file-directory-p dir))
+                       (list dir)))
+                dirs)))
+
 \f
 (put 'revert-buffer-function 'permanent-local t)
 (defvar revert-buffer-function nil
@@ -5048,6 +5245,8 @@ revert buffers without querying for confirmation.)
 Optional third argument PRESERVE-MODES non-nil means don't alter
 the files modes.  Normally we reinitialize them using `normal-mode'.
 
+This function binds `revert-buffer-in-progress-p' non-nil while it operates.
+
 If the value of `revert-buffer-function' is non-nil, it is called to
 do all the work for this command.  Otherwise, the hooks
 `before-revert-hook' and `after-revert-hook' are run at the beginning
@@ -5125,7 +5324,7 @@ non-nil, it is called instead of rereading visited file contents."
                         (unlock-buffer)))
                   (widen)
                   (let ((coding-system-for-read
-                         ;; Auto-saved file should be read by Emacs'
+                         ;; Auto-saved file should be read by Emacs's
                          ;; internal coding.
                          (if auto-save-p 'auto-save-coding
                            (or coding-system-for-read
@@ -5216,7 +5415,7 @@ non-nil, it is called instead of rereading visited file contents."
             (insert-file-contents file-name nil)
             (set-buffer-file-coding-system coding-system))
           (after-find-file nil nil t))
-         (t (error "Recover-file cancelled")))))
+         (t (user-error "Recover-file cancelled")))))
 
 (defun recover-session ()
   "Recover auto save files from a previous Emacs session.
@@ -5745,11 +5944,12 @@ returns nil."
          (when (and directory-free-space-program
                     ;; Avoid failure if the default directory does
                     ;; not exist (Bug#2631, Bug#3911).
-                    (let ((default-directory "/"))
-                      (eq (call-process directory-free-space-program
+                     (let ((default-directory
+                             (locate-dominating-file dir 'file-directory-p)))
+                       (eq (process-file directory-free-space-program
                                         nil t nil
                                         directory-free-space-args
-                                        dir)
+                                         (file-relative-name dir))
                           0)))
            ;; Assume that the "available" column is before the
            ;; "capacity" column.  Find the "%" and scan backward.
@@ -6143,7 +6343,11 @@ be a predicate function such as `yes-or-no-p'."
 
 (defun save-buffers-kill-emacs (&optional arg)
   "Offer to save each buffer, then kill this Emacs process.
-With prefix ARG, silently save all file-visiting buffers, then kill."
+With prefix ARG, silently save all file-visiting buffers without asking.
+If there are active processes where `process-query-on-exit-flag'
+returns non-nil, asks whether processes should be killed.
+Runs the members of `kill-emacs-query-functions' in turn and stops
+if any returns nil.  If `confirm-kill-emacs' is non-nil, calls it."
   (interactive "P")
   (save-some-buffers arg t)
   (and (or (not (memq t (mapcar (function
@@ -6251,20 +6455,20 @@ only these files will be asked to be saved."
                           "/"
                         (substring (car pair) 2)))))
        (setq file-arg-indices (cdr file-arg-indices))))
-    (case method
-      (identity (car arguments))
-      (add (concat "/:" (apply operation arguments)))
-      (insert-file-contents
+    (pcase method
+      (`identity (car arguments))
+      (`add (concat "/:" (apply operation arguments)))
+      (`insert-file-contents
        (let ((visit (nth 1 arguments)))
          (prog1
-              (apply operation arguments)
+             (apply operation arguments)
            (when (and visit buffer-file-name)
              (setq buffer-file-name (concat "/:" buffer-file-name))))))
-      (unquote-then-quote
+      (`unquote-then-quote
        (let ((buffer-file-name (substring buffer-file-name 2)))
          (apply operation arguments)))
-         (t
-          (apply operation arguments)))))
+      (_
+       (apply operation arguments)))))
 \f
 ;; Symbolic modes and read-file-modes.
 
@@ -6416,7 +6620,7 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
                (new-fn      (expand-file-name (file-name-nondirectory fn)
                                               trash-dir)))
           ;; We can't trash a parent directory of trash-directory.
-          (if (string-match fn trash-dir)
+          (if (string-prefix-p fn trash-dir)
               (error "Trash directory `%s' is a subdirectory of `%s'"
                      trash-dir filename))
           (unless (file-directory-p trash-dir)
@@ -6448,21 +6652,23 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
                                     (file-name-directory fn)))
             (error "Cannot move %s to trash: Permission denied" filename))
           ;; The trashed file cannot be the trash dir or its parent.
-          (if (string-match fn trash-files-dir)
+          (if (string-prefix-p fn trash-files-dir)
               (error "The trash directory %s is a subdirectory of %s"
                      trash-files-dir filename))
-          (if (string-match fn trash-info-dir)
+          (if (string-prefix-p fn trash-info-dir)
               (error "The trash directory %s is a subdirectory of %s"
                      trash-info-dir filename))
 
           ;; Ensure that the trash directory exists; otherwise, create it.
           (let ((saved-default-file-modes (default-file-modes)))
-            (set-default-file-modes ?\700)
-            (unless (file-exists-p trash-files-dir)
-              (make-directory trash-files-dir t))
-            (unless (file-exists-p trash-info-dir)
-              (make-directory trash-info-dir t))
-            (set-default-file-modes saved-default-file-modes))
+            (unwind-protect
+                (progn
+                  (set-default-file-modes #o700)
+                  (unless (file-exists-p trash-files-dir)
+                    (make-directory trash-files-dir t))
+                  (unless (file-exists-p trash-info-dir)
+                    (make-directory trash-info-dir t)))
+              (set-default-file-modes saved-default-file-modes)))
 
           ;; Try to move to trash with .trashinfo undo information
           (save-excursion