comparison lisp/gnus/gnus-score.el @ 16:0293115a14e9 r19-15b91

Import from CVS: tag r19-15b91
author cvs
date Mon, 13 Aug 2007 08:49:20 +0200
parents ac2d302a0011
children d95e72db5c07
comparison
equal deleted inserted replaced
15:ad457d5f7d04 16:0293115a14e9
1 ;;; gnus-score.el --- scoring code for Gnus 1 ;;; gnus-score.el --- scoring code for Gnus
2 ;; Copyright (C) 1995,96 Free Software Foundation, Inc. 2 ;; Copyright (C) 1995,96,97 Free Software Foundation, Inc.
3 3
4 ;; Author: Per Abrahamsen <amanda@iesd.auc.dk> 4 ;; Author: Per Abrahamsen <amanda@iesd.auc.dk>
5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no> 5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
6 ;; Keywords: news 6 ;; Keywords: news
7 7
25 ;;; Commentary: 25 ;;; Commentary:
26 26
27 ;;; Code: 27 ;;; Code:
28 28
29 (require 'gnus) 29 (require 'gnus)
30 (eval-when-compile (require 'cl)) 30 (require 'gnus-sum)
31 31 (require 'gnus-range)
32 (defvar gnus-global-score-files nil 32
33 "*List of global score files and directories. 33 (defcustom gnus-global-score-files nil
34 "List of global score files and directories.
34 Set this variable if you want to use people's score files. One entry 35 Set this variable if you want to use people's score files. One entry
35 for each score file or each score file directory. Gnus will decide 36 for each score file or each score file directory. Gnus will decide
36 by itself what score files are applicable to which group. 37 by itself what score files are applicable to which group.
37 38
38 Say you want to use the single score file 39 Say you want to use the single score file
39 \"/ftp.ifi.uio.no@ftp:/pub/larsi/ding/score/soc.motss.SCORE\" and all 40 \"/ftp.ifi.uio.no@ftp:/pub/larsi/ding/score/soc.motss.SCORE\" and all
40 score files in the \"/ftp.some-where:/pub/score\" directory. 41 score files in the \"/ftp.some-where:/pub/score\" directory.
41 42
42 (setq gnus-global-score-files 43 (setq gnus-global-score-files
43 '(\"/ftp.ifi.uio.no:/pub/larsi/ding/score/soc.motss.SCORE\" 44 '(\"/ftp.ifi.uio.no:/pub/larsi/ding/score/soc.motss.SCORE\"
44 \"/ftp.some-where:/pub/score\"))") 45 \"/ftp.some-where:/pub/score\"))"
45 46 :group 'gnus-score
46 (defvar gnus-score-file-single-match-alist nil 47 :type '(repeat file))
47 "*Alist mapping regexps to lists of score files. 48
49 (defcustom gnus-score-file-single-match-alist nil
50 "Alist mapping regexps to lists of score files.
48 Each element of this alist should be of the form 51 Each element of this alist should be of the form
49 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... ) 52 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
50 53
51 If the name of a group is matched by REGEXP, the corresponding scorefiles 54 If the name of a group is matched by REGEXP, the corresponding scorefiles
52 will be used for that group. 55 will be used for that group.
53 The first match found is used, subsequent matching entries are ignored (to 56 The first match found is used, subsequent matching entries are ignored (to
54 use multiple matches, see gnus-score-file-multiple-match-alist). 57 use multiple matches, see gnus-score-file-multiple-match-alist).
55 58
56 These score files are loaded in addition to any files returned by 59 These score files are loaded in addition to any files returned by
57 gnus-score-find-score-files-function (which see).") 60 gnus-score-find-score-files-function (which see)."
58 61 :group 'gnus-score
59 (defvar gnus-score-file-multiple-match-alist nil 62 :type '(repeat (cons regexp (repeat file))))
60 "*Alist mapping regexps to lists of score files. 63
64 (defcustom gnus-score-file-multiple-match-alist nil
65 "Alist mapping regexps to lists of score files.
61 Each element of this alist should be of the form 66 Each element of this alist should be of the form
62 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... ) 67 (\"REGEXP\" [ \"SCORE-FILE-1\" ] [ \"SCORE-FILE-2\" ] ... )
63 68
64 If the name of a group is matched by REGEXP, the corresponding scorefiles 69 If the name of a group is matched by REGEXP, the corresponding scorefiles
65 will be used for that group. 70 will be used for that group.
66 If multiple REGEXPs match a group, the score files corresponding to each 71 If multiple REGEXPs match a group, the score files corresponding to each
67 match will be used (for only one match to be used, see 72 match will be used (for only one match to be used, see
68 gnus-score-file-single-match-alist). 73 gnus-score-file-single-match-alist).
69 74
70 These score files are loaded in addition to any files returned by 75 These score files are loaded in addition to any files returned by
71 gnus-score-find-score-files-function (which see).") 76 gnus-score-find-score-files-function (which see)."
72 77 :group 'gnus-score
73 (defvar gnus-score-file-suffix "SCORE" 78 :type '(repeat (cons regexp (repeat file))))
74 "*Suffix of the score files.") 79
75 80 (defcustom gnus-score-file-suffix "SCORE"
76 (defvar gnus-adaptive-file-suffix "ADAPT" 81 "Suffix of the score files."
77 "*Suffix of the adaptive score files.") 82 :group 'gnus-score
78 83 :type 'string)
79 (defvar gnus-score-find-score-files-function 'gnus-score-find-bnews 84
80 "*Function used to find score files. 85 (defcustom gnus-adaptive-file-suffix "ADAPT"
86 "Suffix of the adaptive score files."
87 :group 'gnus-score
88 :type 'string)
89
90 (defcustom gnus-score-find-score-files-function 'gnus-score-find-bnews
91 "Function used to find score files.
81 The function will be called with the group name as the argument, and 92 The function will be called with the group name as the argument, and
82 should return a list of score files to apply to that group. The score 93 should return a list of score files to apply to that group. The score
83 files do not actually have to exist. 94 files do not actually have to exist.
84 95
85 Predefined values are: 96 Predefined values are:
90 101
91 See the documentation to these functions for more information. 102 See the documentation to these functions for more information.
92 103
93 This variable can also be a list of functions to be called. Each 104 This variable can also be a list of functions to be called. Each
94 function should either return a list of score files, or a list of 105 function should either return a list of score files, or a list of
95 score alists.") 106 score alists."
96 107 :group 'gnus-score
97 (defvar gnus-score-interactive-default-score 1000 108 :type '(radio (function-item gnus-score-find-single)
98 "*Scoring commands will raise/lower the score with this number as the default.") 109 (function-item gnus-score-find-hierarchical)
99 110 (function-item gnus-score-find-bnews)
100 (defvar gnus-score-expiry-days 7 111 (function :tag "Other")))
112
113 (defcustom gnus-score-interactive-default-score 1000
114 "*Scoring commands will raise/lower the score with this number as the default."
115 :group 'gnus-score
116 :type 'integer)
117
118 (defcustom gnus-score-expiry-days 7
101 "*Number of days before unused score file entries are expired. 119 "*Number of days before unused score file entries are expired.
102 If this variable is nil, no score file entries will be expired.") 120 If this variable is nil, no score file entries will be expired."
103 121 :group 'gnus-score
104 (defvar gnus-update-score-entry-dates t 122 :type '(choice (const :tag "never" nil)
123 number))
124
125 (defcustom gnus-update-score-entry-dates t
105 "*In non-nil, update matching score entry dates. 126 "*In non-nil, update matching score entry dates.
106 If this variable is nil, then score entries that provide matches 127 If this variable is nil, then score entries that provide matches
107 will be expired along with non-matching score entries.") 128 will be expired along with non-matching score entries."
108 129 :group 'gnus-score
109 (defvar gnus-orphan-score nil 130 :type 'boolean)
110 "*All orphans get this score added. Set in the score file.") 131
111 132 (defcustom gnus-orphan-score nil
112 (defvar gnus-default-adaptive-score-alist 133 "*All orphans get this score added. Set in the score file."
134 :group 'gnus-score
135 :type 'integer)
136
137 (defcustom gnus-decay-scores nil
138 "*If non-nil, decay non-permanent scores."
139 :group 'gnus-score
140 :type 'boolean)
141
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."
145 :group 'gnus-score
146 :type '(radio (function-item gnus-decay-score)
147 (function :tag "Other")))
148
149 (defcustom gnus-score-decay-constant 3
150 "*Decay all \"small\" scores with this amount."
151 :group 'gnus-score
152 :type 'integer)
153
154 (defcustom gnus-score-decay-scale .05
155 "*Decay all \"big\" scores with this factor."
156 :group 'gnus-score
157 :type 'number)
158
159 (defcustom gnus-home-score-file nil
160 "Variable to control where interactive score entries are to go.
161 It can be:
162
163 * A string
164 This file file will be used as the home score file.
165
166 * A function
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
169 parameter.
170
171 * A list
172 The elements in this list can be:
173
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
178 set this variable.)
179
180 * A function.
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.
184
185 * A string. Use the string as the home score file.
186
187 The list will be traversed from the beginning towards the end looking
188 for matches."
189 :group 'gnus-score
190 :type '(choice string
191 (repeat (choice string
192 (cons regexp (repeat file))
193 function))
194 function))
195
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'."
199 :group 'gnus-score
200 :type '(choice string
201 (repeat (choice string
202 (cons regexp (repeat file))
203 function))
204 function))
205
206 (defcustom gnus-default-adaptive-score-alist
113 '((gnus-kill-file-mark) 207 '((gnus-kill-file-mark)
114 (gnus-unread-mark) 208 (gnus-unread-mark)
115 (gnus-read-mark (from 3) (subject 30)) 209 (gnus-read-mark (from 3) (subject 30))
116 (gnus-catchup-mark (subject -10)) 210 (gnus-catchup-mark (subject -10))
117 (gnus-killed-mark (from -1) (subject -20)) 211 (gnus-killed-mark (from -1) (subject -20))
118 (gnus-del-mark (from -2) (subject -15))) 212 (gnus-del-mark (from -2) (subject -15)))
119 "*Alist of marks and scores.") 213 "Alist of marks and scores."
120 214 :group 'gnus-score
121 (defvar gnus-score-mimic-keymap nil 215 :type '(repeat (cons (symbol :tag "Mark")
122 "*Have the score entry functions pretend that they are a keymap.") 216 (repeat (list (choice :tag "Header"
123 217 (const from)
124 (defvar gnus-score-exact-adapt-limit 10 218 (const subject)
219 (symbol :tag "other"))
220 (integer :tag "Score"))))))
221
222 (defcustom gnus-ignored-adaptive-words nil
223 "List of words to be ignored when doing adaptive word scoring."
224 :group 'gnus-score
225 :type '(repeat string))
226
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"
241 "try" "re")
242 "Default list of words to be ignored when doing adaptive word scoring."
243 :group 'gnus-score
244 :type '(repeat string))
245
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."
252 :group 'gnus-score
253 :type '(repeat (cons (character :tag "Mark")
254 (integer :tag "Score"))))
255
256 (defcustom gnus-score-mimic-keymap nil
257 "*Have the score entry functions pretend that they are a keymap."
258 :group 'gnus-score
259 :type 'boolean)
260
261 (defcustom gnus-score-exact-adapt-limit 10
125 "*Number that says how long a match has to be before using substring matching. 262 "*Number that says how long a match has to be before using substring matching.
126 When doing adaptive scoring, one normally uses fuzzy or substring 263 When doing adaptive scoring, one normally uses fuzzy or substring
127 matching. However, if the header one matches is short, the possibility 264 matching. However, if the header one matches is short, the possibility
128 for false positives is great, so if the length of the match is less 265 for false positives is great, so if the length of the match is less
129 than this variable, exact matching will be used. 266 than this variable, exact matching will be used.
130 267
131 If this variable is nil, exact matching will always be used.") 268 If this variable is nil, exact matching will always be used."
132 269 :group 'gnus-score
133 (defvar gnus-score-uncacheable-files "ADAPT$" 270 :type '(choice (const nil) integer))
134 "*All score files that match this regexp will not be cached.") 271
135 272 (defcustom gnus-score-uncacheable-files "ADAPT$"
136 (defvar gnus-score-default-header nil 273 "All score files that match this regexp will not be cached."
274 :group 'gnus-score
275 :type 'regexp)
276
277 (defcustom gnus-score-default-header nil
137 "Default header when entering new scores. 278 "Default header when entering new scores.
138 279
139 Should be one of the following symbols. 280 Should be one of the following symbols.
140 281
141 a: from 282 a: from
147 x: xref 288 x: xref
148 l: lines 289 l: lines
149 d: date 290 d: date
150 f: followup 291 f: followup
151 292
152 If nil, the user will be asked for a header.") 293 If nil, the user will be asked for a header."
153 294 :group 'gnus-score
154 (defvar gnus-score-default-type nil 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)))
305
306 (defcustom gnus-score-default-type nil
155 "Default match type when entering new scores. 307 "Default match type when entering new scores.
156 308
157 Should be one of the following symbols. 309 Should be one of the following symbols.
158 310
159 s: substring 311 s: substring
165 n: this date 317 n: this date
166 <: less than number 318 <: less than number
167 >: greater than number 319 >: greater than number
168 =: equal to number 320 =: equal to number
169 321
170 If nil, the user will be asked for a match type.") 322 If nil, the user will be asked for a match type."
171 323 :group 'gnus-score
172 (defvar gnus-score-default-fold nil 324 :type '(choice (const :tag "substring" s)
173 "Use case folding for new score file entries iff not nil.") 325 (const :tag "exact string" e)
174 326 (const :tag "fuzzy string" f)
175 (defvar gnus-score-default-duration nil 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" =)))
334
335 (defcustom gnus-score-default-fold nil
336 "Use case folding for new score file entries iff not nil."
337 :group 'gnus-score
338 :type 'boolean)
339
340 (defcustom gnus-score-default-duration nil
176 "Default duration of effect when entering new scores. 341 "Default duration of effect when entering new scores.
177 342
178 Should be one of the following symbols. 343 Should be one of the following symbols.
179 344
180 t: temporary 345 t: temporary
181 p: permanent 346 p: permanent
182 i: immediate 347 i: immediate
183 348
184 If nil, the user will be asked for a duration.") 349 If nil, the user will be asked for a duration."
185 350 :group 'gnus-score
186 (defvar gnus-score-after-write-file-function nil 351 :type '(choice (const :tag "temporary" t)
187 "*Function called with the name of the score file just written to disk.") 352 (const :tag "permanent" p)
353 (const :tag "immediate" i)))
354
355 (defcustom gnus-score-after-write-file-function nil
356 "Function called with the name of the score file just written to disk."
357 :group 'gnus-score
358 :type 'function)
188 359
189 360
190 361
191 ;; Internal variables. 362 ;; Internal variables.
192 363
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)))
367 (while numbers
368 (modify-syntax-entry (pop numbers) " " table))
369 (modify-syntax-entry ?' "w" table)
370 table)
371 "Syntax table used when doing adaptive word scoring.")
372
373 (defvar gnus-scores-exclude-files nil)
193 (defvar gnus-internal-global-score-files nil) 374 (defvar gnus-internal-global-score-files nil)
194 (defvar gnus-score-file-list nil) 375 (defvar gnus-score-file-list nil)
195 376
196 (defvar gnus-short-name-score-file-cache nil) 377 (defvar gnus-short-name-score-file-cache nil)
197 378
198 (defvar gnus-score-help-winconf nil) 379 (defvar gnus-score-help-winconf nil)
199 (defvar gnus-adaptive-score-alist gnus-default-adaptive-score-alist) 380 (defvar gnus-adaptive-score-alist gnus-default-adaptive-score-alist)
381 (defvar gnus-adaptive-word-score-alist gnus-default-adaptive-word-score-alist)
200 (defvar gnus-score-trace nil) 382 (defvar gnus-score-trace nil)
201 (defvar gnus-score-edit-buffer nil) 383 (defvar gnus-score-edit-buffer nil)
202 384
203 (defvar gnus-score-alist nil 385 (defvar gnus-score-alist nil
204 "Alist containing score information. 386 "Alist containing score information.
208 mark: Automatically mark articles below this. 390 mark: Automatically mark articles below this.
209 expunge: Automatically expunge articles below this. 391 expunge: Automatically expunge articles below this.
210 files: List of other score files to load when loading this one. 392 files: List of other score files to load when loading this one.
211 eval: Sexp to be evaluated when the score file is loaded. 393 eval: Sexp to be evaluated when the score file is loaded.
212 394
213 String entries have the form (HEADER (MATCH TYPE SCORE DATE) ...) 395 String entries have the form (HEADER (MATCH TYPE SCORE DATE) ...)
214 where HEADER is the header being scored, MATCH is the string we are 396 where HEADER is the header being scored, MATCH is the string we are
215 looking for, TYPE is a flag indicating whether it should use regexp or 397 looking for, TYPE is a flag indicating whether it should use regexp or
216 substring matching, SCORE is the score to add and DATE is the date 398 substring matching, SCORE is the score to add and DATE is the date
217 of the last successful match.") 399 of the last successful match.")
218 400
225 ;; Name to index alist. 407 ;; Name to index alist.
226 '(("number" 0 gnus-score-integer) 408 '(("number" 0 gnus-score-integer)
227 ("subject" 1 gnus-score-string) 409 ("subject" 1 gnus-score-string)
228 ("from" 2 gnus-score-string) 410 ("from" 2 gnus-score-string)
229 ("date" 3 gnus-score-date) 411 ("date" 3 gnus-score-date)
230 ("message-id" 4 gnus-score-string) 412 ("message-id" 4 gnus-score-string)
231 ("references" 5 gnus-score-string) 413 ("references" 5 gnus-score-string)
232 ("chars" 6 gnus-score-integer) 414 ("chars" 6 gnus-score-integer)
233 ("lines" 7 gnus-score-integer) 415 ("lines" 7 gnus-score-integer)
234 ("xref" 8 gnus-score-string) 416 ("xref" 8 gnus-score-string)
235 ("head" -1 gnus-score-body) 417 ("head" -1 gnus-score-body)
236 ("body" -1 gnus-score-body) 418 ("body" -1 gnus-score-body)
237 ("all" -1 gnus-score-body) 419 ("all" -1 gnus-score-body)
238 ("followup" 2 gnus-score-followup) 420 ("followup" 2 gnus-score-followup)
239 ("thread" 5 gnus-score-thread))) 421 ("thread" 5 gnus-score-thread)))
240 422
241 (eval-and-compile
242 (autoload 'gnus-uu-ctl-map "gnus-uu" nil nil 'keymap))
243
244 ;;; Summary mode score maps. 423 ;;; Summary mode score maps.
245 424
246 (gnus-define-keys 425 (gnus-define-keys (gnus-summary-score-map "V" gnus-summary-mode-map)
247 (gnus-summary-score-map "V" gnus-summary-mode-map) 426 "s" gnus-summary-set-score
248 "s" gnus-summary-set-score 427 "a" gnus-summary-score-entry
249 "a" gnus-summary-score-entry 428 "S" gnus-summary-current-score
250 "S" gnus-summary-current-score 429 "c" gnus-score-change-score-file
251 "c" gnus-score-change-score-file 430 "C" gnus-score-customize
252 "m" gnus-score-set-mark-below 431 "m" gnus-score-set-mark-below
253 "x" gnus-score-set-expunge-below 432 "x" gnus-score-set-expunge-below
254 "R" gnus-summary-rescore 433 "R" gnus-summary-rescore
255 "e" gnus-score-edit-current-scores 434 "e" gnus-score-edit-current-scores
256 "f" gnus-score-edit-file 435 "f" gnus-score-edit-file
257 "F" gnus-score-flush-cache 436 "F" gnus-score-flush-cache
258 "t" gnus-score-find-trace 437 "t" gnus-score-find-trace
259 "C" gnus-score-customize) 438 "w" gnus-score-find-favourite-words)
260 439
261 ;; Summary score file commands 440 ;; Summary score file commands
262 441
263 ;; Much modification of the kill (ahem, score) code and lots of the 442 ;; Much modification of the kill (ahem, score) code and lots of the
264 ;; functions are written by Per Abrahamsen <amanda@iesd.auc.dk>. 443 ;; functions are written by Per Abrahamsen <amanda@iesd.auc.dk>.
269 permanence, and the string to be used. The numerical prefix will be 448 permanence, and the string to be used. The numerical prefix will be
270 used as score." 449 used as score."
271 (interactive "P") 450 (interactive "P")
272 (gnus-summary-increase-score (- (gnus-score-default score)))) 451 (gnus-summary-increase-score (- (gnus-score-default score))))
273 452
274 (defvar gnus-score-default-header nil
275 "*The default header to score on when entering a score rule interactively.")
276
277 (defvar gnus-score-default-type nil
278 "*The default score type to use when entering a score rule interactively.")
279
280 (defvar gnus-score-default-duration nil
281 "*The default score duration to use on when entering a score rule interactively.")
282
283 (defun gnus-score-kill-help-buffer () 453 (defun gnus-score-kill-help-buffer ()
284 (when (get-buffer "*Score Help*") 454 (when (get-buffer "*Score Help*")
285 (kill-buffer "*Score Help*") 455 (kill-buffer "*Score Help*")
286 (and gnus-score-help-winconf 456 (when gnus-score-help-winconf
287 (set-window-configuration gnus-score-help-winconf)))) 457 (set-window-configuration gnus-score-help-winconf))))
288 458
289 (defun gnus-summary-increase-score (&optional score) 459 (defun gnus-summary-increase-score (&optional score)
290 "Make a score entry based on the current article. 460 "Make a score entry based on the current article.
291 The user will be prompted for header to score on, match type, 461 The user will be prompted for header to score on, match type,
292 permanence, and the string to be used. The numerical prefix will be 462 permanence, and the string to be used. The numerical prefix will be
312 '((?s s "substring" string) 482 '((?s s "substring" string)
313 (?e e "exact string" string) 483 (?e e "exact string" string)
314 (?f f "fuzzy string" string) 484 (?f f "fuzzy string" string)
315 (?r r "regexp string" string) 485 (?r r "regexp string" string)
316 (?z s "substring" body-string) 486 (?z s "substring" body-string)
317 (?p s "regexp string" body-string) 487 (?p r "regexp string" body-string)
318 (?b before "before date" date) 488 (?b before "before date" date)
319 (?a at "at date" date) 489 (?a at "at date" date)
320 (?n now "this date" date) 490 (?n now "this date" date)
321 (?< < "less than number" number) 491 (?< < "less than number" number)
322 (?> > "greater than number" number) 492 (?> > "greater than number" number)
323 (?= = "equal to number" number))) 493 (?= = "equal to number" number)))
324 (char-to-perm 494 (char-to-perm
325 (list (list ?t (current-time-string) "temporary") 495 (list (list ?t (current-time-string) "temporary")
326 '(?p perm "permanent") '(?i now "immediate"))) 496 '(?p perm "permanent") '(?i now "immediate")))
327 (mimic gnus-score-mimic-keymap) 497 (mimic gnus-score-mimic-keymap)
328 (hchar (and gnus-score-default-header 498 (hchar (and gnus-score-default-header
329 (aref (symbol-name gnus-score-default-header) 0))) 499 (aref (symbol-name gnus-score-default-header) 0)))
330 (tchar (and gnus-score-default-type 500 (tchar (and gnus-score-default-type
353 (gnus-score-kill-help-buffer) 523 (gnus-score-kill-help-buffer)
354 (unless (setq entry (assq (downcase hchar) char-to-header)) 524 (unless (setq entry (assq (downcase hchar) char-to-header))
355 (if mimic (error "%c %c" prefix hchar) (error ""))) 525 (if mimic (error "%c %c" prefix hchar) (error "")))
356 526
357 (when (/= (downcase hchar) hchar) 527 (when (/= (downcase hchar) hchar)
358 ;; This was a majuscle, so we end reading and set the defaults. 528 ;; This was a majuscule, so we end reading and set the defaults.
359 (if mimic (message "%c %c" prefix hchar) (message "")) 529 (if mimic (message "%c %c" prefix hchar) (message ""))
360 (setq tchar (or tchar ?s) 530 (setq tchar (or tchar ?s)
361 pchar (or pchar ?t))) 531 pchar (or pchar ?t)))
362 532
363 ;; We continue reading - the type. 533 ;; We continue reading - the type.
366 (progn 536 (progn
367 (sit-for 1) (message "%c %c-" prefix hchar)) 537 (sit-for 1) (message "%c %c-" prefix hchar))
368 (message "%s header '%s' with match type (%s?): " 538 (message "%s header '%s' with match type (%s?): "
369 (if increase "Increase" "Lower") 539 (if increase "Increase" "Lower")
370 (nth 1 entry) 540 (nth 1 entry)
371 (mapconcat (lambda (s) 541 (mapconcat (lambda (s)
372 (if (eq (nth 4 entry) 542 (if (eq (nth 4 entry)
373 (nth 3 s)) 543 (nth 3 s))
374 (char-to-string (car s)) 544 (char-to-string (car s))
375 "")) 545 ""))
376 char-to-type ""))) 546 char-to-type "")))
377 (setq tchar (read-char)) 547 (setq tchar (read-char))
378 (when (or (= tchar ??) (= tchar ?\C-h)) 548 (when (or (= tchar ??) (= tchar ?\C-h))
379 (setq tchar nil) 549 (setq tchar nil)
380 (gnus-score-insert-help 550 (gnus-score-insert-help
381 "Match type" 551 "Match type"
382 (delq nil 552 (delq nil
383 (mapcar (lambda (s) 553 (mapcar (lambda (s)
384 (if (eq (nth 4 entry) 554 (if (eq (nth 4 entry)
385 (nth 3 s)) 555 (nth 3 s))
386 s nil)) 556 s nil))
387 char-to-type )) 557 char-to-type))
388 2))) 558 2)))
389 559
390 (gnus-score-kill-help-buffer) 560 (gnus-score-kill-help-buffer)
391 (unless (setq type (nth 1 (assq (downcase tchar) char-to-type))) 561 (unless (setq type (nth 1 (assq (downcase tchar) char-to-type)))
392 (if mimic (error "%c %c" prefix hchar) (error ""))) 562 (if mimic (error "%c %c" prefix hchar) (error "")))
393 563
394 (when (/= (downcase tchar) tchar) 564 (when (/= (downcase tchar) tchar)
395 ;; It was a majuscle, so we end reading and use the default. 565 ;; It was a majuscule, so we end reading and use the default.
396 (if mimic (message "%c %c %c" prefix hchar tchar) 566 (if mimic (message "%c %c %c" prefix hchar tchar)
397 (message "")) 567 (message ""))
398 (setq pchar (or pchar ?p))) 568 (setq pchar (or pchar ?p)))
399 569
400 ;; We continue reading. 570 ;; We continue reading.
412 582
413 (gnus-score-kill-help-buffer) 583 (gnus-score-kill-help-buffer)
414 (if mimic (message "%c %c %c" prefix hchar tchar pchar) 584 (if mimic (message "%c %c %c" prefix hchar tchar pchar)
415 (message "")) 585 (message ""))
416 (unless (setq temporary (cadr (assq pchar char-to-perm))) 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)
590 (eq tchar 114)
591 (eq (- pchar 4) 111))
592 (error "You rang?"))
417 (if mimic 593 (if mimic
418 (error "%c %c %c %c" prefix hchar tchar pchar) 594 (error "%c %c %c %c" prefix hchar tchar pchar)
419 (error "")))) 595 (error ""))))
420 ;; Always kill the score help buffer. 596 ;; Always kill the score help buffer.
421 (gnus-score-kill-help-buffer)) 597 (gnus-score-kill-help-buffer))
437 613
438 (gnus-summary-score-entry 614 (gnus-summary-score-entry
439 (nth 1 entry) ; Header 615 (nth 1 entry) ; Header
440 match ; Match 616 match ; Match
441 type ; Type 617 type ; Type
442 (if (eq 's score) nil score) ; Score 618 (if (eq score 's) nil score) ; Score
443 (if (eq 'perm temporary) ; Temp 619 (if (eq temporary 'perm) ; Temp
444 nil 620 nil
445 temporary) 621 temporary)
446 (not (nth 3 entry))) ; Prompt 622 (not (nth 3 entry))) ; Prompt
447 )) 623 ))
448 624
449 (defun gnus-score-insert-help (string alist idx) 625 (defun gnus-score-insert-help (string alist idx)
450 (setq gnus-score-help-winconf (current-window-configuration)) 626 (setq gnus-score-help-winconf (current-window-configuration))
459 (i 0) 635 (i 0)
460 n width pad format) 636 n width pad format)
461 ;; find the longest string to display 637 ;; find the longest string to display
462 (while list 638 (while list
463 (setq n (length (nth idx (car list)))) 639 (setq n (length (nth idx (car list))))
464 (or (> max n) 640 (unless (> max n)
465 (setq max n)) 641 (setq max n))
466 (setq list (cdr list))) 642 (setq list (cdr list)))
467 (setq max (+ max 4)) ; %c, `:', SPACE, a SPACE at end 643 (setq max (+ max 4)) ; %c, `:', SPACE, a SPACE at end
468 (setq n (/ (1- (window-width)) max)) ; items per line 644 (setq n (/ (1- (window-width)) max)) ; items per line
469 (setq width (/ (1- (window-width)) n)) ; width of each item 645 (setq width (/ (1- (window-width)) n)) ; width of each item
470 ;; insert `n' items, each in a field of width `width' 646 ;; insert `n' items, each in a field of width `width'
471 (while alist 647 (while alist
472 (if (< i n) 648 (if (< i n)
473 () 649 ()
502 (error "No article on current line") 678 (error "No article on current line")
503 nil)))) 679 nil))))
504 680
505 (defun gnus-newsgroup-score-alist () 681 (defun gnus-newsgroup-score-alist ()
506 (or 682 (or
507 (let ((param-file (gnus-group-get-parameter 683 (let ((param-file (gnus-group-find-parameter
508 gnus-newsgroup-name 'score-file))) 684 gnus-newsgroup-name 'score-file)))
509 (when param-file 685 (when param-file
510 (gnus-score-load param-file))) 686 (gnus-score-load param-file)))
511 (gnus-score-load 687 (gnus-score-load
512 (gnus-score-file-name gnus-newsgroup-name))) 688 (gnus-score-file-name gnus-newsgroup-name)))
517 (cdr (assoc symbol 693 (cdr (assoc symbol
518 (or alist 694 (or alist
519 gnus-score-alist 695 gnus-score-alist
520 (gnus-newsgroup-score-alist))))) 696 (gnus-newsgroup-score-alist)))))
521 697
522 (defun gnus-summary-score-entry 698 (defun gnus-summary-score-entry (header match type score date
523 (header match type score date &optional prompt silent) 699 &optional prompt silent)
524 "Enter score file entry. 700 "Enter score file entry.
525 HEADER is the header being scored. 701 HEADER is the header being scored.
526 MATCH is the string we are looking for. 702 MATCH is the string we are looking for.
527 TYPE is the match type: substring, regexp, exact, fuzzy. 703 TYPE is the match type: substring, regexp, exact, fuzzy.
528 SCORE is the score to add. 704 SCORE is the score to add.
542 'now) 718 'now)
543 ((y-or-n-p "Expire kill? ") 719 ((y-or-n-p "Expire kill? ")
544 (current-time-string)) 720 (current-time-string))
545 (t nil)))) 721 (t nil))))
546 ;; Regexp is the default type. 722 ;; Regexp is the default type.
547 (if (eq type t) (setq type 'r)) 723 (when (eq type t)
724 (setq type 'r))
548 ;; Simplify matches... 725 ;; Simplify matches...
549 (cond ((or (eq type 'r) (eq type 's) (eq type nil)) 726 (cond ((or (eq type 'r) (eq type 's) (eq type nil))
550 (setq match (if match (gnus-simplify-subject-re match) ""))) 727 (setq match (if match (gnus-simplify-subject-re match) "")))
551 ((eq type 'f) 728 ((eq type 'f)
552 (setq match (gnus-simplify-subject-fuzzy match)))) 729 (setq match (gnus-simplify-subject-fuzzy match))))
553 (let ((score (gnus-score-default score)) 730 (let ((score (gnus-score-default score))
554 (header (format "%s" (downcase header))) 731 (header (format "%s" (downcase header)))
555 new) 732 new)
556 (and prompt (setq match (read-string 733 (when prompt
557 (format "Match %s on %s, %s: " 734 (setq match (read-string
558 (cond ((eq date 'now) 735 (format "Match %s on %s, %s: "
559 "now") 736 (cond ((eq date 'now)
560 ((stringp date) 737 "now")
561 "temp") 738 ((stringp date)
562 (t "permanent")) 739 "temp")
563 header 740 (t "permanent"))
564 (if (< score 0) "lower" "raise")) 741 header
565 (if (numberp match) 742 (if (< score 0) "lower" "raise"))
566 (int-to-string match) 743 (if (numberp match)
567 match)))) 744 (int-to-string match)
745 match))))
568 746
569 ;; Get rid of string props. 747 ;; Get rid of string props.
570 (setq match (format "%s" match)) 748 (setq match (format "%s" match))
571 749
572 ;; If this is an integer comparison, we transform from string to int. 750 ;; If this is an integer comparison, we transform from string to int.
573 (and (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-integer) 751 (when (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-integer)
574 (setq match (string-to-int match))) 752 (setq match (string-to-int match)))
575 753
576 (unless (eq date 'now) 754 (unless (eq date 'now)
577 ;; Add the score entry to the score file. 755 ;; Add the score entry to the score file.
578 (when (= score gnus-score-interactive-default-score) 756 (when (= score gnus-score-interactive-default-score)
579 (setq score nil)) 757 (setq score nil))
580 (let ((old (gnus-score-get header)) 758 (let ((old (gnus-score-get header))
581 elem) 759 elem)
582 (setq new 760 (setq new
583 (cond 761 (cond
584 (type (list match score (and date (gnus-day-number date)) type)) 762 (type
763 (list match score
764 (and date (if (numberp date) date
765 (gnus-day-number date)))
766 type))
585 (date (list match score (gnus-day-number date))) 767 (date (list match score (gnus-day-number date)))
586 (score (list match score)) 768 (score (list match score))
587 (t (list match)))) 769 (t (list match))))
588 ;; We see whether we can collapse some score entries. 770 ;; We see whether we can collapse some score entries.
589 ;; This isn't quite correct, because there may be more elements 771 ;; This isn't quite correct, because there may be more elements
590 ;; later on with the same key that have matching elems... Hm. 772 ;; later on with the same key that have matching elems... Hm.
591 (if (and old 773 (if (and old
592 (setq elem (assoc match old)) 774 (setq elem (assoc match old))
593 (eq (nth 3 elem) (nth 3 new)) 775 (eq (nth 3 elem) (nth 3 new))
594 (or (and (numberp (nth 2 elem)) (numberp (nth 2 new))) 776 (or (and (numberp (nth 2 elem)) (numberp (nth 2 new)))
595 (and (not (nth 2 elem)) (not (nth 2 new))))) 777 (and (not (nth 2 elem)) (not (nth 2 new)))))
596 ;; Yup, we just add this new score to the old elem. 778 ;; Yup, we just add this new score to the old elem.
597 (setcar (cdr elem) (+ (or (nth 1 elem) 779 (setcar (cdr elem) (+ (or (nth 1 elem)
598 gnus-score-interactive-default-score) 780 gnus-score-interactive-default-score)
599 (or (nth 1 new) 781 (or (nth 1 new)
600 gnus-score-interactive-default-score))) 782 gnus-score-interactive-default-score)))
601 ;; Nope, we have to add a new elem. 783 ;; Nope, we have to add a new elem.
602 (gnus-score-set header (if old (cons new old) (list new)))) 784 (gnus-score-set header (if old (cons new old) (list new))))
615 797
616 (defun gnus-summary-score-effect (header match type score) 798 (defun gnus-summary-score-effect (header match type score)
617 "Simulate the effect of a score file entry. 799 "Simulate the effect of a score file entry.
618 HEADER is the header being scored. 800 HEADER is the header being scored.
619 MATCH is the string we are looking for. 801 MATCH is the string we are looking for.
620 TYPE is a flag indicating if it is a regexp or substring. 802 TYPE is the score type.
621 SCORE is the score to add." 803 SCORE is the score to add."
622 (interactive (list (completing-read "Header: " 804 (interactive (list (completing-read "Header: "
623 gnus-header-index 805 gnus-header-index
624 (lambda (x) (fboundp (nth 2 x))) 806 (lambda (x) (fboundp (nth 2 x)))
625 t) 807 t)
626 (read-string "Match: ") 808 (read-string "Match: ")
627 (y-or-n-p "Use regexp match? ") 809 (y-or-n-p "Use regexp match? ")
628 (prefix-numeric-value current-prefix-arg))) 810 (prefix-numeric-value current-prefix-arg)))
629 (save-excursion 811 (save-excursion
630 (or (and (stringp match) (> (length match) 0)) 812 (unless (and (stringp match) (> (length match) 0))
631 (error "No match")) 813 (error "No match"))
632 (goto-char (point-min)) 814 (goto-char (point-min))
633 (let ((regexp (cond ((eq type 'f) 815 (let ((regexp (cond ((eq type 'f)
634 (gnus-simplify-subject-fuzzy match)) 816 (gnus-simplify-subject-fuzzy match))
635 ((eq type 'r) 817 ((eq type 'r)
636 match) 818 match)
637 ((eq type 'e) 819 ((eq type 'e)
638 (concat "\\`" (regexp-quote match) "\\'")) 820 (concat "\\`" (regexp-quote match) "\\'"))
639 (t 821 (t
640 (regexp-quote match))))) 822 (regexp-quote match)))))
641 (while (not (eobp)) 823 (while (not (eobp))
642 (let ((content (gnus-summary-header header 'noerr)) 824 (let ((content (gnus-summary-header header 'noerr))
643 (case-fold-search t)) 825 (case-fold-search t))
644 (and content 826 (and content
645 (if (if (eq type 'f) 827 (when (if (eq type 'f)
646 (string-equal (gnus-simplify-subject-fuzzy content) 828 (string-equal (gnus-simplify-subject-fuzzy content)
647 regexp) 829 regexp)
648 (string-match regexp content)) 830 (string-match regexp content))
649 (gnus-summary-raise-score score)))) 831 (gnus-summary-raise-score score))))
650 (beginning-of-line 2))))) 832 (beginning-of-line 2)))))
651 833
652 (defun gnus-summary-score-crossposting (score date) 834 (defun gnus-summary-score-crossposting (score date)
653 ;; Enter score file entry for current crossposting. 835 ;; Enter score file entry for current crossposting.
654 ;; SCORE is the score to add. 836 ;; SCORE is the score to add.
655 ;; DATE is the expire date. 837 ;; DATE is the expire date.
656 (let ((xref (gnus-summary-header "xref")) 838 (let ((xref (gnus-summary-header "xref"))
657 (start 0) 839 (start 0)
658 group) 840 group)
659 (or xref (error "This article is not crossposted")) 841 (unless xref
842 (error "This article is not crossposted"))
660 (while (string-match " \\([^ \t]+\\):" xref start) 843 (while (string-match " \\([^ \t]+\\):" xref start)
661 (setq start (match-end 0)) 844 (setq start (match-end 0))
662 (if (not (string= 845 (when (not (string=
663 (setq group 846 (setq group
664 (substring xref (match-beginning 1) (match-end 1))) 847 (substring xref (match-beginning 1) (match-end 1)))
665 gnus-newsgroup-name)) 848 gnus-newsgroup-name))
666 (gnus-summary-score-entry 849 (gnus-summary-score-entry
667 "xref" (concat " " group ":") nil score date t))))) 850 "xref" (concat " " group ":") nil score date t)))))
668 851
669 852
670 ;;; 853 ;;;
671 ;;; Gnus Score Files 854 ;;; Gnus Score Files
672 ;;; 855 ;;;
722 (interactive "P") 905 (interactive "P")
723 (setq score (gnus-score-default score)) 906 (setq score (gnus-score-default score))
724 (when (gnus-buffer-live-p gnus-summary-buffer) 907 (when (gnus-buffer-live-p gnus-summary-buffer)
725 (save-excursion 908 (save-excursion
726 (save-restriction 909 (save-restriction
727 (goto-char (point-min)) 910 (message-narrow-to-headers)
728 (let ((id (mail-fetch-field "message-id"))) 911 (let ((id (mail-fetch-field "message-id")))
729 (when id 912 (when id
730 (set-buffer gnus-summary-buffer) 913 (set-buffer gnus-summary-buffer)
731 (gnus-summary-score-entry 914 (gnus-summary-score-entry
732 "references" (concat id "[ \t]*$") 'r 915 "references" (concat id "[ \t]*$") 'r
767 950
768 (defun gnus-summary-raise-score (n) 951 (defun gnus-summary-raise-score (n)
769 "Raise the score of the current article by N." 952 "Raise the score of the current article by N."
770 (interactive "p") 953 (interactive "p")
771 (gnus-set-global-variables) 954 (gnus-set-global-variables)
772 (gnus-summary-set-score (+ (gnus-summary-article-score) 955 (gnus-summary-set-score (+ (gnus-summary-article-score)
773 (or n gnus-score-interactive-default-score )))) 956 (or n gnus-score-interactive-default-score ))))
774 957
775 (defun gnus-summary-set-score (n) 958 (defun gnus-summary-set-score (n)
776 "Set the score of the current article to N." 959 "Set the score of the current article to N."
777 (interactive "p") 960 (interactive "p")
781 (let ((buffer-read-only nil)) 964 (let ((buffer-read-only nil))
782 ;; Set score. 965 ;; Set score.
783 (gnus-summary-update-mark 966 (gnus-summary-update-mark
784 (if (= n (or gnus-summary-default-score 0)) ? 967 (if (= n (or gnus-summary-default-score 0)) ?
785 (if (< n (or gnus-summary-default-score 0)) 968 (if (< n (or gnus-summary-default-score 0))
786 gnus-score-below-mark gnus-score-over-mark)) 'score)) 969 gnus-score-below-mark gnus-score-over-mark))
970 'score))
787 (let* ((article (gnus-summary-article-number)) 971 (let* ((article (gnus-summary-article-number))
788 (score (assq article gnus-newsgroup-scored))) 972 (score (assq article gnus-newsgroup-scored)))
789 (if score (setcdr score n) 973 (if score (setcdr score n)
790 (setq gnus-newsgroup-scored 974 (push (cons article n) gnus-newsgroup-scored)))
791 (cons (cons article n) gnus-newsgroup-scored))))
792 (gnus-summary-update-line))) 975 (gnus-summary-update-line)))
793 976
794 (defun gnus-summary-current-score () 977 (defun gnus-summary-current-score ()
795 "Return the score of the current article." 978 "Return the score of the current article."
796 (interactive) 979 (interactive)
806 989
807 (defvar gnus-score-edit-exit-function) 990 (defvar gnus-score-edit-exit-function)
808 (defun gnus-score-edit-current-scores (file) 991 (defun gnus-score-edit-current-scores (file)
809 "Edit the current score alist." 992 "Edit the current score alist."
810 (interactive (list gnus-current-score-file)) 993 (interactive (list gnus-current-score-file))
994 (gnus-set-global-variables)
811 (let ((winconf (current-window-configuration))) 995 (let ((winconf (current-window-configuration)))
812 (and (buffer-name gnus-summary-buffer) (gnus-score-save)) 996 (when (buffer-name gnus-summary-buffer)
997 (gnus-score-save))
813 (gnus-make-directory (file-name-directory file)) 998 (gnus-make-directory (file-name-directory file))
814 (setq gnus-score-edit-buffer (find-file-noselect file)) 999 (setq gnus-score-edit-buffer (find-file-noselect file))
815 (gnus-configure-windows 'edit-score) 1000 (gnus-configure-windows 'edit-score)
816 (gnus-score-mode) 1001 (gnus-score-mode)
817 (setq gnus-score-edit-exit-function 'gnus-score-edit-done) 1002 (setq gnus-score-edit-exit-function 'gnus-score-edit-done)
824 (defun gnus-score-edit-file (file) 1009 (defun gnus-score-edit-file (file)
825 "Edit a score file." 1010 "Edit a score file."
826 (interactive 1011 (interactive
827 (list (read-file-name "Edit score file: " gnus-kill-files-directory))) 1012 (list (read-file-name "Edit score file: " gnus-kill-files-directory)))
828 (gnus-make-directory (file-name-directory file)) 1013 (gnus-make-directory (file-name-directory file))
829 (and (buffer-name gnus-summary-buffer) (gnus-score-save)) 1014 (when (buffer-name gnus-summary-buffer)
1015 (gnus-score-save))
830 (let ((winconf (current-window-configuration))) 1016 (let ((winconf (current-window-configuration)))
831 (setq gnus-score-edit-buffer (find-file-noselect file)) 1017 (setq gnus-score-edit-buffer (find-file-noselect file))
832 (gnus-configure-windows 'edit-score) 1018 (gnus-configure-windows 'edit-score)
833 (gnus-score-mode) 1019 (gnus-score-mode)
834 (setq gnus-score-edit-exit-function 'gnus-score-edit-done) 1020 (setq gnus-score-edit-exit-function 'gnus-score-edit-done)
841 (defun gnus-score-load-file (file) 1027 (defun gnus-score-load-file (file)
842 ;; Load score file FILE. Returns a list a retrieved score-alists. 1028 ;; Load score file FILE. Returns a list a retrieved score-alists.
843 (let* ((file (expand-file-name 1029 (let* ((file (expand-file-name
844 (or (and (string-match 1030 (or (and (string-match
845 (concat "^" (expand-file-name 1031 (concat "^" (expand-file-name
846 gnus-kill-files-directory)) 1032 gnus-kill-files-directory))
847 (expand-file-name file)) 1033 (expand-file-name file))
848 file) 1034 file)
849 (concat (file-name-as-directory gnus-kill-files-directory) 1035 (concat (file-name-as-directory gnus-kill-files-directory)
850 file)))) 1036 file))))
851 (cached (assoc file gnus-score-cache)) 1037 (cached (assoc file gnus-score-cache))
857 ;; We load the score file. 1043 ;; We load the score file.
858 (setq gnus-score-alist nil) 1044 (setq gnus-score-alist nil)
859 (setq alist (gnus-score-load-score-alist file)) 1045 (setq alist (gnus-score-load-score-alist file))
860 ;; We add '(touched) to the alist to signify that it hasn't been 1046 ;; We add '(touched) to the alist to signify that it hasn't been
861 ;; touched (yet). 1047 ;; touched (yet).
862 (or (assq 'touched alist) (setq alist (cons (list 'touched nil) alist))) 1048 (unless (assq 'touched alist)
1049 (push (list 'touched nil) alist))
863 ;; If it is a global score file, we make it read-only. 1050 ;; If it is a global score file, we make it read-only.
864 (and global 1051 (and global
865 (not (assq 'read-only alist)) 1052 (not (assq 'read-only alist))
866 (setq alist (cons (list 'read-only t) alist))) 1053 (push (list 'read-only t) alist))
867 (setq gnus-score-cache 1054 (push (cons file alist) gnus-score-cache))
868 (cons (cons file alist) gnus-score-cache)))
869 (let ((a alist) 1055 (let ((a alist)
870 found) 1056 found)
871 (while a 1057 (while a
872 ;; Downcase all header names. 1058 ;; Downcase all header names.
873 (when (stringp (caar a)) 1059 (when (stringp (caar a))
888 (adapt (gnus-score-get 'adapt alist)) 1074 (adapt (gnus-score-get 'adapt alist))
889 (thread-mark-and-expunge 1075 (thread-mark-and-expunge
890 (car (gnus-score-get 'thread-mark-and-expunge alist))) 1076 (car (gnus-score-get 'thread-mark-and-expunge alist)))
891 (adapt-file (car (gnus-score-get 'adapt-file alist))) 1077 (adapt-file (car (gnus-score-get 'adapt-file alist)))
892 (local (gnus-score-get 'local alist)) 1078 (local (gnus-score-get 'local alist))
1079 (decay (car (gnus-score-get 'decay alist)))
893 (eval (car (gnus-score-get 'eval alist)))) 1080 (eval (car (gnus-score-get 'eval alist))))
1081 ;; Perform possible decays.
1082 (when (and gnus-decay-scores
1083 (gnus-decay-scores
1084 alist (or decay (gnus-time-to-day (current-time)))))
1085 (gnus-score-set 'touched '(t) alist)
1086 (gnus-score-set 'decay (list (gnus-time-to-day (current-time)))))
894 ;; We do not respect eval and files atoms from global score 1087 ;; We do not respect eval and files atoms from global score
895 ;; files. 1088 ;; files.
896 (and files (not global) 1089 (and files (not global)
897 (setq lists (apply 'append lists 1090 (setq lists (apply 'append lists
898 (mapcar (lambda (file) 1091 (mapcar (lambda (file)
899 (gnus-score-load-file file)) 1092 (gnus-score-load-file file))
900 (if adapt-file (cons adapt-file files) 1093 (if adapt-file (cons adapt-file files)
901 files))))) 1094 files)))))
902 (and eval (not global) (eval eval)) 1095 (and eval (not global) (eval eval))
903 ;; We then expand any exclude-file directives. 1096 ;; We then expand any exclude-file directives.
904 (setq gnus-scores-exclude-files 1097 (setq gnus-scores-exclude-files
905 (nconc 1098 (nconc
906 (mapcar 1099 (mapcar
907 (lambda (sfile) 1100 (lambda (sfile)
908 (expand-file-name sfile (file-name-directory file))) 1101 (expand-file-name sfile (file-name-directory file)))
909 exclude-files) gnus-scores-exclude-files)) 1102 exclude-files)
1103 gnus-scores-exclude-files))
910 (if (not local) 1104 (if (not local)
911 () 1105 ()
912 (save-excursion 1106 (save-excursion
913 (set-buffer gnus-summary-buffer) 1107 (set-buffer gnus-summary-buffer)
914 (while local 1108 (while local
916 (symbolp (caar local)) 1110 (symbolp (caar local))
917 (progn 1111 (progn
918 (make-local-variable (caar local)) 1112 (make-local-variable (caar local))
919 (set (caar local) (nth 1 (car local))))) 1113 (set (caar local) (nth 1 (car local)))))
920 (setq local (cdr local))))) 1114 (setq local (cdr local)))))
921 (if orphan (setq gnus-orphan-score orphan)) 1115 (when orphan
1116 (setq gnus-orphan-score orphan))
922 (setq gnus-adaptive-score-alist 1117 (setq gnus-adaptive-score-alist
923 (cond ((equal adapt '(t)) 1118 (cond ((equal adapt '(t))
924 (setq gnus-newsgroup-adaptive t) 1119 (setq gnus-newsgroup-adaptive t)
925 gnus-default-adaptive-score-alist) 1120 gnus-default-adaptive-score-alist)
926 ((equal adapt '(ignore)) 1121 ((equal adapt '(ignore))
948 (let ((cache (assoc file gnus-score-cache))) 1143 (let ((cache (assoc file gnus-score-cache)))
949 (if cache 1144 (if cache
950 (setq gnus-score-alist (cdr cache)) 1145 (setq gnus-score-alist (cdr cache))
951 (setq gnus-score-alist nil) 1146 (setq gnus-score-alist nil)
952 (gnus-score-load-score-alist file) 1147 (gnus-score-load-score-alist file)
953 (or gnus-score-alist 1148 (unless gnus-score-alist
954 (setq gnus-score-alist (copy-alist '((touched nil))))) 1149 (setq gnus-score-alist (copy-alist '((touched nil)))))
955 (setq gnus-score-cache 1150 (push (cons file gnus-score-alist) gnus-score-cache))))
956 (cons (cons file gnus-score-alist) gnus-score-cache)))))
957 1151
958 (defun gnus-score-remove-from-cache (file) 1152 (defun gnus-score-remove-from-cache (file)
959 (setq gnus-score-cache 1153 (setq gnus-score-cache
960 (delq (assoc file gnus-score-cache) gnus-score-cache))) 1154 (delq (assoc file gnus-score-cache) gnus-score-cache)))
961 1155
962 (defun gnus-score-load-score-alist (file) 1156 (defun gnus-score-load-score-alist (file)
1157 "Read score FILE."
963 (let (alist) 1158 (let (alist)
964 (if (not (file-readable-p file)) 1159 (if (not (file-readable-p file))
1160 ;; Couldn't read file.
965 (setq gnus-score-alist nil) 1161 (setq gnus-score-alist nil)
1162 ;; Read file.
966 (save-excursion 1163 (save-excursion
967 (gnus-set-work-buffer) 1164 (gnus-set-work-buffer)
968 (insert-file-contents file) 1165 (insert-file-contents file)
969 (goto-char (point-min)) 1166 (goto-char (point-min))
970 ;; Only do the loading if the score file isn't empty. 1167 ;; Only do the loading if the score file isn't empty.
971 (when (save-excursion (re-search-forward "[()0-9a-zA-Z]" nil t)) 1168 (when (save-excursion (re-search-forward "[()0-9a-zA-Z]" nil t))
972 (setq alist 1169 (setq alist
973 (condition-case () 1170 (condition-case ()
974 (read (current-buffer)) 1171 (read (current-buffer))
975 (error 1172 (error
976 (progn 1173 (gnus-error 3.2 "Problem with score file %s" file))))))
977 (gnus-message 3 "Problem with score file %s" file)
978 (ding)
979 (sit-for 2)
980 nil))))))
981 (if (eq (car alist) 'setq) 1174 (if (eq (car alist) 'setq)
982 ;; This is an old-style score file. 1175 ;; This is an old-style score file.
983 (setq gnus-score-alist (gnus-score-transform-old-to-new alist)) 1176 (setq gnus-score-alist (gnus-score-transform-old-to-new alist))
984 (setq gnus-score-alist alist)) 1177 (setq gnus-score-alist alist))
985 ;; Check the syntax of the score file. 1178 ;; Check the syntax of the score file.
1031 (progn 1224 (progn
1032 (ding) 1225 (ding)
1033 (gnus-message 3 err) 1226 (gnus-message 3 err)
1034 (sit-for 2) 1227 (sit-for 2)
1035 nil) 1228 nil)
1036 alist))))) 1229 alist)))))
1037 1230
1038 (defun gnus-score-transform-old-to-new (alist) 1231 (defun gnus-score-transform-old-to-new (alist)
1039 (let* ((alist (nth 2 alist)) 1232 (let* ((alist (nth 2 alist))
1040 out entry) 1233 out entry)
1041 (if (eq (car alist) 'quote) 1234 (when (eq (car alist) 'quote)
1042 (setq alist (nth 1 alist))) 1235 (setq alist (nth 1 alist)))
1043 (while alist 1236 (while alist
1044 (setq entry (car alist)) 1237 (setq entry (car alist))
1045 (if (stringp (car entry)) 1238 (if (stringp (car entry))
1046 (let ((scor (cdr entry))) 1239 (let ((scor (cdr entry)))
1047 (setq out (cons entry out)) 1240 (push entry out)
1048 (while scor 1241 (while scor
1049 (setcar scor 1242 (setcar scor
1050 (list (caar scor) (nth 2 (car scor)) 1243 (list (caar scor) (nth 2 (car scor))
1051 (and (nth 3 (car scor)) 1244 (and (nth 3 (car scor))
1052 (gnus-day-number (nth 3 (car scor)))) 1245 (gnus-day-number (nth 3 (car scor))))
1053 (if (nth 1 (car scor)) 'r 's))) 1246 (if (nth 1 (car scor)) 'r 's)))
1054 (setq scor (cdr scor)))) 1247 (setq scor (cdr scor))))
1055 (setq out (cons (if (not (listp (cdr entry))) 1248 (push (if (not (listp (cdr entry)))
1056 (list (car entry) (cdr entry)) 1249 (list (car entry) (cdr entry))
1057 entry) 1250 entry)
1058 out))) 1251 out))
1059 (setq alist (cdr alist))) 1252 (setq alist (cdr alist)))
1060 (cons (list 'touched t) (nreverse out)))) 1253 (cons (list 'touched t) (nreverse out))))
1061 1254
1062 (defun gnus-score-save () 1255 (defun gnus-score-save ()
1063 ;; Save all score information. 1256 ;; Save all score information.
1064 (let ((cache gnus-score-cache)) 1257 (let ((cache gnus-score-cache)
1258 entry score file)
1065 (save-excursion 1259 (save-excursion
1066 (setq gnus-score-alist nil) 1260 (setq gnus-score-alist nil)
1067 (set-buffer (get-buffer-create "*Score*")) 1261 (nnheader-set-temp-buffer " *Gnus Scores*")
1068 (buffer-disable-undo (current-buffer)) 1262 (while cache
1069 (let (entry score file) 1263 (current-buffer)
1070 (while cache 1264 (setq entry (pop cache)
1071 (setq entry (car cache) 1265 file (car entry)
1072 cache (cdr cache) 1266 score (cdr entry))
1073 file (car entry) 1267 (if (or (not (equal (gnus-score-get 'touched score) '(t)))
1074 score (cdr entry)) 1268 (gnus-score-get 'read-only score)
1075 (if (or (not (equal (gnus-score-get 'touched score) '(t))) 1269 (and (file-exists-p file)
1076 (gnus-score-get 'read-only score) 1270 (not (file-writable-p file))))
1077 (and (file-exists-p file) 1271 ()
1078 (not (file-writable-p file)))) 1272 (setq score (setcdr entry (delq (assq 'touched score) score)))
1079 () 1273 (erase-buffer)
1080 (setq score (setcdr entry (delq (assq 'touched score) score))) 1274 (let (emacs-lisp-mode-hook)
1081 (erase-buffer) 1275 (if (string-match
1082 (let (emacs-lisp-mode-hook) 1276 (concat (regexp-quote gnus-adaptive-file-suffix)
1083 (if (string-match 1277 "$")
1084 (concat (regexp-quote gnus-adaptive-file-suffix) 1278 file)
1085 "$") file) 1279 ;; This is an adaptive score file, so we do not run
1086 ;; This is an adaptive score file, so we do not run 1280 ;; it through `pp'. These files can get huge, and
1087 ;; it through `pp'. These files can get huge, and 1281 ;; are not meant to be edited by human hands.
1088 ;; are not meant to be edited by human hands. 1282 (gnus-prin1 score)
1089 (prin1 score (current-buffer)) 1283 ;; This is a normal score file, so we print it very
1090 ;; This is a normal score file, so we print it very 1284 ;; prettily.
1091 ;; prettily. 1285 (pp score (current-buffer))))
1092 (pp score (current-buffer)))) 1286 (gnus-make-directory (file-name-directory file))
1093 (if (not (gnus-make-directory (file-name-directory file))) 1287 ;; If the score file is empty, we delete it.
1094 () 1288 (if (zerop (buffer-size))
1095 ;; If the score file is empty, we delete it. 1289 (delete-file file)
1096 (if (zerop (buffer-size)) 1290 ;; There are scores, so we write the file.
1097 (delete-file file) 1291 (when (file-writable-p file)
1098 ;; There are scores, so we write the file. 1292 (gnus-write-buffer file)
1099 (when (file-writable-p file) 1293 (when gnus-score-after-write-file-function
1100 (write-region (point-min) (point-max) file nil 'silent) 1294 (funcall gnus-score-after-write-file-function file)))))
1101 (and gnus-score-after-write-file-function 1295 (and gnus-score-uncacheable-files
1102 (funcall gnus-score-after-write-file-function file))))) 1296 (string-match gnus-score-uncacheable-files file)
1103 (and gnus-score-uncacheable-files 1297 (gnus-score-remove-from-cache file)))
1104 (string-match gnus-score-uncacheable-files file)
1105 (gnus-score-remove-from-cache file)))))
1106 (kill-buffer (current-buffer))))) 1298 (kill-buffer (current-buffer)))))
1107 1299
1108 (defun gnus-score-headers (score-files &optional trace) 1300 (defun gnus-score-load-files (score-files)
1109 ;; Score `gnus-newsgroup-headers'. 1301 "Load all score files in SCORE-FILES."
1110 (let (scores news) 1302 ;; Load the score files.
1111 ;; PLM: probably this is not the best place to clear orphan-score 1303 (let (scores)
1112 (setq gnus-orphan-score nil)
1113 (setq gnus-scores-articles nil)
1114 (setq gnus-scores-exclude-files nil)
1115 ;; Load the score files.
1116 (while score-files 1304 (while score-files
1117 (if (stringp (car score-files)) 1305 (if (stringp (car score-files))
1118 ;; It is a string, which means that it's a score file name, 1306 ;; It is a string, which means that it's a score file name,
1119 ;; so we load the score file and add the score alist to 1307 ;; so we load the score file and add the score alist to
1120 ;; the list of alists. 1308 ;; the list of alists.
1129 (while s 1317 (while s
1130 (and (setq c (rassq (car s) gnus-score-cache)) 1318 (and (setq c (rassq (car s) gnus-score-cache))
1131 (member (car c) gnus-scores-exclude-files) 1319 (member (car c) gnus-scores-exclude-files)
1132 (setq scores (delq (car s) scores))) 1320 (setq scores (delq (car s) scores)))
1133 (setq s (cdr s))))) 1321 (setq s (cdr s)))))
1322 scores))
1323
1324 (defun gnus-score-headers (score-files &optional trace)
1325 ;; Score `gnus-newsgroup-headers'.
1326 (let (scores news)
1327 ;; PLM: probably this is not the best place to clear orphan-score
1328 (setq gnus-orphan-score nil
1329 gnus-scores-articles nil
1330 gnus-scores-exclude-files nil
1331 scores (gnus-score-load-files score-files))
1134 (setq news scores) 1332 (setq news scores)
1135 ;; Do the scoring. 1333 ;; Do the scoring.
1136 (while news 1334 (while news
1137 (setq scores news 1335 (setq scores news
1138 news nil) 1336 news nil)
1149 ;; Create articles, an alist of the form `(HEADER . SCORE)'. 1347 ;; Create articles, an alist of the form `(HEADER . SCORE)'.
1150 (while (setq header (pop headers)) 1348 (while (setq header (pop headers))
1151 ;; WARNING: The assq makes the function O(N*S) while it could 1349 ;; WARNING: The assq makes the function O(N*S) while it could
1152 ;; be written as O(N+S), where N is (length gnus-newsgroup-headers) 1350 ;; be written as O(N+S), where N is (length gnus-newsgroup-headers)
1153 ;; and S is (length gnus-newsgroup-scored). 1351 ;; and S is (length gnus-newsgroup-scored).
1154 (or (assq (mail-header-number header) gnus-newsgroup-scored) 1352 (unless (assq (mail-header-number header) gnus-newsgroup-scored)
1155 (setq gnus-scores-articles ;Total of 2 * N cons-cells used. 1353 (setq gnus-scores-articles ;Total of 2 * N cons-cells used.
1156 (cons (cons header (or gnus-summary-default-score 0)) 1354 (cons (cons header (or gnus-summary-default-score 0))
1157 gnus-scores-articles)))) 1355 gnus-scores-articles))))
1158 1356
1159 (save-excursion 1357 (save-excursion
1160 (set-buffer (get-buffer-create "*Headers*")) 1358 (set-buffer (get-buffer-create "*Headers*"))
1161 (buffer-disable-undo (current-buffer)) 1359 (buffer-disable-undo (current-buffer))
1162 1360
1183 ;; Remove the buffer. 1381 ;; Remove the buffer.
1184 (kill-buffer (current-buffer))) 1382 (kill-buffer (current-buffer)))
1185 1383
1186 ;; Add articles to `gnus-newsgroup-scored'. 1384 ;; Add articles to `gnus-newsgroup-scored'.
1187 (while gnus-scores-articles 1385 (while gnus-scores-articles
1188 (or (= gnus-summary-default-score (cdar gnus-scores-articles)) 1386 (when (or (/= gnus-summary-default-score
1189 (setq gnus-newsgroup-scored 1387 (cdar gnus-scores-articles))
1190 (cons (cons (mail-header-number 1388 gnus-save-score)
1191 (caar gnus-scores-articles)) 1389 (push (cons (mail-header-number (caar gnus-scores-articles))
1192 (cdar gnus-scores-articles)) 1390 (cdar gnus-scores-articles))
1193 gnus-newsgroup-scored))) 1391 gnus-newsgroup-scored))
1194 (setq gnus-scores-articles (cdr gnus-scores-articles))) 1392 (setq gnus-scores-articles (cdr gnus-scores-articles)))
1195 1393
1394 (let (score)
1395 (while (setq score (pop scores))
1396 (while score
1397 (when (listp (caar score))
1398 (gnus-score-advanced (car score) trace))
1399 (pop score))))
1400
1196 (gnus-message 5 "Scoring...done")))))) 1401 (gnus-message 5 "Scoring...done"))))))
1197 1402
1198 1403
1199 (defun gnus-get-new-thread-ids (articles) 1404 (defun gnus-get-new-thread-ids (articles)
1200 (let ((index (nth 1 (assoc "message-id" gnus-header-index))) 1405 (let ((index (nth 1 (assoc "message-id" gnus-header-index)))
1203 (while articles 1408 (while articles
1204 (setq art (car articles) 1409 (setq art (car articles)
1205 this (aref (car art) index) 1410 this (aref (car art) index)
1206 tref (aref (car art) refind) 1411 tref (aref (car art) refind)
1207 articles (cdr articles)) 1412 articles (cdr articles))
1208 (if (string-equal tref "") ;no references line 1413 (when (string-equal tref "") ;no references line
1209 (setq id-list (cons this id-list)))) 1414 (push this id-list)))
1210 id-list)) 1415 id-list))
1211 1416
1212 ;; Orphan functions written by plm@atcmp.nl (Peter Mutsaers). 1417 ;; Orphan functions written by plm@atcmp.nl (Peter Mutsaers).
1213 (defun gnus-score-orphans (score) 1418 (defun gnus-score-orphans (score)
1214 (let ((new-thread-ids (gnus-get-new-thread-ids gnus-scores-articles)) 1419 (let ((new-thread-ids (gnus-get-new-thread-ids gnus-scores-articles))
1222 (while articles 1427 (while articles
1223 (setq art (car articles) 1428 (setq art (car articles)
1224 this (aref (car art) gnus-score-index) 1429 this (aref (car art) gnus-score-index)
1225 articles (cdr articles)) 1430 articles (cdr articles))
1226 ;;completely skip if this is empty (not a child, so not an orphan) 1431 ;;completely skip if this is empty (not a child, so not an orphan)
1227 (if (not (string= this "")) 1432 (when (not (string= this ""))
1228 (if (equal last this) 1433 (if (equal last this)
1229 ;; O(N*H) cons-cells used here, where H is the number of 1434 ;; O(N*H) cons-cells used here, where H is the number of
1230 ;; headers. 1435 ;; headers.
1231 (setq alike (cons art alike)) 1436 (push art alike)
1232 (if last 1437 (when last
1233 (progn 1438 ;; Insert the line, with a text property on the
1234 ;; Insert the line, with a text property on the 1439 ;; terminating newline referring to the articles with
1235 ;; terminating newline referring to the articles with 1440 ;; this line.
1236 ;; this line. 1441 (insert last ?\n)
1237 (insert last ?\n) 1442 (put-text-property (1- (point)) (point) 'articles alike))
1238 (put-text-property (1- (point)) (point) 'articles alike))) 1443 (setq alike (list art)
1239 (setq alike (list art) 1444 last this))))
1240 last this)))) 1445 (when last ; Bwadr, duplicate code.
1241 (and last ; Bwadr, duplicate code. 1446 (insert last ?\n)
1242 (progn 1447 (put-text-property (1- (point)) (point) 'articles alike))
1243 (insert last ?\n)
1244 (put-text-property (1- (point)) (point) 'articles alike)))
1245 1448
1246 ;; PLM: now delete those lines that contain an entry from new-thread-ids 1449 ;; PLM: now delete those lines that contain an entry from new-thread-ids
1247 (while new-thread-ids 1450 (while new-thread-ids
1248 (setq this-id (car new-thread-ids) 1451 (setq this-id (car new-thread-ids)
1249 new-thread-ids (cdr new-thread-ids)) 1452 new-thread-ids (cdr new-thread-ids))
1250 (goto-char (point-min)) 1453 (goto-char (point-min))
1251 (while (search-forward this-id nil t) 1454 (while (search-forward this-id nil t)
1252 ;; found a match. remove this line 1455 ;; found a match. remove this line
1253 (beginning-of-line) 1456 (beginning-of-line)
1254 (kill-line 1))) 1457 (kill-line 1)))
1255 1458
1256 ;; now for each line: update its articles with score by moving to 1459 ;; now for each line: update its articles with score by moving to
1257 ;; every end-of-line in the buffer and read the articles property 1460 ;; every end-of-line in the buffer and read the articles property
1274 (while scores 1477 (while scores
1275 (setq alist (car scores) 1478 (setq alist (car scores)
1276 scores (cdr scores) 1479 scores (cdr scores)
1277 entries (assoc header alist)) 1480 entries (assoc header alist))
1278 (while (cdr entries) ;First entry is the header index. 1481 (while (cdr entries) ;First entry is the header index.
1279 (let* ((rest (cdr entries)) 1482 (let* ((rest (cdr entries))
1280 (kill (car rest)) 1483 (kill (car rest))
1281 (match (nth 0 kill)) 1484 (match (nth 0 kill))
1282 (type (or (nth 3 kill) '>)) 1485 (type (or (nth 3 kill) '>))
1283 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 1486 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1284 (date (nth 2 kill)) 1487 (date (nth 2 kill))
1292 ;; `gnus-score-string' does to minimize searches and stuff, 1495 ;; `gnus-score-string' does to minimize searches and stuff,
1293 ;; I will assume that people generally will put so few 1496 ;; I will assume that people generally will put so few
1294 ;; matches on numbers that any cleverness will take more 1497 ;; matches on numbers that any cleverness will take more
1295 ;; time than one would gain. 1498 ;; time than one would gain.
1296 (while articles 1499 (while articles
1297 (and (funcall match-func 1500 (when (funcall match-func
1298 (or (aref (caar articles) gnus-score-index) 0) 1501 (or (aref (caar articles) gnus-score-index) 0)
1299 match) 1502 match)
1300 (progn 1503 (when trace
1301 (and trace (setq gnus-score-trace 1504 (push (cons (car-safe (rassq alist gnus-score-cache)) kill)
1302 (cons 1505 gnus-score-trace))
1303 (cons 1506 (setq found t)
1304 (car-safe (rassq alist gnus-score-cache)) 1507 (setcdr (car articles) (+ score (cdar articles))))
1305 kill)
1306 gnus-score-trace)))
1307 (setq found t)
1308 (setcdr (car articles) (+ score (cdar articles)))))
1309 (setq articles (cdr articles))) 1508 (setq articles (cdr articles)))
1310 ;; Update expire date 1509 ;; Update expire date
1311 (cond ((null date)) ;Permanent entry. 1510 (cond ((null date)) ;Permanent entry.
1312 ((and found gnus-update-score-entry-dates) ;Match, update date. 1511 ((and found gnus-update-score-entry-dates) ;Match, update date.
1313 (gnus-score-set 'touched '(t) alist) 1512 (gnus-score-set 'touched '(t) alist)
1319 (setq entries rest))))) 1518 (setq entries rest)))))
1320 nil) 1519 nil)
1321 1520
1322 (defun gnus-score-date (scores header now expire &optional trace) 1521 (defun gnus-score-date (scores header now expire &optional trace)
1323 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index))) 1522 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
1324 entries alist) 1523 entries alist match match-func article)
1325 1524
1326 ;; Find matches. 1525 ;; Find matches.
1327 (while scores 1526 (while scores
1328 (setq alist (car scores) 1527 (setq alist (car scores)
1329 scores (cdr scores) 1528 scores (cdr scores)
1330 entries (assoc header alist)) 1529 entries (assoc header alist))
1331 (while (cdr entries) ;First entry is the header index. 1530 (while (cdr entries) ;First entry is the header index.
1332 (let* ((rest (cdr entries)) 1531 (let* ((rest (cdr entries))
1333 (kill (car rest)) 1532 (kill (car rest))
1334 (match (timezone-make-date-sortable (nth 0 kill)))
1335 (type (or (nth 3 kill) 'before)) 1533 (type (or (nth 3 kill) 'before))
1336 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 1534 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1337 (date (nth 2 kill)) 1535 (date (nth 2 kill))
1338 (found nil) 1536 (found nil)
1339 (match-func
1340 (cond ((eq type 'after) 'string<)
1341 ((eq type 'before) 'gnus-string>)
1342 ((eq type 'at) 'string=)
1343 (t (error "Illegal match type: %s" type))))
1344 (articles gnus-scores-articles) 1537 (articles gnus-scores-articles)
1345 l) 1538 l)
1539 (cond
1540 ((eq type 'after)
1541 (setq match-func 'string<
1542 match (gnus-date-iso8601 (nth 0 kill))))
1543 ((eq type 'before)
1544 (setq match-func 'gnus-string>
1545 match (gnus-date-iso8601 (nth 0 kill))))
1546 ((eq type 'at)
1547 (setq match-func 'string=
1548 match (gnus-date-iso8601 (nth 0 kill))))
1549 ((eq type 'regexp)
1550 (setq match-func 'string-match
1551 match (nth 0 kill)))
1552 (t (error "Illegal match type: %s" type)))
1346 ;; Instead of doing all the clever stuff that 1553 ;; Instead of doing all the clever stuff that
1347 ;; `gnus-score-string' does to minimize searches and stuff, 1554 ;; `gnus-score-string' does to minimize searches and stuff,
1348 ;; I will assume that people generally will put so few 1555 ;; I will assume that people generally will put so few
1349 ;; matches on numbers that any cleverness will take more 1556 ;; matches on numbers that any cleverness will take more
1350 ;; time than one would gain. 1557 ;; time than one would gain.
1351 (while articles 1558 (while (setq article (pop articles))
1352 (and 1559 (when (and
1353 (setq l (aref (caar articles) gnus-score-index)) 1560 (setq l (aref (car article) gnus-score-index))
1354 (funcall match-func match (timezone-make-date-sortable l)) 1561 (funcall match-func match (gnus-date-iso8601 l)))
1355 (progn 1562 (when trace
1356 (and trace (setq gnus-score-trace 1563 (push (cons (car-safe (rassq alist gnus-score-cache)) kill)
1357 (cons 1564 gnus-score-trace))
1358 (cons 1565 (setq found t)
1359 (car-safe (rassq alist gnus-score-cache)) 1566 (setcdr article (+ score (cdr article)))))
1360 kill)
1361 gnus-score-trace)))
1362 (setq found t)
1363 (setcdr (car articles) (+ score (cdar articles)))))
1364 (setq articles (cdr articles)))
1365 ;; Update expire date 1567 ;; Update expire date
1366 (cond ((null date)) ;Permanent entry. 1568 (cond ((null date)) ;Permanent entry.
1367 ((and found gnus-update-score-entry-dates) ;Match, update date. 1569 ((and found gnus-update-score-entry-dates) ;Match, update date.
1368 (gnus-score-set 'touched '(t) alist) 1570 (gnus-score-set 'touched '(t) alist)
1369 (setcar (nthcdr 2 kill) now)) 1571 (setcar (nthcdr 2 kill) now))
1370 ((and expire (< date expire)) ;Old entry, remove. 1572 ((and expire (< date expire)) ;Old entry, remove.
1371 (gnus-score-set 'touched '(t) alist) 1573 (gnus-score-set 'touched '(t) alist)
1372 (setcdr entries (cdr rest)) 1574 (setcdr entries (cdr rest))
1373 (setq rest entries))) 1575 (setq rest entries)))
1374 (setq entries rest))))) 1576 (setq entries rest)))))
1375 nil) 1577 nil)
1376 1578
1377 (defun gnus-score-body (scores header now expire &optional trace) 1579 (defun gnus-score-body (scores header now expire &optional trace)
1378 (save-excursion 1580 (save-excursion
1379 (set-buffer nntp-server-buffer)
1380 (setq gnus-scores-articles 1581 (setq gnus-scores-articles
1381 (sort gnus-scores-articles 1582 (sort gnus-scores-articles
1382 (lambda (a1 a2) 1583 (lambda (a1 a2)
1383 (< (mail-header-number (car a1)) 1584 (< (mail-header-number (car a1))
1384 (mail-header-number (car a2)))))) 1585 (mail-header-number (car a2))))))
1586 (set-buffer nntp-server-buffer)
1385 (save-restriction 1587 (save-restriction
1386 (let* ((buffer-read-only nil) 1588 (let* ((buffer-read-only nil)
1387 (articles gnus-scores-articles) 1589 (articles gnus-scores-articles)
1388 (all-scores scores) 1590 (all-scores scores)
1389 (request-func (cond ((string= "head" header) 1591 (request-func (cond ((string= "head" header)
1391 ((string= "body" header) 1593 ((string= "body" header)
1392 'gnus-request-body) 1594 'gnus-request-body)
1393 (t 'gnus-request-article))) 1595 (t 'gnus-request-article)))
1394 entries alist ofunc article last) 1596 entries alist ofunc article last)
1395 (when articles 1597 (when articles
1396 (while (cdr articles) 1598 (setq last (mail-header-number (caar (last articles))))
1397 (setq articles (cdr articles)))
1398 (setq last (mail-header-number (caar articles)))
1399 (setq articles gnus-scores-articles)
1400 ;; Not all backends support partial fetching. In that case, 1599 ;; Not all backends support partial fetching. In that case,
1401 ;; we just fetch the entire article. 1600 ;; we just fetch the entire article.
1402 (or (gnus-check-backend-function 1601 (unless (gnus-check-backend-function
1403 (and (string-match "^gnus-" (symbol-name request-func)) 1602 (and (string-match "^gnus-" (symbol-name request-func))
1404 (intern (substring (symbol-name request-func) 1603 (intern (substring (symbol-name request-func)
1405 (match-end 0)))) 1604 (match-end 0))))
1406 gnus-newsgroup-name) 1605 gnus-newsgroup-name)
1407 (progn 1606 (setq ofunc request-func)
1408 (setq ofunc request-func) 1607 (setq request-func 'gnus-request-article))
1409 (setq request-func 'gnus-request-article)))
1410 (while articles 1608 (while articles
1411 (setq article (mail-header-number (caar articles))) 1609 (setq article (mail-header-number (caar articles)))
1412 (gnus-message 7 "Scoring on article %s of %s..." article last) 1610 (gnus-message 7 "Scoring on article %s of %s..." article last)
1413 (when (funcall request-func article gnus-newsgroup-name) 1611 (when (funcall request-func article gnus-newsgroup-name)
1414 (widen) 1612 (widen)
1415 (goto-char (point-min)) 1613 (goto-char (point-min))
1416 ;; If just parts of the article is to be searched, but the 1614 ;; If just parts of the article is to be searched, but the
1417 ;; backend didn't support partial fetching, we just narrow 1615 ;; backend didn't support partial fetching, we just narrow
1418 ;; to the relevant parts. 1616 ;; to the relevant parts.
1419 (if ofunc 1617 (when ofunc
1420 (if (eq ofunc 'gnus-request-head) 1618 (if (eq ofunc 'gnus-request-head)
1421 (narrow-to-region
1422 (point)
1423 (or (search-forward "\n\n" nil t) (point-max)))
1424 (narrow-to-region 1619 (narrow-to-region
1425 (or (search-forward "\n\n" nil t) (point)) 1620 (point)
1426 (point-max)))) 1621 (or (search-forward "\n\n" nil t) (point-max)))
1622 (narrow-to-region
1623 (or (search-forward "\n\n" nil t) (point))
1624 (point-max))))
1427 (setq scores all-scores) 1625 (setq scores all-scores)
1428 ;; Find matches. 1626 ;; Find matches.
1429 (while scores 1627 (while scores
1430 (setq alist (car scores) 1628 (setq alist (pop scores)
1431 scores (cdr scores)
1432 entries (assoc header alist)) 1629 entries (assoc header alist))
1433 (while (cdr entries) ;First entry is the header index. 1630 (while (cdr entries) ;First entry is the header index.
1434 (let* ((rest (cdr entries)) 1631 (let* ((rest (cdr entries))
1435 (kill (car rest)) 1632 (kill (car rest))
1436 (match (nth 0 kill)) 1633 (match (nth 0 kill))
1437 (type (or (nth 3 kill) 's)) 1634 (type (or (nth 3 kill) 's))
1438 (score (or (nth 1 kill) 1635 (score (or (nth 1 kill)
1439 gnus-score-interactive-default-score)) 1636 gnus-score-interactive-default-score))
1440 (date (nth 2 kill)) 1637 (date (nth 2 kill))
1441 (found nil) 1638 (found nil)
1442 (case-fold-search 1639 (case-fold-search
1443 (not (or (eq type 'R) (eq type 'S) 1640 (not (or (eq type 'R) (eq type 'S)
1450 (eq type 'string) (eq type 'String)) 1647 (eq type 'string) (eq type 'String))
1451 'search-forward) 1648 'search-forward)
1452 (t 1649 (t
1453 (error "Illegal match type: %s" type))))) 1650 (error "Illegal match type: %s" type)))))
1454 (goto-char (point-min)) 1651 (goto-char (point-min))
1455 (if (funcall search-func match nil t) 1652 (when (funcall search-func match nil t)
1456 ;; Found a match, update scores. 1653 ;; Found a match, update scores.
1457 (progn 1654 (setcdr (car articles) (+ score (cdar articles)))
1458 (setcdr (car articles) (+ score (cdar articles))) 1655 (setq found t)
1459 (setq found t) 1656 (when trace
1460 (and trace (setq gnus-score-trace 1657 (push
1461 (cons 1658 (cons (car-safe (rassq alist gnus-score-cache)) kill)
1462 (cons 1659 gnus-score-trace)))
1463 (car-safe
1464 (rassq alist gnus-score-cache))
1465 kill)
1466 gnus-score-trace)))))
1467 ;; Update expire date 1660 ;; Update expire date
1468 (cond 1661 (unless trace
1469 ((null date)) ;Permanent entry. 1662 (cond
1470 ((and found gnus-update-score-entry-dates) ;Match, update date. 1663 ((null date)) ;Permanent entry.
1471 (gnus-score-set 'touched '(t) alist) 1664 ((and found gnus-update-score-entry-dates)
1472 (setcar (nthcdr 2 kill) now)) 1665 ;; Match, update date.
1473 ((and expire (< date expire)) ;Old entry, remove. 1666 (gnus-score-set 'touched '(t) alist)
1474 (gnus-score-set 'touched '(t) alist) 1667 (setcar (nthcdr 2 kill) now))
1475 (setcdr entries (cdr rest)) 1668 ((and expire (< date expire)) ;Old entry, remove.
1476 (setq rest entries))) 1669 (gnus-score-set 'touched '(t) alist)
1670 (setcdr entries (cdr rest))
1671 (setq rest entries))))
1477 (setq entries rest))))) 1672 (setq entries rest)))))
1478 (setq articles (cdr articles))))))) 1673 (setq articles (cdr articles)))))))
1479 nil) 1674 nil)
1675
1676 (defun gnus-score-thread (scores header now expire &optional trace)
1677 (gnus-score-followup scores header now expire trace t))
1480 1678
1481 (defun gnus-score-followup (scores header now expire &optional trace thread) 1679 (defun gnus-score-followup (scores header now expire &optional trace thread)
1482 ;; Insert the unique article headers in the buffer. 1680 ;; Insert the unique article headers in the buffer.
1483 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index))) 1681 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
1484 (current-score-file gnus-current-score-file) 1682 (current-score-file gnus-current-score-file)
1503 (while articles 1701 (while articles
1504 (setq art (car articles) 1702 (setq art (car articles)
1505 this (aref (car art) gnus-score-index) 1703 this (aref (car art) gnus-score-index)
1506 articles (cdr articles)) 1704 articles (cdr articles))
1507 (if (equal last this) 1705 (if (equal last this)
1508 (setq alike (cons art alike)) 1706 (push art alike)
1509 (if last 1707 (when last
1510 (progn 1708 (insert last ?\n)
1511 (insert last ?\n) 1709 (put-text-property (1- (point)) (point) 'articles alike))
1512 (put-text-property (1- (point)) (point) 'articles alike)))
1513 (setq alike (list art) 1710 (setq alike (list art)
1514 last this))) 1711 last this)))
1515 (and last ; Bwadr, duplicate code. 1712 (when last ; Bwadr, duplicate code.
1516 (progn 1713 (insert last ?\n)
1517 (insert last ?\n) 1714 (put-text-property (1- (point)) (point) 'articles alike))
1518 (put-text-property (1- (point)) (point) 'articles alike)))
1519 1715
1520 ;; Find matches. 1716 ;; Find matches.
1521 (while scores 1717 (while scores
1522 (setq alist (car scores) 1718 (setq alist (car scores)
1523 scores (cdr scores) 1719 scores (cdr scores)
1524 entries (assoc header alist)) 1720 entries (assoc header alist))
1525 (while (cdr entries) ;First entry is the header index. 1721 (while (cdr entries) ;First entry is the header index.
1526 (let* ((rest (cdr entries)) 1722 (let* ((rest (cdr entries))
1527 (kill (car rest)) 1723 (kill (car rest))
1528 (match (nth 0 kill)) 1724 (match (nth 0 kill))
1529 (type (or (nth 3 kill) 's)) 1725 (type (or (nth 3 kill) 's))
1530 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 1726 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1531 (date (nth 2 kill)) 1727 (date (nth 2 kill))
1567 ;; Update expire date 1763 ;; Update expire date
1568 (cond ((null date)) ;Permanent entry. 1764 (cond ((null date)) ;Permanent entry.
1569 ((and found gnus-update-score-entry-dates) ;Match, update date. 1765 ((and found gnus-update-score-entry-dates) ;Match, update date.
1570 (gnus-score-set 'touched '(t) alist) 1766 (gnus-score-set 'touched '(t) alist)
1571 (setcar (nthcdr 2 kill) now)) 1767 (setcar (nthcdr 2 kill) now))
1572 ((and expire (< date expire)) ;Old entry, remove. 1768 ((and expire (< date expire)) ;Old entry, remove.
1573 (gnus-score-set 'touched '(t) alist) 1769 (gnus-score-set 'touched '(t) alist)
1574 (setcdr entries (cdr rest)) 1770 (setcdr entries (cdr rest))
1575 (setq rest entries))) 1771 (setq rest entries)))
1576 (setq entries rest)))) 1772 (setq entries rest))))
1577 ;; We change the score file back to the previous one. 1773 ;; We change the score file back to the previous one.
1605 ;; than EXPIRE. 1801 ;; than EXPIRE.
1606 1802
1607 ;; Insert the unique article headers in the buffer. 1803 ;; Insert the unique article headers in the buffer.
1608 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index))) 1804 (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
1609 ;; gnus-score-index is used as a free variable. 1805 ;; gnus-score-index is used as a free variable.
1610 alike last this art entries alist articles scores fuzzy) 1806 alike last this art entries alist articles
1807 fuzzies arts words kill)
1611 1808
1612 ;; Sorting the articles costs os O(N*log N) but will allow us to 1809 ;; Sorting the articles costs os O(N*log N) but will allow us to
1613 ;; only match with each unique header. Thus the actual matching 1810 ;; only match with each unique header. Thus the actual matching
1614 ;; will be O(M*U) where M is the number of strings to match with, 1811 ;; will be O(M*U) where M is the number of strings to match with,
1615 ;; and U is the number of unique headers. It is assumed (but 1812 ;; and U is the number of unique headers. It is assumed (but
1617 ;; factor involved with string matching. 1814 ;; factor involved with string matching.
1618 (setq gnus-scores-articles (sort gnus-scores-articles 'gnus-score-string<) 1815 (setq gnus-scores-articles (sort gnus-scores-articles 'gnus-score-string<)
1619 articles gnus-scores-articles) 1816 articles gnus-scores-articles)
1620 1817
1621 (erase-buffer) 1818 (erase-buffer)
1622 (while articles 1819 (while (setq art (pop articles))
1623 (setq art (car articles) 1820 (setq this (aref (car art) gnus-score-index))
1624 this (aref (car art) gnus-score-index)
1625 articles (cdr articles))
1626 (if (equal last this) 1821 (if (equal last this)
1627 ;; O(N*H) cons-cells used here, where H is the number of 1822 ;; O(N*H) cons-cells used here, where H is the number of
1628 ;; headers. 1823 ;; headers.
1629 (setq alike (cons art alike)) 1824 (push art alike)
1630 (if last 1825 (when last
1631 (progn 1826 ;; Insert the line, with a text property on the
1632 ;; Insert the line, with a text property on the 1827 ;; terminating newline referring to the articles with
1633 ;; terminating newline referring to the articles with 1828 ;; this line.
1634 ;; this line. 1829 (insert last ?\n)
1635 (insert last ?\n) 1830 (put-text-property (1- (point)) (point) 'articles alike))
1636 (put-text-property (1- (point)) (point) 'articles alike)))
1637 (setq alike (list art) 1831 (setq alike (list art)
1638 last this))) 1832 last this)))
1639 (and last ; Bwadr, duplicate code. 1833 (when last ; Bwadr, duplicate code.
1640 (progn 1834 (insert last ?\n)
1641 (insert last ?\n) 1835 (put-text-property (1- (point)) (point) 'articles alike))
1642 (put-text-property (1- (point)) (point) 'articles alike))) 1836
1643 1837 ;; Go through all the score alists and pick out the entries
1644 ;; Find ordinary matches. 1838 ;; for this header.
1645 (setq scores score-list) 1839 (while score-list
1646 (while scores 1840 (setq alist (pop score-list)
1647 (setq alist (car scores) 1841 ;; There's only one instance of this header for
1648 scores (cdr scores) 1842 ;; each score alist.
1649 entries (assoc header alist)) 1843 entries (assoc header alist))
1650 (while (cdr entries) ;First entry is the header index. 1844 (while (cdr entries) ;First entry is the header index.
1651 (let* ((rest (cdr entries)) 1845 (let* ((kill (cadr entries))
1652 (kill (car rest))
1653 (match (nth 0 kill)) 1846 (match (nth 0 kill))
1654 (type (or (nth 3 kill) 's)) 1847 (type (or (nth 3 kill) 's))
1655 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 1848 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1656 (date (nth 2 kill)) 1849 (date (nth 2 kill))
1657 (found nil) 1850 (found nil)
1658 (mt (aref (symbol-name type) 0)) 1851 (mt (aref (symbol-name type) 0))
1659 (case-fold-search 1852 (case-fold-search (not (memq mt '(?R ?S ?E ?F))))
1660 (not (or (= mt ?R) (= mt ?S) (= mt ?E) (= mt ?F))))
1661 (dmt (downcase mt)) 1853 (dmt (downcase mt))
1662 (search-func 1854 (search-func
1663 (cond ((= dmt ?r) 're-search-forward) 1855 (cond ((= dmt ?r) 're-search-forward)
1664 ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward) 1856 ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward)
1665 (t (error "Illegal match type: %s" type)))) 1857 ((= dmt ?w) nil)
1666 arts art) 1858 (t (error "Illegal match type: %s" type)))))
1667 (if (= dmt ?f) 1859 (cond
1668 (setq fuzzy t) 1860 ;; Fuzzy matches. We save these for later.
1669 ;; Do non-fuzzy matching. 1861 ((= dmt ?f)
1862 (push (cons entries alist) fuzzies))
1863 ;; Word matches. Save these for even later.
1864 ((= dmt ?w)
1865 (push (cons entries alist) words))
1866 ;; Exact matches.
1867 ((= dmt ?e)
1868 ;; Do exact matching.
1670 (goto-char (point-min)) 1869 (goto-char (point-min))
1671 (if (= dmt ?e) 1870 (while (and (not (eobp))
1672 ;; Do exact matching. 1871 (funcall search-func match nil t))
1673 (while (and (not (eobp)) 1872 ;; Is it really exact?
1674 (funcall search-func match nil t)) 1873 (and (eolp)
1675 (and (= (progn (beginning-of-line) (point)) 1874 (= (point-at-bol) (match-beginning 0))
1676 (match-beginning 0)) 1875 ;; Yup.
1677 (= (progn (end-of-line) (point)) 1876 (progn
1678 (match-end 0)) 1877 (setq found (setq arts (get-text-property
1679 (progn 1878 (point) 'articles)))
1680 (setq found (setq arts (get-text-property 1879 ;; Found a match, update scores.
1681 (point) 'articles))) 1880 (if trace
1682 ;; Found a match, update scores. 1881 (while (setq art (pop arts))
1683 (if trace 1882 (setcdr art (+ score (cdr art)))
1684 (while arts 1883 (push
1685 (setq art (car arts) 1884 (cons
1686 arts (cdr arts)) 1885 (car-safe (rassq alist gnus-score-cache))
1687 (setcdr art (+ score (cdr art))) 1886 kill)
1688 (setq gnus-score-trace 1887 gnus-score-trace))
1689 (cons 1888 (while (setq art (pop arts))
1690 (cons 1889 (setcdr art (+ score (cdr art)))))))
1691 (car-safe 1890 (forward-line 1)))
1692 (rassq alist gnus-score-cache)) 1891 ;; Regexp and substring matching.
1693 kill) 1892 (t
1694 gnus-score-trace))) 1893 (goto-char (point-min))
1695 (while arts 1894 (when (string= match "")
1696 (setq art (car arts) 1895 (setq match "\n"))
1697 arts (cdr arts)) 1896 (while (and (not (eobp))
1698 (setcdr art (+ score (cdr art))))))) 1897 (funcall search-func match nil t))
1699 (forward-line 1)) 1898 (goto-char (match-beginning 0))
1700 ;; Do regexp and substring matching. 1899 (end-of-line)
1701 (and (string= match "") (setq match "\n")) 1900 (setq found (setq arts (get-text-property (point) 'articles)))
1702 (while (and (not (eobp)) 1901 ;; Found a match, update scores.
1703 (funcall search-func match nil t)) 1902 (if trace
1704 (goto-char (match-beginning 0)) 1903 (while (setq art (pop arts))
1705 (end-of-line) 1904 (setcdr art (+ score (cdr art)))
1706 (setq found (setq arts (get-text-property (point) 'articles))) 1905 (push (cons (car-safe (rassq alist gnus-score-cache)) kill)
1906 gnus-score-trace))
1907 (while (setq art (pop arts))
1908 (setcdr art (+ score (cdr art)))))
1909 (forward-line 1))))
1910 ;; Update expiry date
1911 (if trace
1912 (setq entries (cdr entries))
1913 (cond
1914 ;; Permanent entry.
1915 ((null date)
1916 (setq entries (cdr entries)))
1917 ;; We have a match, so we update the date.
1918 ((and found gnus-update-score-entry-dates)
1919 (gnus-score-set 'touched '(t) alist)
1920 (setcar (nthcdr 2 kill) now)
1921 (setq entries (cdr entries)))
1922 ;; This entry has expired, so we remove it.
1923 ((and expire (< date expire))
1924 (gnus-score-set 'touched '(t) alist)
1925 (setcdr entries (cddr entries)))
1926 ;; No match; go to next entry.
1927 (t
1928 (setq entries (cdr entries))))))))
1929
1930 ;; Find fuzzy matches.
1931 (when fuzzies
1932 ;; Simplify the entire buffer for easy matching.
1933 (gnus-simplify-buffer-fuzzy)
1934 (while (setq kill (cadaar fuzzies))
1935 (let* ((match (nth 0 kill))
1936 (type (nth 3 kill))
1937 (score (or (nth 1 kill) gnus-score-interactive-default-score))
1938 (date (nth 2 kill))
1939 (mt (aref (symbol-name type) 0))
1940 (case-fold-search (not (= mt ?F)))
1941 found)
1942 (goto-char (point-min))
1943 (while (and (not (eobp))
1944 (search-forward match nil t))
1945 (when (and (= (point-at-bol) (match-beginning 0))
1946 (eolp))
1947 (setq found (setq arts (get-text-property (point) 'articles)))
1948 (if trace
1949 (while (setq art (pop arts))
1950 (setcdr art (+ score (cdr art)))
1951 (push (cons
1952 (car-safe (rassq (cdar fuzzies) gnus-score-cache))
1953 kill)
1954 gnus-score-trace))
1707 ;; Found a match, update scores. 1955 ;; Found a match, update scores.
1708 (if trace 1956 (while (setq art (pop arts))
1709 (while arts 1957 (setcdr art (+ score (cdr art))))))
1710 (setq art (pop arts)) 1958 (forward-line 1))
1711 (setcdr art (+ score (cdr art))) 1959 ;; Update expiry date
1712 (push (cons 1960 (cond
1713 (car-safe (rassq alist gnus-score-cache)) 1961 ;; Permanent.
1714 kill) 1962 ((null date)
1715 gnus-score-trace)) 1963 )
1716 (while arts 1964 ;; Match, update date.
1717 (setq art (pop arts)) 1965 ((and found gnus-update-score-entry-dates)
1718 (setcdr art (+ score (cdr art))))) 1966 (gnus-score-set 'touched '(t) (cdar fuzzies))
1719 (forward-line 1))) 1967 (setcar (nthcdr 2 kill) now))
1720 ;; Update expire date 1968 ;; Old entry, remove.
1721 (cond 1969 ((and expire (< date expire))
1722 ((null date)) ;Permanent entry. 1970 (gnus-score-set 'touched '(t) (cdar fuzzies))
1723 ((and found gnus-update-score-entry-dates) ;Match, update date. 1971 (setcdr (caar fuzzies) (cddaar fuzzies))))
1724 (gnus-score-set 'touched '(t) alist) 1972 (setq fuzzies (cdr fuzzies)))))
1973
1974 (when words
1975 ;; Enter all words into the hashtb.
1976 (let ((hashtb (gnus-make-hashtable
1977 (* 10 (count-lines (point-min) (point-max))))))
1978 (gnus-enter-score-words-into-hashtb hashtb)
1979 (while (setq kill (cadaar words))
1980 (let* ((score (or (nth 1 kill) gnus-score-interactive-default-score))
1981 (date (nth 2 kill))
1982 found)
1983 (when (setq arts (intern-soft (nth 0 kill) hashtb))
1984 (setq arts (symbol-value arts))
1985 (setq found t)
1986 (if trace
1987 (while (setq art (pop arts))
1988 (setcdr art (+ score (cdr art)))
1989 (push (cons
1990 (car-safe (rassq (cdar words) gnus-score-cache))
1991 kill)
1992 gnus-score-trace))
1993 ;; Found a match, update scores.
1994 (while (setq art (pop arts))
1995 (setcdr art (+ score (cdr art))))))
1996 ;; Update expiry date
1997 (cond
1998 ;; Permanent.
1999 ((null date)
2000 )
2001 ;; Match, update date.
2002 ((and found gnus-update-score-entry-dates)
2003 (gnus-score-set 'touched '(t) (cdar words))
1725 (setcar (nthcdr 2 kill) now)) 2004 (setcar (nthcdr 2 kill) now))
1726 ((and expire (< date expire)) ;Old entry, remove. 2005 ;; Old entry, remove.
1727 (gnus-score-set 'touched '(t) alist) 2006 ((and expire (< date expire))
1728 (setcdr entries (cdr rest)) 2007 (gnus-score-set 'touched '(t) (cdar words))
1729 (setq rest entries)))) 2008 (setcdr (caar words) (cddaar words))))
1730 (setq entries rest)))) 2009 (setq words (cdr words))))))
1731 2010 nil))
1732 ;; Find fuzzy matches. 2011
1733 (when fuzzy 2012 (defun gnus-enter-score-words-into-hashtb (hashtb)
1734 (setq scores score-list) 2013 ;; Find all the words in the buffer and enter them into
1735 (gnus-simplify-buffer-fuzzy) 2014 ;; the hashtable.
1736 (while scores 2015 (let ((syntab (syntax-table))
1737 (setq alist (car scores) 2016 word val)
1738 scores (cdr scores) 2017 (goto-char (point-min))
1739 entries (assoc header alist)) 2018 (unwind-protect
1740 (while (cdr entries) ;First entry is the header index. 2019 (progn
1741 (let* ((rest (cdr entries)) 2020 (set-syntax-table gnus-adaptive-word-syntax-table)
1742 (kill (car rest)) 2021 (while (re-search-forward "\\b\\w+\\b" nil t)
1743 (match (nth 0 kill)) 2022 (setq val
1744 (type (or (nth 3 kill) 's)) 2023 (gnus-gethash
1745 (score (or (nth 1 kill) gnus-score-interactive-default-score)) 2024 (setq word (downcase (buffer-substring
1746 (date (nth 2 kill)) 2025 (match-beginning 0) (match-end 0))))
1747 (found nil) 2026 hashtb))
1748 (mt (aref (symbol-name type) 0)) 2027 (gnus-sethash
1749 (case-fold-search (not (= mt ?F))) 2028 word
1750 (dmt (downcase mt)) 2029 (append (get-text-property (point-at-eol) 'articles) val)
1751 arts art) 2030 hashtb)))
1752 (when (= dmt ?f) 2031 (set-syntax-table syntab))
1753 (goto-char (point-min)) 2032 ;; Make all the ignorable words ignored.
1754 (while (and (not (eobp)) 2033 (let ((ignored (append gnus-ignored-adaptive-words
1755 (search-forward match nil t)) 2034 gnus-default-ignored-adaptive-words)))
1756 (when (and (= (progn (beginning-of-line) (point)) 2035 (while ignored
1757 (match-beginning 0)) 2036 (gnus-sethash (pop ignored) nil hashtb)))))
1758 (= (progn (end-of-line) (point))
1759 (match-end 0)))
1760 (setq found (setq arts (get-text-property
1761 (point) 'articles)))
1762 ;; Found a match, update scores.
1763 (if trace
1764 (while arts
1765 (setq art (pop arts))
1766 (setcdr art (+ score (cdr art)))
1767 (push (cons
1768 (car-safe (rassq alist gnus-score-cache))
1769 kill)
1770 gnus-score-trace))
1771 (while arts
1772 (setq art (pop arts))
1773 (setcdr art (+ score (cdr art))))))
1774 (forward-line 1))
1775 ;; Update expire date
1776 (unless trace
1777 (cond
1778 ((null date)) ;Permanent entry.
1779 ((and found gnus-update-score-entry-dates) ;Match, update date.
1780 (gnus-score-set 'touched '(t) alist)
1781 (setcar (nthcdr 2 kill) now))
1782 ((and expire (< date expire)) ;Old entry, remove.
1783 (gnus-score-set 'touched '(t) alist)
1784 (setcdr entries (cdr rest))
1785 (setq rest entries)))))
1786 (setq entries rest))))))
1787 nil)
1788 2037
1789 (defun gnus-score-string< (a1 a2) 2038 (defun gnus-score-string< (a1 a2)
1790 ;; Compare headers in articles A2 and A2. 2039 ;; Compare headers in articles A2 and A2.
1791 ;; The header index used is the free variable `gnus-score-index'. 2040 ;; The header index used is the free variable `gnus-score-index'.
1792 (string-lessp (aref (car a1) gnus-score-index) 2041 (string-lessp (aref (car a1) gnus-score-index)
1793 (aref (car a2) gnus-score-index))) 2042 (aref (car a2) gnus-score-index)))
1794 2043
1795 (defun gnus-score-build-cons (article)
1796 ;; Build a `gnus-newsgroup-scored' type cons from ARTICLE.
1797 (cons (mail-header-number (car article)) (cdr article)))
1798
1799 (defun gnus-current-score-file-nondirectory (&optional score-file) 2044 (defun gnus-current-score-file-nondirectory (&optional score-file)
1800 (let ((score-file (or score-file gnus-current-score-file))) 2045 (let ((score-file (or score-file gnus-current-score-file)))
1801 (if score-file 2046 (if score-file
1802 (gnus-short-group-name (file-name-nondirectory score-file)) 2047 (gnus-short-group-name (file-name-nondirectory score-file))
1803 "none"))) 2048 "none")))
1804 2049
1805 (defun gnus-score-adaptive () 2050 (defun gnus-score-adaptive ()
1806 (save-excursion 2051 "Create adaptive score rules for this newsgroup."
1807 (let* ((malist (gnus-copy-sequence gnus-adaptive-score-alist)) 2052 (when gnus-use-adaptive-scoring
1808 (alist malist) 2053 ;; We change the score file to the adaptive score file.
1809 (date (current-time-string)) 2054 (save-excursion
1810 (data gnus-newsgroup-data) 2055 (set-buffer gnus-summary-buffer)
1811 elem headers match) 2056 (gnus-score-load-file
1812 ;; First we transform the adaptive rule alist into something 2057 (or gnus-newsgroup-adaptive-score-file
1813 ;; that's faster to process. 2058 (gnus-score-file-name
1814 (while malist 2059 gnus-newsgroup-name gnus-adaptive-file-suffix))))
1815 (setq elem (car malist)) 2060 ;; Perform ordinary line scoring.
1816 (if (symbolp (car elem)) 2061 (when (or (not (listp gnus-use-adaptive-scoring))
1817 (setcar elem (symbol-value (car elem)))) 2062 (memq 'line gnus-use-adaptive-scoring))
1818 (setq elem (cdr elem))
1819 (while elem
1820 (setcdr (car elem)
1821 (cons (if (eq (caar elem) 'followup)
1822 "references"
1823 (symbol-name (caar elem)))
1824 (cdar elem)))
1825 (setcar (car elem)
1826 `(lambda (h)
1827 (,(intern
1828 (concat "mail-header-"
1829 (if (eq (caar elem) 'followup)
1830 "message-id"
1831 (downcase (symbol-name (caar elem))))))
1832 h)))
1833 (setq elem (cdr elem)))
1834 (setq malist (cdr malist)))
1835 ;; We change the score file to the adaptive score file.
1836 (save-excursion 2063 (save-excursion
1837 (set-buffer gnus-summary-buffer) 2064 (let* ((malist (gnus-copy-sequence gnus-adaptive-score-alist))
1838 (gnus-score-load-file 2065 (alist malist)
1839 (or gnus-newsgroup-adaptive-score-file 2066 (date (current-time-string))
1840 (gnus-score-file-name 2067 (data gnus-newsgroup-data)
1841 gnus-newsgroup-name gnus-adaptive-file-suffix)))) 2068 elem headers match)
1842 ;; The we score away. 2069 ;; First we transform the adaptive rule alist into something
1843 (while data 2070 ;; that's faster to process.
1844 (setq elem (cdr (assq (gnus-data-mark (car data)) alist))) 2071 (while malist
1845 (if (or (not elem) 2072 (setq elem (car malist))
1846 (gnus-data-pseudo-p (car data))) 2073 (when (symbolp (car elem))
1847 () 2074 (setcar elem (symbol-value (car elem))))
1848 (when (setq headers (gnus-data-header (car data))) 2075 (setq elem (cdr elem))
1849 (while elem 2076 (while elem
1850 (setq match (funcall (caar elem) headers)) 2077 (setcdr (car elem)
1851 (gnus-summary-score-entry 2078 (cons (if (eq (caar elem) 'followup)
1852 (nth 1 (car elem)) match 2079 "references"
1853 (cond 2080 (symbol-name (caar elem)))
1854 ((numberp match) 2081 (cdar elem)))
1855 '=) 2082 (setcar (car elem)
1856 ((equal (nth 1 (car elem)) "date") 2083 `(lambda (h)
1857 'a) 2084 (,(intern
1858 (t 2085 (concat "mail-header-"
1859 ;; Whether we use substring or exact matches are controlled 2086 (if (eq (caar elem) 'followup)
1860 ;; here. 2087 "message-id"
1861 (if (or (not gnus-score-exact-adapt-limit) 2088 (downcase (symbol-name (caar elem))))))
1862 (< (length match) gnus-score-exact-adapt-limit)) 2089 h)))
1863 'e 2090 (setq elem (cdr elem)))
1864 (if (equal (nth 1 (car elem)) "subject") 2091 (setq malist (cdr malist)))
1865 'f 's)))) 2092 ;; Then we score away.
1866 (nth 2 (car elem)) date nil t) 2093 (while data
1867 (setq elem (cdr elem))))) 2094 (setq elem (cdr (assq (gnus-data-mark (car data)) alist)))
1868 (setq data (cdr data)))))) 2095 (if (or (not elem)
2096 (gnus-data-pseudo-p (car data)))
2097 ()
2098 (when (setq headers (gnus-data-header (car data)))
2099 (while elem
2100 (setq match (funcall (caar elem) headers))
2101 (gnus-summary-score-entry
2102 (nth 1 (car elem)) match
2103 (cond
2104 ((numberp match)
2105 '=)
2106 ((equal (nth 1 (car elem)) "date")
2107 'a)
2108 (t
2109 ;; Whether we use substring or exact matches is
2110 ;; controlled here.
2111 (if (or (not gnus-score-exact-adapt-limit)
2112 (< (length match) gnus-score-exact-adapt-limit))
2113 'e
2114 (if (equal (nth 1 (car elem)) "subject")
2115 'f 's))))
2116 (nth 2 (car elem)) date nil t)
2117 (setq elem (cdr elem)))))
2118 (setq data (cdr data))))))
2119
2120 ;; Perform adaptive word scoring.
2121 (when (and (listp gnus-use-adaptive-scoring)
2122 (memq 'word gnus-use-adaptive-scoring))
2123 (nnheader-temp-write nil
2124 (let* ((hashtb (gnus-make-hashtable 1000))
2125 (date (gnus-day-number (current-time-string)))
2126 (data gnus-newsgroup-data)
2127 (syntab (syntax-table))
2128 word d score val)
2129 (unwind-protect
2130 (progn
2131 (set-syntax-table gnus-adaptive-word-syntax-table)
2132 ;; Go through all articles.
2133 (while (setq d (pop data))
2134 (when (and
2135 (not (gnus-data-pseudo-p d))
2136 (setq score
2137 (cdr (assq
2138 (gnus-data-mark d)
2139 gnus-adaptive-word-score-alist))))
2140 ;; This article has a mark that should lead to
2141 ;; adaptive word rules, so we insert the subject
2142 ;; and find all words in that string.
2143 (insert (mail-header-subject (gnus-data-header d)))
2144 (downcase-region (point-min) (point-max))
2145 (goto-char (point-min))
2146 (while (re-search-forward "\\b\\w+\\b" nil t)
2147 ;; Put the word and score into the hashtb.
2148 (setq val (gnus-gethash (setq word (match-string 0))
2149 hashtb))
2150 (gnus-sethash word (+ (or val 0) score) hashtb))
2151 (erase-buffer))))
2152 (set-syntax-table syntab))
2153 ;; Make all the ignorable words ignored.
2154 (let ((ignored (append gnus-ignored-adaptive-words
2155 gnus-default-ignored-adaptive-words)))
2156 (while ignored
2157 (gnus-sethash (pop ignored) nil hashtb)))
2158 ;; Now we have all the words and scores, so we
2159 ;; add these rules to the ADAPT file.
2160 (set-buffer gnus-summary-buffer)
2161 (mapatoms
2162 (lambda (word)
2163 (when (symbol-value word)
2164 (gnus-summary-score-entry
2165 "subject" (symbol-name word) 'w (symbol-value word)
2166 date nil t)))
2167 hashtb))))))
1869 2168
1870 (defun gnus-score-edit-done () 2169 (defun gnus-score-edit-done ()
1871 (let ((bufnam (buffer-file-name (current-buffer))) 2170 (let ((bufnam (buffer-file-name (current-buffer)))
1872 (winconf gnus-prev-winconf)) 2171 (winconf gnus-prev-winconf))
1873 (and winconf (set-window-configuration winconf)) 2172 (when winconf
2173 (set-window-configuration winconf))
1874 (gnus-score-remove-from-cache bufnam) 2174 (gnus-score-remove-from-cache bufnam)
1875 (gnus-score-load-file bufnam))) 2175 (gnus-score-load-file bufnam)))
1876 2176
1877 (defun gnus-score-find-trace () 2177 (defun gnus-score-find-trace ()
1878 "Find all score rules that applies to the current article." 2178 "Find all score rules that applies to the current article."
1879 (interactive) 2179 (interactive)
1880 (let ((gnus-newsgroup-headers 2180 (let ((gnus-newsgroup-headers
1881 (list (gnus-summary-article-header))) 2181 (list (gnus-summary-article-header)))
1882 (gnus-newsgroup-scored nil) 2182 (gnus-newsgroup-scored nil)
1883 (buf (current-buffer))
1884 trace) 2183 trace)
1885 (when (get-buffer "*Gnus Scores*") 2184 (save-excursion
1886 (save-excursion 2185 (nnheader-set-temp-buffer "*Score Trace*"))
1887 (set-buffer "*Gnus Scores*")
1888 (erase-buffer)))
1889 (setq gnus-score-trace nil) 2186 (setq gnus-score-trace nil)
1890 (gnus-possibly-score-headers 'trace) 2187 (gnus-possibly-score-headers 'trace)
1891 (if (not (setq trace gnus-score-trace)) 2188 (if (not (setq trace gnus-score-trace))
1892 (gnus-error 1 "No score rules apply to the current article.") 2189 (gnus-error 1 "No score rules apply to the current article.")
1893 (pop-to-buffer "*Gnus Scores*") 2190 (set-buffer "*Score Trace*")
1894 (gnus-add-current-to-buffer-list) 2191 (gnus-add-current-to-buffer-list)
1895 (erase-buffer)
1896 (while trace 2192 (while trace
1897 (insert (format "%S -> %s\n" (cdar trace) 2193 (insert (format "%S -> %s\n" (cdar trace)
1898 (file-name-nondirectory (caar trace)))) 2194 (file-name-nondirectory (caar trace))))
1899 (setq trace (cdr trace))) 2195 (setq trace (cdr trace)))
1900 (goto-char (point-min)) 2196 (goto-char (point-min))
1901 (pop-to-buffer buf)))) 2197 (gnus-configure-windows 'score-trace))))
2198
2199 (defun gnus-score-find-favourite-words ()
2200 "List words used in scoring."
2201 (interactive)
2202 (let ((alists (gnus-score-load-files (gnus-all-score-files)))
2203 alist rule rules kill)
2204 ;; Go through all the score alists for this group
2205 ;; and find all `w' rules.
2206 (while (setq alist (pop alists))
2207 (while (setq rule (pop alist))
2208 (when (and (stringp (car rule))
2209 (equal "subject" (downcase (pop rule))))
2210 (while (setq kill (pop rule))
2211 (when (memq (nth 3 kill) '(w W word Word))
2212 (push (cons (or (nth 1 kill)
2213 gnus-score-interactive-default-score)
2214 (car kill))
2215 rules))))))
2216 (setq rules (sort rules (lambda (r1 r2)
2217 (string-lessp (cdr r1) (cdr r2)))))
2218 ;; Add up words that have appeared several times.
2219 (let ((r rules))
2220 (while (cdr r)
2221 (if (equal (cdar r) (cdadr r))
2222 (progn
2223 (setcar (car r) (+ (caar r) (caadr r)))
2224 (setcdr r (cddr r)))
2225 (pop r))))
2226 ;; Insert the words.
2227 (nnheader-set-temp-buffer "*Score Words*")
2228 (if (not (setq rules (sort rules (lambda (r1 r2) (> (car r1) (car r2))))))
2229 (gnus-error 3 "No word score rules")
2230 (while rules
2231 (insert (format "%-5d: %s\n" (caar rules) (cdar rules)))
2232 (pop rules))
2233 (gnus-add-current-to-buffer-list)
2234 (goto-char (point-min))
2235 (gnus-configure-windows 'score-words))))
1902 2236
1903 (defun gnus-summary-rescore () 2237 (defun gnus-summary-rescore ()
1904 "Redo the entire scoring process in the current summary." 2238 "Redo the entire scoring process in the current summary."
1905 (interactive) 2239 (interactive)
1906 (gnus-score-save) 2240 (gnus-score-save)
1948 (while (gnus-summary-find-subject subject) 2282 (while (gnus-summary-find-subject subject)
1949 (gnus-summary-raise-score score)) 2283 (gnus-summary-raise-score score))
1950 (gnus-summary-next-subject 1 t))) 2284 (gnus-summary-next-subject 1 t)))
1951 2285
1952 (defun gnus-score-default (level) 2286 (defun gnus-score-default (level)
1953 (if level (prefix-numeric-value level) 2287 (if level (prefix-numeric-value level)
1954 gnus-score-interactive-default-score)) 2288 gnus-score-interactive-default-score))
1955 2289
1956 (defun gnus-summary-raise-thread (&optional score) 2290 (defun gnus-summary-raise-thread (&optional score)
1957 "Raise the score of the articles in the current thread with SCORE." 2291 "Raise the score of the articles in the current thread with SCORE."
1958 (interactive "P") 2292 (interactive "P")
1964 (gnus-summary-goto-subject (car articles)) 2298 (gnus-summary-goto-subject (car articles))
1965 (gnus-summary-raise-score score) 2299 (gnus-summary-raise-score score)
1966 (setq articles (cdr articles)))) 2300 (setq articles (cdr articles))))
1967 (setq e (point))) 2301 (setq e (point)))
1968 (let ((gnus-summary-check-current t)) 2302 (let ((gnus-summary-check-current t))
1969 (or (zerop (gnus-summary-next-subject 1 t)) 2303 (unless (zerop (gnus-summary-next-subject 1 t))
1970 (goto-char e)))) 2304 (goto-char e))))
1971 (gnus-summary-recenter) 2305 (gnus-summary-recenter)
1972 (gnus-summary-position-point) 2306 (gnus-summary-position-point)
1973 (gnus-set-mode-line 'summary)) 2307 (gnus-set-mode-line 'summary))
1974 2308
1975 (defun gnus-summary-lower-same-subject-and-select (score) 2309 (defun gnus-summary-lower-same-subject-and-select (score)
1990 ;;; Finding score files. 2324 ;;; Finding score files.
1991 2325
1992 (defun gnus-score-score-files (group) 2326 (defun gnus-score-score-files (group)
1993 "Return a list of all possible score files." 2327 "Return a list of all possible score files."
1994 ;; Search and set any global score files. 2328 ;; Search and set any global score files.
1995 (and gnus-global-score-files 2329 (when gnus-global-score-files
1996 (or gnus-internal-global-score-files 2330 (unless gnus-internal-global-score-files
1997 (gnus-score-search-global-directories gnus-global-score-files))) 2331 (gnus-score-search-global-directories gnus-global-score-files)))
1998 ;; Fix the kill-file dir variable. 2332 ;; Fix the kill-file dir variable.
1999 (setq gnus-kill-files-directory 2333 (setq gnus-kill-files-directory
2000 (file-name-as-directory gnus-kill-files-directory)) 2334 (file-name-as-directory gnus-kill-files-directory))
2001 ;; If we can't read it, there are no score files. 2335 ;; If we can't read it, there are no score files.
2002 (if (not (file-exists-p (expand-file-name gnus-kill-files-directory))) 2336 (if (not (file-exists-p (expand-file-name gnus-kill-files-directory)))
2026 (gnus-score-file-regexp))))))) 2360 (gnus-score-file-regexp)))))))
2027 (cdr gnus-score-file-list))) 2361 (cdr gnus-score-file-list)))
2028 2362
2029 (defun gnus-score-score-files-1 (dir) 2363 (defun gnus-score-score-files-1 (dir)
2030 "Return all possible score files under DIR." 2364 "Return all possible score files under DIR."
2031 (let ((files (directory-files (expand-file-name dir) t nil t)) 2365 (let ((files (list (expand-file-name dir)))
2032 (regexp (gnus-score-file-regexp)) 2366 (regexp (gnus-score-file-regexp))
2033 out file) 2367 (case-fold-search nil)
2368 seen out file)
2034 (while (setq file (pop files)) 2369 (while (setq file (pop files))
2035 (cond 2370 (cond
2036 ;; Ignore "." and "..". 2371 ;; Ignore "." and "..".
2037 ((member (file-name-nondirectory file) '("." "..")) 2372 ((member (file-name-nondirectory file) '("." ".."))
2038 nil) 2373 nil)
2039 ;; Recurse down directories. 2374 ;; Add subtrees of directory to also be searched.
2040 ((file-directory-p file) 2375 ((and (file-directory-p file)
2041 (setq out (nconc (gnus-score-score-files-1 file) out))) 2376 (not (member (file-truename file) seen)))
2377 (push (file-truename file) seen)
2378 (setq files (nconc (directory-files file t nil t) files)))
2042 ;; Add files to the list of score files. 2379 ;; Add files to the list of score files.
2043 ((string-match regexp file) 2380 ((string-match regexp file)
2044 (push file out)))) 2381 (push file out))))
2045 (or out 2382 (or out
2046 ;; Return a dummy value. 2383 ;; Return a dummy value.
2072 (erase-buffer) 2409 (erase-buffer)
2073 (insert (car sfiles)) 2410 (insert (car sfiles))
2074 (goto-char (point-min)) 2411 (goto-char (point-min))
2075 ;; First remove the suffix itself. 2412 ;; First remove the suffix itself.
2076 (when (re-search-forward (concat "." score-regexp) nil t) 2413 (when (re-search-forward (concat "." score-regexp) nil t)
2077 (replace-match "" t t) 2414 (replace-match "" t t)
2078 (goto-char (point-min)) 2415 (goto-char (point-min))
2079 (if (looking-at (regexp-quote kill-dir)) 2416 (if (looking-at (regexp-quote kill-dir))
2080 ;; If the file name was just "SCORE", `klen' is one character 2417 ;; If the file name was just "SCORE", `klen' is one character
2081 ;; too much. 2418 ;; too much.
2082 (delete-char (min (1- (point-max)) klen)) 2419 (delete-char (min (1- (point-max)) klen))
2087 (goto-char (point-min)) 2424 (goto-char (point-min))
2088 (let ((regexp (concat 2425 (let ((regexp (concat
2089 "[/:" (if trans (char-to-string trans) "") "]"))) 2426 "[/:" (if trans (char-to-string trans) "") "]")))
2090 (while (re-search-forward regexp nil t) 2427 (while (re-search-forward regexp nil t)
2091 (replace-match "." t t))) 2428 (replace-match "." t t)))
2092 ;; Cludge to get rid of "nntp+" problems. 2429 ;; Kludge to get rid of "nntp+" problems.
2093 (goto-char (point-min)) 2430 (goto-char (point-min))
2094 (and (looking-at "nn[a-z]+\\+") 2431 (when (looking-at "nn[a-z]+\\+")
2095 (progn 2432 (search-forward "+")
2096 (search-forward "+") 2433 (forward-char -1)
2097 (forward-char -1) 2434 (insert "\\")
2098 (insert "\\"))) 2435 (forward-char 1))
2099 ;; Kludge to deal with "++". 2436 ;; Kludge to deal with "++".
2100 (goto-char (point-min)) 2437 (while (search-forward "+" nil t)
2101 (while (search-forward "++" nil t) 2438 (replace-match "\\+" t t))
2102 (replace-match "\\+\\+" t t))
2103 ;; Translate "all" to ".*". 2439 ;; Translate "all" to ".*".
2104 (goto-char (point-min)) 2440 (goto-char (point-min))
2105 (while (search-forward "all" nil t) 2441 (while (search-forward "all" nil t)
2106 (replace-match ".*" t t)) 2442 (replace-match ".*" t t))
2107 (goto-char (point-min)) 2443 (goto-char (point-min))
2108 ;; Deal with "not."s. 2444 ;; Deal with "not."s.
2109 (if (looking-at "not.") 2445 (if (looking-at "not.")
2110 (progn 2446 (progn
2111 (setq not-match t) 2447 (setq not-match t)
2112 (setq regexp (buffer-substring 5 (point-max)))) 2448 (setq regexp (concat "^" (buffer-substring 5 (point-max)))))
2113 (setq regexp (buffer-substring 1 (point-max))) 2449 (setq regexp (concat "^" (buffer-substring 1 (point-max))))
2114 (setq not-match nil)) 2450 (setq not-match nil))
2115 ;; Finally - if this resulting regexp matches the group name, 2451 ;; Finally - if this resulting regexp matches the group name,
2116 ;; we add this score file to the list of score files 2452 ;; we add this score file to the list of score files
2117 ;; applicable to this group. 2453 ;; applicable to this group.
2118 (if (or (and not-match 2454 (when (or (and not-match
2119 (not (string-match regexp group))) 2455 (not (string-match regexp group)))
2120 (and (not not-match) 2456 (and (not not-match)
2121 (string-match regexp group))) 2457 (string-match regexp group)))
2122 (setq ofiles (cons (car sfiles) ofiles)))) 2458 (push (car sfiles) ofiles)))
2123 (setq sfiles (cdr sfiles))) 2459 (setq sfiles (cdr sfiles)))
2124 (kill-buffer (current-buffer)) 2460 (kill-buffer (current-buffer))
2125 ;; Slight kludge here - the last score file returned should be 2461 ;; Slight kludge here - the last score file returned should be
2126 ;; the local score file, whether it exists or not. This is so 2462 ;; the local score file, whether it exists or not. This is so
2127 ;; that any score commands the user enters will go to the right 2463 ;; that any score commands the user enters will go to the right
2128 ;; file, and not end up in some global score file. 2464 ;; file, and not end up in some global score file.
2129 (let ((localscore (gnus-score-file-name group))) 2465 (let ((localscore (gnus-score-file-name group)))
2130 (setq ofiles (cons localscore (delete localscore ofiles)))) 2466 (setq ofiles (cons localscore (delete localscore ofiles))))
2131 (nreverse ofiles)))) 2467 (gnus-sort-score-files (nreverse ofiles)))))
2132 2468
2133 (defun gnus-score-find-single (group) 2469 (defun gnus-score-find-single (group)
2134 "Return list containing the score file for GROUP." 2470 "Return list containing the score file for GROUP."
2135 (list (or gnus-newsgroup-adaptive-score-file 2471 (list (or gnus-newsgroup-adaptive-score-file
2136 (gnus-score-file-name group gnus-adaptive-file-suffix)) 2472 (gnus-score-file-name group gnus-adaptive-file-suffix))
2137 (gnus-score-file-name group))) 2473 (gnus-score-file-name group)))
2138 2474
2139 (defun gnus-score-find-hierarchical (group) 2475 (defun gnus-score-find-hierarchical (group)
2140 "Return list of score files for GROUP. 2476 "Return list of score files for GROUP.
2141 This includes the score file for the group and all its parents." 2477 This includes the score file for the group and all its parents."
2142 (let ((all (copy-sequence '(nil))) 2478 (let* ((prefix (gnus-group-real-prefix group))
2143 (start 0)) 2479 (all (list nil))
2480 (group (gnus-group-real-name group))
2481 (start 0))
2144 (while (string-match "\\." group (1+ start)) 2482 (while (string-match "\\." group (1+ start))
2145 (setq start (match-beginning 0)) 2483 (setq start (match-beginning 0))
2146 (setq all (cons (substring group 0 start) all))) 2484 (push (substring group 0 start) all))
2147 (setq all (cons group all)) 2485 (push group all)
2148 (nconc 2486 (setq all
2149 (mapcar (lambda (newsgroup) 2487 (nconc
2150 (gnus-score-file-name newsgroup gnus-adaptive-file-suffix)) 2488 (mapcar (lambda (group)
2151 (setq all (nreverse all))) 2489 (gnus-score-file-name group gnus-adaptive-file-suffix))
2152 (mapcar 'gnus-score-file-name all)))) 2490 (setq all (nreverse all)))
2491 (mapcar 'gnus-score-file-name all)))
2492 (if (equal prefix "")
2493 all
2494 (mapcar
2495 (lambda (file)
2496 (concat (file-name-directory file) prefix
2497 (file-name-nondirectory file)))
2498 all))))
2499
2500 (defun gnus-score-file-rank (file)
2501 "Return a number that says how specific score FILE is.
2502 Destroys the current buffer."
2503 (if (member file gnus-internal-global-score-files)
2504 0
2505 (when (string-match
2506 (concat "^" (regexp-quote
2507 (expand-file-name
2508 (file-name-as-directory gnus-kill-files-directory))))
2509 file)
2510 (setq file (substring file (match-end 0))))
2511 (insert file)
2512 (goto-char (point-min))
2513 (let ((beg (point))
2514 elems)
2515 (while (re-search-forward "[./]" nil t)
2516 (push (buffer-substring beg (1- (point)))
2517 elems))
2518 (erase-buffer)
2519 (setq elems (delete "all" elems))
2520 (length elems))))
2521
2522 (defun gnus-sort-score-files (files)
2523 "Sort FILES so that the most general files come first."
2524 (nnheader-temp-write nil
2525 (let ((alist
2526 (mapcar
2527 (lambda (file)
2528 (cons (inline (gnus-score-file-rank file)) file))
2529 files)))
2530 (mapcar
2531 (lambda (f) (cdr f))
2532 (sort alist (lambda (f1 f2) (< (car f1) (car f2))))))))
2153 2533
2154 (defun gnus-score-find-alist (group) 2534 (defun gnus-score-find-alist (group)
2155 "Return list of score files for GROUP. 2535 "Return list of score files for GROUP.
2156 The list is determined from the variable gnus-score-file-alist." 2536 The list is determined from the variable gnus-score-file-alist."
2157 (let ((alist gnus-score-file-multiple-match-alist) 2537 (let ((alist gnus-score-file-multiple-match-alist)
2159 ;; if this group has been seen before, return the cached entry 2539 ;; if this group has been seen before, return the cached entry
2160 (if (setq score-files (assoc group gnus-score-file-alist-cache)) 2540 (if (setq score-files (assoc group gnus-score-file-alist-cache))
2161 (cdr score-files) ;ensures caching groups with no matches 2541 (cdr score-files) ;ensures caching groups with no matches
2162 ;; handle the multiple match alist 2542 ;; handle the multiple match alist
2163 (while alist 2543 (while alist
2164 (and (string-match (caar alist) group) 2544 (when (string-match (caar alist) group)
2165 (setq score-files 2545 (setq score-files
2166 (nconc score-files (copy-sequence (cdar alist))))) 2546 (nconc score-files (copy-sequence (cdar alist)))))
2167 (setq alist (cdr alist))) 2547 (setq alist (cdr alist)))
2168 (setq alist gnus-score-file-single-match-alist) 2548 (setq alist gnus-score-file-single-match-alist)
2169 ;; handle the single match alist 2549 ;; handle the single match alist
2170 (while alist 2550 (while alist
2171 (and (string-match (caar alist) group) 2551 (when (string-match (caar alist) group)
2172 ;; progn used just in case ("regexp") has no files 2552 ;; progn used just in case ("regexp") has no files
2173 ;; and score-files is still nil. -sj 2553 ;; and score-files is still nil. -sj
2174 ;; this can be construed as a "stop searching here" feature :> 2554 ;; this can be construed as a "stop searching here" feature :>
2175 ;; and used to simplify regexps in the single-alist 2555 ;; and used to simplify regexps in the single-alist
2176 (progn 2556 (setq score-files
2177 (setq score-files 2557 (nconc score-files (copy-sequence (cdar alist))))
2178 (nconc score-files (copy-sequence (cdar alist)))) 2558 (setq alist nil))
2179 (setq alist nil)))
2180 (setq alist (cdr alist))) 2559 (setq alist (cdr alist)))
2181 ;; cache the score files 2560 ;; cache the score files
2182 (setq gnus-score-file-alist-cache 2561 (push (cons group score-files) gnus-score-file-alist-cache)
2183 (cons (cons group score-files) gnus-score-file-alist-cache))
2184 score-files))) 2562 score-files)))
2185 2563
2186 (defun gnus-possibly-score-headers (&optional trace) 2564 (defun gnus-all-score-files (&optional group)
2565 "Return a list of all score files for the current group."
2187 (let ((funcs gnus-score-find-score-files-function) 2566 (let ((funcs gnus-score-find-score-files-function)
2567 (group (or group gnus-newsgroup-name))
2188 score-files) 2568 score-files)
2189 ;; Make sure funcs is a list. 2569 ;; Make sure funcs is a list.
2190 (and funcs 2570 (and funcs
2191 (not (listp funcs)) 2571 (not (listp funcs))
2192 (setq funcs (list funcs))) 2572 (setq funcs (list funcs)))
2193 ;; Get the initial score files for this group. 2573 ;; Get the initial score files for this group.
2194 (when funcs 2574 (when funcs
2195 (setq score-files (gnus-score-find-alist gnus-newsgroup-name))) 2575 (setq score-files (nreverse (gnus-score-find-alist group))))
2576 ;; Add any home adapt files.
2577 (let ((home (gnus-home-score-file group t)))
2578 (when home
2579 (push home score-files)
2580 (setq gnus-newsgroup-adaptive-score-file home)))
2581 ;; Check whether there is a `adapt-file' group parameter.
2582 (let ((param-file (gnus-group-find-parameter group 'adapt-file)))
2583 (when param-file
2584 (push param-file score-files)
2585 (setq gnus-newsgroup-adaptive-score-file param-file)))
2196 ;; Go through all the functions for finding score files (or actual 2586 ;; Go through all the functions for finding score files (or actual
2197 ;; scores) and add them to a list. 2587 ;; scores) and add them to a list.
2198 (while funcs 2588 (while funcs
2199 (when (gnus-functionp (car funcs)) 2589 (when (gnus-functionp (car funcs))
2200 (setq score-files 2590 (setq score-files
2201 (nconc score-files (funcall (car funcs) gnus-newsgroup-name)))) 2591 (nconc score-files (nreverse (funcall (car funcs) group)))))
2202 (setq funcs (cdr funcs))) 2592 (setq funcs (cdr funcs)))
2593 ;; Add any home score files.
2594 (let ((home (gnus-home-score-file group)))
2595 (when home
2596 (push home score-files)))
2203 ;; Check whether there is a `score-file' group parameter. 2597 ;; Check whether there is a `score-file' group parameter.
2204 (let ((param-file (gnus-group-get-parameter 2598 (let ((param-file (gnus-group-find-parameter group 'score-file)))
2205 gnus-newsgroup-name 'score-file)))
2206 (when param-file 2599 (when param-file
2207 (push param-file score-files))) 2600 (push param-file score-files)))
2601 ;; Expand all files names.
2602 (let ((files score-files))
2603 (while files
2604 (when (stringp (car files))
2605 (setcar files (expand-file-name
2606 (car files) gnus-kill-files-directory)))
2607 (pop files)))
2608 (setq score-files (nreverse score-files))
2609 ;; Remove any duplicate score files.
2610 (while (and score-files
2611 (member (car score-files) (cdr score-files)))
2612 (pop score-files))
2613 (let ((files score-files))
2614 (while (cdr files)
2615 (when (member (cadr files) (cddr files))
2616 (setcdr files (cddr files)))
2617 (pop files)))
2208 ;; Do the scoring if there are any score files for this group. 2618 ;; Do the scoring if there are any score files for this group.
2619 score-files))
2620
2621 (defun gnus-possibly-score-headers (&optional trace)
2622 "Do scoring if scoring is required."
2623 (let ((score-files (gnus-all-score-files)))
2209 (when score-files 2624 (when score-files
2210 (gnus-score-headers score-files trace)))) 2625 (gnus-score-headers score-files trace))))
2211 2626
2212 (defun gnus-score-file-name (newsgroup &optional suffix) 2627 (defun gnus-score-file-name (newsgroup &optional suffix)
2213 "Return the name of a score file for NEWSGROUP." 2628 "Return the name of a score file for NEWSGROUP."
2239 (while files 2654 (while files
2240 (if (string-match "/$" (car files)) 2655 (if (string-match "/$" (car files))
2241 (setq out (nconc (directory-files 2656 (setq out (nconc (directory-files
2242 (car files) t 2657 (car files) t
2243 (concat (gnus-score-file-regexp) "$")))) 2658 (concat (gnus-score-file-regexp) "$"))))
2244 (setq out (cons (car files) out))) 2659 (push (car files) out))
2245 (setq files (cdr files))) 2660 (setq files (cdr files)))
2246 (setq gnus-internal-global-score-files out))) 2661 (setq gnus-internal-global-score-files out)))
2247 2662
2248 (defun gnus-score-default-fold-toggle () 2663 (defun gnus-score-default-fold-toggle ()
2249 "Toggle folding for new score file entries." 2664 "Toggle folding for new score file entries."
2251 (setq gnus-score-default-fold (not gnus-score-default-fold)) 2666 (setq gnus-score-default-fold (not gnus-score-default-fold))
2252 (if gnus-score-default-fold 2667 (if gnus-score-default-fold
2253 (gnus-message 1 "New score file entries will be case insensitive.") 2668 (gnus-message 1 "New score file entries will be case insensitive.")
2254 (gnus-message 1 "New score file entries will be case sensitive."))) 2669 (gnus-message 1 "New score file entries will be case sensitive.")))
2255 2670
2671 ;;; Home score file.
2672
2673 (defun gnus-home-score-file (group &optional adapt)
2674 "Return the home score file for GROUP.
2675 If ADAPT, return the home adaptive file instead."
2676 (let ((list (if adapt gnus-home-adapt-file gnus-home-score-file))
2677 elem found)
2678 ;; Make sure we have a list.
2679 (unless (listp list)
2680 (setq list (list list)))
2681 ;; Go through the list and look for matches.
2682 (while (and (not found)
2683 (setq elem (pop list)))
2684 (setq found
2685 (cond
2686 ;; Simple string.
2687 ((stringp elem)
2688 elem)
2689 ;; Function.
2690 ((gnus-functionp elem)
2691 (funcall elem group))
2692 ;; Regexp-file cons
2693 ((consp elem)
2694 (when (string-match (car elem) group)
2695 (cadr elem))))))
2696 (when found
2697 (nnheader-concat gnus-kill-files-directory found))))
2698
2699 (defun gnus-hierarchial-home-score-file (group)
2700 "Return the score file of the top-level hierarchy of GROUP."
2701 (if (string-match "^[^.]+\\." group)
2702 (concat (match-string 0 group) gnus-score-file-suffix)
2703 ;; Group name without any dots.
2704 (concat group "." gnus-score-file-suffix)))
2705
2706 (defun gnus-hierarchial-home-adapt-file (group)
2707 "Return the adapt file of the top-level hierarchy of GROUP."
2708 (if (string-match "^[^.]+\\." group)
2709 (concat (match-string 0 group) gnus-adaptive-file-suffix)
2710 ;; Group name without any dots.
2711 (concat group "." gnus-adaptive-file-suffix)))
2712
2713 ;;;
2714 ;;; Score decays
2715 ;;;
2716
2717 (defun gnus-decay-score (score)
2718 "Decay SCORE."
2719 (floor
2720 (- score
2721 (* (if (< score 0) 1 -1)
2722 (min score
2723 (max gnus-score-decay-constant
2724 (* (abs score)
2725 gnus-score-decay-scale)))))))
2726
2727 (defun gnus-decay-scores (alist day)
2728 "Decay non-permanent scores in ALIST."
2729 (let ((times (- (gnus-time-to-day (current-time)) day))
2730 kill entry updated score n)
2731 (unless (zerop times) ;Done decays today already?
2732 (while (setq entry (pop alist))
2733 (when (stringp (car entry))
2734 (setq entry (cdr entry))
2735 (while (setq kill (pop entry))
2736 (when (nth 2 kill)
2737 (setq updated t)
2738 (setq score (or (car kill) gnus-score-interactive-default-score)
2739 n times)
2740 (while (natnump (decf n))
2741 (setq score (funcall gnus-decay-score-function score)))
2742 (setcar kill score))))))
2743 ;; Return whether this score file needs to be saved. By Je-haysuss!
2744 updated))
2745
2256 (provide 'gnus-score) 2746 (provide 'gnus-score)
2257 2747
2258 ;;; gnus-score.el ends here 2748 ;;; gnus-score.el ends here