Timestamp fixes for undo.
[bpt/emacs.git] / lisp / files.el
index 496f9bf..26f229e 100644 (file)
@@ -1,6 +1,6 @@
 ;;; files.el --- file input and output commands for Emacs
 
-;; Copyright (C) 1985-1987, 1992-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1987, 1992-2013 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Package: emacs
@@ -206,10 +206,9 @@ have fast storage with limited space, such as a RAM disk."
 (declare-function msdos-long-file-names "msdos.c")
 (declare-function w32-long-file-name "w32proc.c")
 (declare-function dired-get-filename "dired" (&optional localp no-error-if-not-filep))
-(declare-function dired-unmark "dired" (arg))
+(declare-function dired-unmark "dired" (arg &optional interactive))
 (declare-function dired-do-flagged-delete "dired" (&optional nomessage))
 (declare-function dos-8+3-filename "dos-fns" (filename))
-(declare-function view-mode-disable "view" ())
 (declare-function dosified-file-name "dos-fns" (file-name))
 
 (defvar file-name-invalid-regexp
@@ -317,12 +316,14 @@ A value of nil means don't add newlines.
 
 Certain major modes set this locally to the value obtained
 from `mode-require-final-newline'."
+  :safe #'symbolp
   :type '(choice (const :tag "When visiting" visit)
                 (const :tag "When saving" t)
                 (const :tag "When visiting or saving" visit-save)
                 (const :tag "Don't add newlines" nil)
                 (other :tag "Ask each time" ask))
-  :group 'editing-basics)
+  :group 'editing-basics
+  :version "24.4")
 
 (defcustom mode-require-final-newline t
   "Whether to add a newline at end of file, in certain major modes.
@@ -508,6 +509,11 @@ and ignores this variable."
                 (other :tag "Query" other))
   :group 'find-file)
 
+(defvar enable-dir-local-variables t
+  "Non-nil means enable use of directory-local variables.
+Some modes may wish to set this to nil to prevent directory-local
+settings being applied, but still respect file-local ones.")
+
 ;; This is an odd variable IMO.
 ;; You might wonder why it is needed, when we could just do:
 ;; (set (make-local-variable 'enable-local-variables) nil)
@@ -660,11 +666,14 @@ Not actually set up until the first time you use it.")
   "Explode a search path into a list of directory names.
 Directories are separated by `path-separator' (which is colon in
 GNU and Unix systems).  Substitute environment variables into the
-resulting list of directory names."
+resulting list of directory names.  For an empty path element (i.e.,
+a leading or trailing separator, or two adjacent separators), return
+nil (meaning `default-directory') as the associated list element."
   (when (stringp search-path)
     (mapcar (lambda (f)
-             (substitute-in-file-name (file-name-as-directory f)))
-           (split-string search-path path-separator t))))
+             (if (equal "" f) nil
+               (substitute-in-file-name (file-name-as-directory f))))
+           (split-string search-path path-separator))))
 
 (defun cd-absolute (dir)
   "Change current directory to given absolute file name DIR."
@@ -1509,7 +1518,10 @@ expand wildcards (if any) and replace the file with multiple files."
 (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'.")
+See `kill-buffer'.
+
+Note: Be careful with let-binding this hook considering it is
+frequently used for cleanup.")
 
 (defun find-alternate-file (filename &optional wildcards)
   "Find file FILENAME, select its buffer, kill previous buffer.
@@ -1849,13 +1861,12 @@ the various files."
                      (setq buffer-read-only read-only)))
                  (setq buffer-file-read-only read-only))
 
