]> code.delx.au - gnu-emacs/blobdiff - lisp/files.el
(risky-local-variable-p): New function.
[gnu-emacs] / lisp / files.el
index b2b75ac3cd7bf64191457a7bf18fc10f8e5684c3..be6cdfeb9b7c773fa3c84d31b4fde8eb90ca9c75 100644 (file)
@@ -445,6 +445,7 @@ and ignores this variable."
 (defvar view-read-only nil
   "*Non-nil means buffers visiting files read-only, do it in view mode.")
 
+(put 'ange-ftp-completion-hook-function 'safe-magic t)
 (defun ange-ftp-completion-hook-function (op &rest args)
   "Provides support for ange-ftp host name completion.
 Runs the usual ange-ftp hook, but only for completion operations."
@@ -963,9 +964,10 @@ If the current buffer now contains an empty file that you just visited
        (lock-buffer)
        (rename-buffer oname)))
     (unless (eq (current-buffer) obuf)
-      ;; We already asked; don't ask again.
-      (setq kill-buffer-query-functions nil)
-      (kill-buffer obuf))))
+      (with-current-buffer obuf
+       ;; We already asked; don't ask again.
+       (let ((kill-buffer-query-functions))
+         (kill-buffer obuf))))))
 \f
 (defun create-file-buffer (filename)
   "Create a suitably named buffer for visiting FILENAME, and return it.
@@ -1074,7 +1076,7 @@ If there is no such live buffer, return nil."
                            ;; Verify this buffer's file number
                            ;; still belongs to its file.
                            (file-exists-p buffer-file-name)
-                           (equal (file-attributes buffer-file-name)
+                           (equal (file-attributes buffer-file-truename)
                                   attributes))
                       (setq found (car list))))
                 (setq list (cdr list))))
@@ -1528,6 +1530,7 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\$CHANGE_LOG\\$\\.TXT" . change-log-mode)
      ("\\.scm\\.[0-9]*\\'" . scheme-mode)
      ("\\.[ck]?sh\\'\\|\\.shar\\'\\|/\\.z?profile\\'" . sh-mode)
+     ("\\.bash\\'" . sh-mode)
      ("\\(/\\|\\`\\)\\.\\(bash_profile\\|z?login\\|bash_login\\|z?logout\\)\\'" . sh-mode)
      ("\\(/\\|\\`\\)\\.\\(bash_logout\\|shrc\\|[kz]shrc\\|bashrc\\|t?cshrc\\|esrc\\)\\'" . sh-mode)
      ("\\(/\\|\\`\\)\\.\\([kz]shenv\\|xinitrc\\|startxrc\\|xsession\\)\\'" . sh-mode)
@@ -1566,8 +1569,8 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\.awk\\'" . awk-mode)
      ("\\.prolog\\'" . prolog-mode)
      ("\\.tar\\'" . tar-mode)
-     ("\\.\\(arc\\|zip\\|lzh\\|zoo\\|jar\\)\\'" . archive-mode)
-     ("\\.\\(ARC\\|ZIP\\|LZH\\|ZOO\\|JAR\\)\\'" . archive-mode)
+     ("\\.\\(arc\\|zip\\|lzh\\|zoo\\|ear\\|jar\\|war\\)\\'" . archive-mode)
+     ("\\.\\(ARC\\|ZIP\\|LZH\\|ZOO\\|EAR\\|JAR\\|WAR\\)\\'" . archive-mode)
      ("\\.sx[dmicw]\\'" . archive-mode)        ; OpenOffice.org
      ;; Mailer puts message to be edited in
      ;; /tmp/Re.... or Message
@@ -1585,9 +1588,9 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\.dtd\\'" . sgml-mode)
      ("\\.ds\\(ss\\)?l\\'" . dsssl-mode)
      ("\\.idl\\'" . idl-mode)
-     ;; .emacs following a directory delimiter
-     ;; in Unix, MSDOG or VMS syntax.
-     ("[]>:/\\]\\..*emacs\\'" . emacs-lisp-mode)
+     ;; .emacs or .gnus or .viper following a directory delimiter in
+     ;; Unix, MSDOG or VMS syntax.
+     ("[]>:/\\]\\..*\\(emacs\\|gnus\\|viper\\)\\'" . emacs-lisp-mode)
      ("\\`\\..*emacs\\'" . emacs-lisp-mode)
      ;; _emacs following a directory delimiter
      ;; in MsDos syntax
