1 ;;; gnus-score.el --- scoring code for Gnus
2 ;; Copyright (C) 1995,96 Free Software Foundation, Inc.
4 ;; Author: Per Abrahamsen <amanda@iesd.auc.dk>
5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
33 (defcustom gnus-global-score-files nil
34 "List of global score files and directories.
35 Set this variable if you want to use people's score files. One entry
36 for each score file or each score file directory. Gnus will decide
37 by itself what score files are applicable to which group.
39 Say you want to use the single score file
40 \"/ftp.ifi.uio.no@ftp:/pub/larsi/ding/score/soc.motss.SCORE\" and all
41 score files in the \"/ftp.some-where:/pub/score\" directory.
43 (setq gnus-global-score-files
44 '(\"/ftp.ifi.uio.no:/pub/larsi/ding/score/soc.motss.SCORE\"
45 \"/ftp.some-where:/pub/score\"))"
49 (defcustom gnus-score-file-single-match-alist nil
50 "Alist mapping regexps to lists of score files.
51 Each element of this alist should be of the form
52 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
54 If the name of a group is matched by REGEXP, the corresponding scorefiles
55 will be used for that group.
56 The first match found is used, subsequent matching entries are ignored (to
57 use multiple matches, see gnus-score-file-multiple-match-alist).
59 These score files are loaded in addition to any files returned by
60 gnus-score-find-score-files-function (which see)."
62 :type '(repeat (cons regexp (repeat file))))
64 (defcustom gnus-score-file-multiple-match-alist nil
65 "Alist mapping regexps to lists of score files.
66 Each element of this alist should be of the form
67 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
69 If the name of a group is matched by REGEXP, the corresponding scorefiles
70 will be used for that group.
71 If multiple REGEXPs match a group, the score files corresponding to each
72 match will be used (for only one match to be used, see
73 gnus-score-file-single-match-alist).
75 These score files are loaded in addition to any files returned by
76 gnus-score-find-score-files-function (which see)."
78 :type '(repeat (cons regexp (repeat file))))
80 (defcustom gnus-score-file-suffix "SCORE"
81 "Suffix of the score files."
85 (defcustom gnus-adaptive-file-suffix "ADAPT"
86 "Suffix of the adaptive score files."
90 (defcustom gnus-score-find-score-files-function 'gnus-score-find-bnews
91 "Function used to find score files.
92 The function will be called with the group name as the argument, and
93 should return a list of score files to apply to that group. The score
94 files do not actually have to exist.
96 Predefined values are:
98 gnus-score-find-single: Only apply the group's own score file.
99 gnus-score-find-hierarchical: Also apply score files from parent groups.
100 gnus-score-find-bnews: Apply score files whose names matches.
102 See the documentation to these functions for more information.
104 This variable can also be a list of functions to be called. Each
105 function should either return a list of score files, or a list of
108 :type '(radio (function-item gnus-score-find-single)
109 (function-item gnus-score-find-hierarchical)
110 (function-item gnus-score-find-bnews)
111 (function :tag "Other")))
113 (defcustom gnus-score-interactive-default-score 1000
114 "*Scoring commands will raise/lower the score with this number as the default."
118 (defcustom gnus-score-expiry-days 7
119 "*Number of days before unused score file entries are expired.
120 If this variable is nil, no score file entries will be expired."
122 :type '(choice (const :tag "never" nil)
125 (defcustom gnus-update-score-entry-dates t
126 "*In non-nil, update matching score entry dates.
127 If this variable is nil, then score entries that provide matches
128 will be expired along with non-matching score entries."
132 (defcustom gnus-orphan-score nil
133 "*All orphans get this score added. Set in the score file."
137 (defcustom gnus-decay-scores nil
138 "*If non-nil, decay non-permanent scores."
142 (defcustom gnus-decay-score-function 'gnus-decay-score
143 "*Function called to decay a score.
144 It is called with one parameter -- the score to be decayed."
146 :type '(radio (function-item gnus-decay-score)
147 (function :tag "Other")))
149 (defcustom gnus-score-decay-constant 3
150 "*Decay all \"small\" scores with this amount."
154 (defcustom gnus-score-decay-scale .05
155 "*Decay all \"big\" scores with this factor."
159 (defcustom gnus-home-score-file nil
160 "Variable to control where interactive score entries are to go.
164 This file file will be used as the home score file.
167 The result of this function will be used as the home score file.
168 The function will be passed the name of the group as its
172 The elements in this list can be:
174 * `(regexp file-name ...)'
175 If the `regexp' matches the group name, the first `file-name' will
176 will be used as the home score file. (Multiple filenames are
177 allowed so that one may use gnus-score-file-single-match-alist to
181 If the function returns non-nil, the result will be used
182 as the home score file. The function will be passed the
183 name of the group as its parameter.
185 * A string. Use the string as the home score file.
187 The list will be traversed from the beginning towards the end looking
190 :type '(choice string
191 (repeat (choice string
192 (cons regexp (repeat file))
196 (defcustom gnus-home-adapt-file nil
197 "Variable to control where new adaptive score entries are to go.
198 This variable allows the same syntax as `gnus-home-score-file'."
200 :type '(choice string
201 (repeat (choice string
202 (cons regexp (repeat file))
206 (defcustom gnus-default-adaptive-score-alist
207 '((gnus-kill-file-mark)
209 (gnus-read-mark (from 3) (subject 30))
210 (gnus-catchup-mark (subject -10))
211 (gnus-killed-mark (from -1) (subject -20))
212 (gnus-del-mark (from -2) (subject -15)))
213 "Alist of marks and scores."
215 :type '(repeat (cons (symbol :tag "Mark")
216 (repeat (list (choice :tag "Header"
219 (symbol :tag "other"))
220 (integer :tag "Score"))))))
222 (defcustom gnus-ignored-adaptive-words nil
223 "List of words to be ignored when doing adaptive word scoring."
225 :type '(repeat string))
227 (defcustom gnus-default-ignored-adaptive-words
228 '("a" "i" "the" "to" "of" "and" "in" "is" "it" "for" "that" "if" "you"
229 "this" "be" "on" "with" "not" "have" "are" "or" "as" "from" "can"
230 "but" "by" "at" "an" "will" "no" "all" "was" "do" "there" "my" "one"
231 "so" "we" "they" "what" "would" "any" "which" "about" "get" "your"
232 "use" "some" "me" "then" "name" "like" "out" "when" "up" "time"
233 "other" "more" "only" "just" "end" "also" "know" "how" "new" "should"
234 "been" "than" "them" "he" "who" "make" "may" "people" "these" "now"
235 "their" "here" "into" "first" "could" "way" "had" "see" "work" "well"
236 "were" "two" "very" "where" "while" "us" "because" "good" "same"
237 "even" "much" "most" "many" "such" "long" "his" "over" "last" "since"
238 "right" "before" "our" "without" "too" "those" "why" "must" "part"
239 "being" "current" "back" "still" "go" "point" "value" "each" "did"
240 "both" "true" "off" "say" "another" "state" "might" "under" "start"
242 "Default list of words to be ignored when doing adaptive word scoring."
244 :type '(repeat string))
246 (defcustom gnus-default-adaptive-word-score-alist
247 `((,gnus-read-mark . 30)
248 (,gnus-catchup-mark . -10)
249 (,gnus-killed-mark . -20)
250 (,gnus-del-mark . -15))
251 "Alist of marks and scores."
253 :type '(repeat (cons (character :tag "Mark")
254 (integer :tag "Score"))))
256 (defcustom gnus-score-mimic-keymap nil
257 "*Have the score entry functions pretend that they are a keymap."
261 (defcustom gnus-score-exact-adapt-limit 10
262 "*Number that says how long a match has to be before using substring matching.
263 When doing adaptive scoring, one normally uses fuzzy or substring
264 matching. However, if the header one matches is short, the possibility
265 for false positives is great, so if the length of the match is less
266 than this variable, exact matching will be used.
268 If this variable is nil, exact matching will always be used."
270 :type '(choice (const nil) integer))
272 (defcustom gnus-score-uncacheable-files "ADAPT$"
273 "All score files that match this regexp will not be cached."
277 (defcustom gnus-score-default-header nil
278 "Default header when entering new scores.
280 Should be one of the following symbols.
293 If nil, the user will be asked for a header."
295 :type '(choice (const :tag "from" a)
296 (const :tag "subject" s)
297 (const :tag "body" b)
298 (const :tag "head" h)
299 (const :tag "message-id" i)
300 (const :tag "references" t)
301 (const :tag "xref" x)
302 (const :tag "lines" l)
303 (const :tag "date" d)
304 (const :tag "followup" f)))
306 (defcustom gnus-score-default-type nil
307 "Default match type when entering new scores.
309 Should be one of the following symbols.
319 >: greater than number
322 If nil, the user will be asked for a match type."
324 :type '(choice (const :tag "substring" s)
325 (const :tag "exact string" e)
326 (const :tag "fuzzy string" f)
327 (const :tag "regexp string" r)
328 (const :tag "before date" b)
329 (const :tag "at date" a)
330 (const :tag "this date" n)
331 (const :tag "less than number" <)
332 (const :tag "greater than number" >)
333 (const :tag "equal than number" =)))
335 (defcustom gnus-score-default-fold nil
336 "Use case folding for new score file entries iff not nil."
340 (defcustom gnus-score-default-duration nil
341 "Default duration of effect when entering new scores.
343 Should be one of the following symbols.
349 If nil, the user will be asked for a duration."
351 :type '(choice (const :tag "temporary" t)
352 (const :tag "permanent" p)
353 (const :tag "immediate" i)))
355 (defcustom gnus-score-after-write-file-function nil
356 "Function called with the name of the score file just written to disk."
362 ;; Internal variables.
364 (defvar gnus-adaptive-word-syntax-table
365 (let ((table (copy-syntax-table (standard-syntax-table)))
366 (numbers '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)))
368 (modify-syntax-entry (pop numbers) " " table))
369 (modify-syntax-entry ?' "w" table)
371 "Syntax table used when doing adaptive word scoring.")
373 (defvar gnus-scores-exclude-files nil)
374 (defvar gnus-internal-global-score-files nil)
375 (defvar gnus-score-file-list nil)
377 (defvar gnus-short-name-score-file-cache nil)
379 (defvar gnus-score-help-winconf nil)
380 (defvar gnus-adaptive-score-alist gnus-default-adaptive-score-alist)
381 (defvar gnus-adaptive-word-score-alist gnus-default-adaptive-word-score-alist)
382 (defvar gnus-score-trace nil)
383 (defvar gnus-score-edit-buffer nil)
385 (defvar gnus-score-alist nil
386 "Alist containing score information.
387 The keys can be symbols or strings. The following symbols are defined.
389 touched: If this alist has been modified.
390 mark: Automatically mark articles below this.
391 expunge: Automatically expunge articles below this.
392 files: List of other score files to load when loading this one.
393 eval: Sexp to be evaluated when the score file is loaded.
395 String entries have the form (HEADER (MATCH TYPE SCORE DATE) ...)
396 where HEADER is the header being scored, MATCH is the string we are
397 looking for, TYPE is a flag indicating whether it should use regexp or
398 substring matching, SCORE is the score to add and DATE is the date
399 of the last successful match.")
401 (defvar gnus-score-cache nil)
402 (defvar gnus-scores-articles nil)
403 (defvar gnus-score-index nil)
406 (defconst gnus-header-index
407 ;; Name to index alist.
408 '(("number" 0 gnus-score-integer)
409 ("subject" 1 gnus-score-string)
410 ("from" 2 gnus-score-string)
411 ("date" 3 gnus-score-date)
412 ("message-id" 4 gnus-score-string)
413 ("references" 5 gnus-score-string)
414 ("chars" 6 gnus-score-integer)
415 ("lines" 7 gnus-score-integer)
416 ("xref" 8 gnus-score-string)
417 ("head" -1 gnus-score-body)
418 ("body" -1 gnus-score-body)
419 ("all" -1 gnus-score-body)
420 ("followup" 2 gnus-score-followup)
421 ("thread" 5 gnus-score-thread)))
423 ;;; Summary mode score maps.
425 (gnus-define-keys (gnus-summary-score-map "V" gnus-summary-mode-map)
426 "s" gnus-summary-set-score
427 "a" gnus-summary-score-entry
428 "S" gnus-summary-current-score
429 "c" gnus-score-change-score-file
430 "C" gnus-score-customize
431 "m" gnus-score-set-mark-below
432 "x" gnus-score-set-expunge-below
433 "R" gnus-summary-rescore
434 "e" gnus-score-edit-current-scores
435 "f" gnus-score-edit-file
436 "F" gnus-score-flush-cache
437 "t" gnus-score-find-trace
438 "w" gnus-score-find-favourite-words)
440 ;; Summary score file commands
442 ;; Much modification of the kill (ahem, score) code and lots of the
443 ;; functions are written by Per Abrahamsen <amanda@iesd.auc.dk>.
445 (defun gnus-summary-lower-score (&optional score)
446 "Make a score entry based on the current article.
447 The user will be prompted for header to score on, match type,
448 permanence, and the string to be used. The numerical prefix will be
451 (gnus-summary-increase-score (- (gnus-score-default score))))
453 (defun gnus-score-kill-help-buffer ()
454 (when (get-buffer "*Score Help*")
455 (kill-buffer "*Score Help*")
456 (when gnus-score-help-winconf
457 (set-window-configuration gnus-score-help-winconf))))
459 (defun gnus-summary-increase-score (&optional score)
460 "Make a score entry based on the current article.
461 The user will be prompted for header to score on, match type,
462 permanence, and the string to be used. The numerical prefix will be
465 (gnus-set-global-variables)
466 (let* ((nscore (gnus-score-default score))
467 (prefix (if (< nscore 0) ?L ?I))
468 (increase (> nscore 0))
470 '((?a "from" nil nil string)
471 (?s "subject" nil nil string)
472 (?b "body" "" nil body-string)
473 (?h "head" "" nil body-string)
474 (?i "message-id" nil t string)
475 (?t "references" "message-id" nil string)
476 (?x "xref" nil nil string)
477 (?l "lines" nil nil number)
478 (?d "date" nil nil date)
479 (?f "followup" nil nil string)
480 (?T "thread" nil nil string)))
482 '((?s s "substring" string)
483 (?e e "exact string" string)
484 (?f f "fuzzy string" string)
485 (?r r "regexp string" string)
486 (?z s "substring" body-string)
487 (?p r "regexp string" body-string)
488 (?b before "before date" date)
489 (?a at "at date" date)
490 (?n now "this date" date)
491 (?< < "less than number" number)
492 (?> > "greater than number" number)
493 (?= = "equal to number" number)))
495 (list (list ?t (current-time-string) "temporary")
496 '(?p perm "permanent") '(?i now "immediate")))
497 (mimic gnus-score-mimic-keymap)
498 (hchar (and gnus-score-default-header
499 (aref (symbol-name gnus-score-default-header) 0)))
500 (tchar (and gnus-score-default-type
501 (aref (symbol-name gnus-score-default-type) 0)))
502 (pchar (and gnus-score-default-duration
503 (aref (symbol-name gnus-score-default-duration) 0)))
504 entry temporary type match)
509 ;; First we read the header to score.
514 (message "%c-" prefix))
515 (message "%s header (%s?): " (if increase "Increase" "Lower")
516 (mapconcat (lambda (s) (char-to-string (car s)))
518 (setq hchar (read-char))
519 (when (or (= hchar ??) (= hchar ?\C-h))
521 (gnus-score-insert-help "Match on header" char-to-header 1)))
523 (gnus-score-kill-help-buffer)
524 (unless (setq entry (assq (downcase hchar) char-to-header))
525 (if mimic (error "%c %c" prefix hchar) (error "")))
527 (when (/= (downcase hchar) hchar)
528 ;; This was a majuscule, so we end reading and set the defaults.
529 (if mimic (message "%c %c" prefix hchar) (message ""))
530 (setq tchar (or tchar ?s)
531 pchar (or pchar ?t)))
533 ;; We continue reading - the type.
537 (sit-for 1) (message "%c %c-" prefix hchar))
538 (message "%s header '%s' with match type (%s?): "
539 (if increase "Increase" "Lower")
541 (mapconcat (lambda (s)
542 (if (eq (nth 4 entry)
544 (char-to-string (car s))
547 (setq tchar (read-char))
548 (when (or (= tchar ??) (= tchar ?\C-h))
550 (gnus-score-insert-help
554 (if (eq (nth 4 entry)
560 (gnus-score-kill-help-buffer)
561 (unless (setq type (nth 1 (assq (downcase tchar) char-to-type)))
562 (if mimic (error "%c %c" prefix hchar) (error "")))
564 (when (/= (downcase tchar) tchar)
565 ;; It was a majuscule, so we end reading and use the default.
566 (if mimic (message "%c %c %c" prefix hchar tchar)
568 (setq pchar (or pchar ?p)))
570 ;; We continue reading.
574 (sit-for 1) (message "%c %c %c-" prefix hchar tchar))
575 (message "%s permanence (%s?): " (if increase "Increase" "Lower")
576 (mapconcat (lambda (s) (char-to-string (car s)))
578 (setq pchar (read-char))
579 (when (or (= pchar ??) (= pchar ?\C-h))
581 (gnus-score-insert-help "Match permanence" char-to-perm 2)))
583 (gnus-score-kill-help-buffer)
584 (if mimic (message "%c %c %c" prefix hchar tchar pchar)
586 (unless (setq temporary (cadr (assq pchar char-to-perm)))
587 ;; Deal with der(r)ided superannuated paradigms.
588 (when (and (eq (1+ prefix) 77)
589 (eq (+ hchar 12) 109)
591 (eq (- pchar 4) 111))
594 (error "%c %c %c %c" prefix hchar tchar pchar)
596 ;; Always kill the score help buffer.
597 (gnus-score-kill-help-buffer))
599 ;; We have all the data, so we enter this score.
600 (setq match (if (string= (nth 2 entry) "") ""
601 (gnus-summary-header (or (nth 2 entry) (nth 1 entry)))))
603 ;; Modify the match, perhaps.
605 ((equal (nth 1 entry) "xref")
606 (when (string-match "^Xref: *" match)
607 (setq match (substring match (match-end 0))))
608 (when (string-match "^[^:]* +" match)
609 (setq match (substring match (match-end 0))))))
611 (when (memq type '(r R regexp Regexp))
612 (setq match (regexp-quote match)))
614 (gnus-summary-score-entry
615 (nth 1 entry) ; Header
618 (if (eq score 's) nil score) ; Score
619 (if (eq temporary 'perm) ; Temp
622 (not (nth 3 entry))) ; Prompt
625 (defun gnus-score-insert-help (string alist idx)
626 (setq gnus-score-help-winconf (current-window-configuration))
628 (set-buffer (get-buffer-create "*Score Help*"))
629 (buffer-disable-undo (current-buffer))
630 (delete-windows-on (current-buffer))
632 (insert string ":\n\n")
637 ;; find the longest string to display
639 (setq n (length (nth idx (car list))))
642 (setq list (cdr list)))
643 (setq max (+ max 4)) ; %c, `:', SPACE, a SPACE at end
644 (setq n (/ (1- (window-width)) max)) ; items per line
645 (setq width (/ (1- (window-width)) n)) ; width of each item
646 ;; insert `n' items, each in a field of width `width'
651 (delete-char -1) ; the `\n' takes a char
653 (setq pad (- width 3))
654 (setq format (concat "%c: %-" (int-to-string pad) "s"))
655 (insert (format format (caar alist) (nth idx (car alist))))
656 (setq alist (cdr alist))
658 ;; display ourselves in a small window at the bottom
659 (gnus-appt-select-lowest-window)
661 (pop-to-buffer "*Score Help*")
662 (let ((window-min-height 1))
663 (shrink-window-if-larger-than-buffer))
664 (select-window (get-buffer-window gnus-summary-buffer))))
666 (defun gnus-summary-header (header &optional no-err)
667 ;; Return HEADER for current articles, or error.
668 (let ((article (gnus-summary-article-number))
671 (if (and (setq headers (gnus-summary-article-header article))
673 (aref headers (nth 1 (assoc header gnus-header-index)))
676 (error "Pseudo-articles can't be scored")))
678 (error "No article on current line")
681 (defun gnus-newsgroup-score-alist ()
683 (let ((param-file (gnus-group-find-parameter
684 gnus-newsgroup-name 'score-file)))
686 (gnus-score-load param-file)))
688 (gnus-score-file-name gnus-newsgroup-name)))
691 (defsubst gnus-score-get (symbol &optional alist)
692 ;; Get SYMBOL's definition in ALIST.
696 (gnus-newsgroup-score-alist)))))
698 (defun gnus-summary-score-entry (header match type score date
699 &optional prompt silent)
700 "Enter score file entry.
701 HEADER is the header being scored.
702 MATCH is the string we are looking for.
703 TYPE is the match type: substring, regexp, exact, fuzzy.
704 SCORE is the score to add.
705 DATE is the expire date, or nil for no expire, or 'now for immediate expire.
706 If optional argument `PROMPT' is non-nil, allow user to edit match.
707 If optional argument `SILENT' is nil, show effect of score entry."
709 (list (completing-read "Header: "
711 (lambda (x) (fboundp (nth 2 x)))
713 (read-string "Match: ")
714 (if (y-or-n-p "Use regexp match? ") 'r 's)
715 (and current-prefix-arg
716 (prefix-numeric-value current-prefix-arg))
717 (cond ((not (y-or-n-p "Add to score file? "))
719 ((y-or-n-p "Expire kill? ")
720 (current-time-string))
722 ;; Regexp is the default type.
725 ;; Simplify matches...
726 (cond ((or (eq type 'r) (eq type 's) (eq type nil))
727 (setq match (if match (gnus-simplify-subject-re match) "")))
729 (setq match (gnus-simplify-subject-fuzzy match))))
730 (let ((score (gnus-score-default score))
731 (header (format "%s" (downcase header)))
734 (setq match (read-string
735 (format "Match %s on %s, %s: "
736 (cond ((eq date 'now)
742 (if (< score 0) "lower" "raise"))
744 (int-to-string match)
747 ;; Get rid of string props.
748 (setq match (format "%s" match))
750 ;; If this is an integer comparison, we transform from string to int.
751 (when (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-integer)
752 (setq match (string-to-int match)))
754 (unless (eq date 'now)
755 ;; Add the score entry to the score file.
756 (when (= score gnus-score-interactive-default-score)
758 (let ((old (gnus-score-get header))
764 (and date (if (numberp date) date
765 (gnus-day-number date)))
767 (date (list match score (gnus-day-number date)))
768 (score (list match score))
770 ;; We see whether we can collapse some score entries.
771 ;; This isn't quite correct, because there may be more elements
772 ;; later on with the same key that have matching elems... Hm.
774 (setq elem (assoc match old))
775 (eq (nth 3 elem) (nth 3 new))
776 (or (and (numberp (nth 2 elem)) (numberp (nth 2 new)))
777 (and (not (nth 2 elem)) (not (nth 2 new)))))
778 ;; Yup, we just add this new score to the old elem.
779 (setcar (cdr elem) (+ (or (nth 1 elem)
780 gnus-score-interactive-default-score)
782 gnus-score-interactive-default-score)))
783 ;; Nope, we have to add a new elem.
784 (gnus-score-set header (if old (cons new old) (list new))))
785 (gnus-score-set 'touched '(t))))
787 ;; Score the current buffer.
789 (if (and (>= (nth 1 (assoc header gnus-header-index)) 0)
790 (eq (nth 2 (assoc header gnus-header-index))
792 (gnus-summary-score-effect header match type score)
793 (gnus-summary-rescore)))
795 ;; Return the new scoring rule.
798 (defun gnus-summary-score-effect (header match type score)
799 "Simulate the effect of a score file entry.
800 HEADER is the header being scored.