home *** CD-ROM | disk | FTP | other *** search
- ;; -*-Emacs-Lisp-*-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; File: expire-kill.el
- ;; RCS: $Id: expire-kill.el,v 2.3 1993/05/07 05:16:45 liblit Exp $
- ;; Description: Expiring kill patterns for GNUS
- ;; Author: Ben Liblit, liblit@cs.psu.edu
- ;; Created: Wed Mar 2 1993
- ;; Modified: Fri May 7 01:13:25 1993 (Ben) liblit@cs.psu.edu
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; Copyright (C) 1993 Ben Liblit.
- ;;;
- ;;; Author: Ben Liblit (liblit@cs.psu.edu)
- ;;;
- ;;; This program is free software; you can redistribute it and/or
- ;;; modify it under the terms of the GNU General Public License as
- ;;; published by the Free Software Foundation; either version 1, or
- ;;; (at your option) any later version.
- ;;;
- ;;; This program is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;; General Public License for more details.
- ;;;
- ;;; A copy of the GNU General Public License can be obtained from this
- ;;; program's author (send electronic mail to liblit@cs.psu.edu) or
- ;;; from the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
- ;;; MA 02139, USA.
-
- ;;; Description:
- ;;;
- ;;; This package augments the standard GNUS kill file mechanism to
- ;;; allow expiring kill patterns. Time stamps may be stored with
- ;;; patterns, and if a pattern's time stamp indicates that it has not
- ;;; been matched in a long period of time, that pattern is removed.
-
- ;;; Installation:
- ;;;
- ;;; Optionally byte-compile expire-kill.el to expire-kill.elc and put
- ;;; them both in a directory on your load-path. To load expire-kill
- ;;; when GNUS first up, add the following to your .emacs:
- ;;;
- ;;; (setq gnus-Startup-hook
- ;;; '(lambda ()
- ;;; (require 'expire-kill)))
- ;;;
- ;;; Autoloading based on the function "expire-kill" will *not* work
- ;;; properly, as expire-kill needs to hook itself into other parts of
- ;;; GNUS before the first kill file is loaded.
- ;;;
- ;;; Also, please note that expire-kill needs to use either Dave
- ;;; Gillespie's calc package or Edward Reingold's calendar package for
- ;;; performing date calculations. The variable expire-date-package
- ;;; should be set to either 'calc or 'calendar, depending on which you
- ;;; wish to use.
-
- ;;; Background and Motivation:
- ;;;
- ;;; The standard GNUS kill file mechanism is fairly powerful and
- ;;; flexible. However, its usefulness is limited by the fact that
- ;;; kill patterns remain active indefinitely, unless manually removed
- ;;; by the user. This makes certain uses of kill files highly
- ;;; impractical.
- ;;;
- ;;; For example, one might wish to use a subject-matching kill pattern
- ;;; to mark articles in a discussion thread that one is not interested
- ;;; in. Using standard (gnus-kill ...), though, means that the
- ;;; pattern will remain in the kill file long after the thread itself
- ;;; has died out. As time goes on, kill files will become bloated
- ;;; with patterns that have long ceased to be active.
- ;;;
- ;;; This package provides a time stamped alternative to "gnus-kill".
- ;;; The function "expire-kill" takes similar arguments, and performs
- ;;; the same function. However, "expire-kill" also takes a time stamp
- ;;; argument (stored as a string) that indicates the last time its
- ;;; pattern was successfully matched. Thus, instead of:
- ;;;
- ;;; (gnus-kill "Subject" "cheese")
- ;;;
- ;;; in a kill file, one might see:
- ;;;
- ;;; (expire-kill "Subject" "cheese" "<Tue Mar 2, 1993>")
- ;;;
- ;;; which would perform the same kill actions as "gnus-kill", but
- ;;; which additionally records that it hasn't actually seen a subject
- ;;; of "cheese" since March 2.
- ;;;
- ;;; A new method of applying kill files allows these time stamps to be
- ;;; updated when matches are made. Other support functions sweep
- ;;; through a newsgroup's kill file and delete patterns that have not
- ;;; been matched in a long enough time (seven days, by default).
- ;;; Updated kill files are saved back to disk, or optionally deleted
- ;;; entirely if *all* their patterns have expired.
- ;;;
- ;;; Note that expire-kill is backward compatible with standard GNUS
- ;;; kill files. Calls to "gnus-kill" and other elisp still work as
- ;;; before, and will never be expired.
-
- ;;; Usage:
- ;;;
- ;;; To use this package, simply add calls to "expire-kill" to your
- ;;; GNUS kill files. The first two arguments specify a header field
- ;;; and a regexp pattern, just as for "gnus-kill". The third should
- ;;; be a string or integer that can be parsed into an initial time
- ;;; stamp.
- ;;;
- ;;; You should never need to construct these calls by hand, though.
- ;;; Instead, a suite of functions are provided that add calls, or
- ;;; portions of calls, for you. All of these are suitable for calling
- ;;; via M-x, or for binding onto keys. In fact, each function
- ;;; described below has a corresponding variable with the same name.
- ;;; If that variable is set to a string representing some sequence of
- ;;; keys, that key seuqence will be bound to evoke the corresponding
- ;;; function in the appropriate buffers. These functions (and
- ;;; associated variables) are as follows:
- ;;;
- ;;; - expire-Subject-kill-same-subject
- ;;; - expire-Subject-kill-same-subject-and-select
- ;;; - expire-Subject-kill-thread
- ;;; - expire-Subject-kill-thread-and-select
- ;;;
- ;;; These functions should be used from the *Subject* buffer. The
- ;;; first two functions add expiring kill patterns for the subject of
- ;;; the article at the cursor. The second two functions add expiring
- ;;; kill patterns for followups to the article at the cursor. All
- ;;; four functions mark any articles already in the *Subject* buffer
- ;;; that match their targets. Furthermore, the "-and-select" forms
- ;;; immediately select the next unread article.
- ;;;
- ;;; - expire-Kill-file-kill-by-subject
- ;;; - expire-Kill-file-kill-by-thread
- ;;;
- ;;; These two functions may be used while editing a kill file. They
- ;;; insert "expire-kill" calls to match the most recently seen subject
- ;;; and followups to the current article, respectively.
- ;;;
- ;;; - expire-Kill-file-insert-time-stamp
- ;;;
- ;;; This function is also intended for use while editing kill files.
- ;;; It will insert a time stamp corresponding to the present time
- ;;; after the cursor. This can be useful for finishing up hand
- ;;; written calls to "expire-kill".
- ;;;
- ;;; By default, expire-Subject-kill-same-subject-and-select is bound
- ;;; to "C-c C-k" and expire-Subject-kill-thread-and-select is bound to
- ;;; "C-c k", both in the *Subject* buffer. None of the others are
- ;;; bound by default, although this may easily be customized by
- ;;; setting the same-named variable to the desired key sequence.
-
- ;;; Known Bugs and Limitations:
- ;;;
- ;;; For simplicity's sake, empty (whitespace only) kill files are not
- ;;; deleted until the next time their newsgroup is selected. It might
- ;;; be nicer to delete empties as soon as the last s-expression is removed.
- ;;;
- ;;; Some users symlink kill files, using one such file for several
- ;;; related newsgroups. We try to do right by these users, not
- ;;; deleting empty kill files that are also symlinks. However,
- ;;; patterns may tend to expire more quickly when a kill file is
- ;;; shared. If a pattern doesn't match in one group, it can be
- ;;; expired before it even gets to look at a second group.
-
- ;;; Acknowledgments:
- ;;;
- ;;; Thanks for release 2.x go out to the many users who were kind
- ;;; enough to discuss and suggest improvements over earlier releases.
- ;;; In particular, Dave Disser's insightful correspondence has
- ;;; inspired many of 2.x's enhancements. Don Wells and Bill Oakley
- ;;; also deserve recognition for their suggestions, bug reports, and
- ;;; invaluable feedback.
-
- ;;; LISPDIR ENTRY for the Elisp Archive
- ;;;
- ;;; LCD Archive Entry:
- ;;; expire-kill|Ben Liblit|liblit@cs.psu.edu|
- ;;; expiring kill patterns for GNUS|
- ;;; 7-May-1993|2.3|~/misc/expire-kill.el.Z|
-
- ;;;; ------------------------------------------------------------
- ;;;; User customization variables.
- ;;;; ------------------------------------------------------------
-
- (defvar expire-maximum-age 7
- "*Longest time a pattern can go unmatched before being removed.
- The units on this measure are in days, and its value should be an
- integer.")
-
-
- (defvar expire-flush-frequency 'group
- "*Determines how often modified kill file buffers are flushed.
- If set to the atom 'always, then flushing happens after every
- modification. If set to 'group, then flushing occurs when you exit
- the current newsgroup. If set to 'session, then modifications are
- flushed only when you exit GNUS itself. Otherwise, no automatic
- actions are taken.
-
- The variable expire-flush-action determines what it actually means to
- \"flush\" a modified kill file buffer.")
-
-
- (defvar expire-flush-action 'kill
- "*Determines what to do with buffers of modified kill files.
- If set to the atom 'kill, then the modified buffer is saved and
- killed. If set to 'save, then the modified buffer is saved, but is
- not killed. Otherwise, no actions are taken.
-
- The variable expire-flush-frequency determines how frequently the
- requested action is taken.")
-
-
- (defvar expire-Subject-kill-same-subject nil
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the *Subject* buffer. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Subject-kill-same-subject-and-select "\C-c\C-k"
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the *Subject* buffer. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Subject-kill-thread nil
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the *Subject* buffer. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Subject-kill-thread-and-select "\C-ck"
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the *Subject* buffer. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Kill-file-kill-by-subject "\C-c\C-x\C-s"
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the kill file buffers. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Kill-file-kill-by-thread "\C-c\C-x\C-t"
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the kill file buffers. Otherwise, no
- binding will be made for this function.")
-
- (defvar expire-Kill-file-insert-time-stamp "\C-c\C-x\C-i"
- "*Key sequence to which to bind the function of the same name.
- If set to a string representing a key sequence, that sequence will
- evoke the same-named function in the kill file buffers. Otherwise, no
- binding will be made for this function.")
-
-
- (defvar expire-delete-empties 't
- "*Determines whether or not empty kill files should be deleted.
- If set to the atom 'ask, then the user will be asked each time. If
- set to some other non-nil value, then empties will be deleted
- automatically the first time they are applied to a newsgroup. If nil,
- no deletions will be performed.")
-
-
- (defvar expire-kill-default-command '(gnus-Subject-mark-as-read nil "x")
- "*Default command to be evaluated by expire-kill if none is given.
- This must be a lisp expression or a string representing a key sequence.")
-
-
- (defvar expire-after-apply-hook (function
- (lambda ()
- (gnus-expunge "Xx")))
- "*A hook called after all kill files have been applied.
- This hook is called after the global and local kill files have been
- applied to the currently-selected newsgroup.
-
- If you used to finish off all of your kill files with a call to
- gnus-expunge, you can factor all of that out into this hook. This
- reduces kill file size, and makes it easy to delete empty ones.")
-
-
- (defvar expire-load-hook nil
- "*A hook called after expire-kill is loaded in.
- This can be a good place to put custom key bindings.")
-
-
- (defvar expire-date-package 'calendar
- "*The name of the package to use for date calculations.
- This should be set to an atom for which expire-date-package-profiles
- has an entry, and which may be loaded using (require ...). Currently
- supported packages include calc and calendar.
-
- Note that stamps may be read from either format, assuming both are
- available. New stamps, however, will always be generated using the
- selected package.")
-
-
- (defvar expire-date-package-profiles
- '((calendar (calendar-absolute-from-gregorian (calendar-current-date))
- (- (calendar-absolute-from-gregorian (calendar-current-date))
- timestamp))
- (calc (calc-eval "floor(now())")
- (calc-eval "floor(now() - $)" 'raw timestamp)))
- "*A list of profiles of known date calculation packages.
- Each profile is, itself, a list of three values.
-
- - The first is the package name as an atom, which may be used in
- expire-date-package and which is used in (require ...) at load
- time.
-
- - The second is an s-expression that returns the current date.
-
- - The third is an s-expression that returns the (integral) difference
- between the date stored in the variable \"timestamp\" and the
- current date.")
-
-
- (defconst expire-version "$Revision: 2.3 $"
- "The current version, of revision number, of expire-kill.
- Be sure to include this in any bug reports.")
-
- ;;;; ------------------------------------------------------------
- ;;;; Dependencies.
- ;;;; ------------------------------------------------------------
-
- ;;; We need to plug into several GNUS hooks, and add-hook is the
- ;;; cleanest way to do so. Several implementations exist; any should
- ;;; suffice.
-
- (require 'add-hook)
-
- ;;; Backquoting is used to construct the augmented commands that
- ;;; expire-kill passes down to gnus-kill.
-
- (require 'backquote)
-
- ;;; All of expire-kill's date calculations are handled by outside
- ;;; packages. Currently supported are Dave Gillespie's winning calc
- ;;; package, and Edward Reingold's equally winning calendar package.
- ;;; If you don't have either, you should. Inquire at your
- ;;; neighborhood elisp archive.
-
- ;;; Note that if you are using calendar and receive errors about a
- ;;; void function named current-time-zone, you will need to initialize
- ;;; some of calendar's variables for it. Those variables are
- ;;; calendar-time-zone, calendar-standard-time-zone-name, and
- ;;; calendar-daylight-time-zone-name. See calendar.el for further
- ;;; information.
-
- (and (boundp 'expire-date-package)
- (require expire-date-package))
-
- ;;; Common Lisp defines a really handy (case...) form, that we use for
- ;;; checking the value of various user options. We also use its
- ;;; convenient push and pop functions.
-
- (require 'cl)
-
- ;;; Normally, GNUS should already be loaded by the time we are loaded.
- ;;; Just in case, though, make sure it is there.
-
- (require 'gnus)
-
- ;;;; ------------------------------------------------------------
- ;;;; Internal-use variables.
- ;;;; ------------------------------------------------------------
-
- (defvar expire-current-kill-buffer nil
- "The buffer of the kill file currently (or recently) being applied.")
-
- (defvar expire-modified-buffers nil
- "A list of all kill file buffers that may have been modified recently.")
-
-
- (defun expire-current-date ()
- "Return some representation of today's date."
- (eval (nth 1 (assq expire-date-package
- expire-date-package-profiles))))
-
-
- (defun expire-days-since (timestamp)
- "Return the integral number of days between today and TIMESTAMP."
- (eval (nth 2 (assq expire-date-package
- expire-date-package-profiles))))
-
- ;;;; ------------------------------------------------------------
- ;;;; User functions.
- ;;;; ------------------------------------------------------------
-
- (defun expire-kill (field pattern timestamp &optional command all)
- "If FIELD of an article matches REGEXP, update TIMESTAMP and execute COMMAND.
- If no COMMAND is given, the value of expire-kill-default-command is
- used. If optional 5th argument ALL is non-nil, articles marked are
- also applied to. If FIELD is an empty string (or nil), entire article
- body is searched for. COMMAND must be a lisp expression or a string
- representing a key sequence."
- (let (successful)
- (let ((command (or command expire-kill-default-command)))
- (gnus-kill field pattern
- (` (progn (setq successful t)
- (, (if (stringp command)
- (list 'execute-kbd-macro command)
- command))))
- all))
- (if successful
- (expire-restamp field pattern
- (expire-convert-timestamp timestamp)
- command all)
- (expire-filter (expire-convert-timestamp timestamp)))))
-
-
- (defun expire-Kill-file-kill-by-subject (ask)
- "Insert expiring KILL command for current subject.
- Argument ASK non-nil (C-u if called interactively) allows the user to
- edit the pattern before it is inserted."
- (interactive "P")
- (expire-insert-kill "Subject"
- (regexp-quote
- (nntp-header-subject
- (gnus-find-header-by-number
- gnus-newsgroup-headers
- gnus-current-kill-article)))
- ask))
-
-
- (defun expire-Kill-file-kill-by-thread (ask)
- "Insert expiring KILL command for current thread.
- Argument ASK non-nil (C-u if called interactively) allows the user
- to edit the pattern before it is inserted."
- (interactive "P")
- (expire-insert-kill "References"
- (regexp-quote
- (gnus-header-id
- (gnus-find-header-by-number
- gnus-newsgroup-headers
- gnus-current-kill-article)))
- ask))
-
-
- (defun expire-Kill-file-insert-time-stamp ()
- "Insert a time stamp for the current date after point.
- Handy for finishing up hand written calls to expire-kill."
- (interactive)
- (prin1 (expire-current-date)
- (current-buffer)))
-
-
- (defun expire-Subject-kill-same-subject (ask)
- "Add a local, expiring kill pattern for the current subject.
- Also, mark all articles with this subject in the current buffer as
- read, but do not select the next unread article. Argument ASK non-nil
- (C-u if called interactively) allows the user to edit the pattern
- before it is inserted."
- (interactive "P")
- (expire-Subject-kill-using 'expire-Kill-file-kill-by-subject ask)
- (gnus-Subject-kill-same-subject nil))
-
-
- (defun expire-Subject-kill-same-subject-and-select (ask)
- "Add a local, expiring kill pattern for the current subject.
- Also, mark all articles with this subject in the current buffer as
- read and select the next unread article. Argument ASK non-nil (C-u if
- called interactively) allows the user to edit the pattern before it is
- inserted."
- (interactive "P")
- (expire-Subject-kill-using 'expire-Kill-file-kill-by-subject ask)
- (gnus-Subject-kill-same-subject-and-select nil))
-
-
- (defun expire-Subject-kill-thread (ask)
- "Add a local, expiring kill pattern for the current thread. Also,
- mark all articles in the current thread as read. Argument ASK non-nil
- (C-u if called interactively) allows the user to edit the pattern
- before it is inserted."
- (interactive "P")
- (expire-Subject-kill-using 'expire-Kill-file-kill-by-thread ask)
- (gnus-Subject-kill-thread nil))
-
-
- (defun expire-Subject-kill-thread-and-select (ask)
- "Add a local, expiring kill pattern for the current thread. Also,
- mark all articles in the current thread as read and select the next
- unread article. Argument ASK non-nil (C-u if called interactively)
- allows the user to edit the pattern before it is inserted."
- (interactive "P")
- (expire-Subject-kill-using 'expire-Kill-file-kill-by-thread ask)
- (gnus-Subject-kill-thread nil)
- (if (memq (gnus-Subject-article-number)
- gnus-newsgroup-unreads)
- (gnus-Subject-select-article)
- (gnus-Subject-next-unread-article)))
-
- ;;;; ------------------------------------------------------------
- ;;;; Internal-use hook functions.
- ;;;; ------------------------------------------------------------
-
- (defun expire-apply-kill-file ()
- "Apply kill files to the current newsgroup. The global kill file,
- if it exists, is loaded in the standard manner. The local kill file,
- however, is read and evaluated one s-expression at a time. This
- allows calls to \"expire-kill\" to modify themselves. If the local
- kill file consists of nothing but whitespace, it may be deleted,
- depending upon the value of expire-delete-empties.
-
- The hook expire-after-apply-hook is executed after both the global and
- local kill files have been applied. If neither the global nor the
- local kill file actually existed, though, this hook is ignored."
- (let (kill-files-applied)
- (let ((global-kill-file (gnus-newsgroup-kill-file nil)))
- (if (file-readable-p global-kill-file)
- (progn
- (message "Loading %s..." global-kill-file)
- (load (gnus-newsgroup-kill-file nil) 'noerr nil 'nosufx)
- (message "Loading %s...done" global-kill-file)
- (setq kill-files-applied 't))))
- (let ((local-kill-file (gnus-newsgroup-kill-file gnus-newsgroup-name)))
- (if (or (file-readable-p local-kill-file)
- (get-file-buffer local-kill-file))
- (save-excursion
- (message "Loading %s..." local-kill-file)
- (find-file local-kill-file)
- (push (setq expire-current-kill-buffer (current-buffer))
- expire-modified-buffers)
- (goto-char (point-min))
- (if (re-search-forward "[^ \t\r\n\f]" nil 'noerr)
- (progn
- (goto-char (point-min))
- (condition-case nil
- (expire-eval-buffer local-kill-file)
- (end-of-file))
- (setq kill-files-applied 't)
- (message "Loading %s...done" local-kill-file)
- (bury-buffer expire-current-kill-buffer)
- (expire-possibly-flush 'always))
- (if (and expire-delete-empties
- (not (file-symlink-p local-kill-file))
- (if (eq expire-delete-empties 'ask)
- (y-or-n-p
- (format "Delete empty %s " local-kill-file))
- t))
- (progn
- (message "Deleting %s..." local-kill-file)
- (delete-file local-kill-file)
- (set-buffer-modified-p nil)
- (kill-buffer nil)
- (message "Deleting %s...done" local-kill-file))
- (bury-buffer expire-current-kill-buffer)
- (expire-possibly-flush 'always))))))
- (and kill-files-applied
- (run-hooks 'expire-after-apply-hook))))
-
-
- (defun expire-Exit-group ()
- "Possibly flush all modified buffers.
- Should be called from gnus-Exit-group-hook."
- (expire-possibly-flush 'group))
-
-
- (defun expire-Exit-gnus ()
- "Possibly flush all modified buffers.
- Should be called from gnus-Exit-gnus-hook."
- (expire-possibly-flush 'session))
-
- ;;;; ------------------------------------------------------------
- ;;;; Internal-use buffer functions.
- ;;;; ------------------------------------------------------------
-
- (defun expire-eval-buffer (name)
- "Evaluate the s-expressions following point in the current buffer,
- one at a time. NAME gives the buffer's displayed name."
- ;; Note: while this function could be quite elegant if written
- ;; tail-recursively, even optimizing byte-compilers have difficulty
- ;; making tail-recursion as efficient as a flat loop. This is
- ;; primarily due to Lisp's dynamic scoping.
- (while 't
- (set-buffer gnus-Subject-buffer)
- (eval (read expire-current-kill-buffer))
- (set-buffer expire-current-kill-buffer)
- (message "Loading %s...%d%%"
- name
- (/ (* 100 (point)) ;; Re-evaluate (point-max) each time,
- (point-max))))) ;; as the buffer can change in size.
-
-
- (defun expire-possibly-flush (frequency)
- "Possibly flush modified kill file buffers.
- Flushing actually happens only if argument FREQUENCY and the variable
- expire-flush-frequency are the same."
- (if (eq frequency expire-flush-frequency)
- (expire-flush)))
-
-
- (defun expire-flush ()
- "Flush modified buffers as called for by expire-flush-action.
- Depending on the value of this variable, we either save and kill the
- buffers in expire-modified-buffers just save them, or don't do
- anything. Also, reset expire-modified-buffers to nil when we are done."
- ;; Note: while this function could be quite elegant if written
- ;; tail-recursively, even optimizing byte-compilers have difficulty
- ;; making tail-recursion as efficient as a flat loop. This is
- ;; primarily due to Lisp's dynamic scoping.
- (while expire-modified-buffers
- (let ((buffer (pop expire-modified-buffers)))
- (if (buffer-name buffer)
- (case expire-flush-action
- ('kill (expire-save-if-modified buffer)
- (kill-buffer buffer))
- ('save (expire-save-if-modified buffer)))))))
-
-
- (defun expire-save-if-modified (buffer)
- "Save BUFFER, but only if it has been modified.
- This prevents unsightly \"(No changes need to be saved)\" messages."
- (if (buffer-modified-p buffer)
- (save-excursion
- (set-buffer buffer)
- (save-buffer)
- (bury-buffer))))
-
- ;;;; ------------------------------------------------------------
- ;;;; Internal-use pattern functions.
- ;;;; ------------------------------------------------------------
-
- (defun expire-convert-timestamp (timestamp)
- "Convert a time stamp to the user's preferred format.
- If TIMESTAMP is a string, it is assumed to be in calc format; integers
- are assumed to belong to calendar. An equivalent string or integer is
- returned, depending upon the value of expire-date-package."
- (cond ((integerp timestamp)
- (case expire-date-package
- (calendar timestamp)
- (calc (require 'calendar)
- (calc-eval "date($)" nil (1+ timestamp)))))
- ((stringp timestamp)
- (case expire-date-package
- (calc timestamp)
- (calendar (require 'calc)
- (1- (calc-eval "date($)" 'raw timestamp)))))))
-
-
- (defun expire-restamp (field pattern timestamp &optional command all)
- "Replace a call to \"expire-kill\" with one having an updated time
- stamp. The s-expression before the point is deleted, and a new one is
- inserted that calls \"expire-kill\" with the given FIELD and REGEXP,
- and the current time as its time stamp. If the current time is not
- different from TIMESTAMP, however, the buffer is not modified.
- Optional arguments COMMAND and ALL correspond to those passed to the
- original expire-kill call, and if given will be reproduced in the new
- call."
- (let ((now (expire-current-date)))
- (or (equal timestamp now)
- (progn (set-buffer expire-current-kill-buffer)
- (backward-sexp)
- (kill-sexp 1)
- (delete-blank-lines)
- (delete-blank-lines)
- (expire-insert-kill field pattern nil now command all)))))
-
-
- (defun expire-insert-kill (field pattern ask &optional timestamp command all)
- "General purpose function to produce \"expire-kill\" calls.
- Inserts a call to \"expire-kill\" with the given FIELD and REGEXP. If
- third argument ASK is non-nil, the user will be allowed to edit the
- regexp. An optional fourth argument provides the TIMESTAMP; if none is
- given, a stamp for the current time is used. Fifth and sixth optional
- arguments COMMAND and ALL specify the corresponding optional arguments
- to the expire-kill call."
- (prin1 (append (list 'expire-kill
- field
- (if ask
- (read-from-minibuffer (concat field ": ")
- pattern)
- pattern)
- (or timestamp
- (expire-current-date)))
- (cond (all (list command all))
- (command (list command))))
- (current-buffer))
- (or (eolp)
- (insert ?\n)))
-
-
- (defun expire-filter (timestamp)
- "Delete outdated calls to \"expire-kill.\"
- If TIMESTAMP is older than the age limit given by expire-maximum-age,
- delete the s-expression before the point. Presumably, this
- corresponds to an outdated \"expire-kill\" call."
- (if (> (expire-days-since timestamp)
- expire-maximum-age)
- (progn
- (set-buffer expire-current-kill-buffer)
- (let ((end-of-sexp (point)))
- (backward-sexp)
- (delete-region (point) end-of-sexp)
- (delete-blank-lines)
- (delete-blank-lines)))))
-
-
- (defun expire-Subject-kill-using (kill-mode-function ask)
- "In the *Subject* buffer, add a new expiring kill pattern.
- First argument FUNCTION should be the name of a function to be called,
- with no arguments, in the local kill file to actually insert the new
- pattern. If second argument ASK is non-nil, allow the user to edit
- the kill pattern before it is inserted."
- (let ((gnus-current-kill-article (gnus-Subject-article-number)))
- (save-window-excursion
- (find-file (gnus-newsgroup-kill-file gnus-newsgroup-name))
- (goto-char (point-min))
- (funcall kill-mode-function ask)
- (push (current-buffer) expire-modified-buffers)
- (bury-buffer)
- (expire-possibly-flush 'always))))
-
- ;;;; ------------------------------------------------------------
- ;;;; Initialization.
- ;;;; ------------------------------------------------------------
-
- ;;; Bind each of the major user-callable functions, if the same-named
- ;;; variable is set to a string representing a key sequence.
-
- (mapcar (function
- (lambda (binding)
- (let ((function (car binding))
- (keymap (cdr binding)))
- (if (stringp (symbol-value function))
- (define-key
- (symbol-value keymap)
- (symbol-value function)
- function)))))
- '((expire-Subject-kill-same-subject . gnus-Subject-mode-map)
- (expire-Subject-kill-same-subject-and-select . gnus-Subject-mode-map)
- (expire-Subject-kill-thread . gnus-Subject-mode-map)
- (expire-Subject-kill-thread-and-select . gnus-Subject-mode-map)
- (expire-Kill-file-kill-by-subject . gnus-Kill-file-mode-map)
- (expire-Kill-file-kill-by-thread . gnus-Kill-file-mode-map)
- (expire-Kill-file-insert-time-stamp . gnus-Kill-file-mode-map)))
-
-
- ;;; Install ourselves as the kill file applier of choice.
-
- (setq gnus-Apply-kill-hook 'expire-apply-kill-file)
-
-
- ;;; Plug in to some exit conditions that might prompt flushing.
-
- (add-hook 'gnus-Exit-group-hook 'expire-Exit-group)
- (add-hook 'gnus-Exit-gnus-hook 'expire-Exit-gnus)
-
-
- ;;; Announce our presence and call any user hooks.
-
- (provide 'expire-kill)
- (run-hooks 'expire-load-hook)
-
-
- ;;; The End
-