Mercurial > emacs
changeset 68866:5340157fbfc2
* files.el (safe-local-variable-values): New option.
(hack-local-variables-prop-line): Return a list of variable-value
pairs if MODE-ONLY is non-nil.
(hack-local-variables): Construct list of variable-value pairs,
and apply or reject them in one go. Ask for confirmation if
variables are not known safe.
(hack-local-variables-confirm): Complete rewrite. Support
`safe-local-variable-values'.
(enable-local-variables): Update docstring to reflect new
behavior.
(ignored-local-variables): Ignore ignored-local-variables and
safe-local-variable-values.
(safe-local-variable-p): New function.
(risky-local-variable-p): `safe-local-variable' property check
moved to safe-local-variable-p.
(hack-one-local-variable): Checks moved to hack-local-variables.
(byte-compile-dynamic, c-basic-offset, c-file-style,
c-indent-level, comment-column, fill-column, fill-prefix,
indent-tabs-mode, kept-new-versions, no-byte-compile,
no-update-autoloads, outline-regexp, page-delimiter,
paragraph-start, paragraph-separate, sentence-end,
sentence-end-double-space tab-width, version-control): Add
`safe-local-variable' property.
* find-lisp.el: Delete nonexistent `autocompile' file variable.
* icomplete.el, play/landmark.el: Change nonexistent
`outline-layout' file variable to `allout-layout'.
author | Chong Yidong <cyd@stupidchicken.com> |
---|---|
date | Tue, 14 Feb 2006 01:21:31 +0000 |
parents | 90c105dcb4a0 |
children | 4ea80a7bd411 |
files | lisp/ChangeLog lisp/files.el lisp/find-lisp.el lisp/icomplete.el lisp/play/landmark.el |
diffstat | 5 files changed, 308 insertions(+), 186 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ChangeLog Mon Feb 13 22:57:54 2006 +0000 +++ b/lisp/ChangeLog Tue Feb 14 01:21:31 2006 +0000 @@ -1,3 +1,35 @@ +2006-02-13 Chong Yidong <cyd@stupidchicken.com> + + * files.el (safe-local-variable-values): New option. + (hack-local-variables-prop-line): Return a list of variable-value + pairs if MODE-ONLY is non-nil. + (hack-local-variables): Construct list of variable-value pairs, + and apply or reject them in one go. Ask for confirmation if + variables are not known safe. + (hack-local-variables-confirm): Complete rewrite. Support + `safe-local-variable-values'. + (enable-local-variables): Update docstring to reflect new + behavior. + (ignored-local-variables): Ignore ignored-local-variables and + safe-local-variable-values. + (safe-local-variable-p): New function. + (risky-local-variable-p): `safe-local-variable' property check + moved to safe-local-variable-p. + (hack-one-local-variable): Checks moved to hack-local-variables. + + (byte-compile-dynamic, c-basic-offset, c-file-style, + c-indent-level, comment-column, fill-column, fill-prefix, + indent-tabs-mode, kept-new-versions, no-byte-compile, + no-update-autoloads, outline-regexp, page-delimiter, + paragraph-start, paragraph-separate, sentence-end, + sentence-end-double-space tab-width, version-control): Add + `safe-local-variable' property. + + * find-lisp.el: Delete nonexistent `autocompile' file variable. + + * icomplete.el, play/landmark.el: Change nonexistent + `outline-layout' file variable to `allout-layout'. + 2006-02-14 Nick Roberts <nickrob@snap.net.nz> * progmodes/gdb-ui.el (gud-watch, gdb-invalidate-registers-1)
--- a/lisp/files.el Mon Feb 13 22:57:54 2006 +0000 +++ b/lisp/files.el Tue Feb 14 01:21:31 2006 +0000 @@ -445,8 +445,13 @@ (defcustom enable-local-variables t "*Control use of local variables in files you visit. The value can be t, nil or something else. -A value of t means file local variables specifications are obeyed; -nil means they are ignored; anything else means query. + +A value of t means file local variables specifications are obeyed +if all the specified variables are safe. If any variables are +not safe, you will be queries before setting them. +A value of nil means file local variables are ignored. +Any other value means to always query. + This variable also controls use of major modes specified in a -*- line. @@ -2214,42 +2219,79 @@ (goto-char beg) end)))) -(defun hack-local-variables-confirm (string flag-to-check) - (or (eq flag-to-check t) - (and flag-to-check - (save-window-excursion - (condition-case nil - (switch-to-buffer (current-buffer)) - (error - ;; If we fail to switch in the selected window, - ;; it is probably a minibuffer or dedicated window. - ;; So try another window. - (let ((pop-up-frames nil)) - ;; Refrain from popping up frames since it can't - ;; be undone by save-window-excursion. - (pop-to-buffer (current-buffer))))) - (save-excursion - (beginning-of-line) - (set-window-start (selected-window) (point))) - (y-or-n-p (format string - (if buffer-file-name - (file-name-nondirectory buffer-file-name) - (concat "buffer " (buffer-name))))))))) +(defun hack-local-variables-confirm (vars unsafe-vars risky-vars) + (if noninteractive + nil + (let ((name (if buffer-file-name + (file-name-nondirectory buffer-file-name) + (concat "buffer " (buffer-name)))) + char) + (save-window-excursion + (with-output-to-temp-buffer "*Local Variables*" + (if unsafe-vars + (progn (princ "The local variables list in ") + (princ name) + (princ "\ncontains values that may not be safe (*)") + (if risky-vars + (princ ", and variables that are risky (**).") + (princ "."))) + (if risky-vars + (progn (princ "The local variables list in ") + (princ name) + (princ "\ncontains variables that are risky (**).")) + (princ "A local variables list is specified in ") + (princ name) + (princ "."))) + (princ "\n\nDo you want to apply it? You can type +y -- to apply the local variables list. +n -- to ignore the local variables list. +! -- to apply the local variables list, and mark these values (*) as + safe (in the future, they can be set automatically.)\n\n") + (dolist (elt vars) + (cond ((member elt unsafe-vars) + (princ " * ")) + ((member elt risky-vars) + (princ " ** ")) + (t + (princ " "))) + (princ (car elt)) + (princ " : ") + (princ (cdr elt)) + (princ "\n"))) + (message "Please type y, n, or !: ") + (let ((inhibit-quit t) + (cursor-in-echo-area t)) + (while (or (not (numberp (setq char (read-event)))) + (not (memq (downcase char) + '(?! ?y ?n ?\s ?\C-g)))) + (message "Please type y, n, or !: ")) + (if (= char ?\C-g) + (setq quit-flag nil))) + (setq char (downcase char)) + (when (and (= char ?!) unsafe-vars) + (dolist (elt unsafe-vars) + (push elt safe-local-variable-values)) + (customize-save-variable + 'safe-local-variable-values + safe-local-variable-values)) + (or (= char ?!) + (= char ?\s) + (= char ?y)))))) (defun hack-local-variables-prop-line (&optional mode-only) - "Set local variables specified in the -*- line. + "Return local variables specified in the -*- line. Ignore any specification for `mode:' and `coding:'; `set-auto-mode' should already have handled `mode:', `set-auto-coding' should already have handled `coding:'. -If MODE-ONLY is non-nil, all we do is check whether the major mode -is specified, returning t if it is specified." + +If MODE-ONLY is non-nil, all we do is check whether the major +mode is specified, returning t if it is specified. Otherwise, +return an alist of elements (VAR . VAL), where VAR is a variable +and VAL is the specified value." (save-excursion (goto-char (point-min)) - (let ((result nil) - (end (set-auto-mode-1)) - mode-specified - (enable-local-variables - (and local-enable-local-variables enable-local-variables))) + (let ((end (set-auto-mode-1)) + result mode-specified) ;; Parse the -*- line into the RESULT alist. ;; Also set MODE-SPECIFIED if we see a spec or `mode'. (cond ((not end) @@ -2279,128 +2321,162 @@ ;; so we must do that here as well. ;; That is inconsistent, but we're stuck with it. ;; The same can be said for `coding' in set-auto-coding. - (or (equal (downcase (symbol-name key)) "mode") + (or (and (equal (downcase (symbol-name key)) "mode") + (setq mode-specified t)) (equal (downcase (symbol-name key)) "coding") - (setq result (cons (cons key val) result))) - (if (equal (downcase (symbol-name key)) "mode") - (setq mode-specified t)) - (skip-chars-forward " \t;"))) - (setq result (nreverse result)))) - - (if mode-only mode-specified - (if (and result - (or mode-only - (hack-local-variables-confirm - "Set local variables as specified in -*- line of %s? " - enable-local-variables))) - (let ((enable-local-eval enable-local-eval)) - (while result - (hack-one-local-variable (car (car result)) (cdr (car result))) - (setq result (cdr result))))) - nil)))) + (condition-case nil + (push (cons (if (eq key 'eval) + 'eval + (indirect-variable key)) + val) result) + (error nil))) + (skip-chars-forward " \t;"))))) + + (if mode-only + mode-specified + result)))) (defvar hack-local-variables-hook nil "Normal hook run after processing a file's local variables specs. Major modes can use this to examine user-specified local variables in order to initialize other data structure based on them.") +(defcustom safe-local-variable-values nil + "List variable-value pairs that are considered safe. +Each element is a cons cell (VAR . VAL), where VAR is a variable +symbol and VAL is a value that is considered safe." + :group 'find-file + :type 'alist) + (defun hack-local-variables (&optional mode-only) "Parse and put into effect this buffer's local variables spec. If MODE-ONLY is non-nil, all we do is check whether the major mode is specified, returning t if it is specified." - (let ((mode-specified - ;; If MODE-ONLY is t, we check here for specifying the mode - ;; in the -*- line. If MODE-ONLY is nil, we process - ;; the -*- line here. - (hack-local-variables-prop-line mode-only)) - (enable-local-variables - (and local-enable-local-variables enable-local-variables))) - ;; Look for "Local variables:" line in last page. - (save-excursion - (goto-char (point-max)) - (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move) - (when (let ((case-fold-search t)) - (and (search-forward "Local Variables:" nil t) - (or mode-only - (hack-local-variables-confirm - "Set local variables as specified at end of %s? " - enable-local-variables)))) - (skip-chars-forward " \t") - (let ((enable-local-eval enable-local-eval) - ;; suffix is what comes after "local variables:" in its line. - (suffix - (concat - (regexp-quote (buffer-substring (point) (line-end-position))) - "$")) - ;; prefix is what comes before "local variables:" in its line. - (prefix - (concat "^" (regexp-quote - (buffer-substring (line-beginning-position) - (match-beginning 0))))) - beg) - - (forward-line 1) - (let ((startpos (point)) - endpos - (thisbuf (current-buffer))) - (save-excursion - (unless (let ((case-fold-search t)) - (re-search-forward - (concat prefix "[ \t]*End:[ \t]*" suffix) - nil t)) - (error "Local variables list is not properly terminated")) - (beginning-of-line) - (setq endpos (point))) - - (with-temp-buffer - (insert-buffer-substring thisbuf startpos endpos) - (goto-char (point-min)) - (subst-char-in-region (point) (point-max) ?\^m ?\n) - (while (not (eobp)) - ;; Discard the prefix. - (if (looking-at prefix) - (delete-region (point) (match-end 0)) - (error "Local variables entry is missing the prefix")) - (end-of-line) - ;; Discard the suffix. - (if (looking-back suffix) - (delete-region (match-beginning 0) (point)) - (error "Local variables entry is missing the suffix")) - (forward-line 1)) - (goto-char (point-min)) - - (while (not (eobp)) - ;; Find the variable name; strip whitespace. - (skip-chars-forward " \t") - (setq beg (point)) - (skip-chars-forward "^:\n") - (if (eolp) (error "Missing colon in local variables entry")) - (skip-chars-backward " \t") - (let* ((str (buffer-substring beg (point))) - (var (read str)) - val) - ;; Read the variable value. - (skip-chars-forward "^:") - (forward-char 1) - (setq val (read (current-buffer))) - (if mode-only - (if (eq var 'mode) - (setq mode-specified t)) - ;; Set the variable. "Variables" mode and eval are funny. - (with-current-buffer thisbuf - (hack-one-local-variable var val)))) - (forward-line 1))))))) - (unless mode-only - (run-hooks 'hack-local-variables-hook)) - mode-specified)) - -(defvar ignored-local-variables () + (let ((enable-local-variables + (and local-enable-local-variables enable-local-variables)) + result) + (when (or mode-only enable-local-variables) + (setq result (hack-local-variables-prop-line mode-only)) + ;; Look for "Local variables:" line in last page. + (save-excursion + (goto-char (point-max)) + (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) + 'move) + (when (let ((case-fold-search t)) + (search-forward "Local Variables:" nil t)) + (skip-chars-forward " \t") + ;; suffix is what comes after "local variables:" in its line. + ;; prefix is what comes before "local variables:" in its line. + (let ((suffix + (concat + (regexp-quote (buffer-substring (point) + (line-end-position))) + "$")) + (prefix + (concat "^" (regexp-quote + (buffer-substring (line-beginning-position) + (match-beginning 0))))) + beg) + + (forward-line 1) + (let ((startpos (point)) + endpos + (thisbuf (current-buffer))) + (save-excursion + (unless (let ((case-fold-search t)) + (re-search-forward + (concat prefix "[ \t]*End:[ \t]*" suffix) + nil t)) + (error "Local variables list is not properly terminated")) + (beginning-of-line) + (setq endpos (point))) + + (with-temp-buffer + (insert-buffer-substring thisbuf startpos endpos) + (goto-char (point-min)) + (subst-char-in-region (point) (point-max) ?\^m ?\n) + (while (not (eobp)) + ;; Discard the prefix. + (if (looking-at prefix) + (delete-region (point) (match-end 0)) + (error "Local variables entry is missing the prefix")) + (end-of-line) + ;; Discard the suffix. + (if (looking-back suffix) + (delete-region (match-beginning 0) (point)) + (error "Local variables entry is missing the suffix")) + (forward-line 1)) + (goto-char (point-min)) + + (while (not (eobp)) + ;; Find the variable name; strip whitespace. + (skip-chars-forward " \t") + (setq beg (point)) + (skip-chars-forward "^:\n") + (if (eolp) (error "Missing colon in local variables entry")) + (skip-chars-backward " \t") + (let* ((str (buffer-substring beg (point))) + (var (read str)) + val) + ;; Read the variable value. + (skip-chars-forward "^:") + (forward-char 1) + (setq val (read (current-buffer))) + (if mode-only + (if (eq var 'mode) + (setq result t)) + (unless (eq var 'coding) + (condition-case nil + (push (cons (if (eq var 'eval) + 'eval + (indirect-variable var)) + val) result) + (error nil))))) + (forward-line 1))))))) + + ;; We've read all the local variables. Now, return whether the + ;; mode is specified (if MODE-ONLY is non-nil), or set the + ;; variables (if MODE-ONLY is nil.) + (if mode-only + result + (setq result (nreverse result)) + (dolist (ignored ignored-local-variables) + (setq result (assq-delete-all ignored result))) + (if (null enable-local-eval) + (setq result (assq-delete-all 'eval result))) + ;; Find those variables that we may want to save to + ;; `safe-local-variable-values'. + (let (risky-vars unsafe-vars) + (dolist (elt result) + (let ((var (car elt)) + (val (cdr elt))) + (or (eq var 'mode) + (and (eq var 'eval) + (or (eq enable-local-eval t) + (hack-one-local-variable-eval-safep + (eval (quote val))))) + (safe-local-variable-p var val) + (and (risky-local-variable-p var val) + (push elt risky-vars)) + (push elt unsafe-vars)))) + (if (or (and (eq enable-local-variables t) + (null unsafe-vars) + (null risky-vars)) + (hack-local-variables-confirm + result unsafe-vars risky-vars)) + (dolist (elt result) + (hack-one-local-variable (car elt) (cdr elt))))) + (run-hooks 'hack-local-variables-hook))))) + +(defvar ignored-local-variables + '(ignored-local-variables safe-local-variable-values) "Variables to be ignored in a file's local variable spec.") ;; Get confirmation before setting these variables as locals in a file. (put 'debugger 'risky-local-variable t) (put 'enable-local-eval 'risky-local-variable t) (put 'ignored-local-variables 'risky-local-variable t) +(put 'ignored-local-variables 'safe-local-variable-values t) (put 'eval 'risky-local-variable t) (put 'file-name-handler-alist 'risky-local-variable t) (put 'inhibit-quit 'risky-local-variable t) @@ -2452,27 +2528,68 @@ (put 'display-time-string 'risky-local-variable t) (put 'parse-time-rules 'risky-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 &optional val) - "Non-nil if SYM could be dangerous as a file-local variable with value VAL. -If VAL is nil or omitted, the question is whether any value might be -dangerous." +;; Commonly-encountered local variables that are safe: +(let ((string-or-null (lambda (a) (or (stringp a) (null a))))) + (eval + `(mapc (lambda (pair) + (put (car pair) 'safe-local-variable (cdr pair))) + '((byte-compile-dynamic . t) + (c-basic-offset . integerp) + (c-file-style . stringp) + (c-indent-level . integerp) + (comment-column . integerp) + (compile-command . ,string-or-null) + (fill-column . integerp) + (fill-prefix . ,string-or-null) + (indent-tabs-mode . t) + (kept-new-versions . integerp) + (no-byte-compile . t) + (no-update-autoloads . t) + (outline-regexp . ,string-or-null) + (page-delimiter . ,string-or-null) + (paragraph-start . ,string-or-null) + (paragraph-separate . ,string-or-null) + (sentence-end . ,string-or-null) + (sentence-end-double-space . t) + (tab-width . integerp) + (version-control . t))))) + +(defun safe-local-variable-p (sym val) + "Non-nil if SYM is safe as a file-local variable with value VAL. +It is safe if any of these conditions are met: + + * There is a matching entry (SYM . VAL) in the + `safe-local-variable-values' user option. + + * The `safe-local-variable' property of SYM is t. + + * The `safe-local-variable' property of SYM is a function that + evaluates to a non-nil value with VAL as an argument." + (or (member (cons sym val) safe-local-variable-values) + (let ((safep (get sym 'safe-local-variable))) + (or (eq safep t) + (and (functionp safep) + (funcall safep val)))))) + +(defun risky-local-variable-p (sym &optional ignored) + "Non-nil if SYM could be dangerous as a file-local variable. +It is dangerous if either of these conditions are met: + + * Its `risky-local-variable' property is non-nil. + + * Its name ends with \"hook(s)\", \"function(s)\", \"form(s)\", \"map\", + \"program\", \"command(s)\", \"predicate(s)\", \"frame-alist\", + \"mode-alist\", \"font-lock-(syntactic-)keyword*\", or + \"map-alist\"." ;; If this is an alias, check the base name. (condition-case nil (setq sym (indirect-variable sym)) (error nil)) - (let ((safep (get sym 'safe-local-variable))) - (or (get sym 'risky-local-variable) - (and (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|-commands?$\\|-predicates?$\\|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))) - (or (null val) - (not (funcall safep val))))))) + (or (get sym 'risky-local-variable) + (string-match "-hooks?$\\|-functions?$\\|-forms?$\\|-program$\\|\ +-commands?$\\|-predicates?$\\|font-lock-keywords$\\|font-lock-keywords\ +-[0-9]+$\\|font-lock-syntactic-keywords$\\|-frame-alist$\\|-mode-alist$\\|\ +-map$\\|-map-alist$" (symbol-name sym)))) (defcustom safe-local-eval-forms nil "*Expressions that are considered \"safe\" in an `eval:' local variable. @@ -2529,35 +2646,12 @@ ok))))))) (defun hack-one-local-variable (var val) - "\"Set\" one variable in a local variables spec. -A few patterns are specified so that any name which matches one -is considered risky." + "Set local variable VAR with value VAL." (cond ((eq var 'mode) (funcall (intern (concat (downcase (symbol-name val)) "-mode")))) - ((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. - ((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) - (hack-one-local-variable-eval-safep val)) - ;; Permit eval if not root and user says ok. - (and (not (zerop (user-uid))) - (hack-local-variables-confirm - "Process `eval' or hook local variables in %s? " - enable-local-eval))) - (if (eq var 'eval) - (save-excursion (eval val)) - (make-local-variable var) - (set var val)) - (message "Ignoring risky spec in the local variables list"))) - ;; Ordinary variable, really set it. + ((eq var 'eval) + (save-excursion (eval val))) (t (make-local-variable var) ;; Make sure the string has no text properties. ;; Some text properties can get evaluated in various ways,
--- a/lisp/find-lisp.el Mon Feb 13 22:57:54 2006 +0000 +++ b/lisp/find-lisp.el Tue Feb 14 01:21:31 2006 +0000 @@ -359,9 +359,5 @@ (provide 'find-lisp) -;; Local Variables: -;; autocompile: t -;; End: - ;;; arch-tag: a711374c-f12a-46f6-aa18-ba7d77b9602a ;;; find-lisp.el ends here
--- a/lisp/icomplete.el Mon Feb 13 22:57:54 2006 +0000 +++ b/lisp/icomplete.el Tue Feb 14 01:21:31 2006 +0000 @@ -325,7 +325,7 @@ ;;;_* Local emacs vars. ;;;Local variables: -;;;outline-layout: (-2 :) +;;;allout-layout: (-2 :) ;;;End: ;; arch-tag: 339ec25a-0741-4eb6-be63-997532e89b0f
--- a/lisp/play/landmark.el Mon Feb 13 22:57:54 2006 +0000 +++ b/lisp/play/landmark.el Tue Feb 14 01:21:31 2006 +0000 @@ -1694,13 +1694,13 @@ ;;;_ + Local variables -;;; The following `outline-layout' local variable setting: +;;; The following `allout-layout' local variable setting: ;;; - closes all topics from the first topic to just before the third-to-last, ;;; - shows the children of the third to last (config vars) ;;; - and the second to last (code section), ;;; - and closes the last topic (this local-variables section). ;;;Local variables: -;;;outline-layout: (0 : -1 -1 0) +;;;allout-layout: (0 : -1 -1 0) ;;;End: (provide 'landmark)