@@ -1614,6 +1617,7 @@ in that case, this function acts as if `enable-local-variables' were t."
      ;; and after the .scm.[0-9] and CVS' <file>.<rev> patterns too.
      ("\\.[1-9]\\'" . nroff-mode)
      ("\\.g\\'" . antlr-mode)
+     ("\\.ses\\'" . ses-mode)
      ("\\.in\\'" nil t)))
   "Alist of filename patterns vs corresponding major mode functions.
 Each element looks like (REGEXP . FUNCTION) or (REGEXP FUNCTION NON-NIL).
@@ -2007,6 +2011,7 @@ is specified, returning t if it is specified."
 (put 'ignored-local-variables 'risky-local-variable t)
 (put 'eval 'risky-local-variable t)
 (put 'file-name-handler-alist 'risky-local-variable t)
+(put 'inhibit-quit 'risky-local-variable t)
 (put 'minor-mode-alist 'risky-local-variable t)
 (put 'minor-mode-map-alist 'risky-local-variable t)
 (put 'minor-mode-overriding-map-alist 'risky-local-variable t)
@@ -2052,8 +2057,21 @@ is specified, returning t if it is specified."
 (put 'mode-line-position 'risky-local-variable t)
 (put 'display-time-string 'risky-local-variable t)
 
-;; This one is safe because the user gets to check it before it is used.
-(put 'compile-command 'safe-local-variable t)
+;; This case is safe because the user gets to check it before it is used.
+(put 'compile-command 'safe-local-variable 'stringp)
+
+(defun risky-local-variable-p (sym val)
+  "Non-nil if SYM could be dangerous as a file-local variable with value VAL."
+  (let ((safep (get sym 'safe-local-variable)))
+    (or (memq sym ignored-local-variables)
+       (get sym 'risky-local-variable)
+       (and (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|-command$\\|-predicate$\\|font-lock-keywords$\\|font-lock-keywords-[0-9]+$\\|font-lock-syntactic-keywords$\\|-frame-alist$\\|-mode-alist$\\|-map$\\|-map-alist$"
+                          (symbol-name sym))
+            (not safep))
+       ;; If the safe-local-variable property isn't t or nil,
+       ;; then it must return non-nil on the proposed value to be safe.
+       (and (not (memq safep '(t nil)))
+            (not (funcall safep val))))))
 
 (defcustom safe-local-eval-forms nil
   "*Expressions that are considered \"safe\" in an `eval:' local variable.
