;;; custom.el --- tools for declaring and initializing options
;;
-;; Copyright (C) 1996, 1997, 1999, 2001, 2002, 2003, 2004,
-;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 1996-1997, 1999, 2001-2011 Free Software Foundation, Inc.
;;
;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
;; Maintainer: FSF
"Like `deftheme', but THEME is evaluated as a normal argument.
FEATURE is the feature this theme provides. Normally, this is a symbol
created from THEME by `custom-make-theme-feature'."
- (if (memq theme '(user changed))
- (error "Custom theme cannot be named %S" theme))
+ (unless (custom-theme-name-valid-p theme)
+ (error "Custom theme cannot be named %S" theme))
(add-to-list 'custom-known-themes theme)
(put theme 'theme-feature feature)
(when doc (put theme 'theme-documentation doc)))
(defcustom custom-theme-load-path (list 'custom-theme-directory t)
"List of directories to search for custom theme files.
-Emacs commands for loading custom themes (e.g. `customize-themes'
-and `load-theme') search for custom theme files in the specified
+When loading custom themes (e.g. in `customize-themes' and
+`load-theme'), Emacs searches for theme files in the specified
order. Each element in the list should be one of the following:
- the symbol `custom-theme-directory', meaning the value of
`custom-theme-directory'.
- the symbol t, meaning the built-in theme directory (a directory
named \"themes\" in `data-directory').
-- a directory name (a string)."
+- a directory name (a string).
+
+Each theme file is named NAME-theme.el, where THEME is the theme
+name."
:type '(repeat (choice (const :tag "custom-theme-directory"
custom-theme-directory)
(const :tag "Built-in theme directory" t)
This calls `provide' to provide the feature name stored in THEME's
property `theme-feature' (which is usually a symbol created by
`custom-make-theme-feature')."
- (if (memq theme '(user changed))
- (error "Custom theme cannot be named %S" theme))
+ (unless (custom-theme-name-valid-p theme)
+ (error "Custom theme cannot be named %S" theme))
(custom-check-theme theme)
(provide (get theme 'theme-feature))
(unless custom--inhibit-theme-enable
- ;; Loading a theme also enables it.
+ ;; By default, loading a theme also enables it.
(push theme custom-enabled-themes)
;; `user' must always be the highest-precedence enabled theme.
- ;; Make that remain true. (This has the effect of making user settings
- ;; override the ones just loaded, too.)
+ ;; Make that remain true. (This has the effect of making user
+ ;; settings override the ones just loaded, too.)
(let ((custom-enabling-themes t))
(enable-theme 'user))))
+(defcustom custom-safe-themes '(default)
+ "List of themes that are considered safe to load.
+Each list element should be the `sha1' hash of a theme file, or
+the symbol `default', which stands for any theme in the built-in
+Emacs theme directory (a directory named \"themes\" in
+`data-directory')."
+ :type '(repeat
+ (choice string (const :tag "Built-in themes" default)))
+ :group 'customize
+ :risky t
+ :version "24.1")
+
+(defvar safe-functions) ; From unsafep.el
+
(defun load-theme (theme &optional no-enable)
"Load a theme's settings from its file.
Normally, this also enables the theme; use `disable-theme' to
disable it. If optional arg NO-ENABLE is non-nil, don't enable
-the theme."
- ;; Note we do no check for validity of the theme here.
- ;; This allows to pull in themes by a file-name convention
+the theme.
+
+A theme file is named THEME-theme.el, where THEME is the theme name,
+in one of the directories specified by `custom-theme-load-path'."
(interactive
(list
(intern (completing-read "Load custom theme: "
(put theme 'theme-documentation nil))
(let ((fn (locate-file (concat (symbol-name theme) "-theme.el")
(custom-theme--load-path)
- '("" "c"))))
+ '("" "c")))
+ hash)
(unless fn
(error "Unable to find theme file for `%s'." theme))
- ;; Instead of simply loading the theme file, read it manually.
(with-temp-buffer
(insert-file-contents fn)
- (let ((custom--inhibit-theme-enable no-enable)
- sexp scar)
- (while (setq sexp (let ((read-circle nil))
- (condition-case nil
- (read (current-buffer))
- (end-of-file nil))))
- ;; Perform some checks on each sexp before evaluating it.
- (cond
- ((not (listp sexp)))
- ((eq (setq scar (car sexp)) 'deftheme)
- (unless (eq (cadr sexp) theme)
- (error "Incorrect theme name in `deftheme'"))
- (and (symbolp (nth 1 sexp))
- (stringp (nth 2 sexp))
- (eval (list scar (nth 1 sexp) (nth 2 sexp)))))
- ((or (eq scar 'custom-theme-set-variables)
- (eq scar 'custom-theme-set-faces))
- (unless (equal (nth 1 sexp) `(quote ,theme))
- (error "Incorrect theme name in theme settings"))
- (dolist (entry (cddr sexp))
- (unless (eq (car-safe entry) 'quote)
- (error "Unsafe expression in theme settings")))
- (eval sexp))
- ((and (eq scar 'provide-theme)
- (equal (cadr sexp) `(quote ,theme))
- (= (length sexp) 2))
- (eval sexp))))))))
+ (setq hash (sha1 (current-buffer)))
+ ;; Check file safety.
+ (when (or (and (memq 'default custom-safe-themes)
+ (equal (file-name-directory fn)
+ (expand-file-name "themes/" data-directory)))
+ (member hash custom-safe-themes)
+ ;; If the theme is not in `custom-safe-themes', check
+ ;; it with unsafep.
+ (progn
+ (require 'unsafep)
+ (let ((safe-functions
+ (append '(provide-theme deftheme
+ custom-theme-set-variables
+ custom-theme-set-faces)
+ safe-functions))
+ unsafep form)
+ (while (and (setq form (condition-case nil
+ (let ((read-circle nil))
+ (read (current-buffer)))
+ (end-of-file nil)))
+ (null (setq unsafep (unsafep form)))))
+ (or (null unsafep)
+ (custom-theme-load-confirm hash)))))
+ (let ((custom--inhibit-theme-enable no-enable))
+ (eval-buffer))))))
+
+(defun custom-theme-load-confirm (hash)
+ "Query the user about loading a Custom theme that may not be safe.
+The theme should be in the current buffer. If the user agrees,
+query also about adding HASH to `custom-safe-themes'."
+ (if noninteractive
+ nil
+ (let ((exit-chars '(?y ?n ?\s))
+ prompt char)
+ (save-window-excursion
+ (rename-buffer "*Custom Theme*" t)
+ (emacs-lisp-mode)
+ (display-buffer (current-buffer))
+ (setq prompt
+ (format "This theme is not guaranteed to be safe. Really load? %s"
+ (if (< (line-number-at-pos (point-max))
+ (window-body-height))
+ "(y or n) "
+ (push ?\C-v exit-chars)
+ "Type y or n, or C-v to scroll: ")))
+ (goto-char (point-min))
+ (while (null char)
+ (setq char (read-char-choice prompt exit-chars))
+ (when (eq char ?\C-v)
+ (condition-case nil
+ (scroll-up)
+ (error (goto-char (point-min))))
+ (setq char nil)))
+ (when (memq char '(?\s ?y))
+ (push hash custom-safe-themes)
+ ;; Offer to save to `custom-safe-themes'.
+ (and (or custom-file user-init-file)
+ (y-or-n-p "Treat this theme as safe for future loads? ")
+ (let ((coding-system-for-read nil))
+ (customize-save-variable 'custom-safe-themes
+ custom-safe-themes)))
+ t)))))
(defun custom-theme-name-valid-p (name)
"Return t if NAME is a valid name for a Custom theme, nil otherwise.
(and (symbolp name)
name
(not (or (zerop (length (symbol-name name)))
- ;; There's a third-party package named color-theme.el.
- ;; Don't treat that as a theme.
- (eq name 'color)
- (eq name 'cus)
(eq name 'user)
(eq name 'changed)))))
:group 'customize
:type '(repeat symbol)
:set-after '(custom-theme-directory custom-theme-load-path)
+ :risky t
:set (lambda (symbol themes)
;; Avoid an infinite loop when custom-enabled-themes is
;; defined in a theme (e.g. `user'). Enabling the theme sets
(provide 'custom)
-;; arch-tag: 041b6116-aabe-4f9a-902d-74092bc3dab2
;;; custom.el ends here