(command-line): Require whitespace delimiter when
[bpt/emacs.git] / lisp / midnight.el
CommitLineData
ac06bd0f
RS
1;;; midnight.el --- run something every midnight, e.g., kill old buffers.
2
3;;; Copyright (C) 1998 Free Software Foundation, Inc.
4
5;;; Author: Sam Steingold <sds@usa.net>
6;;; Maintainer: Sam Steingold <sds@usa.net>
7;;; Created: 1998-05-18
8;;; Keywords: utilities
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
27;;; Commentary:
28
29;; To use the file, put (require 'midnight) into your .emacs. Then, at
30;; midnight, Emacs will run the normal hook `midnight-hook'. You can
31;; put whatever you like there, say, `calendar'; by default there is
32;; only one function there - `clean-buffer-list'. It will kill the
33;; buffers matching `clean-buffer-list-kill-buffer-names' and
34;; `clean-buffer-list-kill-regexps' and the buffers which where last
35;; displayed more than `clean-buffer-list-delay-general' days ago,
36;; keeping `clean-buffer-list-kill-never-buffer-names' and
37;; `clean-buffer-list-kill-never-regexps'.
38
849ac835
RS
39(eval-when-compile
40 (require 'cl)
41 (require 'timer))
ac06bd0f
RS
42
43(defgroup midnight nil
44 "Run something every day at midnight."
1e484d64
DN
45 :group 'calendar
46 :version "20.3")
ac06bd0f 47
e9d7cff0
RS
48(defcustom midnight-mode t
49 "*Non-nil means run `midnight-hook' at midnight.
50Setting this variable outside customize has no effect;
51call `cancel-timer' or `timer-activate' on `midnight-timer' instead."
52 :type 'boolean
53 :group 'midnight
54 :require 'midnight
e9d7cff0
RS
55 :set (lambda (symb val)
56 (set symb val) (require 'midnight)
57 (if val (timer-activate midnight-timer)
58 (cancel-timer midnight-timer))))
59
ac06bd0f
RS
60;;; time conversion
61
62(defun float-time (&optional tm)
63 "Convert `current-time' to a float number of seconds."
64 (multiple-value-bind (s0 s1 s2) (or tm (current-time))
65 (+ (* (float (ash 1 16)) s0) (float s1) (* 0.0000001 s2))))
66
67(defun time-float (num)
68 "Convert the float number of seconds since epoch to the list of 3 integers."
69 (let* ((div (ash 1 16)) (1st (floor num div)))
70 (list 1st (floor (- num (* (float div) 1st)))
71 (round (* 10000000 (mod num 1))))))
72
73(defun buffer-display-time (&optional buf)
74 "Return the time-stamp of the given buffer, or current buffer, as float."
75 (save-excursion
76 (set-buffer (or buf (current-buffer)))
77 (when buffer-display-time (float-time buffer-display-time))))
78
79;;; clean-buffer-list stuff
80
81(defcustom clean-buffer-list-delay-general 3
82 "*The number of days before any buffer becomes eligible for autokilling.
83The autokilling is done by `clean-buffer-list' when is it in `midnight-hook'.
84Currently displayed and/or modified (unsaved) buffers, as well as buffers
85matching `clean-buffer-list-kill-never-buffer-names' and
86`clean-buffer-list-kill-never-regexps' are excluded."
87 :type 'integer
1e484d64 88 :group 'midnight)
ac06bd0f
RS
89
90(defcustom clean-buffer-list-delay-special 3600
91 "*The number of seconds before some buffers become eligible for autokilling.
92Buffers matched by `clean-buffer-list-kill-regexps' and
93`clean-buffer-list-kill-buffer-names' are killed if they were last
94displayed more than this many seconds ago."
95 :type 'integer
1e484d64 96 :group 'midnight)
ac06bd0f 97
b2f9ab3c 98(defcustom clean-buffer-list-kill-regexps nil
ac06bd0f
RS
99 "*List of regexps saying which buffers will be killed at midnight.
100If buffer name matches a regexp in the list and the buffer was not displayed
101in the last `clean-buffer-list-delay-special' seconds, it is killed by
102`clean-buffer-list' when is it in `midnight-hook'.
103If a member of the list is a cons, it's `car' is the regexp and its `cdr' is
104the number of seconds to use instead of `clean-buffer-list-delay-special'.
105See also `clean-buffer-list-kill-buffer-names',
106`clean-buffer-list-kill-never-regexps' and
107`clean-buffer-list-kill-never-buffer-names'."
108 :type 'list
1e484d64 109 :group 'midnight)
ac06bd0f
RS
110
111(defcustom clean-buffer-list-kill-buffer-names
b2f9ab3c
RS
112 '("*Help*" "*Apropos*" "*Man " "*Buffer List*" "*Compile-Log*" "*info*"
113 "*vc*" "*vc-diff*")
ac06bd0f
RS
114 "*List of strings saying which buffers will be killed at midnight.
115Buffers with names in this list, which were not displayed in the last
116`clean-buffer-list-delay-special' seconds, are killed by `clean-buffer-list'
117when is it in `midnight-hook'.
118If a member of the list is a cons, it's `car' is the name and its `cdr' is
119the number of seconds to use instead of `clean-buffer-list-delay-special'.
120See also `clean-buffer-list-kill-regexps',
121`clean-buffer-list-kill-never-regexps' and
122`clean-buffer-list-kill-never-buffer-names'."
123 :type 'list
1e484d64 124 :group 'midnight)
ac06bd0f
RS
125
126(defcustom clean-buffer-list-kill-never-buffer-names
127 '("*scratch*" "*Messages*")
128 "*List of buffer names which will never be killed by `clean-buffer-list'.
129See also `clean-buffer-list-kill-never-regexps'.
130Note that this does override `clean-buffer-list-kill-regexps' and
131`clean-buffer-list-kill-buffer-names' so a buffer matching any of these
132two lists will NOT be killed if it is also present in this list."
133 :type 'list
1e484d64
DN
134 :group 'midnight)
135
ac06bd0f
RS
136
137(defcustom clean-buffer-list-kill-never-regexps '("^ \*Minibuf-.*\*$")
138 "*List of regexp saying which buffers will never be killed at midnight.
139See also `clean-buffer-list-kill-never-buffer-names'.
140Killing is done by `clean-buffer-list'.
141Note that this does override `clean-buffer-list-kill-regexps' and
142`clean-buffer-list-kill-buffer-names' so a buffer matching any of these
143two lists will NOT be killed if it also matches anything in this list."
144 :type 'list
1e484d64 145 :group 'midnight)
ac06bd0f
RS
146
147(defun midnight-find (el ls test &optional key)
148 "A stopgap solution to the absence of `find' in ELisp."
149 (if (fboundp 'find)
150 (find el ls :test test :key (or key 'eql))
849ac835
RS
151 (dolist (rr ls)
152 (when (funcall test el (if key (funcall key rr) rr))
153 (return rr)))))
154
b2f9ab3c
RS
155(defun clean-buffer-list-delay (name)
156 "Return the delay, in seconds, before killing a buffer named NAME.
ac06bd0f
RS
157Uses `clean-buffer-list-kill-buffer-names', `clean-buffer-list-kill-regexps'
158`clean-buffer-list-delay-general' and `clean-buffer-list-delay-special'.
159Autokilling is done by `clean-buffer-list'."
b2f9ab3c 160 (or (assoc-default name clean-buffer-list-kill-buffer-names 'string=
849ac835 161 clean-buffer-list-delay-special)
b2f9ab3c 162 (assoc-default name clean-buffer-list-kill-regexps 'string-match
849ac835
RS
163 clean-buffer-list-delay-special)
164 (* clean-buffer-list-delay-general 24 60 60)))
ac06bd0f
RS
165
166(defun clean-buffer-list ()
b2f9ab3c 167 "Kill old buffers that have not been displayed recently.
ac06bd0f
RS
168The relevant vartiables are `clean-buffer-list-delay-general',
169`clean-buffer-list-delay-special', `clean-buffer-list-kill-buffer-names',
170`clean-buffer-list-kill-never-buffer-names',
171`clean-buffer-list-kill-regexps' and `clean-buffer-list-kill-never-regexps'."
172 (interactive)
173 (let ((tm (float-time)) bts (ts (format-time-string "%Y-%m-%d %T")) bn)
174 (dolist (buf (buffer-list))
175 (message "[%s] processing `%s'..." ts buf)
176 (setq bts (buffer-display-time buf) bn (buffer-name buf))
849ac835 177 (unless (or (midnight-find bn clean-buffer-list-kill-never-regexps
ac06bd0f
RS
178 'string-match)
179 (midnight-find bn clean-buffer-list-kill-never-buffer-names
180 'string-equal)
181 (buffer-modified-p buf) (get-buffer-window buf 'visible)
182 (null bts) (< (- tm bts) (clean-buffer-list-delay bn)))
183 (message "[%s] killing `%s'" ts bn)
184 (kill-buffer buf)))))
185
186;;; midnight hook
187
188(defvar midnight-period (* 24 60 60)
b2f9ab3c 189 "The number of seconds in a day--the delta for `midnight-timer'.")
ac06bd0f
RS
190
191(defcustom midnight-hook 'clean-buffer-list
192 "The hook run `midnight-delay' seconds after midnight every day.
193The default value is `clean-buffer-list'."
194 :type 'hook
1e484d64 195 :group 'midnight)
ac06bd0f
RS
196
197(defun midnight-next ()
198 "Return the number of seconds till the next midnight."
199 (multiple-value-bind (sec min hrs) (decode-time)
200 (- (* 24 60 60) (* 60 60 hrs) (* 60 min) sec)))
201
202(defvar midnight-timer nil
203 "Timer running the `midnight-hook' `midnight-delay' seconds after midnight.
204Use `cancel-timer' to stop it and `midnight-delay-set' to change
205the time when it is run.")
206
207;;;###autoload
208(defun midnight-delay-set (symb tm)
209 "Modify `midnight-timer' according to `midnight-delay'.
210Sets the first argument (which must be symbol `midnight-delay')
211to its second argument."
212 (unless (eq symb 'midnight-delay)
213 (error "Illegal argument to `midnight-delay-set': `%s'" symb))
214 (set symb tm)
215 (when (timerp midnight-timer) (cancel-timer midnight-timer))
216 (setq midnight-timer
217 (run-at-time (if (numberp tm) (+ (midnight-next) tm) tm)
e9d7cff0
RS
218 midnight-period 'midnight-timer-function)))
219
220(defun midnight-timer-function ()
221 "This is the function run by the `midnight-mode' timer once each day."
cbb41490 222 (run-hooks 'midnight-hook))
ac06bd0f
RS
223
224(defcustom midnight-delay 3600
225 "*The number of seconds after the midnight when the `midnight-timer' is run.
226You should set this variable before loading midnight.el, or
227set it by calling `midnight-delay-set', or use `custom'.
228If you wish, you can use a string instead, it will be passed as the
229first argument to `run-at-time'."
230 :type 'sexp
231 :set 'midnight-delay-set
1e484d64 232 :group 'midnight)
ac06bd0f
RS
233
234(provide 'midnight)
235
236;;; midnight.el ends here