;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
;; Author: Per Abrahamsen <amanda@iesd.auc.dk>
-;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;; Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: news
;; This file is part of GNU Emacs.
(require 'gnus-sum)
(require 'gnus-range)
(require 'message)
+(require 'score-mode)
(defcustom gnus-global-score-files nil
- "*List of global score files and directories.
+ "List of global score files and directories.
Set this variable if you want to use people's score files. One entry
for each score file or each score file directory. Gnus will decide
by itself what score files are applicable to which group.
:type '(repeat file))
(defcustom gnus-score-file-single-match-alist nil
- "*Alist mapping regexps to lists of score files.
+ "Alist mapping regexps to lists of score files.
Each element of this alist should be of the form
(\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
:type '(repeat (cons regexp (repeat file))))
(defcustom gnus-score-file-multiple-match-alist nil
- "*Alist mapping regexps to lists of score files.
+ "Alist mapping regexps to lists of score files.
Each element of this alist should be of the form
(\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
:type '(repeat (cons regexp (repeat file))))
(defcustom gnus-score-file-suffix "SCORE"
- "*Suffix of the score files."
+ "Suffix of the score files."
:group 'gnus-score-files
:type 'string)
(defcustom gnus-adaptive-file-suffix "ADAPT"
- "*Suffix of the adaptive score files."
+ "Suffix of the adaptive score files."
:group 'gnus-score-files
:group 'gnus-score-adapt
:type 'string)
(defcustom gnus-score-find-score-files-function 'gnus-score-find-bnews
- "*Function used to find score files.
+ "Function used to find score files.
The function will be called with the group name as the argument, and
should return a list of score files to apply to that group. The score
files do not actually have to exist.
This variable can also be a list of functions to be called. Each
function should either return a list of score files, or a list of
-score alists."
+score alists.
+
+If functions other than these pre-defined functions are used,
+the `a' symbolic prefix to the score commands will always use
+\"all.SCORE\"."
:group 'gnus-score-files
:type '(radio (function-item gnus-score-find-single)
(function-item gnus-score-find-hierarchical)
:type 'number)
(defcustom gnus-home-score-file nil
- "*Variable to control where interactive score entries are to go.
+ "Variable to control where interactive score entries are to go.
It can be:
* A string
(function :value fun)))
(defcustom gnus-home-adapt-file nil
- "*Variable to control where new adaptive score entries are to go.
+ "Variable to control where new adaptive score entries are to go.
This variable allows the same syntax as `gnus-home-score-file'."
:group 'gnus-score-adapt
:group 'gnus-score-files
(integer :tag "Score"))))))
(defcustom gnus-ignored-adaptive-words nil
- "*List of words to be ignored when doing adaptive word scoring."
+ "List of words to be ignored when doing adaptive word scoring."
:group 'gnus-score-adapt
:type '(repeat string))
(integer :tag "Score"))))
(defcustom gnus-adaptive-word-minimum nil
- "*If a number, this is the minimum score value that can be assigned to a word."
+ "If a number, this is the minimum score value that can be assigned to a word."
:group 'gnus-score-adapt
:type '(choice (const nil) integer))
+(defcustom gnus-adaptive-word-no-group-words nil
+ "If t, don't adaptively score words included in the group name."
+ :group 'gnus-score-adapt
+ :type 'boolean)
+
(defcustom gnus-score-mimic-keymap nil
"*Have the score entry functions pretend that they are a keymap."
:group 'gnus-score-default
:type '(choice (const nil) integer))
(defcustom gnus-score-uncacheable-files "ADAPT$"
- "*All score files that match this regexp will not be cached."
+ "All score files that match this regexp will not be cached."
:group 'gnus-score-adapt
:group 'gnus-score-files
:type 'regexp)
(defcustom gnus-score-default-header nil
- "*Default header when entering new scores.
+ "Default header when entering new scores.
Should be one of the following symbols.
(const :tag "ask" nil)))
(defcustom gnus-score-default-type nil
- "*Default match type when entering new scores.
+ "Default match type when entering new scores.
Should be one of the following symbols.
f: fuzzy string
r: regexp string
b: before date
- a: at date
+ a: after date
n: this date
<: less than number
>: greater than number
(const :tag "fuzzy string" f)
(const :tag "regexp string" r)
(const :tag "before date" b)
- (const :tag "at date" a)
+ (const :tag "after date" a)
(const :tag "this date" n)
(const :tag "less than number" <)
(const :tag "greater than number" >)
(const :tag "ask" nil)))
(defcustom gnus-score-default-fold nil
- "*Use case folding for new score file entries iff not nil."
+ "Use case folding for new score file entries iff not nil."
:group 'gnus-score-default
:type 'boolean)
(defcustom gnus-score-default-duration nil
- "*Default duration of effect when entering new scores.
+ "Default duration of effect when entering new scores.
Should be one of the following symbols.
(const :tag "ask" nil)))
(defcustom gnus-score-after-write-file-function nil
- "*Function called with the name of the score file just written to disk."
+ "Function called with the name of the score file just written to disk."
:group 'gnus-score-files
:type 'function)
(defcustom gnus-score-thread-simplify nil
- "*If non-nil, subjects will simplified as in threading."
+ "If non-nil, subjects will simplified as in threading."
:group 'gnus-score-various
:type 'boolean)
(gnus-define-keys (gnus-summary-score-map "V" gnus-summary-mode-map)
"s" gnus-summary-set-score
- "a" gnus-summary-score-entry
"S" gnus-summary-current-score
"c" gnus-score-change-score-file
"C" gnus-score-customize
(?z s "substring" body-string)
(?p r "regexp string" body-string)
(?b before "before date" date)
- (?a at "at date" date)
- (?n now "this date" date)
+ (?a after "after date" date)
+ (?n at "this date" date)
(?< < "less than number" number)
(?> > "greater than number" number)
(?= = "equal to number" number)))
DATE is the expire date, or nil for no expire, or 'now for immediate expire.
If optional argument `PROMPT' is non-nil, allow user to edit match.
If optional argument `SILENT' is nil, show effect of score entry."
- (interactive
- (list (completing-read "Header: "
- gnus-header-index
- (lambda (x) (fboundp (nth 2 x)))
- t)
- (read-string "Match: ")
- (if (y-or-n-p "Use regexp match? ") 'r 's)
- (and current-prefix-arg
- (prefix-numeric-value current-prefix-arg))
- (cond ((not (y-or-n-p "Add to score file? "))
- 'now)
- ((y-or-n-p "Expire kill? ")
- (current-time-string))
- (t nil))))
;; Regexp is the default type.
(when (eq type t)
(setq type 'r))
(or (nth 1 new)
gnus-score-interactive-default-score)))
;; Nope, we have to add a new elem.
- (gnus-score-set header (if old (cons new old) (list new))))
+ (gnus-score-set header (if old (cons new old) (list new)) nil t))
(gnus-score-set 'touched '(t))))
;; Score the current buffer.
"references" id 's
score (current-time-string))))))))
-(defun gnus-score-set (symbol value &optional alist)
+(defun gnus-score-set (symbol value &optional alist warn)
;; Set SYMBOL to VALUE in ALIST.
(let* ((alist
(or alist
(entry (assoc symbol alist)))
(cond ((gnus-score-get 'read-only alist)
;; This is a read-only score file, so we do nothing.
- )
+ (when warn
+ (gnus-message 4 "Note: read-only score file; entry discarded")))
(entry
(setcdr entry value))
((null alist)
;; Load score file FILE. Returns a list a retrieved score-alists.
(let* ((file (expand-file-name
(or (and (string-match
- (concat "^" (expand-file-name
- gnus-kill-files-directory))
+ (concat "^" (regexp-quote
+ (expand-file-name
+ gnus-kill-files-directory)))
(expand-file-name file))
file)
(concat (file-name-as-directory gnus-kill-files-directory)
found)
(while a
;; Downcase all header names.
- (when (stringp (caar a))
+ (cond
+ ((stringp (caar a))
(setcar (car a) (downcase (caar a)))
(setq found t))
+ ;; Advanced scoring.
+ ((consp (caar a))
+ (setq found t)))
(pop a))
;; If there are actual scores in the alist, we add it to the
;; return value of this function.
(expand-file-name sfile gnus-kill-files-directory)))
exclude-files))
gnus-scores-exclude-files))
- (unless local
+ (when local
(save-excursion
(set-buffer gnus-summary-buffer)
(while local
(and (file-exists-p file)
(not (file-writable-p file))))
()
- (setq score (setcdr entry (delq (assq 'touched score) score)))
+ (setq score (setcdr entry (gnus-delete-alist 'touched score)))
(erase-buffer)
(let (emacs-lisp-mode-hook)
(if (string-match
(gnus-prin1 score)
;; This is a normal score file, so we print it very
;; prettily.
- (pp score (current-buffer))))
+ (let ((lisp-mode-syntax-table score-mode-syntax-table))
+ (pp score (current-buffer)))))
(gnus-make-directory (file-name-directory file))
;; If the score file is empty, we delete it.
(if (zerop (buffer-size))
(while (setq art (pop articles))
(setq this (aref (car art) gnus-score-index))
(if simplify
- (setq this (gnus-map-function gnus-simplify-subject-functions this)))
+ (setq this (gnus-map-function gnus-simplify-subject-functions this)))
(if (equal last this)
;; O(N*H) cons-cells used here, where H is the number of
;; headers.
(mt (aref (symbol-name type) 0))
(case-fold-search (not (memq mt '(?R ?S ?E ?F))))
(dmt (downcase mt))
- ; Assume user already simplified regexp and fuzzies
+ ; Assume user already simplified regexp and fuzzies
(match (if (and simplify (not (memq dmt '(?f ?r))))
(gnus-map-function
gnus-simplify-subject-functions
(cond
;; Fuzzy matches. We save these for later.
((= dmt ?f)
- (push (cons entries alist) fuzzies))
+ (push (cons entries alist) fuzzies)
+ (setq entries (cdr entries)))
;; Word matches. Save these for even later.
((= dmt ?w)
- (push (cons entries alist) words))
+ (push (cons entries alist) words)
+ (setq entries (cdr entries)))
;; Exact matches.
((= dmt ?e)
;; Do exact matching.
gnus-score-trace))
(while (setq art (pop arts))
(setcdr art (+ score (cdr art)))))))
- (forward-line 1)))
+ (forward-line 1))
+ ;; Update expiry date
+ (if trace
+ (setq entries (cdr entries))
+ (cond
+ ;; Permanent entry.
+ ((null date)
+ (setq entries (cdr entries)))
+ ;; We have a match, so we update the date.
+ ((and found gnus-update-score-entry-dates)
+ (gnus-score-set 'touched '(t) alist)
+ (setcar (nthcdr 2 kill) now)
+ (setq entries (cdr entries)))
+ ;; This entry has expired, so we remove it.
+ ((and expire (< date expire))
+ (gnus-score-set 'touched '(t) alist)
+ (setcdr entries (cddr entries)))
+ ;; No match; go to next entry.
+ (t
+ (setq entries (cdr entries))))))
;; Regexp and substring matching.
(t
(goto-char (point-min))
gnus-score-trace))
(while (setq art (pop arts))
(setcdr art (+ score (cdr art)))))
- (forward-line 1))))
- ;; Update expiry date
- (if trace
- (setq entries (cdr entries))
- (cond
- ;; Permanent entry.
- ((null date)
- (setq entries (cdr entries)))
- ;; We have a match, so we update the date.
- ((and found gnus-update-score-entry-dates)
- (gnus-score-set 'touched '(t) alist)
- (setcar (nthcdr 2 kill) now)
- (setq entries (cdr entries)))
- ;; This entry has expired, so we remove it.
- ((and expire (< date expire))
- (gnus-score-set 'touched '(t) alist)
- (setcdr entries (cddr entries)))
- ;; No match; go to next entry.
- (t
- (setq entries (cdr entries))))))))
+ (forward-line 1))
+ ;; Update expiry date
+ (if trace
+ (setq entries (cdr entries))
+ (cond
+ ;; Permanent entry.
+ ((null date)
+ (setq entries (cdr entries)))
+ ;; We have a match, so we update the date.
+ ((and found gnus-update-score-entry-dates)
+ (gnus-score-set 'touched '(t) alist)
+ (setcar (nthcdr 2 kill) now)
+ (setq entries (cdr entries)))
+ ;; This entry has expired, so we remove it.
+ ((and expire (< date expire))
+ (gnus-score-set 'touched '(t) alist)
+ (setcdr entries (cddr entries)))
+ ;; No match; go to next entry.
+ (t
+ (setq entries (cdr entries))))))))))
;; Find fuzzy matches.
(when fuzzies
(setcdr art (+ score (cdr art))))))
(forward-line 1))
;; Update expiry date
- (cond
- ;; Permanent.
- ((null date)
- )
- ;; Match, update date.
- ((and found gnus-update-score-entry-dates)
- (gnus-score-set 'touched '(t) (cdar fuzzies))
- (setcar (nthcdr 2 kill) now))
- ;; Old entry, remove.
- ((and expire (< date expire))
- (gnus-score-set 'touched '(t) (cdar fuzzies))
- (setcdr (caar fuzzies) (cddaar fuzzies))))
+ (if (not trace)
+ (cond
+ ;; Permanent.
+ ((null date)
+ )
+ ;; Match, update date.
+ ((and found gnus-update-score-entry-dates)
+ (gnus-score-set 'touched '(t) (cdar fuzzies))
+ (setcar (nthcdr 2 kill) now))
+ ;; Old entry, remove.
+ ((and expire (< date expire))
+ (gnus-score-set 'touched '(t) (cdar fuzzies))
+ (setcdr (caar fuzzies) (cddaar fuzzies)))))
(setq fuzzies (cdr fuzzies)))))
(when words
(while (setq art (pop arts))
(setcdr art (+ score (cdr art))))))
;; Update expiry date
- (cond
- ;; Permanent.
- ((null date)
- )
- ;; Match, update date.
- ((and found gnus-update-score-entry-dates)
- (gnus-score-set 'touched '(t) (cdar words))
- (setcar (nthcdr 2 kill) now))
- ;; Old entry, remove.
- ((and expire (< date expire))
- (gnus-score-set 'touched '(t) (cdar words))
- (setcdr (caar words) (cddaar words))))
+ (if (not trace)
+ (cond
+ ;; Permanent.
+ ((null date)
+ )
+ ;; Match, update date.
+ ((and found gnus-update-score-entry-dates)
+ (gnus-score-set 'touched '(t) (cdar words))
+ (setcar (nthcdr 2 kill) now))
+ ;; Old entry, remove.
+ ((and expire (< date expire))
+ (gnus-score-set 'touched '(t) (cdar words))
+ (setcdr (caar words) (cddaar words)))))
(setq words (cdr words))))))
nil))
(set-syntax-table syntab))
;; Make all the ignorable words ignored.
(let ((ignored (append gnus-ignored-adaptive-words
+ (if gnus-adaptive-word-no-group-words
+ (message-tokenize-header
+ (gnus-group-real-name gnus-newsgroup-name)
+ "."))
gnus-default-ignored-adaptive-words)))
(while ignored
(gnus-sethash (pop ignored) nil hashtb)))))
(set-syntax-table syntab))
;; Make all the ignorable words ignored.
(let ((ignored (append gnus-ignored-adaptive-words
+ (if gnus-adaptive-word-no-group-words
+ (message-tokenize-header
+ (gnus-group-real-name
+ gnus-newsgroup-name)
+ "."))
gnus-default-ignored-adaptive-words)))
(while ignored
(gnus-sethash (pop ignored) nil hashtb)))
(interactive (list gnus-global-score-files))
(let (out)
(while files
+ ;; #### /$ Unix-specific?
(if (string-match "/$" (car files))
(setq out (nconc (directory-files
(car files) t