-               (when (and (not (eq (not (null rawfile))
-                                   (not (null find-file-literally))))
-                          (not nonexistent)
-                          ;; It is confusing to ask whether to visit
-                          ;; non-literally if they have the file in
-                          ;; hexl-mode or image-mode.
-                          (not (memq major-mode '(hexl-mode image-mode))))
+               (unless (or (eq (null rawfile) (null find-file-literally))
+                           nonexistent
+                           ;; It is confusing to ask whether to visit
+                           ;; non-literally if they have the file in
+                           ;; hexl-mode or image-mode.
+                           (memq major-mode '(hexl-mode image-mode)))
                  (if (buffer-modified-p)
                      (if (y-or-n-p
                           (format
@@ -1976,13 +1987,10 @@ Do you want to revisit the file normally now? ")
            (set-buffer-multibyte nil)
            (setq buffer-file-coding-system 'no-conversion)
            (set-buffer-major-mode buf)
-           (make-local-variable 'find-file-literally)
-           (setq find-file-literally t))
+           (setq-local find-file-literally t))
        (after-find-file error (not nowarn)))
       (current-buffer))))
 \f
-(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,
@@ -1994,7 +2002,6 @@ 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)
-       (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!
@@ -2122,7 +2129,7 @@ unless NOMODES is non-nil."
     (setq buffer-read-only t))
   (unless nomodes
     (when (and view-read-only view-mode)
-      (view-mode-disable))
+      (view-mode -1))
     (normal-mode t)
     ;; If requested, add a newline at the end of the file.
     (and (memq require-final-newline '(visit visit-save))
@@ -2168,7 +2175,7 @@ not set local variables (though we do notice a mode specified with -*-.)
 or from Lisp without specifying the optional argument FIND-FILE;
 in that case, this function acts as if `enable-local-variables' were t."
   (interactive)
-  (funcall (or (default-value 'major-mode) 'fundamental-mode))
+  (fundamental-mode)
   (let ((enable-local-variables (or (not find-file) enable-local-variables)))
     ;; FIXME this is less efficient than it could be, since both
     ;; s-a-m and h-l-v may parse the same regions, looking for "mode:".
@@ -2306,7 +2313,7 @@ since only a single case-insensitive search through the alist is made."
      ("\\.\\(\
 arc\\|zip\\|lzh\\|lha\\|zoo\\|[jew]ar\\|xpi\\|rar\\|7z\\|\
 ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
-     ("\\.\\(sx[dmicw]\\|od[fgpst]\\|oxt\\)\\'" . archive-mode) ;OpenOffice.org
+     ("\\.oxt\\'" . archive-mode) ;(Open|Libre)Office extensions.
      ("\\.\\(deb\\|[oi]pk\\)\\'" . archive-mode) ; Debian/Opkg packages.
      ;; Mailer puts message to be edited in
      ;; /tmp/Re.... or Message
@@ -2355,7 +2362,7 @@ ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . archive-mode)
      ("\\.\\(diffs?\\|patch\\|rej\\)\\'" . diff-mode)
      ("\\.\\(dif\\|pat\\)\\'" . diff-mode) ; for MSDOG
      ("\\.[eE]?[pP][sS]\\'" . ps-mode)
-     ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|dvi\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe)
+     ("\\.\\(?:PDF\\|DVI\\|OD[FGPST]\\|DOCX?\\|XLSX?\\|PPTX?\\|pdf\\|djvu\\|dvi\\|od[fgpst]\\|docx?\\|xlsx?\\|pptx?\\)\\'" . doc-view-mode-maybe)
      ("configure\\.\\(ac\\|in\\)\\'" . autoconf-mode)
      ("\\.s\\(v\\|iv\\|ieve\\)\\'" . sieve-mode)
      ("BROWSE\\'" . ebrowse-tree-mode)
@@ -2494,6 +2501,7 @@ See also `auto-mode-alist'.")
                      "\\.zoo\\'" "\\.[jew]ar\\'" "\\.xpi\\'" "\\.rar\\'"
                      "\\.7z\\'"
                      "\\.sx[dmicw]\\'" "\\.odt\\'"
+                     "\\.diff\\'" "\\.patch\\'"
                      "\\.tiff?\\'" "\\.gif\\'" "\\.png\\'" "\\.jpe?g\\'"))
   "List of regexps matching file names in which to ignore local variables.
 This includes `-*-' lines as well as trailing \"Local Variables\" sections.
@@ -2502,25 +2510,31 @@ 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'.")
+The function `inhibit-local-variables-p' uses this.")
 
 (define-obsolete-variable-alias 'inhibit-first-line-modes-suffixes
   'inhibit-local-variables-suffixes "24.1")
 
 (defvar inhibit-local-variables-suffixes nil
   "List of regexps matching suffixes to remove from file names.
-When checking `inhibit-local-variables-regexps', we first discard
-from the end of the file name anything that matches one of these regexps.")
+The function `inhibit-local-variables-p' uses this: when checking
+a file name, it first discards from the end of the name anything that
+matches one of these regexps.")
+
+;; Can't think of any situation in which you'd want this to be nil...
+(defvar inhibit-local-variables-ignore-case t
+  "Non-nil means `inhibit-local-variables-p' ignores case.")
 
-;; TODO explicitly add case-fold-search t?
 (defun inhibit-local-variables-p ()
   "Return non-nil if file local variables should be ignored.
 This checks the file (or buffer) name against `inhibit-local-variables-regexps'
-and `inhibit-local-variables-suffixes'."
+and `inhibit-local-variables-suffixes'.  If
+`inhibit-local-variables-ignore-case' is non-nil, this ignores case."
   (let ((temp inhibit-local-variables-regexps)
        (name (if buffer-file-name
                  (file-name-sans-versions buffer-file-name)
-               (buffer-name))))
+               (buffer-name)))
+       (case-fold-search inhibit-local-variables-ignore-case))
     (while (let ((sufs inhibit-local-variables-suffixes))
             (while (and sufs (not (string-match (car sufs) name)))
               (setq sufs (cdr sufs)))
@@ -2745,7 +2759,9 @@ we don't actually set it to the same mode the buffer already has."
                                          (if (functionp re)
                                              (funcall re)
                                            (looking-at re)))))))
-         (set-auto-mode-0 done keep-mode-if-same)))))
+         (set-auto-mode-0 done keep-mode-if-same)))
+    (unless done
+      (set-buffer-major-mode (current-buffer)))))
 
 ;; When `keep-mode-if-same' is set, we are working on behalf of
 ;; set-visited-file-name.  In that case, if the major mode specified is the
@@ -3015,6 +3031,9 @@ n  -- to ignore the local variables list.")
          (prog1 (memq char '(?! ?\s ?y))
            (quit-window t)))))))
 
+(defconst hack-local-variable-regexp
+  "[ \t]*\\([^][;\"'?()\\ \t\n]+\\)[ \t]*:[ \t]*")
+
 (defun hack-local-variables-prop-line (&optional mode-only)
   "Return local variables specified in the -*- line.
 Returns an alist of elements (VAR . VAL), where VAR is a variable
@@ -3041,11 +3060,11 @@ mode, if there is one, otherwise nil."
               ;; (last ";" is optional).
               ;; If MODE-ONLY, just check for `mode'.
               ;; Otherwise, parse the -*- line into the RESULT alist.
