(Fmove_to_window_line): Doc fix.
[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
849ac835 98(defcustom clean-buffer-list-kill-regexps '("\\*vc\\.")
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
112 '("*Help*" "*Apropos*" "*Man " "*Buffer List*" "*Compile-Log*" "*info*")
113 "*List of strings saying which buffers will be killed at midnight.
114Buffers with names in this list, which were not displayed in the last
115`clean-buffer-list-delay-special' seconds, are killed by `clean-buffer-list'
116when is it in `midnight-hook'.
117If a member of the list is a cons, it's `car' is the name and its `cdr' is
118the number of seconds to use instead of `clean-buffer-list-delay-special'.
119See also `clean-buffer-list-kill-regexps',
120`clean-buffer-list-kill-never-regexps' and
121`clean-buffer-list-kill-never-buffer-names'."
122 :type 'list
1e484d64 123 :group 'midnight)
ac06bd0f
RS
124
125(defcustom clean-buffer-list-kill-never-buffer-names
126 '("*scratch*" "*Messages*")
127 "*List of buffer names which will never be killed by `clean-buffer-list'.
128See also `clean-buffer-list-kill-never-regexps'.
129Note that this does override `clean-buffer-list-kill-regexps' and
130`clean-buffer-list-kill-buffer-names' so a buffer matching any of these
131two lists will NOT be killed if it is also present in this list."
132 :type 'list
1e484d64
DN
133 :group 'midnight)
134
ac06bd0f
RS
135
136(defcustom clean-buffer-list-kill-never-regexps '("^ \*Minibuf-.*\*$")
137 "*List of regexp saying which buffers will never be killed at midnight.
138See also `clean-buffer-list-kill-never-buffer-names'.
139Killing is done by `clean-buffer-list'.
140Note that this does override `clean-buffer-list-kill-regexps' and
141`clean-buffer-list-kill-buffer-names' so a buffer matching any of these
142two lists will NOT be killed if it also matches anything in this list."
143 :type 'list
1e484d64 144 :group 'midnight)
ac06bd0f
RS
145
146(defun midnight-find (el ls test &optional key)
147 "A stopgap solution to the absence of `find' in ELisp."
148 (if (fboundp 'find)
149 (find el ls :test test :key (or key 'eql))
849ac835
RS
150 (dolist (rr ls)
151 (when (funcall test el (if key (funcall key rr) rr))
152 (return rr)))))
153
154(defun assoc-default (el alist test default)
155 "Find object EL in a pseudo-alist ALIST.
156ALIST is a list of conses or objects. EL is compared (using TEST) with
157CAR (or the object itself, if it is not a cons) of elements of ALIST.
158When TEST returns non-nil, CDR (or DEFAULT, if the object is not a cons)
159of the object is returned.
160This is a non-consing analogue of
161 (cdr (assoc el (mapcar (lambda (el) (if (consp el) el (cons el default)))
162 alist)
163 :test test))
164The calling sequence is: (ASSOC-DEFAULT EL ALIST TEST DEFAULT)"
165 (dolist (rr alist)
166 (when (funcall test el (if (consp rr) (car rr) rr))
167 (return (if (consp rr) (cdr rr) default)))))
ac06bd0f
RS
168
169(defun clean-buffer-list-delay (bn)
170 "Return the delay, in seconds, before this buffer name is auto-killed.
171Uses `clean-buffer-list-kill-buffer-names', `clean-buffer-list-kill-regexps'
172`clean-buffer-list-delay-general' and `clean-buffer-list-delay-special'.
173Autokilling is done by `clean-buffer-list'."
849ac835
RS
174 (or (assoc-default bn clean-buffer-list-kill-buffer-names 'string=
175 clean-buffer-list-delay-special)
176 (assoc-default bn clean-buffer-list-kill-regexps 'string-match
177 clean-buffer-list-delay-special)
178 (* clean-buffer-list-delay-general 24 60 60)))
ac06bd0f
RS
179
180(defun clean-buffer-list ()
181 "Kill old buffers.
182The relevant vartiables are `clean-buffer-list-delay-general',
183`clean-buffer-list-delay-special', `clean-buffer-list-kill-buffer-names',
184`clean-buffer-list-kill-never-buffer-names',
185`clean-buffer-list-kill-regexps' and `clean-buffer-list-kill-never-regexps'."
186 (interactive)
187 (let ((tm (float-time)) bts (ts (format-time-string "%Y-%m-%d %T")) bn)
188 (dolist (buf (buffer-list))
189 (message "[%s] processing `%s'..." ts buf)
190 (setq bts (buffer-display-time buf) bn (buffer-name buf))
849ac835 191 (unless (or (midnight-find bn clean-buffer-list-kill-never-regexps
ac06bd0f
RS
192 'string-match)
193 (midnight-find bn clean-buffer-list-kill-never-buffer-names
194 'string-equal)
195 (buffer-modified-p buf) (get-buffer-window buf 'visible)
196 (null bts) (< (- tm bts) (clean-buffer-list-delay bn)))
197 (message "[%s] killing `%s'" ts bn)
198 (kill-buffer buf)))))
199
200;;; midnight hook
201
202(defvar midnight-period (* 24 60 60)
203 "The number of seconds in a day - the delta for `midnight-timer'.")
204
205(defcustom midnight-hook 'clean-buffer-list
206 "The hook run `midnight-delay' seconds after midnight every day.
207The default value is `clean-buffer-list'."
208 :type 'hook
1e484d64 209 :group 'midnight)
ac06bd0f
RS
210
211(defun midnight-next ()
212 "Return the number of seconds till the next midnight."
213 (multiple-value-bind (sec min hrs) (decode-time)
214 (- (* 24 60 60) (* 60 60 hrs) (* 60 min) sec)))
215
216(defvar midnight-timer nil
217 "Timer running the `midnight-hook' `midnight-delay' seconds after midnight.
218Use `cancel-timer' to stop it and `midnight-delay-set' to change
219the time when it is run.")
220
221;;;###autoload
222(defun midnight-delay-set (symb tm)
223 "Modify `midnight-timer' according to `midnight-delay'.
224Sets the first argument (which must be symbol `midnight-delay')
225to its second argument."
226 (unless (eq symb 'midnight-delay)
227 (error "Illegal argument to `midnight-delay-set': `%s'" symb))
228 (set symb tm)
229 (when (timerp midnight-timer) (cancel-timer midnight-timer))
230 (setq midnight-timer
231 (run-at-time (if (numberp tm) (+ (midnight-next) tm) tm)
e9d7cff0
RS
232 midnight-period 'midnight-timer-function)))
233
234(defun midnight-timer-function ()
235 "This is the function run by the `midnight-mode' timer once each day."
236 (when midnight-mode
237 (run-hooks 'midnight-hook)))
ac06bd0f
RS
238
239(defcustom midnight-delay 3600
240 "*The number of seconds after the midnight when the `midnight-timer' is run.
241You should set this variable before loading midnight.el, or
242set it by calling `midnight-delay-set', or use `custom'.
243If you wish, you can use a string instead, it will be passed as the
244first argument to `run-at-time'."
245 :type 'sexp
246 :set 'midnight-delay-set
1e484d64 247 :group 'midnight)
ac06bd0f
RS
248
249(provide 'midnight)
250
251;;; midnight.el ends here