comparison lisp/gnus/gnus-score.el @ 88155:d7ddb3e565de

sync with trunk
author Henrik Enberg <henrik.enberg@telia.com>
date Mon, 16 Jan 2006 00:03:54 +0000
parents 933ab100fb4a
children
comparison
equal deleted inserted replaced
88154:8ce476d3ba36 88155:d7ddb3e565de
1 ;;; gnus-score.el --- scoring code for Gnus 1 ;;; gnus-score.el --- scoring code for Gnus
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001 2
3 ;; Free Software Foundation, Inc. 3 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
4 ;; 2004, 2005 Free Software Foundation, Inc.
4 5
5 ;; Author: Per Abrahamsen <amanda@iesd.auc.dk> 6 ;; Author: Per Abrahamsen <amanda@iesd.auc.dk>
6 ;; Lars Magne Ingebrigtsen <larsi@gnus.org> 7 ;; Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;; Keywords: news 8 ;; Keywords: news
8 9
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details. 20 ;; GNU General Public License for more details.
20 21
21 ;; You should have received a copy of the GNU General Public License 22 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the 23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02111-1307, USA. 25 ;; Boston, MA 02110-1301, USA.
25 26
26 ;;; Commentary: 27 ;;; Commentary:
27 28
28 ;;; Code: 29 ;;; Code:
29 30
30 (eval-when-compile (require 'cl)) 31 (eval-when-compile (require 'cl))
31 32
32 (require 'gnus) 33 (require 'gnus)
33 (require 'gnus-sum) 34 (require 'gnus-sum)
34 (require 'gnus-range) 35 (require 'gnus-range)
36 (require 'gnus-win)
35 (require 'message) 37 (require 'message)
36 (require 'score-mode) 38 (require 'score-mode)
39
40 (autoload 'ffap-string-at-point "ffap")
37 41
38 (defcustom gnus-global-score-files nil 42 (defcustom gnus-global-score-files nil
39 "List of global score files and directories. 43 "List of global score files and directories.
40 Set this variable if you want to use people's score files. One entry 44 Set this variable if you want to use people's score files. One entry
41 for each score file or each score file directory. Gnus will decide 45 for each score file or each score file directory. Gnus will decide
45 \"/ftp.gnus.org@ftp:/pub/larsi/ding/score/soc.motss.SCORE\" and all 49 \"/ftp.gnus.org@ftp:/pub/larsi/ding/score/soc.motss.SCORE\" and all
46 score files in the \"/ftp.some-where:/pub/score\" directory. 50 score files in the \"/ftp.some-where:/pub/score\" directory.
47 51
48 (setq gnus-global-score-files 52 (setq gnus-global-score-files
49 '(\"/ftp.gnus.org:/pub/larsi/ding/score/soc.motss.SCORE\" 53 '(\"/ftp.gnus.org:/pub/larsi/ding/score/soc.motss.SCORE\"
50 \"/ftp.some-where:/pub/score\"))" 54 \"/ftp.some-where:/pub/score\"))"
51 :group 'gnus-score-files 55 :group 'gnus-score-files
52 :type '(repeat file)) 56 :type '(repeat file))
53 57
54 (defcustom gnus-score-file-single-match-alist nil 58 (defcustom gnus-score-file-single-match-alist nil
55 "Alist mapping regexps to lists of score files. 59 "Alist mapping regexps to lists of score files.
136 :group 'gnus-score-expire 140 :group 'gnus-score-expire
137 :type '(choice (const :tag "never" nil) 141 :type '(choice (const :tag "never" nil)
138 number)) 142 number))
139 143
140 (defcustom gnus-update-score-entry-dates t 144 (defcustom gnus-update-score-entry-dates t
141 "*In non-nil, update matching score entry dates. 145 "*If non-nil, update matching score entry dates.
142 If this variable is nil, then score entries that provide matches 146 If this variable is nil, then score entries that provide matches
143 will be expired along with non-matching score entries." 147 will be expired along with non-matching score entries."
144 :group 'gnus-score-expire 148 :group 'gnus-score-expire
145 :type 'boolean) 149 :type 'boolean)
146 150
169 (defcustom gnus-home-score-file nil 173 (defcustom gnus-home-score-file nil
170 "Variable to control where interactive score entries are to go. 174 "Variable to control where interactive score entries are to go.
171 It can be: 175 It can be:
172 176
173 * A string 177 * A string
174 This file file will be used as the home score file. 178 This file will be used as the home score file.
175 179
176 * A function 180 * A function
177 The result of this function will be used as the home score file. 181 The result of this function will be used as the home score file.
178 The function will be passed the name of the group as its 182 The function will be passed the name of the group as its
179 parameter. 183 parameter.
180 184
181 * A list 185 * A list
182 The elements in this list can be: 186 The elements in this list can be:
183 187
184 * `(regexp file-name ...)' 188 * `(regexp file-name ...)'
185 If the `regexp' matches the group name, the first `file-name' will 189 If the `regexp' matches the group name, the first `file-name'
186 will be used as the home score file. (Multiple filenames are 190 will be used as the home score file. (Multiple filenames are
187 allowed so that one may use gnus-score-file-single-match-alist to 191 allowed so that one may use gnus-score-file-single-match-alist to
188 set this variable.) 192 set this variable.)
189 193
190 * A function. 194 * A function.
215 (cons regexp (repeat file)) 219 (cons regexp (repeat file))
216 (function :value fun))) 220 (function :value fun)))
217 (function :value fun))) 221 (function :value fun)))
218 222
219 (defcustom gnus-default-adaptive-score-alist 223 (defcustom gnus-default-adaptive-score-alist
220 '((gnus-kill-file-mark) 224 `((gnus-kill-file-mark)
221 (gnus-unread-mark) 225 (gnus-unread-mark)
222 (gnus-read-mark (from 3) (subject 30)) 226 (gnus-read-mark
223 (gnus-catchup-mark (subject -10)) 227 (from , (+ 2 gnus-score-decay-constant))
224 (gnus-killed-mark (from -1) (subject -20)) 228 (subject , (+ 27 gnus-score-decay-constant)))
225 (gnus-del-mark (from -2) (subject -15))) 229 (gnus-catchup-mark
226 "*Alist of marks and scores." 230 (subject , (+ -7 (* -1 gnus-score-decay-constant))))
231 (gnus-killed-mark
232 (from , (- -1 gnus-score-decay-constant))
233 (subject , (+ -17 (* -1 gnus-score-decay-constant))))
234 (gnus-del-mark
235 (from , (- -1 gnus-score-decay-constant))
236 (subject , (+ -12 (* -1 gnus-score-decay-constant)))))
237 "Alist of marks and scores.
238 If you use score decays, you might want to set values higher than
239 `gnus-score-decay-constant'."
227 :group 'gnus-score-adapt 240 :group 'gnus-score-adapt
228 :type '(repeat (cons (symbol :tag "Mark") 241 :type '(repeat (cons (symbol :tag "Mark")
229 (repeat (list (choice :tag "Header" 242 (repeat (list (choice :tag "Header"
230 (const from) 243 (const from)
231 (const subject) 244 (const subject)
232 (symbol :tag "other")) 245 (symbol :tag "other"))
233 (integer :tag "Score")))))) 246 (integer :tag "Score"))))))
247
248 (defcustom gnus-adaptive-word-length-limit nil
249 "*Words of a length lesser than this limit will be ignored when doing adaptive scoring."
250 :version "22.1"
251 :group 'gnus-score-adapt
252 :type '(radio (const :format "Unlimited " nil)
253 (integer :format "Maximum length: %v")))
234 254
235 (defcustom gnus-ignored-adaptive-words nil 255 (defcustom gnus-ignored-adaptive-words nil
236 "List of words to be ignored when doing adaptive word scoring." 256 "List of words to be ignored when doing adaptive word scoring."
237 :group 'gnus-score-adapt 257 :group 'gnus-score-adapt
238 :type '(repeat string)) 258 :type '(repeat string))
481 501
482 (defun gnus-summary-lower-score (&optional score symp) 502 (defun gnus-summary-lower-score (&optional score symp)
483 "Make a score entry based on the current article. 503 "Make a score entry based on the current article.
484 The user will be prompted for header to score on, match type, 504 The user will be prompted for header to score on, match type,
485 permanence, and the string to be used. The numerical prefix will be 505 permanence, and the string to be used. The numerical prefix will be
486 used as score." 506 used as score. A symbolic prefix of `a' says to use the `all.SCORE'
507 file for the command instead of the current score file."
487 (interactive (gnus-interactive "P\ny")) 508 (interactive (gnus-interactive "P\ny"))
488 (gnus-summary-increase-score (- (gnus-score-delta-default score)) symp)) 509 (gnus-summary-increase-score (- (gnus-score-delta-default score)) symp))
489 510
490 (defun gnus-score-kill-help-buffer () 511 (defun gnus-score-kill-help-buffer ()
491 (when (get-buffer "*Score Help*") 512 (when (get-buffer "*Score Help*")
495 516
496 (defun gnus-summary-increase-score (&optional score symp) 517 (defun gnus-summary-increase-score (&optional score symp)
497 "Make a score entry based on the current article. 518 "Make a score entry based on the current article.
498 The user will be prompted for header to score on, match type, 519 The user will be prompted for header to score on, match type,
499 permanence, and the string to be used. The numerical prefix will be 520 permanence, and the string to be used. The numerical prefix will be
500 used as score." 521 used as score. A symbolic prefix of `a' says to use the `all.SCORE'
522 file for the command instead of the current score file."
501 (interactive (gnus-interactive "P\ny")) 523 (interactive (gnus-interactive "P\ny"))
502 (let* ((nscore (gnus-score-delta-default score)) 524 (let* ((nscore (gnus-score-delta-default score))
503 (prefix (if (< nscore 0) ?L ?I)) 525 (prefix (if (< nscore 0) ?L ?I))
504 (increase (> nscore 0)) 526 (increase (> nscore 0))
505 (char-to-header 527 (char-to-header
614 (when (or (= pchar ??) (= pchar ?\C-h)) 636 (when (or (= pchar ??) (= pchar ?\C-h))
615 (setq pchar nil) 637 (setq pchar nil)
616 (gnus-score-insert-help "Match permanence" char-to-perm 2))) 638 (gnus-score-insert-help "Match permanence" char-to-perm 2)))
617 639
618 (gnus-score-kill-help-buffer) 640 (gnus-score-kill-help-buffer)
619 (if mimic (message "%c %c %c" prefix hchar tchar pchar) 641 (if mimic (message "%c %c %c %c" prefix hchar tchar pchar)
620 (message "")) 642 (message ""))
621 (unless (setq temporary (cadr (assq pchar char-to-perm))) 643 (unless (setq temporary (cadr (assq pchar char-to-perm)))
622 ;; Deal with der(r)ided superannuated paradigms. 644 ;; Deal with der(r)ided superannuated paradigms.
623 (when (and (eq (1+ prefix) 77) 645 (when (and (eq (1+ prefix) 77)
624 (eq (+ hchar 12) 109) 646 (eq (+ hchar 12) 109)
635 ;; we must find out which header is in question. 657 ;; we must find out which header is in question.
636 (setq extra 658 (setq extra
637 (and gnus-extra-headers 659 (and gnus-extra-headers
638 (equal (nth 1 entry) "extra") 660 (equal (nth 1 entry) "extra")
639 (intern ; need symbol 661 (intern ; need symbol
640 (gnus-completing-read 662 (gnus-completing-read-with-default
641 (symbol-name (car gnus-extra-headers)) ; default response 663 (symbol-name (car gnus-extra-headers)) ; default response
642 "Score extra header:" ; prompt 664 "Score extra header" ; prompt
643 (mapcar (lambda (x) ; completion list 665 (mapcar (lambda (x) ; completion list
644 (cons (symbol-name x) x)) 666 (cons (symbol-name x) x))
645 gnus-extra-headers) 667 gnus-extra-headers)
646 nil ; no completion limit 668 nil ; no completion limit
647 t)))) ; require match 669 t)))) ; require match
727 (setq pad (- width 3)) 749 (setq pad (- width 3))
728 (setq format (concat "%c: %-" (int-to-string pad) "s")) 750 (setq format (concat "%c: %-" (int-to-string pad) "s"))
729 (insert (format format (caar alist) (nth idx (car alist)))) 751 (insert (format format (caar alist) (nth idx (car alist))))
730 (setq alist (cdr alist)) 752 (setq alist (cdr alist))
731 (setq i (1+ i)))) 753 (setq i (1+ i))))
754 (goto-char (point-min))
732 ;; display ourselves in a small window at the bottom 755 ;; display ourselves in a small window at the bottom
733 (gnus-appt-select-lowest-window) 756 (gnus-appt-select-lowest-window)
734 (split-window) 757 (if (< (/ (window-height) 2) window-min-height)
735 (pop-to-buffer "*Score Help*") 758 (switch-to-buffer "*Score Help*")
759 (split-window)
760 (pop-to-buffer "*Score Help*"))
736 (let ((window-min-height 1)) 761 (let ((window-min-height 1))
737 (shrink-window-if-larger-than-buffer)) 762 (shrink-window-if-larger-than-buffer))
738 (select-window (get-buffer-window gnus-summary-buffer t)))) 763 (select-window (gnus-get-buffer-window gnus-summary-buffer t))))
739 764
740 (defun gnus-summary-header (header &optional no-err extra) 765 (defun gnus-summary-header (header &optional no-err extra)
741 ;; Return HEADER for current articles, or error. 766 ;; Return HEADER for current articles, or error.
742 (let ((article (gnus-summary-article-number)) 767 (let ((article (gnus-summary-article-number))
743 headers) 768 headers)
809 match)))) 834 match))))
810 835
811 ;; If this is an integer comparison, we transform from string to int. 836 ;; If this is an integer comparison, we transform from string to int.
812 (if (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-integer) 837 (if (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-integer)
813 (if (stringp match) 838 (if (stringp match)
814 (setq match (string-to-int match))) 839 (setq match (string-to-number match)))
815 (set-text-properties 0 (length match) nil match)) 840 (set-text-properties 0 (length match) nil match))
816 841
817 (unless (eq date 'now) 842 (unless (eq date 'now)
818 ;; Add the score entry to the score file. 843 ;; Add the score entry to the score file.
819 (when (= score gnus-score-interactive-default-score) 844 (when (= score gnus-score-interactive-default-score)
861 (gnus-summary-rescore))) 886 (gnus-summary-rescore)))
862 887
863 ;; Return the new scoring rule. 888 ;; Return the new scoring rule.
864 new)) 889 new))
865 890
866 (defun gnus-summary-score-effect (header match type score extra) 891 (defun gnus-summary-score-effect (header match type score &optional extra)
867 "Simulate the effect of a score file entry. 892 "Simulate the effect of a score file entry.
868 HEADER is the header being scored. 893 HEADER is the header being scored.
869 MATCH is the string we are looking for. 894 MATCH is the string we are looking for.
870 TYPE is the score type. 895 TYPE is the score type.
871 SCORE is the score to add. 896 SCORE is the score to add.
873 (interactive (list (completing-read "Header: " 898 (interactive (list (completing-read "Header: "
874 gnus-header-index 899 gnus-header-index
875 (lambda (x) (fboundp (nth 2 x))) 900 (lambda (x) (fboundp (nth 2 x)))
876 t) 901 t)
877 (read-string "Match: ") 902 (read-string "Match: ")
878 (y-or-n-p "Use regexp match? ") 903 (if (y-or-n-p "Use regexp match? ") 'r 's)
879 (prefix-numeric-value current-prefix-arg))) 904 (string-to-number (read-string "Score: "))))
880 (save-excursion 905 (save-excursion
881 (unless (and (stringp match) (> (length match) 0)) 906 (unless (and (stringp match) (> (length match) 0))
882 (error "No match")) 907 (error "No match"))
883 (goto-char (point-min)) 908 (goto-char (point-min))
884 (let ((regexp (cond ((eq type 'f) 909 (let ((regexp (cond ((eq type 'f)
924 ;;; Gnus Score Files 949 ;;; Gnus Score Files
925 ;;; 950 ;;;
926 951
927 ;; All score code written by Per Abrahamsen <abraham@iesd.auc.dk>. 952 ;; All score code written by Per Abrahamsen <abraham@iesd.auc.dk>.
928 953
929 ;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
930 (defun gnus-score-set-mark-below (score) 954 (defun gnus-score-set-mark-below (score)
931 "Automatically mark articles with score below SCORE as read." 955 "Automatically mark articles with score below SCORE as read."
932 (interactive 956 (interactive
933 (list (or (and current-prefix-arg (prefix-numeric-value current-prefix-arg)) 957 (list (or (and current-prefix-arg (prefix-numeric-value current-prefix-arg))
934 (string-to-int (read-string "Mark below: "))))) 958 (string-to-number (read-string "Mark below: ")))))
935 (setq score (or score gnus-summary-default-score 0)) 959 (setq score (or score gnus-summary-default-score 0))
936 (gnus-score-set 'mark (list score)) 960 (gnus-score-set 'mark (list score))
937 (gnus-score-set 'touched '(t)) 961 (gnus-score-set 'touched '(t))
938 (setq gnus-summary-mark-below score) 962 (setq gnus-summary-mark-below score)
939 (gnus-score-update-lines)) 963 (gnus-score-update-lines))
963 987
964 (defun gnus-score-set-expunge-below (score) 988 (defun gnus-score-set-expunge-below (score)
965 "Automatically expunge articles with score below SCORE." 989 "Automatically expunge articles with score below SCORE."
966 (interactive 990 (interactive
967 (list (or (and current-prefix-arg (prefix-numeric-value current-prefix-arg)) 991 (list (or (and current-prefix-arg (prefix-numeric-value current-prefix-arg))
968 (string-to-int (read-string "Set expunge below: "))))) 992 (string-to-number (read-string "Set expunge below: ")))))
969 (setq score (or score gnus-summary-default-score 0)) 993 (setq score (or score gnus-summary-default-score 0))
970 (gnus-score-set 'expunge (list score)) 994 (gnus-score-set 'expunge (list score))
971 (gnus-score-set 'touched '(t))) 995 (gnus-score-set 'touched '(t)))
972 996
973 (defun gnus-score-followup-article (&optional score) 997 (defun gnus-score-followup-article (&optional score)
1090 (make-local-variable 'gnus-prev-winconf) 1114 (make-local-variable 'gnus-prev-winconf)
1091 (setq gnus-prev-winconf winconf)) 1115 (setq gnus-prev-winconf winconf))
1092 (gnus-message 1116 (gnus-message
1093 4 (substitute-command-keys 1117 4 (substitute-command-keys
1094 "\\<gnus-score-mode-map>\\[gnus-score-edit-exit] to save edits"))) 1118 "\\<gnus-score-mode-map>\\[gnus-score-edit-exit] to save edits")))
1119
1120 (defun gnus-score-edit-file-at-point (&optional format)
1121 "Edit score file at point in Score Trace buffers.
1122 If FORMAT, also format the current score file."
1123 (let* ((rule (save-excursion
1124 (beginning-of-line)
1125 (read (current-buffer))))
1126 (sep "[ \n\r\t]*")
1127 ;; Must be synced with `gnus-score-find-trace':
1128 (reg " -> +")
1129 (file (save-excursion
1130 (end-of-line)
1131 (if (and (re-search-backward reg (gnus-point-at-bol) t)
1132 (re-search-forward reg (gnus-point-at-eol) t))
1133 (buffer-substring (point) (gnus-point-at-eol))
1134 nil))))
1135 (if (or (not file)
1136 (string-match "\\<\\(non-file rule\\|A file\\)\\>" file)
1137 ;; (see `gnus-score-find-trace' and `gnus-score-advanced')
1138 (string= "" file))
1139 (gnus-error 3 "Can't find a score file in current line.")
1140 (gnus-score-edit-file file)
1141 (when format
1142 (gnus-score-pretty-print))
1143 (when (consp rule) ;; the rule exists
1144 (setq rule (mapconcat #'(lambda (obj)
1145 (regexp-quote (format "%S" obj)))
1146 rule
1147 sep))
1148 (goto-char (point-min))
1149 (re-search-forward rule nil t)
1150 ;; make it easy to use `kill-sexp':
1151 (goto-char (1- (match-beginning 0)))))))
1095 1152
1096 (defun gnus-score-load-file (file) 1153 (defun gnus-score-load-file (file)
1097 ;; Load score file FILE. Returns a list a retrieved score-alists. 1154 ;; Load score file FILE. Returns a list a retrieved score-alists.
1098 (let* ((file (expand-file-name 1155 (let* ((file (expand-file-name
1099 (or (and (string-match 1156 (or (and (string-match
1141 (let ((mark (car (gnus-score-get 'mark alist))) 1198 (let ((mark (car (gnus-score-get 'mark alist)))
1142 (expunge (car (gnus-score-get 'expunge alist))) 1199 (expunge (car (gnus-score-get 'expunge alist)))
1143 (mark-and-expunge (car (gnus-score-get 'mark-and-expunge alist))) 1200 (mark-and-expunge (car (gnus-score-get 'mark-and-expunge alist)))
1144 (files (gnus-score-get 'files alist)) 1201 (files (gnus-score-get 'files alist))
1145 (exclude-files (gnus-score-get 'exclude-files alist)) 1202 (exclude-files (gnus-score-get 'exclude-files alist))
1146 (orphan (car (gnus-score-get 'orphan alist))) 1203 (orphan (car (gnus-score-get 'orphan alist)))
1147 (adapt (gnus-score-get 'adapt alist)) 1204 (adapt (gnus-score-get 'adapt alist))
1148 (thread-mark-and-expunge 1205 (thread-mark-and-expunge
1149 (car (gnus-score-get 'thread-mark-and-expunge alist))) 1206 (car (gnus-score-get 'thread-mark-and-expunge alist)))
1150 (adapt-file (car (gnus-score-get 'adapt-file alist))) 1207 (adapt-file (car (gnus-score-get 'adapt-file alist)))
1151 (local (gnus-score-get 'local alist)) 1208 (local (gnus-score-get 'local alist))
1200 (setq gnus-newsgroup-adaptive nil)) 1257 (setq gnus-newsgroup-adaptive nil))
1201 ((consp adapt) 1258 ((consp adapt)
1202 (setq gnus-newsgroup-adaptive t) 1259 (setq gnus-newsgroup-adaptive t)
1203 adapt) 1260 adapt)
1204 (t 1261 (t
1205 ;;(setq gnus-newsgroup-adaptive gnus-use-adaptive-scoring)
1206 gnus-default-adaptive-score-alist))) 1262 gnus-default-adaptive-score-alist)))
1207 (setq gnus-thread-expunge-below 1263 (setq gnus-thread-expunge-below
1208 (or thread-mark-and-expunge gnus-thread-expunge-below)) 1264 (or thread-mark-and-expunge gnus-thread-expunge-below))
1209 (setq gnus-summary-mark-below 1265 (setq gnus-summary-mark-below
1210 (or mark mark-and-expunge gnus-summary-mark-below)) 1266 (or mark mark-and-expunge gnus-summary-mark-below))
1364 ;; are not meant to be edited by human hands. 1420 ;; are not meant to be edited by human hands.
1365 (gnus-prin1 score) 1421 (gnus-prin1 score)
1366 ;; This is a normal score file, so we print it very 1422 ;; This is a normal score file, so we print it very
1367 ;; prettily. 1423 ;; prettily.
1368 (let ((lisp-mode-syntax-table score-mode-syntax-table)) 1424 (let ((lisp-mode-syntax-table score-mode-syntax-table))
1369 (pp score (current-buffer))))) 1425 (gnus-pp score))))
1370 (gnus-make-directory (file-name-directory file)) 1426 (gnus-make-directory (file-name-directory file))
1371 ;; If the score file is empty, we delete it. 1427 ;; If the score file is empty, we delete it.
1372 (if (zerop (buffer-size)) 1428 (if (zerop (buffer-size))
1373 (delete-file file) 1429 (delete-file file)
1374 ;; There are scores, so we write the file. 1430 ;; There are scores, so we write the file.
1426 (expire (and gnus-score-expiry-days 1482 (expire (and gnus-score-expiry-days
1427 (- now gnus-score-expiry-days))) 1483 (- now gnus-score-expiry-days)))
1428 (headers gnus-newsgroup-headers) 1484 (headers gnus-newsgroup-headers)
1429 (current-score-file gnus-current-score-file) 1485 (current-score-file gnus-current-score-file)
1430 entry header new) 1486 entry header new)
1431 (gnus-message 5 "Scoring...") 1487 (gnus-message 7 "Scoring...")
1432 ;; Create articles, an alist of the form `(HEADER . SCORE)'. 1488 ;; Create articles, an alist of the form `(HEADER . SCORE)'.
1433 (while (setq header (pop headers)) 1489 (while (setq header (pop headers))
1434 ;; WARNING: The assq makes the function O(N*S) while it could 1490 ;; WARNING: The assq makes the function O(N*S) while it could
1435 ;; be written as O(N+S), where N is (length gnus-newsgroup-headers) 1491 ;; be written as O(N+S), where N is (length gnus-newsgroup-headers)
1436 ;; and S is (length gnus-newsgroup-scored). 1492 ;; and S is (length gnus-newsgroup-scored).
1468 (when (gnus-buffer-live-p gnus-summary-buffer) 1524 (when (gnus-buffer-live-p gnus-summary-buffer)
1469 (let ((scored gnus-newsgroup-scored)) 1525 (let ((scored gnus-newsgroup-scored))
1470 (with-current-buffer gnus-summary-buffer 1526 (with-current-buffer gnus-summary-buffer
1471 (setq gnus-newsgroup-scored scored)))) 1527 (setq gnus-newsgroup-scored scored))))
1472 ;; Remove the buffer. 1528 ;; Remove the buffer.
1473 (kill-buffer (current-buffer))) 1529 (gnus-kill-buffer (current-buffer)))
1474 1530
1475 ;; Add articles to `gnus-newsgroup-scored'. 1531 ;; Add articles to `gnus-newsgroup-scored'.
1476 (while gnus-scores-articles 1532 (while gnus-scores-articles
1477 (when (or (/= gnus-summary-default-score 1533 (when (or (/= gnus-summary-default-score
1478 (cdar gnus-scores-articles)) 1534 (cdar gnus-scores-articles))
1487 (while score 1543 (while score
1488 (when (consp (caar score)) 1544 (when (consp (caar score))
1489 (gnus-score-advanced (car score) trace)) 1545 (gnus-score-advanced (car score) trace))
1490 (pop score)))) 1546 (pop score))))
1491 1547
1492 (gnus-message 5 "Scoring...done")))))) 1548 (gnus-message 7 "Scoring...done"))))))
1493 1549
1494 (defun gnus-score-lower-thread (thread score-adjust) 1550 (defun gnus-score-lower-thread (thread score-adjust)
1495 "Lower the score on THREAD with SCORE-ADJUST. 1551 "Lower the score on THREAD with SCORE-ADJUST.
1496 THREAD is expected to contain a list of the form `(PARENT [CHILD1 1552 THREAD is expected to contain a list of the form `(PARENT [CHILD1
1497 CHILD2 ...])' where PARENT is a header array and each CHILD is a list 1553 CHILD2 ...])' where PARENT is a header array and each CHILD is a list
1498 of the same form as THREAD. The empty list `nil' is valid. For each 1554 of the same form as THREAD. The empty list nil is valid. For each
1499 article in the tree, the score of the corresponding entry in 1555 article in the tree, the score of the corresponding entry in
1500 `gnus-newsgroup-scored' is adjusted by SCORE-ADJUST." 1556 `gnus-newsgroup-scored' is adjusted by SCORE-ADJUST."
1501 (while thread 1557 (while thread
1502 (let ((head (car thread))) 1558 (let ((head (car thread)))
1503 (if (listp head) 1559 (if (listp head)
1514 "Score orphans. 1570 "Score orphans.
1515 A root is an article with no references. An orphan is an article 1571 A root is an article with no references. An orphan is an article
1516 which has references, but is not connected via its references to a 1572 which has references, but is not connected via its references to a
1517 root article. This function finds all the orphans, and adjusts their 1573 root article. This function finds all the orphans, and adjusts their
1518 score in `gnus-newsgroup-scored' by SCORE." 1574 score in `gnus-newsgroup-scored' by SCORE."
1519 (let ((threads (gnus-make-threads))) 1575 ;; gnus-make-threads produces a list, where each entry is a "thread"
1520 ;; gnus-make-threads produces a list, where each entry is a "thread" 1576 ;; as described in the gnus-score-lower-thread docs. This function
1521 ;; as described in the gnus-score-lower-thread docs. This function 1577 ;; will be called again (after limiting has been done) if the display
1522 ;; will be called again (after limiting has been done) if the display 1578 ;; is threaded. It would be nice to somehow save this info and use
1523 ;; is threaded. It would be nice to somehow save this info and use 1579 ;; it later.
1524 ;; it later. 1580 (dolist (thread (gnus-make-threads))
1525 (while threads 1581 (let ((id (aref (car thread) gnus-score-index)))
1526 (let* ((thread (car threads)) 1582 ;; If the parent of the thread is not a root, lower the score of
1527 (id (aref (car thread) gnus-score-index))) 1583 ;; it and its descendants. Note that some roots seem to satisfy
1528 ;; If the parent of the thread is not a root, lower the score of 1584 ;; (eq id nil) and some (eq id ""); not sure why.
1529 ;; it and its descendants. Note that some roots seem to satisfy 1585 (when (and id
1530 ;; (eq id nil) and some (eq id ""); not sure why. 1586 (not (string= id "")))
1531 (if (and id (not (string= id ""))) 1587 (gnus-score-lower-thread thread score)))))
1532 (gnus-score-lower-thread thread score)))
1533 (setq threads (cdr threads)))))
1534 1588
1535 (defun gnus-score-integer (scores header now expire &optional trace) 1589 (defun gnus-score-integer (scores header now expire &optional trace)
1536 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index))) 1590 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
1537 entries alist) 1591 entries alist)
1538 ;; Find matches. 1592 ;; Find matches.
1716 ;; Found a match, update scores. 1770 ;; Found a match, update scores.
1717 (setcdr (car articles) (+ score (cdar articles))) 1771 (setcdr (car articles) (+ score (cdar articles)))
1718 (setq found t) 1772 (setq found t)
1719 (when trace 1773 (when trace
1720 (push 1774 (push
1721 (cons (car-safe (rassq alist gnus-score-cache)) kill) 1775 (cons (car-safe (rassq alist gnus-score-cache))
1776 kill)
1722 gnus-score-trace))) 1777 gnus-score-trace)))
1723 ;; Update expire date 1778 ;; Update expire date
1724 (unless trace 1779 (unless trace
1725 (cond 1780 (cond
1726 ((null date)) ;Permanent entry. 1781 ((null date)) ;Permanent entry.
1774 (when last 1829 (when last
1775 (insert last ?\n) 1830 (insert last ?\n)
1776 (put-text-property (1- (point)) (point) 'articles alike)) 1831 (put-text-property (1- (point)) (point) 'articles alike))
1777 (setq alike (list art) 1832 (setq alike (list art)
1778 last this))) 1833 last this)))
1779 (when last ; Bwadr, duplicate code. 1834 (when last ; Bwadr, duplicate code.
1780 (insert last ?\n) 1835 (insert last ?\n)
1781 (put-text-property (1- (point)) (point) 'articles alike)) 1836 (put-text-property (1- (point)) (point) 'articles alike))
1782 1837
1783 ;; Find matches. 1838 ;; Find matches.
1784 (while scores 1839 (while scores
1785 (setq alist (car scores) 1840 (setq alist (car scores)
1786 scores (cdr scores) 1841 scores (cdr scores)
1787 entries (assoc header alist)) 1842 entries (assoc header alist))
1788 (while (cdr entries) ;First entry is the header index. 1843 (while (cdr entries) ;First entry is the header index.
1789 (let* ((rest (cdr entries)) 1844 (let* ((rest (cdr entries))
1790 (kill (car rest)) 1845 (kill (car rest))
1791 (match (nth 0 kill)) 1846 (match (nth 0 kill))
1792 (type (or (nth 3 kill) 's)) 1847 (type (or (nth 3 kill) 's))
1793 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 1848 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1803 (t (error "Invalid match type: %s" type)))) 1858 (t (error "Invalid match type: %s" type))))
1804 arts art) 1859 arts art)
1805 (goto-char (point-min)) 1860 (goto-char (point-min))
1806 (if (= dmt ?e) 1861 (if (= dmt ?e)
1807 (while (funcall search-func match nil t) 1862 (while (funcall search-func match nil t)
1808 (and (= (progn (beginning-of-line) (point)) 1863 (and (= (gnus-point-at-bol)
1809 (match-beginning 0)) 1864 (match-beginning 0))
1810 (= (progn (end-of-line) (point)) 1865 (= (progn (end-of-line) (point))
1811 (match-end 0)) 1866 (match-end 0))
1812 (progn 1867 (progn
1813 (setq found (setq arts (get-text-property 1868 (setq found (setq arts (get-text-property
1822 (while (funcall search-func match nil t) 1877 (while (funcall search-func match nil t)
1823 (end-of-line) 1878 (end-of-line)
1824 (setq found (setq arts (get-text-property (point) 'articles))) 1879 (setq found (setq arts (get-text-property (point) 'articles)))
1825 ;; Found a match, update scores. 1880 ;; Found a match, update scores.
1826 (while (setq art (pop arts)) 1881 (while (setq art (pop arts))
1882 (setcdr art (+ score (cdr art)))
1883 (when trace
1884 (push (cons
1885 (car-safe (rassq alist gnus-score-cache))
1886 kill)
1887 gnus-score-trace))
1827 (when (setq new (gnus-score-add-followups 1888 (when (setq new (gnus-score-add-followups
1828 (car art) score all-scores thread)) 1889 (car art) score all-scores thread))
1829 (push new news))))) 1890 (push new news)))))
1830 ;; Update expire date 1891 ;; Update expire date
1831 (cond ((null date)) ;Permanent entry. 1892 (cond ((null date)) ;Permanent entry.
1869 ;; than EXPIRE. 1930 ;; than EXPIRE.
1870 1931
1871 ;; Insert the unique article headers in the buffer. 1932 ;; Insert the unique article headers in the buffer.
1872 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index))) 1933 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
1873 ;; gnus-score-index is used as a free variable. 1934 ;; gnus-score-index is used as a free variable.
1874 (simplify (and gnus-score-thread-simplify 1935 (simplify (and gnus-score-thread-simplify
1875 (string= "subject" header))) 1936 (string= "subject" header)))
1876 alike last this art entries alist articles 1937 alike last this art entries alist articles
1877 fuzzies arts words kill) 1938 fuzzies arts words kill)
1878 1939
1879 ;; Sorting the articles costs os O(N*log N) but will allow us to 1940 ;; Sorting the articles costs os O(N*log N) but will allow us to
1880 ;; only match with each unique header. Thus the actual matching 1941 ;; only match with each unique header. Thus the actual matching
1895 1956
1896 ;; If we're working with non-standard headers, we are stuck 1957 ;; If we're working with non-standard headers, we are stuck
1897 ;; with working on them as a group. What a hassle. 1958 ;; with working on them as a group. What a hassle.
1898 ;; Just wait 'til you see what horrors we commit against `match'... 1959 ;; Just wait 'til you see what horrors we commit against `match'...
1899 (if (= gnus-score-index 9) 1960 (if (= gnus-score-index 9)
1900 (setq this (prin1-to-string this))) ; ick. 1961 (setq this (gnus-prin1-to-string this))) ; ick.
1901 1962
1902 (if simplify 1963 (if simplify
1903 (setq this (gnus-map-function gnus-simplify-subject-functions this))) 1964 (setq this (gnus-map-function gnus-simplify-subject-functions this)))
1904 (if (equal last this) 1965 (if (equal last this)
1905 ;; O(N*H) cons-cells used here, where H is the number of 1966 ;; O(N*H) cons-cells used here, where H is the number of
1934 (mt (aref (symbol-name type) 0)) 1995 (mt (aref (symbol-name type) 0))
1935 (case-fold-search (not (memq mt '(?R ?S ?E ?F)))) 1996 (case-fold-search (not (memq mt '(?R ?S ?E ?F))))
1936 (dmt (downcase mt)) 1997 (dmt (downcase mt))
1937 ;; Assume user already simplified regexp and fuzzies 1998 ;; Assume user already simplified regexp and fuzzies
1938 (match (if (and simplify (not (memq dmt '(?f ?r)))) 1999 (match (if (and simplify (not (memq dmt '(?f ?r))))
1939 (gnus-map-function 2000 (gnus-map-function
1940 gnus-simplify-subject-functions 2001 gnus-simplify-subject-functions
1941 (nth 0 kill)) 2002 (nth 0 kill))
1942 (nth 0 kill))) 2003 (nth 0 kill)))
1943 (search-func 2004 (search-func
1944 (cond ((= dmt ?r) 're-search-forward) 2005 (cond ((= dmt ?r) 're-search-forward)
1945 ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward) 2006 ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward)
1946 ((= dmt ?w) nil) 2007 ((= dmt ?w) nil)
1947 (t (error "Invalid match type: %s" type))))) 2008 (t (error "Invalid match type: %s" type)))))
1948 2009
1949 ;; Evil hackery to make match usable in non-standard headers. 2010 ;; Evil hackery to make match usable in non-standard headers.
1950 (when extra 2011 (when extra
1951 (setq match (concat "[ (](" extra " \\. \"[^)]*" 2012 (setq match (concat "[ (](" extra " \\. \"[^)]*"
1952 match "[^(]*\")[ )]") 2013 match "[^\"]*\")[ )]")
1953 search-func 're-search-forward)) ; XXX danger?!? 2014 search-func 're-search-forward)) ; XXX danger?!?
1954 2015
1955 (cond 2016 (cond
1956 ;; Fuzzy matches. We save these for later. 2017 ;; Fuzzy matches. We save these for later.
1957 ((= dmt ?f) 2018 ((= dmt ?f)
2273 (goto-char (point-min)) 2334 (goto-char (point-min))
2274 (while (re-search-forward "\\b\\w+\\b" nil t) 2335 (while (re-search-forward "\\b\\w+\\b" nil t)
2275 ;; Put the word and score into the hashtb. 2336 ;; Put the word and score into the hashtb.
2276 (setq val (gnus-gethash (setq word (match-string 0)) 2337 (setq val (gnus-gethash (setq word (match-string 0))
2277 hashtb)) 2338 hashtb))
2278 (setq val (+ score (or val 0))) 2339 (when (or (not gnus-adaptive-word-length-limit)
2279 (if (and gnus-adaptive-word-minimum 2340 (> (length word)
2280 (< val gnus-adaptive-word-minimum)) 2341 gnus-adaptive-word-length-limit))
2281 (setq val gnus-adaptive-word-minimum)) 2342 (setq val (+ score (or val 0)))
2282 (gnus-sethash word val hashtb)) 2343 (if (and gnus-adaptive-word-minimum
2344 (< val gnus-adaptive-word-minimum))
2345 (setq val gnus-adaptive-word-minimum))
2346 (gnus-sethash word val hashtb)))
2283 (erase-buffer)))) 2347 (erase-buffer))))
2284 (set-syntax-table syntab)) 2348 (set-syntax-table syntab))
2285 ;; Make all the ignorable words ignored. 2349 ;; Make all the ignorable words ignored.
2286 (let ((ignored (append gnus-ignored-adaptive-words 2350 (let ((ignored (append gnus-ignored-adaptive-words
2287 (if gnus-adaptive-word-no-group-words 2351 (if gnus-adaptive-word-no-group-words
2316 (interactive) 2380 (interactive)
2317 (let ((old-scored gnus-newsgroup-scored)) 2381 (let ((old-scored gnus-newsgroup-scored))
2318 (let ((gnus-newsgroup-headers 2382 (let ((gnus-newsgroup-headers
2319 (list (gnus-summary-article-header))) 2383 (list (gnus-summary-article-header)))
2320 (gnus-newsgroup-scored nil) 2384 (gnus-newsgroup-scored nil)
2321 trace) 2385 ;; Must be synced with `gnus-score-edit-file-at-point':
2386 (frmt "%S [%s] -> %s\n")
2387 trace
2388 file)
2322 (save-excursion 2389 (save-excursion
2323 (nnheader-set-temp-buffer "*Score Trace*")) 2390 (nnheader-set-temp-buffer "*Score Trace*"))
2324 (setq gnus-score-trace nil) 2391 (setq gnus-score-trace nil)
2325 (gnus-possibly-score-headers 'trace) 2392 (gnus-possibly-score-headers 'trace)
2326 (if (not (setq trace gnus-score-trace)) 2393 (if (not (setq trace gnus-score-trace))
2327 (gnus-error 2394 (gnus-error
2328 1 "No score rules apply to the current article (default score %d)." 2395 1 "No score rules apply to the current article (default score %d)."
2329 gnus-summary-default-score) 2396 gnus-summary-default-score)
2330 (set-buffer "*Score Trace*") 2397 (set-buffer "*Score Trace*")
2398 ;; Use a keymap instead?
2399 (local-set-key "q"
2400 (lambda ()
2401 (interactive)
2402 (bury-buffer nil)
2403 (gnus-summary-expand-window)))
2404 (local-set-key "e" (lambda ()
2405 "Run `gnus-score-edit-file-at-point'."
2406 (interactive)
2407 (gnus-score-edit-file-at-point)))
2408 (local-set-key "f" (lambda ()
2409 "Run `gnus-score-edit-file-at-point'."
2410 (interactive)
2411 (gnus-score-edit-file-at-point 'format)))
2412 (local-set-key "t" 'toggle-truncate-lines)
2331 (setq truncate-lines t) 2413 (setq truncate-lines t)
2332 (while trace 2414 (dolist (entry trace)
2333 (insert (format "%S -> %s\n" (cdar trace) 2415 (setq file (or (car entry)
2334 (or (caar trace) "(non-file rule)"))) 2416 ;; Must be synced with
2335 (setq trace (cdr trace))) 2417 ;; `gnus-score-edit-file-at-point':
2418 "(non-file rule)"))
2419 (insert
2420 (format frmt
2421 (cdr entry)
2422 ;; Don't use `file-name-sans-extension' to see .SCORE and
2423 ;; .ADAPT directly:
2424 (file-name-nondirectory file)
2425 (abbreviate-file-name file))))
2426 (insert
2427 "\n\nQuick help:
2428
2429 Type `e' to edit score file corresponding to the score rule on current line,
2430 `f' to format (pretty print) the score file and edit it,
2431 `t' toggle to truncate long lines in this buffer,
2432 `q' to quit.
2433
2434 The first sexp on each line is the score rule, followed by the file name of
2435 the score file and its full name, including the directory.")
2336 (goto-char (point-min)) 2436 (goto-char (point-min))
2337 (gnus-configure-windows 'score-trace))) 2437 (gnus-configure-windows 'score-trace)))
2338 (set-buffer gnus-summary-buffer) 2438 (set-buffer gnus-summary-buffer)
2339 (setq gnus-newsgroup-scored old-scored))) 2439 (setq gnus-newsgroup-scored old-scored)))
2340 2440
2458 (gnus-summary-raise-same-subject (- score))) 2558 (gnus-summary-raise-same-subject (- score)))
2459 2559
2460 (defun gnus-summary-lower-thread (&optional score) 2560 (defun gnus-summary-lower-thread (&optional score)
2461 "Lower score of articles in the current thread with SCORE." 2561 "Lower score of articles in the current thread with SCORE."
2462 (interactive "P") 2562 (interactive "P")
2463 (gnus-summary-raise-thread (- (1- (gnus-score-delta-default score))))) 2563 (gnus-summary-raise-thread (- (gnus-score-delta-default score))))
2464 2564
2465 ;;; Finding score files. 2565 ;;; Finding score files.
2466 2566
2467 (defun gnus-score-score-files (group) 2567 (defun gnus-score-score-files (group)
2468 "Return a list of all possible score files." 2568 "Return a list of all possible score files."
2520 ;; Add files to the list of score files. 2620 ;; Add files to the list of score files.
2521 ((string-match regexp file) 2621 ((string-match regexp file)
2522 (push file out)))) 2622 (push file out))))
2523 (or out 2623 (or out
2524 ;; Return a dummy value. 2624 ;; Return a dummy value.
2525 (list "~/News/this.file.does.not.exist.SCORE")))) 2625 (list (expand-file-name "this.file.does.not.exist.SCORE"
2626 gnus-kill-files-directory)))))
2526 2627
2527 (defun gnus-score-file-regexp () 2628 (defun gnus-score-file-regexp ()
2528 "Return a regexp that match all score files." 2629 "Return a regexp that match all score files."
2529 (concat "\\(" (regexp-quote gnus-score-file-suffix ) 2630 (concat "\\(" (regexp-quote gnus-score-file-suffix )
2530 "\\|" (regexp-quote gnus-adaptive-file-suffix) "\\)\\'")) 2631 "\\|" (regexp-quote gnus-adaptive-file-suffix) "\\)\\'"))
2601 (not (string-match regexp group-trans)))) 2702 (not (string-match regexp group-trans))))
2602 (and (not not-match) 2703 (and (not not-match)
2603 (ignore-errors (string-match regexp group-trans)))) 2704 (ignore-errors (string-match regexp group-trans))))
2604 (push (car sfiles) ofiles))) 2705 (push (car sfiles) ofiles)))
2605 (setq sfiles (cdr sfiles))) 2706 (setq sfiles (cdr sfiles)))
2606 (kill-buffer (current-buffer)) 2707 (gnus-kill-buffer (current-buffer))
2607 ;; Slight kludge here - the last score file returned should be 2708 ;; Slight kludge here - the last score file returned should be
2608 ;; the local score file, whether it exists or not. This is so 2709 ;; the local score file, whether it exists or not. This is so
2609 ;; that any score commands the user enters will go to the right 2710 ;; that any score commands the user enters will go to the right
2610 ;; file, and not end up in some global score file. 2711 ;; file, and not end up in some global score file.
2611 (let ((localscore (gnus-score-file-name group))) 2712 (let ((localscore (gnus-score-file-name group)))
2733 (push param-file score-files) 2834 (push param-file score-files)
2734 (setq gnus-newsgroup-adaptive-score-file param-file)))) 2835 (setq gnus-newsgroup-adaptive-score-file param-file))))
2735 ;; Go through all the functions for finding score files (or actual 2836 ;; Go through all the functions for finding score files (or actual
2736 ;; scores) and add them to a list. 2837 ;; scores) and add them to a list.
2737 (while funcs 2838 (while funcs
2738 (when (gnus-functionp (car funcs)) 2839 (when (functionp (car funcs))
2739 (setq score-files 2840 (setq score-files
2740 (nconc score-files (nreverse (funcall (car funcs) group))))) 2841 (append score-files
2842 (nreverse (funcall (car funcs) group)))))
2741 (setq funcs (cdr funcs))) 2843 (setq funcs (cdr funcs)))
2742 (when gnus-score-use-all-scores 2844 (when gnus-score-use-all-scores
2743 ;; Add any home score files. 2845 ;; Add any home score files.
2744 (let ((home (gnus-home-score-file group))) 2846 (let ((home (gnus-home-score-file group)))
2745 (when home 2847 (when home
2800 ;; available global score files. 2902 ;; available global score files.
2801 (interactive (list gnus-global-score-files)) 2903 (interactive (list gnus-global-score-files))
2802 (let (out) 2904 (let (out)
2803 (while files 2905 (while files
2804 ;; #### /$ Unix-specific? 2906 ;; #### /$ Unix-specific?
2805 (if (string-match "/$" (car files)) 2907 (if (file-directory-p (car files))
2806 (setq out (nconc (directory-files 2908 (setq out (nconc (directory-files
2807 (car files) t 2909 (car files) t
2808 (concat (gnus-score-file-regexp) "$")))) 2910 (concat (gnus-score-file-regexp) "$"))))
2809 (push (car files) out)) 2911 (push (car files) out))
2810 (setq files (cdr files))) 2912 (setq files (cdr files)))
2835 (cond 2937 (cond
2836 ;; Simple string. 2938 ;; Simple string.
2837 ((stringp elem) 2939 ((stringp elem)
2838 elem) 2940 elem)
2839 ;; Function. 2941 ;; Function.
2840 ((gnus-functionp elem) 2942 ((functionp elem)
2841 (funcall elem group)) 2943 (funcall elem group))
2842 ;; Regexp-file cons. 2944 ;; Regexp-file cons.
2843 ((consp elem) 2945 ((consp elem)
2844 (when (string-match (gnus-globalify-regexp (car elem)) group) 2946 (when (string-match (gnus-globalify-regexp (car elem)) group)
2845 (replace-match (cadr elem) t nil group)))))) 2947 (replace-match (cadr elem) t nil group))))))
2846 (when found 2948 (when found
2949 (setq found (nnheader-translate-file-chars found))
2847 (if (file-name-absolute-p found) 2950 (if (file-name-absolute-p found)
2848 found 2951 found
2849 (nnheader-concat gnus-kill-files-directory found))))) 2952 (nnheader-concat gnus-kill-files-directory found)))))
2850 2953
2851 (defun gnus-hierarchial-home-score-file (group) 2954 (defun gnus-hierarchial-home-score-file (group)
2852 "Return the score file of the top-level hierarchy of GROUP." 2955 "Return the score file of the top-level hierarchy of GROUP."
2853 (if (string-match "^[^.]+\\." group) 2956 (if (string-match "^[^.]+\\." group)
2854 (concat (match-string 0 group) gnus-score-file-suffix) 2957 (concat (match-string 0 group) gnus-score-file-suffix)
2872 ;;; Score decays 2975 ;;; Score decays
2873 ;;; 2976 ;;;
2874 2977
2875 (defun gnus-decay-score (score) 2978 (defun gnus-decay-score (score)
2876 "Decay SCORE according to `gnus-score-decay-constant' and `gnus-score-decay-scale'." 2979 "Decay SCORE according to `gnus-score-decay-constant' and `gnus-score-decay-scale'."
2877 (floor 2980 (let ((n (- score
2878 (- score 2981 (* (if (< score 0) -1 1)
2879 (* (if (< score 0) -1 1) 2982 (min (abs score)
2880 (min (abs score) 2983 (max gnus-score-decay-constant
2881 (max gnus-score-decay-constant 2984 (* (abs score)
2882 (* (abs score) 2985 gnus-score-decay-scale)))))))
2883 gnus-score-decay-scale))))))) 2986 (if (and (featurep 'xemacs)
2987 ;; XEmacs' floor can handle only the floating point
2988 ;; number below the half of the maximum integer.
2989 (> (abs n) (lsh -1 -2)))
2990 (string-to-number
2991 (car (split-string (number-to-string n) "\\.")))
2992 (floor n))))
2884 2993
2885 (defun gnus-decay-scores (alist day) 2994 (defun gnus-decay-scores (alist day)
2886 "Decay non-permanent scores in ALIST." 2995 "Decay non-permanent scores in ALIST."
2887 (let ((times (- (time-to-days (current-time)) day)) 2996 (let ((times (- (time-to-days (current-time)) day))
2888 kill entry updated score n) 2997 kill entry updated score n)
2911 cons cell (SYM . STRING), where the symbol SYM is `new' or `bad'. 3020 cons cell (SYM . STRING), where the symbol SYM is `new' or `bad'.
2912 In the `new' case, the string is a safe replacement for REGEXP. 3021 In the `new' case, the string is a safe replacement for REGEXP.
2913 In the `bad' case, the string is a unsafe subexpression of REGEXP, 3022 In the `bad' case, the string is a unsafe subexpression of REGEXP,
2914 and we do not have a simple replacement to suggest. 3023 and we do not have a simple replacement to suggest.
2915 3024
2916 See `(Gnus)Scoring Tips' for examples of good regular expressions." 3025 See Info node `(gnus)Scoring Tips' for examples of good regular expressions."
2917 (let (case-fold-search) 3026 (let (case-fold-search)
2918 (and 3027 (and
2919 ;; First, try a relatively fast necessary condition. 3028 ;; First, try a relatively fast necessary condition.
2920 ;; Notice ranges (like [^:] or [\t-\r]), \s>, \Sw, \W, \', \`: 3029 ;; Notice ranges (like [^:] or [\t-\r]), \s>, \Sw, \W, \', \`:
2921 (string-match "\n\\|\\\\[SsW`']\\|\\[\\^\\|[\0-\n]-" regexp) 3030 (string-match "\n\\|\\\\[SsW`']\\|\\[\\^\\|[\0-\n]-" regexp)
2959 (new (cons 'new new)) 3068 (new (cons 'new new))
2960 (t nil)))))) 3069 (t nil))))))
2961 3070
2962 (provide 'gnus-score) 3071 (provide 'gnus-score)
2963 3072
3073 ;;; arch-tag: d3922589-764d-46ae-9954-9330fd192634
2964 ;;; gnus-score.el ends here 3074 ;;; gnus-score.el ends here