-              (while (and (or (not mode-only)
-                              (not result))
-                          (< (point) end))
-                (unless (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
-                  (message "Malformed mode-line")
+              (while (not (or (and mode-only result)
+                               (>= (point) end)))
+                (unless (looking-at hack-local-variable-regexp)
+                  (message "Malformed mode-line: %S"
+                            (buffer-substring-no-properties (point) end))
                   (throw 'malformed-line nil))
                 (goto-char (match-end 0))
                 ;; There used to be a downcase here,
@@ -3197,8 +3216,7 @@ local variables, but directory-local variables may still be applied."
                  (prefix
                   (concat "^" (regexp-quote
                                (buffer-substring (line-beginning-position)
-                                                 (match-beginning 0)))))
-                 beg)
+                                                 (match-beginning 0))))))
 
              (forward-line 1)
              (let ((startpos (point))
@@ -3233,18 +3251,16 @@ local variables, but directory-local variables may still be applied."
                    (forward-line 1))
                  (goto-char (point-min))
 
-                 (while (and (not (eobp))
-                             (or (not mode-only)
-                                 (not result)))
-                   ;; Find the variable name; strip whitespace.
-                   (skip-chars-forward " \t")
-                   (setq beg (point))
-                   (skip-chars-forward "^:\n")
-                   (if (eolp) (error "Missing colon in local variables entry"))
-                   (skip-chars-backward " \t")
-                   (let* ((str (buffer-substring beg (point)))
-                          (var (let ((read-circle nil))
-                                 (read str)))
+                 (while (not (or (eobp)
+                                  (and mode-only result)))
+                   ;; Find the variable name;
+                   (unless (looking-at hack-local-variable-regexp)
+                      (error "Malformed local variable line: %S"
+                             (buffer-substring-no-properties
+                              (point) (line-end-position))))
+                    (goto-char (match-end 1))
+                   (let* ((str (match-string 1))
+                          (var (intern str))
                           val val2)
                      (and (equal (downcase (symbol-name var)) "mode")
                           (setq var 'mode))
@@ -3634,14 +3650,15 @@ is found.  Returns the new class name."
       (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))
+           (unless (zerop (buffer-size))
+             (let* ((dir-name (file-name-directory file))
+                    (class-name (intern dir-name))
+                    (variables (let ((read-circle nil))
+                                 (read (current-buffer)))))
+               (dir-locals-set-class-variables class-name variables)
+               (dir-locals-set-directory-class dir-name class-name
+                                               (nth 5 (file-attributes file)))
+               class-name)))
        (error (message "Error reading dir-locals: %S" err) nil)))))
 
 (defcustom enable-remote-dir-locals nil
@@ -3653,8 +3670,12 @@ is found.  Returns the new class name."
 (defun hack-dir-local-variables ()
   "Read per-directory local variables for the current buffer.
 Store the directory-local variables in `dir-local-variables-alist'
-and `file-local-variables-alist', without applying them."
+and `file-local-variables-alist', without applying them.
+
+This does nothing if either `enable-local-variables' or
+`enable-dir-local-variables' are nil."
   (when (and enable-local-variables
+            enable-dir-local-variables
             (or enable-remote-dir-locals
                 (not (file-remote-p (or (buffer-file-name)
                                         default-directory)))))
@@ -3676,10 +3697,13 @@ and `file-local-variables-alist', without applying them."
                (dir-locals-get-class-variables class) dir-name nil)))
          (when variables
            (dolist (elt variables)
-             (unless (memq (car elt) '(eval mode))
-               (setq dir-local-variables-alist
-                     (assq-delete-all (car elt) dir-local-variables-alist)))
-             (push elt dir-local-variables-alist))
+             (if (eq (car elt) 'coding)
+                 (display-warning :warning
+                                  "Coding cannot be specified by dir-locals")
+               (unless (memq (car elt) '(eval mode))
+                 (setq dir-local-variables-alist
+                       (assq-delete-all (car elt) dir-local-variables-alist)))
+               (push elt dir-local-variables-alist)))
            (hack-local-variables-filter variables dir-name)))))))
 
 (defun hack-dir-local-variables-non-file-buffer ()
@@ -3856,6 +3880,10 @@ Interactively, confirmation is required unless you supply a prefix argument."
                                    (or buffer-file-name (buffer-name))))))
        (and confirm
             (file-exists-p filename)
+            ;; NS does its own confirm dialog.
+            (not (and (eq (framep-on-display) 'ns)
+                      (listp last-nonmenu-event)
+                      use-dialog-box))
             (or (y-or-n-p (format "File `%s' exists; overwrite? " filename))
                 (error "Canceled")))
        (set-visited-file-name filename (not confirm))))