@@ -2119,15 +2137,9 @@ is considered risky."
        ((eq var 'coding)
         ;; We have already handled coding: tag in set-auto-coding.
         nil)
-       ((memq var ignored-local-variables)
-        nil)
        ;; "Setting" eval means either eval it or do nothing.
        ;; Likewise for setting hook variables.
-       ((or (get var 'risky-local-variable)
-            (and
-             (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|-command$\\|-predicate$\\|font-lock-keywords$\\|font-lock-keywords-[0-9]+$\\|font-lock-syntactic-keywords$\\|-frame-alist$\\|-mode-alist$\\|-map$\\|-map-alist$"
-                           (symbol-name var))
-             (not (get var 'safe-local-variable))))
+       ((risky-local-variable-p var val)
         ;; Permit evalling a put of a harmless property.
         ;; if the args do nothing tricky.
         (if (or (and (eq var 'eval)
@@ -2150,7 +2162,7 @@ is considered risky."
                 (save-excursion (eval val))
               (make-local-variable var)
               (set var val))
-          (message "Ignoring `eval:' in the local variables list")))
+          (message "Ignoring risky spec in the local variables list")))
        ;; Ordinary variable, really set it.
        (t (make-local-variable var)
           ;; Make sure the string has no text properties.
@@ -2362,12 +2374,15 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                            (or (eq delete-old-versions t) (eq delete-old-versions nil))
                            (or delete-old-versions
                                (y-or-n-p (format "Delete excess backup versions of %s? "
-                                                 real-file-name))))))
+                                                 real-file-name)))))
+                     (modes (file-modes buffer-file-name)))
                  ;; Actually write the back up file.
                  (condition-case ()
                      (if (or file-precious-flag
     ;                        (file-symlink-p buffer-file-name)
                              backup-by-copying
+                             ;; Don't rename a suid or sgid file.
+                             (< 0 (logand modes #o6000))
                              (and backup-by-copying-when-linked
                                   (> (file-nlinks real-file-name) 1))
                              (and (or backup-by-copying-when-mismatch
@@ -2379,19 +2394,10 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                                                   (<= (nth 2 attr) backup-by-copying-when-privileged-mismatch)))
                                          (or (nth 9 attr)
                                              (not (file-ownership-preserved-p real-file-name)))))))
-                         (condition-case ()
-                             (copy-file real-file-name backupname t t)
-                           (file-error
-                            ;; If copying fails because file BACKUPNAME
-                            ;; is not writable, delete that file and try again.
-                            (if (and (file-exists-p backupname)
-                                     (not (file-writable-p backupname)))
-                                (delete-file backupname))
-                            (copy-file real-file-name backupname t t)))
+                         (backup-buffer-copy real-file-name backupname modes)
                        ;; rename-file should delete old backup.
                        (rename-file real-file-name backupname t)
-                       (setq setmodes
-                             (cons (file-modes backupname) backupname)))
+                       (setq setmodes (cons modes backupname)))
                    (file-error
                     ;; If trouble writing the backup, write it in ~.
                     (setq backupname (expand-file-name
@@ -2400,15 +2406,7 @@ BACKUPNAME is the backup file name, which is the old file renamed."
                     (message "Cannot write backup file; backing up in %s"
                              (file-name-nondirectory backupname))
                     (sleep-for 1)
-                    (condition-case ()
-                        (copy-file real-file-name backupname t t)
-                      (file-error
-                       ;; If copying fails because file BACKUPNAME
-                       ;; is not writable, delete that file and try again.
-                       (if (and (file-exists-p backupname)
-                                (not (file-writable-p backupname)))
-                           (delete-file backupname))
-                       (copy-file real-file-name backupname t t)))))
+                    (backup-buffer-copy real-file-name backupname modes)))
                  (setq buffer-backed-up t)
                  ;; Now delete the old versions, if desired.
                  (if delete-old-versions
@@ -2420,6 +2418,18 @@ 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)
+  (condition-case ()
+      (copy-file from-name to-name t t)
+    (file-error
+     ;; If copying fails because file TO-NAME
+     ;; is not writable, delete that file and try again.
+     (if (and (file-exists-p to-name)
+             (not (file-writable-p to-name)))
+        (delete-file to-name))
+     (copy-file from-name to-name t t)))
+  (set-file-modes to-name (logand modes #o1777)))
+
 (defun file-name-sans-versions (name &optional keep-backup-version)
   "Return file NAME sans backup versions or strings.
 This is a separate procedure so your site-init or startup file can
@@ -2572,44 +2582,43 @@ doesn't exist, it is created."
 (defun make-backup-file-name-1 (file)
   "Subroutine of `make-backup-file-name' and `find-backup-file-name'."
   (let ((alist backup-directory-alist)
-       elt backup-directory dir-sep-string)
+       elt backup-directory failed)
     (while alist
       (setq elt (pop alist))
       (if (string-match (car elt) file)
          (setq backup-directory (cdr elt)
                alist nil)))
-    (if (null backup-directory)
-       file
-      (unless (file-exists-p backup-directory)
+    (if (and backup-directory (not (file-exists-p backup-directory)))
        (condition-case nil
            (make-directory backup-directory 'parents)
-         (file-error file)))
+         (file-error (setq backup-directory nil))))
+    (if (null backup-directory)
+       file
       (if (file-name-absolute-p backup-directory)
          (progn
            (when (memq system-type '(windows-nt ms-dos))
-             ;; Normalize DOSish file names: convert all slashes to
-             ;; directory-sep-char, downcase the drive letter, if any,
-             ;; and replace the leading "x:" with "/drive_x".
+             ;; Normalize DOSish file names: downcase the drive
+             ;; letter, if any, and replace the leading "x:" with
+             ;; "/drive_x".
              (or (file-name-absolute-p file)
                  (setq file (expand-file-name file))) ; make defaults explicit
              ;; Replace any invalid file-name characters (for the
              ;; case of backing up remote files).
              (setq file (expand-file-name (convert-standard-filename file)))
-             (setq dir-sep-string (char-to-string directory-sep-char))
              (if (eq (aref file 1) ?:)
-                 (setq file (concat dir-sep-string
+                 (setq file (concat "/"
                                     "drive_"
                                     (char-to-string (downcase (aref file 0)))
-                                    (if (eq (aref file 2) directory-sep-char)
+                                    (if (eq (aref file 2) ?/)
                                         ""
-                                      dir-sep-string)
+                                      "/")
                                     (substring file 2)))))
            ;; Make the name unique by substituting directory
            ;; separators.  It may not really be worth bothering about
            ;; doubling `!'s in the original name...
            (expand-file-name
             (subst-char-in-string
-             directory-sep-char ?!
+             ?/ ?!
              (replace-regexp-in-string "!" "!!" file))
             backup-directory))
        (expand-file-name (file-name-nondirectory file)
@@ -2992,17 +3001,60 @@ After saving the buffer, this function runs `after-save-hook'."
                 (rename-file (cdr setmodes) buffer-file-name))))))
     setmodes))
 
+(defun diff-buffer-with-file (&optional buffer)
+  "View the differences between BUFFER and its associated file.
+This requires the external program `diff' to be in your `exec-path'."
+  (interactive "bBuffer: ")
+  (with-current-buffer (get-buffer (or buffer (current-buffer)))
+    (if (and buffer-file-name
+            (file-exists-p buffer-file-name))
+       (let ((tempfile (make-temp-file "buffer-content-")))
+         (unwind-protect
+             (save-restriction
+               (widen)
+               (write-region (point-min) (point-max) tempfile nil 'nomessage)
+               (diff buffer-file-name tempfile nil t)
+               (sit-for 0))
+           (when (file-exists-p tempfile)
+             (delete-file tempfile))))
+      (message "Buffer %s has no associated file on disc" (buffer-name))
+      ;; Display that message for 1 second so that user can read it
+      ;; in the minibuffer.
+      (sit-for 1)))
+  ;; return always nil, so that save-buffers-kill-emacs will not move
+  ;; over to the next unsaved buffer when calling `d'.
+  nil)
+
+(defvar save-some-buffers-action-alist
+  '((?\C-r
+     (lambda (buf)
+       (view-buffer buf
+                   (lambda (ignore)
+                     (exit-recursive-edit)))
+       (recursive-edit)
+       ;; Return nil to ask about BUF again.
+       nil)
+     "display the current buffer")
+    (?d diff-buffer-with-file
+       "show difference to last saved version"))
+  "ACTION-ALIST argument used in call to `map-y-or-n-p'.")
+(put 'save-some-buffers-action-alist 'risky-local-variable t)
+
 (defun save-some-buffers (&optional arg pred)
   "Save some modified file-visiting buffers.  Asks user about each one.
-You can answer `y' to save, `n' not to save, or `C-r' to look at the
-buffer in question with `view-buffer' before deciding.
+You can answer `y' to save, `n' not to save, `C-r' to look at the
+buffer in question with `view-buffer' before deciding or `d' to
+view the differences using `diff-buffer-to-file'.
 
 Optional argument (the prefix) non-nil means save all with no questions.
 Optional second argument PRED determines which buffers are considered:
 If PRED is nil, all the file-visiting buffers are considered.
 If PRED is t, then certain non-file buffers will also be considered.
 If PRED is a zero-argument function, it indicates for each buffer whether
-to consider it or not when called with that buffer current."
+to consider it or not when called with that buffer current.
+
+See `save-some-buffers-action-alist' if you want to
+change the additional actions you can take on files."
   (interactive "P")
   (save-window-excursion
     (let* ((queried nil)
@@ -3034,15 +3086,7 @@ to consider it or not when called with that buffer current."
                (save-buffer)))
             (buffer-list)
             '("buffer" "buffers" "save")
-            (list (list ?\C-r (lambda (buf)
-                                (view-buffer buf
-                                             (function
-                                              (lambda (ignore)
-                                                (exit-recursive-edit))))
-                                (recursive-edit)
-                                ;; Return nil to ask about BUF again.
-                                nil)
-                        "display the current buffer"))))
+            save-some-buffers-action-alist))
           (abbrevs-done
            (and save-abbrevs abbrevs-changed
                 (progn
@@ -3167,6 +3211,9 @@ to create parent directories if they don't exist."
    (list (read-file-name "Make directory: " default-directory default-directory
                         nil nil)
         t))
+  ;; If default-directory is a remote directory,
+  ;; make sure we find its make-directory handler.
+  (setq dir (expand-file-name dir))
   (let ((handler (find-file-name-handler dir 'make-directory)))
     (if handler
        (funcall handler 'make-directory dir parents)
@@ -3561,7 +3608,7 @@ See also `auto-save-file-name-p'."
                (setq filename (concat
                                (file-name-directory result)
                                (subst-char-in-string
-                                directory-sep-char ?!
+                                ?/ ?!
                                 (replace-regexp-in-string "!" "!!"
                                                           filename))))
              (setq filename result)))
@@ -3929,6 +3976,9 @@ program specified by `directory-free-space-program' if that is non-nil."
 ;;              dired-insert-headerline
 ;;              dired-after-subdir-garbage (defines what a "total" line is)
 ;;   - variable dired-subdir-regexp
+;; - may be passed "--dired" as the first argument in SWITCHES.
+;;   Filename handlers might have to remove this switch if their
+;;   "ls" command does not support it.
 (defun insert-directory (file switches &optional wildcard full-directory-p)
   "Insert directory listing for FILE, formatted according to SWITCHES.
 Leaves point after the inserted text.
@@ -4027,6 +4077,9 @@ If WILDCARD, it also runs the shell specified by `shell-file-name'."
 
          (when (string-match "--dired\\>" switches)
            (forward-line -2)
+            (when (looking-at "//SUBDIRED//")
+              (delete-region (point) (progn (forward-line 1) (point)))
+              (forward-line -1))
            (let ((end (line-end-position)))
              (forward-word 1)
              (forward-char 3)