From: moore@src.bae.co.UK (Chris Moore)
Newsgroups: gnu.emacs.help
Subject: making inhibit-local-variables selective:
Date: 21 Jul 92 16:15:51 GMT
Organization: Gatewayed from the GNU Project mailing list help-gnu-emacs@prep.ai.mit.edu


raman@edu.cornell.cs (T. V. Raman) said:

> I normally set inhibit-local-variables to t to avoid nasty surprises.

Me too.

> Given this, is there any way I can specify that for certain files the
> local variables are to be obeyed quietly?

The following code (probably) does this.  I had the same idea about a year
ago, but never managed to get it working.  Seeing your message got me
interested again.  I decided to go back and look at my code, with the
experience of a year's elisp hacking, so now I have a working solution.

It's not particularly well tested (I only just got it working) but it seems
to do what you want.  Please tell me whether it works for you, or if I've
done anything stupid.

Chris.
-----cut-----8<-----here-----8<--------------8<--------------
;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Emacs-Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;
;; safe-local.el --- 
;; Author          : Chris Moore (moore@src.bae.co.uk)
;; Created On      : Tue Jul 21 16:31:35 1992
;; Last Modified By: Chris Moore
;; Last Modified On: Tue Jul 21 17:13:33 1992
;; Update Count    : 8
;; RCS             : $Id: safe-local.el,v 1.2 1992/07/21 16:14:09 moore Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This redefines hack-local-variables to silently accept the local
;; variables contained in any file whose path name matches any member of
;; the list safe-local-file-regexp-list.
;;
;; To install, place it in one of the directories specified in your
;; load-path, add (require 'safe-local) to your .emacs file and define the
;; list of regexps which are acceptable 'local variable' files, for example:
;;
;; (setq safe-local-file-regexp-list
;;	 '("/home/aip1/moore/\\(\\.emacs\\|\\.zlogin\\|\\.zshenv\\|\\.zshrc\\)"
;;	   "/gnu/src/\\(gcc\\|emacs\\).*"))
;;
;; in .emacs would mean that my zsh initialisation files and .emacs, along
;; with the entire emacs and gcc sources would be trusted to contain
;; sensible local variables.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'cl)

(defvar safe-local-file-regexp-list nil
  "*Files which are trusted not to contain silly local variable definitions.")

;; inhibit all except those we know to be good
(setq inhibit-local-variables t)

(defun safe-re-member (item list)
  "Similar to member, but uses string-match rather than eql.  Maintains the
match-data."
  (let ((data (match-data))
	(ptr list)
        (done nil)
        (result '()))
    (unwind-protect
	 (progn
	   (while (not (or done (endp ptr)))
	     (cond ((string-match (car ptr) item)
		    (setq done t)
		    (setq result ptr)))
	     (setq ptr (cdr ptr)))
	   result)
      (store-match-data data))))

(defun hack-local-variables (&optional force)
  "Parse, and bind or evaluate as appropriate, any local variables
for current buffer."
  ;; 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)
    (if (let ((case-fold-search t))
	  (and (search-forward "Local Variables:" nil t)
	       (or (not inhibit-local-variables)
		   force
		   (safe-re-member buffer-file-name safe-local-file-regexp-list)
		   (save-window-excursion
		     (switch-to-buffer (current-buffer))
		     (save-excursion
		       (beginning-of-line)
		       (set-window-start (selected-window) (point)))
		     (y-or-n-p (format "Set local variables as specified at end of %s? "
				       (file-name-nondirectory buffer-file-name)))))))
	(let ((continue t)
	      prefix prefixlen suffix beg)
	  ;; The prefix is what comes before "local variables:" in its line.
	  ;; The suffix is what comes after "local variables:" in its line.
	  (skip-chars-forward " \t")
	  (or (eolp)
	      (setq suffix (buffer-substring (point)
					     (progn (end-of-line) (point)))))
	  (goto-char (match-beginning 0))
	  (or (bolp)
	      (setq prefix
		    (buffer-substring (point)
				      (progn (beginning-of-line) (point)))))
	  (if prefix (setq prefixlen (length prefix)
			   prefix (regexp-quote prefix)))
	  (if suffix (setq suffix (concat (regexp-quote suffix) "$")))
	  (while continue
	    ;; Look at next local variable spec.
	    (if selective-display (re-search-forward "[\n\C-m]")
	      (forward-line 1))
	    ;; Skip the prefix, if any.
	    (if prefix
		(if (looking-at prefix)
		    (forward-char prefixlen)
		  (error "Local variables entry is missing the prefix")))
	    ;; 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)
	      ;; Setting variable named "end" means end of list.
	      (if (string-equal (downcase str) "end")
		  (setq continue nil)
		;; Otherwise read the variable value.
		(skip-chars-forward "^:")
		(forward-char 1)
		(setq val (read (current-buffer)))
		(skip-chars-backward "\n")
		(skip-chars-forward " \t")
		(or (if suffix (looking-at suffix) (eolp))
		    (error "Local variables entry is terminated incorrectly"))
		;; Set the variable.  "Variables" mode and eval are funny.
		(cond ((eq var 'mode)
		       (funcall (intern (concat (downcase (symbol-name val))
						"-mode"))))
		      ((eq var 'eval)
		       (if (string= (user-login-name) "root")
			   (message "Ignoring `eval:' in file's local variables")
			 (eval val)))
		      (t (make-local-variable var)
			 (set var val))))))))))
;--
;                        /      Chris Moore      \
; moore@src.bae.co.uk   / Sowerby Research Centre \   Kids, look!  Street crime!
; +44 272 363375        \  British Aerospace PLC  /        -- Homer Simpson
;                        \      BRISTOL, UK      /
Newsgroups: gnu.emacs.help