@@ -3869,6 +3897,27 @@ Interactively, confirmation is required unless you supply a prefix argument."
   ;; the one at the old location.
   (vc-find-file-hook))
 \f
+(defun file-extended-attributes (filename)
+  "Return an alist of extended attributes of file FILENAME.
+
+Extended attributes are platform-specific metadata about the file,
+such as SELinux context, list of ACL entries, etc."
+  `((acl . ,(file-acl filename))
+    (selinux-context . ,(file-selinux-context filename))))
+
+(defun set-file-extended-attributes (filename attributes)
+  "Set extended attributes of file FILENAME to ATTRIBUTES.
+
+ATTRIBUTES must be an alist of file attributes as returned by
+`file-extended-attributes'."
+  (dolist (elt attributes)
+    (let ((attr (car elt))
+         (val (cdr elt)))
+      (cond ((eq attr 'acl)
+            (set-file-acl filename val))
+           ((eq attr 'selinux-context)
+            (set-file-selinux-context filename val))))))
+\f
 (defun backup-buffer ()
   "Make a backup of the disk file visited by the current buffer, if appropriate.
 This is normally done before saving the buffer the first time.
@@ -3878,13 +3927,14 @@ variable `make-backup-files'.  If it's done by renaming, then the file is
 no longer accessible under its old name.
 
 The value is non-nil after a backup was made by renaming.
-It has the form (MODES SELINUXCONTEXT BACKUPNAME).
+It has the form (MODES EXTENDED-ATTRIBUTES BACKUPNAME).
 MODES is the result of `file-modes' on the original
 file; this means that the caller, after saving the buffer, should change
 the modes of the new file to agree with the old modes.
-SELINUXCONTEXT is the result of `file-selinux-context' on the original
-file; this means that the caller, after saving the buffer, should change
-the SELinux context of the new file to agree with the old context.
+EXTENDED-ATTRIBUTES is the result of `file-extended-attributes'
+on the original file; this means that the caller, after saving
+the buffer, should change the extended attributes of the new file
+to agree with the old attributes.
 BACKUPNAME is the backup file name, which is the old file renamed."
   (if (and make-backup-files (not backup-inhibited)
           (not buffer-backed-up)
@@ -3913,7 +3963,8 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                                (y-or-n-p (format "Delete excess backup versions of %s? "
                                                  real-file-name)))))
                      (modes (file-modes buffer-file-name))
-                     (context (file-selinux-context buffer-file-name)))
+                     (extended-attributes
+                      (file-extended-attributes buffer-file-name)))
                  ;; Actually write the back up file.
                  (condition-case ()
                      (if (or file-precious-flag
@@ -3931,12 +3982,15 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                                              (and (integerp (nth 2 attr))
                                                   (integerp backup-by-copying-when-privileged-mismatch)
                                                   (<= (nth 2 attr) backup-by-copying-when-privileged-mismatch)))
-                                         (or (nth 9 attr)
-                                             (not (file-ownership-preserved-p real-file-name)))))))
-                         (backup-buffer-copy real-file-name backupname modes context)
+                                         (not (file-ownership-preserved-p
+                                               real-file-name t))))))
+                         (backup-buffer-copy real-file-name
+                                             backupname modes
+                                             extended-attributes)
                        ;; rename-file should delete old backup.
                        (rename-file real-file-name backupname t)
-                       (setq setmodes (list modes context backupname)))
+                       (setq setmodes (list modes extended-attributes
+                                            backupname)))
                    (file-error
                     ;; If trouble writing the backup, write it in
                     ;; .emacs.d/%backup%.
@@ -3944,7 +3998,8 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                     (message "Cannot write backup file; backing up in %s"
                              backupname)
                     (sleep-for 1)
-                    (backup-buffer-copy real-file-name backupname modes context)))
+                    (backup-buffer-copy real-file-name backupname
+                                        modes extended-attributes)))
                  (setq buffer-backed-up t)
                  ;; Now delete the old versions, if desired.
                  (if delete-old-versions
@@ -3956,7 +4011,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                  setmodes)
            (file-error nil))))))
 
-(defun backup-buffer-copy (from-name to-name modes context)
+(defun backup-buffer-copy (from-name to-name modes extended-attributes)
   (let ((umask (default-file-modes)))
     (unwind-protect
        (progn
@@ -3982,10 +4037,12 @@ BACKUPNAME is the backup file name, which is the old file renamed."
              nil)))
       ;; Reset the umask.
       (set-default-file-modes umask)))
-  (and modes
-       (set-file-modes to-name (logand modes #o1777)))
-  (and context
-       (set-file-selinux-context to-name context)))
+  ;; If set-file-extended-attributes fails, fall back on set-file-modes.
+  (unless (and extended-attributes
+              (with-demoted-errors
+                (set-file-extended-attributes to-name extended-attributes)))
+    (and modes
+        (set-file-modes to-name (logand modes #o1777)))))
 
 (defvar file-name-version-regexp
   "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)"
@@ -4009,22 +4066,44 @@ See also `file-name-version-regexp'."
                    (string-match (concat file-name-version-regexp "\\'")
                                  name))))))
 
-(defun file-ownership-preserved-p (file)
-  "Return t if deleting FILE and rewriting it would preserve the owner."
+(defun file-ownership-preserved-p (file &optional group)
+  "Return t if deleting FILE and rewriting it would preserve the owner.
+Return nil if FILE does not exist, or if deleting and recreating it
+might not preserve the owner.  If GROUP is non-nil, check whether
+the group would be preserved too."
   (let ((handler (find-file-name-handler file 'file-ownership-preserved-p)))
     (if handler
-       (funcall handler 'file-ownership-preserved-p file)
+       (funcall handler 'file-ownership-preserved-p file group)
       (let ((attributes (file-attributes file 'integer)))
        ;; Return t if the file doesn't exist, since it's true that no
        ;; information would be lost by an (attempted) delete and create.
        (or (null attributes)
-           (= (nth 2 attributes) (user-uid))
-           ;; Files created on Windows by Administrator (RID=500)
-           ;; have the Administrators group (RID=544) recorded as
-           ;; their owner.  Rewriting them will still preserve the
-           ;; owner.
-           (and (eq system-type 'windows-nt)
-                (= (user-uid) 500) (= (nth 2 attributes) 544)))))))
+           (and (or (= (nth 2 attributes) (user-uid))
+                    ;; Files created on Windows by Administrator (RID=500)
+                    ;; have the Administrators group (RID=544) recorded as
+                    ;; their owner.  Rewriting them will still preserve the
+                    ;; owner.
+                    (and (eq system-type 'windows-nt)
+                         (= (user-uid) 500) (= (nth 2 attributes) 544)))
+                (or (not group)
+                    ;; On BSD-derived systems files always inherit the parent
+                    ;; directory's group, so skip the group-gid test.
+                    (memq system-type '(berkeley-unix darwin gnu/kfreebsd))
+                    (= (nth 3 attributes) (group-gid)))
+                (let* ((parent (or (file-name-directory file) "."))
+                       (parent-attributes (file-attributes parent 'integer)))
+                  (and parent-attributes
+                       ;; On some systems, a file created in a setuid directory
+                       ;; inherits that directory's owner.
+                       (or
+                        (= (nth 2 parent-attributes) (user-uid))
+                        (string-match "^...[^sS]" (nth 8 parent-attributes)))
+                       ;; On many systems, a file created in a setgid directory
+                       ;; inherits that directory's group.  On some systems
+                       ;; this happens even if the setgid bit is not set.
+                       (or (not group)
+                           (= (nth 3 parent-attributes)
+                              (nth 3 attributes)))))))))))
 
 (defun file-name-sans-extension (filename)
   "Return FILENAME sans final \"extension\".
@@ -4107,23 +4186,31 @@ ignored."
   "Default `backup-enable-predicate' function.
 Checks for files in `temporary-file-directory',
 `small-temporary-file-directory', and /tmp."
-  (not (or (let ((comp (compare-strings temporary-file-directory 0 nil
-                                       name 0 nil)))
-            ;; Directory is under temporary-file-directory.
-            (and (not (eq comp t))
-                 (< comp (- (length temporary-file-directory)))))
-          (let ((comp (compare-strings "/tmp" 0 nil
-                                       name 0 nil)))
-            ;; Directory is under /tmp.
-            (and (not (eq comp t))
-                 (< comp (- (length "/tmp")))))
-          (if small-temporary-file-directory
-              (let ((comp (compare-strings small-temporary-file-directory
-                                           0 nil
-                                           name 0 nil)))
-                ;; Directory is under small-temporary-file-directory.
-                (and (not (eq comp t))
-                     (< comp (- (length small-temporary-file-directory)))))))))
+  (let ((temporary-file-directory temporary-file-directory)
+       caseless)
+    ;; On MS-Windows, file-truename will convert short 8+3 aliases to
+    ;; their long file-name equivalents, so compare-strings does TRT.
+    (if (memq system-type '(ms-dos windows-nt))
+       (setq temporary-file-directory (file-truename temporary-file-directory)
+             name (file-truename name)
+             caseless t))
+    (not (or (let ((comp (compare-strings temporary-file-directory 0 nil
+                                         name 0 nil caseless)))
+              ;; Directory is under temporary-file-directory.
+              (and (not (eq comp t))
+                   (< comp (- (length temporary-file-directory)))))
+            (let ((comp (compare-strings "/tmp" 0 nil
+                                         name 0 nil)))
+              ;; Directory is under /tmp.
+              (and (not (eq comp t))
+                   (< comp (- (length "/tmp")))))
+            (if small-temporary-file-directory
+                (let ((comp (compare-strings small-temporary-file-directory
+                                             0 nil
+                                             name 0 nil caseless)))
+                  ;; Directory is under small-temporary-file-directory.
+                  (and (not (eq comp t))
+                       (< comp (- (length small-temporary-file-directory))))))))))
 
 (defun make-backup-file-name (file)
   "Create the non-numeric backup file name for FILE.
@@ -4494,28 +4581,21 @@ Before and after saving the buffer, this function runs
                 (not (file-exists-p buffer-file-name))))
        (let ((recent-save (recent-auto-save-p))
              setmodes)
-         ;; If buffer has no file name, ask user for one.
+          ;; If buffer has no file name, ask user for one.
          (or buffer-file-name
-             (let ((filename
-                    (expand-file-name
-                     (read-file-name "File to save in: "
-                                     nil (expand-file-name (buffer-name))))))
-               (if (file-exists-p filename)
-                   (if (file-directory-p filename)
-                       ;; Signal an error if the user specified the name of an
-                       ;; existing directory.
-                       (error "%s is a directory" filename)
-                     (unless (y-or-n-p (format "File `%s' exists; overwrite? "
-                                               filename))
-                       (error "Canceled")))
-                 ;; Signal an error if the specified name refers to a
-                 ;; non-existing directory.
-                 (let ((dir (file-name-directory filename)))
-                   (unless (file-directory-p dir)
-                     (if (file-exists-p dir)
-                         (error "%s is not a directory" dir)
-                       (error "%s: no such directory" dir)))))
-               (set-visited-file-name filename)))
+              (let ((filename
+                     (expand-file-name
+                      (read-file-name "File to save in: "
+                                      nil (expand-file-name (buffer-name))))))
+                (if (file-exists-p filename)
+                    (if (file-directory-p filename)
+                        ;; Signal an error if the user specified the name of an
+                        ;; existing directory.
+                        (error "%s is a directory" filename)
+                      (unless (y-or-n-p (format "File `%s' exists; overwrite? "
+                                                filename))
+                        (error "Canceled"))))
+                (set-visited-file-name filename)))
          (or (verify-visited-file-modtime (current-buffer))
              (not (file-exists-p buffer-file-name))
              (yes-or-no-p
@@ -4542,13 +4622,21 @@ Before and after saving the buffer, this function runs
                     (insert ?\n))))
            ;; Support VC version backups.
            (vc-before-save)
-           (run-hooks 'before-save-hook)
+           ;; Don't let errors prevent saving the buffer.
+           (with-demoted-errors (run-hooks 'before-save-hook))
            (or (run-hook-with-args-until-success 'write-contents-functions)
                (run-hook-with-args-until-success 'local-write-file-hooks)
                (run-hook-with-args-until-success 'write-file-functions)
                ;; If a hook returned t, file is already "written".
                ;; Otherwise, write it the usual way now.
-               (setq setmodes (basic-save-buffer-1)))
+               (let ((dir (file-name-directory
+                           (expand-file-name buffer-file-name))))
+                 (unless (file-exists-p dir)
+                   (if (y-or-n-p
+                        (format "Directory `%s' does not exist; create? " dir))
+                       (make-directory dir t)
+                     (error "Canceled")))
+                 (setq setmodes (basic-save-buffer-1))))
            ;; Now we have saved the current buffer.  Let's make sure
            ;; that buffer-file-coding-system is fixed to what
            ;; actually used for saving by binding it locally.
@@ -4560,8 +4648,11 @@ Before and after saving the buffer, this function runs
            (if setmodes
                (condition-case ()
                    (progn
-                     (set-file-modes buffer-file-name (car setmodes))
-                     (set-file-selinux-context buffer-file-name (nth 1 setmodes)))
+                     (unless
+                         (with-demoted-errors
+                           (set-file-modes buffer-file-name (car setmodes)))
+                       (set-file-extended-attributes buffer-file-name
+                                                     (nth 1 setmodes))))
                  (error nil))))
          ;; If the auto-save file was recent before this command,
          ;; delete it now.
@@ -4574,7 +4665,8 @@ Before and after saving the buffer, this function runs
 ;; This does the "real job" of writing a buffer into its visited file
 ;; and making a backup file.  This is what is normally done
 ;; but inhibited if one of write-file-functions returns non-nil.
-;; It returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; It returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
 (defun basic-save-buffer-1 ()
   (prog1
       (if save-buffer-coding-system
@@ -4582,13 +4674,12 @@ Before and after saving the buffer, this function runs
            (basic-save-buffer-2))
        (basic-save-buffer-2))
     (if buffer-file-coding-system-explicit
-       (setcar buffer-file-coding-system-explicit last-coding-system-used)
-      (setq buffer-file-coding-system-explicit
-           (cons last-coding-system-used nil)))))
+       (setcar buffer-file-coding-system-explicit last-coding-system-used))))
 
-;; This returns a value (MODES SELINUXCONTEXT BACKUPNAME), like backup-buffer.
+;; This returns a value (MODES EXTENDED-ATTRIBUTES BACKUPNAME), like
+;; backup-buffer.
 (defun basic-save-buffer-2 ()
-  (let (tempsetmodes setmodes)
+  (let (tempsetmodes setmodes writecoding)
     (if (not (file-writable-p buffer-file-name))
        (let ((dir (file-name-directory buffer-file-name)))
          (if (not (file-directory-p dir))
@@ -4604,6 +4695,14 @@ Before and after saving the buffer, this function runs
                     buffer-file-name)))
                  (setq tempsetmodes t)
                (error "Attempt to save to a file which you aren't allowed to write"))))))
+    ;; This may involve prompting, so do it now before backing up the file.
+    ;; Otherwise there can be a delay while the user answers the
+    ;; prompt during which the original file has been renamed.  (Bug#13522)
+    (setq writecoding
+         ;; Args here should match write-region call below around
+         ;; which we use writecoding.
+         (choose-write-coding-system nil nil buffer-file-name nil t
+                                     buffer-file-truename))
     (or buffer-backed-up
        (setq setmodes (backup-buffer)))
     (let* ((dir (file-name-directory buffer-file-name))
@@ -4661,7 +4760,7 @@ Before and after saving the buffer, this function runs
            (setq setmodes (or setmodes
                               (list (or (file-modes buffer-file-name)
                                         (logand ?\666 umask))
-                                    (file-selinux-context buffer-file-name)
+                                    (file-extended-attributes buffer-file-name)
                                     buffer-file-name)))
            ;; We succeeded in writing the temp file,
            ;; so rename it.
@@ -4673,16 +4772,23 @@ Before and after saving the buffer, this function runs
        (cond ((and tempsetmodes (not setmodes))
               ;; Change the mode back, after writing.
               (setq setmodes (list (file-modes buffer-file-name)
-                                   (file-selinux-context buffer-file-name)
+                                   (file-extended-attributes buffer-file-name)
                                    buffer-file-name))
-              (set-file-modes buffer-file-name (logior (car setmodes) 128))
-              (set-file-selinux-context buffer-file-name (nth 1 setmodes)))))
+              ;; If set-file-extended-attributes fails, fall back on
+              ;; set-file-modes.
+              (unless
+                  (with-demoted-errors
+                    (set-file-extended-attributes buffer-file-name
+                                                  (nth 1 setmodes)))
+                (set-file-modes buffer-file-name
+                                (logior (car setmodes) 128))))))
        (let (success)
          (unwind-protect
-             (progn
                 ;; Pass in nil&nil rather than point-min&max to indicate
                 ;; we're saving the buffer rather than just a region.
                 ;; write-region-annotate-functions may make us of it.
+             (let ((coding-system-for-write writecoding)
+                   (coding-system-require-warning nil))
                (write-region nil nil
                              buffer-file-name nil t buffer-file-truename)
                (setq success t))
@@ -4812,6 +4918,11 @@ change the additional actions you can take on files."
                     (length autosaved-buffers)
                     (mapconcat 'identity autosaved-buffers ", "))))))))
 \f
+(defun clear-visited-file-modtime ()
+  "Clear out records of last mod time of visited file.
+Next attempt to save will certainly not complain of a discrepancy."
+  (set-visited-file-modtime 0))
+
 (defun not-modified (&optional arg)
   "Mark current buffer as unmodified, not needing to be saved.
 With prefix ARG, mark buffer as modified, so \\[save-buffer] will save.
@@ -5408,18 +5519,20 @@ Then you'll be asked about a number of files to recover."
   (let ((ls-lisp-support-shell-wildcards t))
     (dired (concat auto-save-list-file-prefix "*")
           (concat dired-listing-switches " -t")))
+  (use-local-map (nconc (make-sparse-keymap) (current-local-map)))
+  (define-key (current-local-map) "\C-c\C-c" 'recover-session-finish)
   (save-excursion
     (goto-char (point-min))
     (or (looking-at " Move to the session you want to recover,")
        (let ((inhibit-read-only t))
          ;; Each line starts with a space
          ;; so that Font Lock mode won't highlight the first character.
-         (insert " Move to the session you want to recover,\n"
-                 " then type C-c C-c to select it.\n\n"
-                 " You can also delete some of these files;\n"
-                 " type d on a line to mark that file for deletion.\n\n"))))
-  (use-local-map (nconc (make-sparse-keymap) (current-local-map)))
-  (define-key (current-local-map) "\C-c\C-c" 'recover-session-finish))
+         (insert " To recover a session, move to it and type C-c C-c.\n"
+                 (substitute-command-keys
+                  " To delete a session file, type \
+\\[dired-flag-file-deletion] on its line to flag
+ the file for deletion, then \\[dired-do-flagged-delete] to \
+delete flagged files.\n\n"))))))
 
 (defun recover-session-finish ()
   "Choose one saved session to recover auto-save files from.
@@ -5653,7 +5766,7 @@ See also `auto-save-file-name-p'."
 (defun auto-save-file-name-p (filename)
   "Return non-nil if FILENAME can be yielded by `make-auto-save-file-name'.
 FILENAME should lack slashes.  You can redefine this for customization."
-  (string-match "^#.*#$" filename))
+  (string-match "\\`#.*#\\'" filename))
 \f
 (defun wildcard-to-regexp (wildcard)
   "Given a shell file name pattern WILDCARD, return an equivalent regexp.