X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fgnus-score.el;h=7baf55cc84c6891889d0ab10392738124d40ddae;hb=3838ece772c8d8a81940b6e97e6f59c1ade01389;hp=4d81005a3e056bc2ab9422f4f80ced22221c77ed;hpb=04182e95fe9d71ea57d96059242119c4a151054d;p=elisp%2Fgnus.git- diff --git a/lisp/gnus-score.el b/lisp/gnus-score.el index 4d81005..7baf55c 100644 --- a/lisp/gnus-score.el +++ b/lisp/gnus-score.el @@ -1,5 +1,5 @@ ;;; gnus-score.el --- scoring code for Gnus -;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc. +;; Copyright (C) 1995,96,97,98,99 Free Software Foundation, Inc. ;; Author: Per Abrahamsen ;; Lars Magne Ingebrigtsen @@ -308,6 +308,7 @@ Should be one of the following symbols. i: message-id t: references x: xref + e: `extra' (non-standard overview) l: lines d: date f: followup @@ -321,6 +322,7 @@ If nil, the user will be asked for a header." (const :tag "message-id" i) (const :tag "references" t) (const :tag "xref" x) + (const :tag "extra" e) (const :tag "lines" l) (const :tag "date" d) (const :tag "followup" f) @@ -385,7 +387,7 @@ If nil, the user will be asked for a duration." (defcustom gnus-score-thread-simplify nil "If non-nil, subjects will simplified as in threading." :group 'gnus-score-various - :type 'boolean) + :type 'boolean) @@ -435,20 +437,55 @@ of the last successful match.") (defconst gnus-header-index ;; Name to index alist. - '(("number" 0 gnus-score-integer) - ("subject" 1 gnus-score-string) - ("from" 2 gnus-score-string) - ("date" 3 gnus-score-date) - ("message-id" 4 gnus-score-string) - ("references" 5 gnus-score-string) - ("chars" 6 gnus-score-integer) - ("lines" 7 gnus-score-integer) - ("xref" 8 gnus-score-string) + `(("number" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'location) + gnus-score-integer) + ("subject" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'subject) + gnus-score-string) + ("from" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'from) + gnus-score-string) + ("date" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'date) + gnus-score-date) + ("message-id" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'id) + gnus-score-string) + ("references" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'references) + gnus-score-string) + ("chars" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'chars) + gnus-score-integer) + ("lines" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'lines) + gnus-score-integer) + ("xref" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'xref) + gnus-score-string) +;; ("extra" 16 gnus-score-string) + ("extra" -1 gnus-score-body) ("head" -1 gnus-score-body) ("body" -1 gnus-score-body) ("all" -1 gnus-score-body) - ("followup" 2 gnus-score-followup) - ("thread" 5 gnus-score-thread))) + ("followup" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'from) + gnus-score-followup) + ("thread" + ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) + 'references) + gnus-score-thread))) ;;; Summary mode score maps. @@ -500,12 +537,13 @@ used as score." (?b "body" "" nil body-string) (?h "head" "" nil body-string) (?i "message-id" nil t string) - (?t "references" "message-id" nil string) + (?r "references" "message-id" nil string) (?x "xref" nil nil string) + (?e "extra" nil nil string) (?l "lines" nil nil number) (?d "date" nil nil date) (?f "followup" nil nil string) - (?T "thread" nil nil string))) + (?t "thread" "message-id" nil string))) (char-to-type '((?s s "substring" string) (?e e "exact string" string) @@ -530,7 +568,7 @@ used as score." (aref (symbol-name gnus-score-default-type) 0))) (pchar (and gnus-score-default-duration (aref (symbol-name gnus-score-default-duration) 0))) - entry temporary type match) + entry temporary type match extra) (unwind-protect (progn @@ -552,7 +590,7 @@ used as score." (gnus-score-kill-help-buffer) (unless (setq entry (assq (downcase hchar) char-to-header)) (if mimic (error "%c %c" prefix hchar) - (error "Illegal header type"))) + (error "Invalid header type"))) (when (/= (downcase hchar) hchar) ;; This was a majuscule, so we end reading and set the defaults. @@ -585,13 +623,13 @@ used as score." (gnus-score-kill-help-buffer) (unless (setq type (nth 1 (assq (downcase tchar) legal-types))) (if mimic (error "%c %c" prefix hchar) - (error "Illegal match type")))) + (error "Invalid match type")))) (when (/= (downcase tchar) tchar) ;; It was a majuscule, so we end reading and use the default. (if mimic (message "%c %c %c" prefix hchar tchar) (message "")) - (setq pchar (or pchar ?p))) + (setq pchar (or pchar ?t))) ;; We continue reading. (while (not pchar) @@ -618,13 +656,30 @@ used as score." (error "You rang?")) (if mimic (error "%c %c %c %c" prefix hchar tchar pchar) - (error "Illegal match duration")))) + (error "Invalid match duration")))) ;; Always kill the score help buffer. (gnus-score-kill-help-buffer)) + ;; If scoring an extra (non-standard overview) header, + ;; we must find out which header is in question. + (setq extra + (and gnus-extra-headers + (equal (nth 1 entry) "extra") + (intern ; need symbol + (gnus-completing-read + (symbol-name (car gnus-extra-headers)) ; default response + "Score extra header:" ; prompt + (mapcar (lambda (x) ; completion list + (cons (symbol-name x) x)) + gnus-extra-headers) + nil ; no completion limit + t)))) ; require match + ;; extra is now nil or a symbol. + ;; We have all the data, so we enter this score. (setq match (if (string= (nth 2 entry) "") "" - (gnus-summary-header (or (nth 2 entry) (nth 1 entry))))) + (gnus-summary-header (or (nth 2 entry) (nth 1 entry)) + nil extra))) ;; Modify the match, perhaps. (cond @@ -651,7 +706,7 @@ used as score." current-score-file) (t (gnus-score-file-name "all")))))) - + (gnus-summary-score-entry (nth 1 entry) ; Header match ; Match @@ -660,7 +715,9 @@ used as score." (if (eq temporary 'perm) ; Temp nil temporary) - (not (nth 3 entry))) ; Prompt + (not (nth 3 entry)) ; Prompt + nil ; not silent + extra) ; non-standard overview. (when (eq symp 'a) ;; We change the score file back to the previous one. @@ -671,8 +728,8 @@ used as score." (defun gnus-score-insert-help (string alist idx) (setq gnus-score-help-winconf (current-window-configuration)) (save-excursion - (set-buffer (get-buffer-create "*Score Help*")) - (buffer-disable-undo (current-buffer)) + (set-buffer (gnus-get-buffer-create "*Score Help*")) + (buffer-disable-undo) (delete-windows-on (current-buffer)) (erase-buffer) (insert string ":\n\n") @@ -709,14 +766,16 @@ used as score." (shrink-window-if-larger-than-buffer)) (select-window (get-buffer-window gnus-summary-buffer)))) -(defun gnus-summary-header (header &optional no-err) +(defun gnus-summary-header (header &optional no-err extra) ;; Return HEADER for current articles, or error. (let ((article (gnus-summary-article-number)) headers) (if article (if (and (setq headers (gnus-summary-article-header article)) (vectorp headers)) - (aref headers (nth 1 (assoc header gnus-header-index))) + (if extra ; `header' must be "extra" + (or (cdr (assq extra (mail-header-extra headers))) "") + (aref headers (nth 1 (assoc header gnus-header-index)))) (if no-err nil (error "Pseudo-articles can't be scored"))) @@ -742,7 +801,8 @@ used as score." (gnus-newsgroup-score-alist))))) (defun gnus-summary-score-entry (header match type score date - &optional prompt silent) + &optional prompt silent extra) + (interactive) "Enter score file entry. HEADER is the header being scored. MATCH is the string we are looking for. @@ -750,7 +810,8 @@ TYPE is the match type: substring, regexp, exact, fuzzy. SCORE is the score to add. DATE is the expire date, or nil for no expire, or 'now for immediate expire. If optional argument `PROMPT' is non-nil, allow user to edit match. -If optional argument `SILENT' is nil, show effect of score entry." +If optional argument `SILENT' is nil, show effect of score entry. +If optional argument `EXTRA' is non-nil, it's a non-standard overview header." ;; Regexp is the default type. (when (eq type t) (setq type 'r)) @@ -791,12 +852,17 @@ If optional argument `SILENT' is nil, show effect of score entry." elem) (setq new (cond + (extra + (list match score + (and date (if (numberp date) date + (date-to-day date))) + type (symbol-name extra))) (type (list match score (and date (if (numberp date) date - (gnus-day-number date))) + (date-to-day date))) type)) - (date (list match score (gnus-day-number date))) + (date (list match score (date-to-day date))) (score (list match score)) (t (list match)))) ;; We see whether we can collapse some score entries. @@ -821,18 +887,19 @@ If optional argument `SILENT' is nil, show effect of score entry." (if (and (>= (nth 1 (assoc header gnus-header-index)) 0) (eq (nth 2 (assoc header gnus-header-index)) 'gnus-score-string)) - (gnus-summary-score-effect header match type score) + (gnus-summary-score-effect header match type score extra) (gnus-summary-rescore))) ;; Return the new scoring rule. new)) -(defun gnus-summary-score-effect (header match type score) +(defun gnus-summary-score-effect (header match type score extra) "Simulate the effect of a score file entry. HEADER is the header being scored. MATCH is the string we are looking for. TYPE is the score type. -SCORE is the score to add." +SCORE is the score to add. +EXTRA is the possible non-standard header." (interactive (list (completing-read "Header: " gnus-header-index (lambda (x) (fboundp (nth 2 x))) @@ -853,7 +920,7 @@ SCORE is the score to add." (t (regexp-quote match))))) (while (not (eobp)) - (let ((content (gnus-summary-header header 'noerr)) + (let ((content (gnus-summary-header header 'noerr extra)) (case-fold-search t)) (and content (when (if (eq type 'f) @@ -996,7 +1063,7 @@ SCORE is the score to add." (let ((buffer-read-only nil)) ;; Set score. (gnus-summary-update-mark - (if (= n (or gnus-summary-default-score 0)) ? + (if (= n (or gnus-summary-default-score 0)) ? ;Whitespace (if (< n (or gnus-summary-default-score 0)) gnus-score-below-mark gnus-score-over-mark)) 'score)) @@ -1121,7 +1188,7 @@ SCORE is the score to add." (or (not decay) (gnus-decay-scores alist decay))) (gnus-score-set 'touched '(t) alist) - (gnus-score-set 'decay (list (gnus-time-to-day (current-time))))) + (gnus-score-set 'decay (list (time-to-days (current-time))) alist)) ;; We do not respect eval and files atoms from global score ;; files. (when (and files (not global)) @@ -1202,9 +1269,9 @@ SCORE is the score to add." ;; Couldn't read file. (setq gnus-score-alist nil) ;; Read file. - (save-excursion - (gnus-set-work-buffer) - (insert-file-contents file) + (with-temp-buffer + (insert-file-contents-as-coding-system + score-mode-coding-system file) (goto-char (point-min)) ;; Only do the loading if the score file isn't empty. (when (save-excursion (re-search-forward "[()0-9a-zA-Z]" nil t)) @@ -1213,10 +1280,16 @@ SCORE is the score to add." (read (current-buffer)) (error (gnus-error 3.2 "Problem with score file %s" file)))))) - (if (eq (car alist) 'setq) - ;; This is an old-style score file. - (setq gnus-score-alist (gnus-score-transform-old-to-new alist)) - (setq gnus-score-alist alist)) + (cond + ((and alist + (atom alist)) + ;; Bogus score file. + (error "Invalid syntax with score file %s" file)) + ((eq (car alist) 'setq) + ;; This is an old-style score file. + (setq gnus-score-alist (gnus-score-transform-old-to-new alist))) + (t + (setq gnus-score-alist alist))) ;; Check the syntax of the score file. (setq gnus-score-alist (gnus-score-check-syntax gnus-score-alist file))))) @@ -1238,11 +1311,11 @@ SCORE is the score to add." err (cond ((not (listp (car a))) - (format "Illegal score element %s in %s" (car a) file)) + (format "Invalid score element %s in %s" (car a) file)) ((stringp (caar a)) (cond ((not (listp (setq sr (cdar a)))) - (format "Illegal header match %s in %s" (nth 1 (car a)) file)) + (format "Invalid header match %s in %s" (nth 1 (car a)) file)) (t (setq type (caar a)) (while (and sr (not err)) @@ -1253,7 +1326,7 @@ SCORE is the score to add." ((if (member (downcase type) '("lines" "chars")) (not (numberp (car s))) (not (stringp (car s)))) - (format "Illegal match %s in %s" (car s) file)) + (format "Invalid match %s in %s" (car s) file)) ((and (cadr s) (not (integerp (cadr s)))) (format "Non-integer score %s in %s" (cadr s) file)) ((and (caddr s) (not (integerp (caddr s)))) @@ -1284,7 +1357,7 @@ SCORE is the score to add." (setcar scor (list (caar scor) (nth 2 (car scor)) (and (nth 3 (car scor)) - (gnus-day-number (nth 3 (car scor)))) + (date-to-day (nth 3 (car scor)))) (if (nth 1 (car scor)) 'r 's))) (setq scor (cdr scor)))) (push (if (not (listp (cdr entry))) @@ -1331,7 +1404,8 @@ SCORE is the score to add." (delete-file file) ;; There are scores, so we write the file. (when (file-writable-p file) - (gnus-write-buffer file) + (gnus-write-buffer-as-coding-system + score-mode-coding-system file) (when gnus-score-after-write-file-function (funcall gnus-score-after-write-file-function file))))) (and gnus-score-uncacheable-files @@ -1379,7 +1453,7 @@ SCORE is the score to add." (when (and gnus-summary-default-score scores) (let* ((entries gnus-header-index) - (now (gnus-day-number (current-time-string))) + (now (date-to-day (current-time-string))) (expire (and gnus-score-expiry-days (- now gnus-score-expiry-days))) (headers gnus-newsgroup-headers) @@ -1397,8 +1471,8 @@ SCORE is the score to add." gnus-scores-articles)))) (save-excursion - (set-buffer (get-buffer-create "*Headers*")) - (buffer-disable-undo (current-buffer)) + (set-buffer (gnus-get-buffer-create "*Headers*")) + (buffer-disable-undo) (when (gnus-buffer-live-p gnus-summary-buffer) (message-clone-locals gnus-summary-buffer)) @@ -1533,7 +1607,7 @@ SCORE is the score to add." (match-func (if (or (eq type '>) (eq type '<) (eq type '<=) (eq type '>=) (eq type '=)) type - (error "Illegal match type: %s" type))) + (error "Invalid match type: %s" type))) (articles gnus-scores-articles)) ;; Instead of doing all the clever stuff that ;; `gnus-score-string' does to minimize searches and stuff, @@ -1593,7 +1667,7 @@ SCORE is the score to add." ((eq type 'regexp) (setq match-func 'string-match match (nth 0 kill))) - (t (error "Illegal match type: %s" type))) + (t (error "Invalid match type: %s" type))) ;; Instead of doing all the clever stuff that ;; `gnus-score-string' does to minimize searches and stuff, ;; I will assume that people generally will put so few @@ -1651,7 +1725,7 @@ SCORE is the score to add." (setq request-func 'gnus-request-article)) (while articles (setq article (mail-header-number (caar articles))) - (gnus-message 7 "Scoring on article %s of %s..." article last) + (gnus-message 7 "Scoring article %s of %s..." article last) (when (funcall request-func article gnus-newsgroup-name) (widen) (goto-char (point-min)) @@ -1691,7 +1765,7 @@ SCORE is the score to add." (eq type 'string) (eq type 'String)) 'search-forward) (t - (error "Illegal match type: %s" type))))) + (error "Invalid match type: %s" type))))) (goto-char (point-min)) (when (funcall search-func match nil t) ;; Found a match, update scores. @@ -1777,7 +1851,7 @@ SCORE is the score to add." (search-func (cond ((= dmt ?r) 're-search-forward) ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward) - (t (error "Illegal match type: %s" type)))) + (t (error "Invalid match type: %s" type)))) arts art) (goto-char (point-min)) (if (= dmt ?e) @@ -1858,12 +1932,23 @@ SCORE is the score to add." ;; and U is the number of unique headers. It is assumed (but ;; untested) this will be a net win because of the large constant ;; factor involved with string matching. - (setq gnus-scores-articles (sort gnus-scores-articles 'gnus-score-string<) + (setq gnus-scores-articles + ;; We cannot string-sort the extra headers list. *sigh* + (if (= gnus-score-index 9) + gnus-scores-articles + (sort gnus-scores-articles 'gnus-score-string<)) articles gnus-scores-articles) (erase-buffer) (while (setq art (pop articles)) (setq this (aref (car art) gnus-score-index)) + + ;; If we're working with non-standard headers, we are stuck + ;; with working on them as a group. What a hassle. + ;; Just wait 'til you see what horrors we commit against `match'... + (if (= gnus-score-index 9) + (setq this (prin1-to-string this))) ; ick. + (if simplify (setq this (gnus-map-function gnus-simplify-subject-functions this))) (if (equal last this) @@ -1894,11 +1979,12 @@ SCORE is the score to add." (type (or (nth 3 kill) 's)) (score (or (nth 1 kill) gnus-score-interactive-default-score)) (date (nth 2 kill)) + (extra (nth 4 kill)) ; non-standard header; string. (found nil) (mt (aref (symbol-name type) 0)) (case-fold-search (not (memq mt '(?R ?S ?E ?F)))) (dmt (downcase mt)) - ; Assume user already simplified regexp and fuzzies + ;; Assume user already simplified regexp and fuzzies (match (if (and simplify (not (memq dmt '(?f ?r)))) (gnus-map-function gnus-simplify-subject-functions @@ -1908,7 +1994,14 @@ SCORE is the score to add." (cond ((= dmt ?r) 're-search-forward) ((or (= dmt ?e) (= dmt ?s) (= dmt ?f)) 'search-forward) ((= dmt ?w) nil) - (t (error "Illegal match type: %s" type))))) + (t (error "Invalid match type: %s" type))))) + + ;; Evil hackery to make match usable in non-standard headers. + (when extra + (setq match (concat "[ (](" extra " \\. \"[^)]*" + match "[^(]*\")[ )]") + search-func 're-search-forward)) ; XXX danger?!? + (cond ;; Fuzzy matches. We save these for later. ((= dmt ?f) @@ -2203,9 +2296,9 @@ SCORE is the score to add." ;; Perform adaptive word scoring. (when (and (listp gnus-newsgroup-adaptive) (memq 'word gnus-newsgroup-adaptive)) - (nnheader-temp-write nil + (with-temp-buffer (let* ((hashtb (gnus-make-hashtable 1000)) - (date (gnus-day-number (current-time-string))) + (date (date-to-day (current-time-string))) (data gnus-newsgroup-data) (syntab (syntax-table)) word d score val) @@ -2241,7 +2334,7 @@ SCORE is the score to add." (let ((ignored (append gnus-ignored-adaptive-words (if gnus-adaptive-word-no-group-words (message-tokenize-header - (gnus-group-real-name + (gnus-group-real-name gnus-newsgroup-name) ".")) gnus-default-ignored-adaptive-words))) @@ -2283,12 +2376,10 @@ SCORE is the score to add." 1 "No score rules apply to the current article (default score %d)." gnus-summary-default-score) (set-buffer "*Score Trace*") - (gnus-add-current-to-buffer-list) + (setq truncate-lines t) (while trace (insert (format "%S -> %s\n" (cdar trace) - (if (caar trace) - (file-name-nondirectory (caar trace)) - "(non-file rule)"))) + (or (caar trace) "(non-file rule)"))) (setq trace (cdr trace))) (goto-char (point-min)) (gnus-configure-windows 'score-trace))) @@ -2329,7 +2420,6 @@ SCORE is the score to add." (while rules (insert (format "%-5d: %s\n" (caar rules) (cdar rules))) (pop rules)) - (gnus-add-current-to-buffer-list) (goto-char (point-min)) (gnus-configure-windows 'score-words)))) @@ -2467,8 +2557,8 @@ SCORE is the score to add." seen out file) (while (setq file (pop files)) (cond - ;; Ignore "." and "..". - ((member (file-name-nondirectory file) '("." "..")) + ;; Ignore files that start with a dot. + ((string-match "^\\." (file-name-nondirectory file)) nil) ;; Add subtrees of directory to also be searched. ((and (file-directory-p file) @@ -2498,10 +2588,11 @@ GROUP using BNews sys file syntax." (klen (length kill-dir)) (score-regexp (gnus-score-file-regexp)) (trans (cdr (assq ?: nnheader-file-name-translation-alist))) + (group-trans (nnheader-translate-file-chars group t)) ofiles not-match regexp) (save-excursion - (set-buffer (get-buffer-create "*gnus score files*")) - (buffer-disable-undo (current-buffer)) + (set-buffer (gnus-get-buffer-create "*gnus score files*")) + (buffer-disable-undo) ;; Go through all score file names and create regexp with them ;; as the source. (while sfiles @@ -2544,16 +2635,18 @@ GROUP using BNews sys file syntax." (if (looking-at "not.") (progn (setq not-match t) - (setq regexp (concat "^" (buffer-substring 5 (point-max)) "$"))) + (setq regexp + (concat "^" (buffer-substring 5 (point-max)) "$"))) (setq regexp (concat "^" (buffer-substring 1 (point-max)) "$")) (setq not-match nil)) ;; Finally - if this resulting regexp matches the group name, ;; we add this score file to the list of score files ;; applicable to this group. (when (or (and not-match - (not (string-match regexp group))) - (and (not not-match) - (string-match regexp group))) + (ignore-errors + (not (string-match regexp group-trans)))) + (and (not not-match) + (ignore-errors (string-match regexp group-trans)))) (push (car sfiles) ofiles))) (setq sfiles (cdr sfiles))) (kill-buffer (current-buffer)) @@ -2621,7 +2714,7 @@ Destroys the current buffer." (defun gnus-sort-score-files (files) "Sort FILES so that the most general files come first." - (nnheader-temp-write nil + (with-temp-buffer (let ((alist (mapcar (lambda (file) @@ -2792,8 +2885,8 @@ If ADAPT, return the home adaptive file instead." (funcall elem group)) ;; Regexp-file cons ((consp elem) - (when (string-match (car elem) group) - (cadr elem)))))) + (when (string-match (gnus-globalify-regexp (car elem)) group) + (replace-match (cadr elem) t nil group )))))) (when found (nnheader-concat gnus-kill-files-directory found)))) @@ -2813,6 +2906,10 @@ If ADAPT, return the home adaptive file instead." (concat group (if (gnus-use-long-file-name 'not-score) "." "/") gnus-adaptive-file-suffix))) +(defun gnus-current-home-score-file (group) + "Return the \"current\" regular score file." + (car (nreverse (gnus-score-find-alist group)))) + ;;; ;;; Score decays ;;; @@ -2829,7 +2926,7 @@ If ADAPT, return the home adaptive file instead." (defun gnus-decay-scores (alist day) "Decay non-permanent scores in ALIST." - (let ((times (- (gnus-time-to-day (current-time)) day)) + (let ((times (- (time-to-days (current-time)) day)) kill entry updated score n) (unless (zerop times) ;Done decays today already? (while (setq entry (pop alist)) @@ -2843,7 +2940,7 @@ If ADAPT, return the home adaptive file instead." n times) (while (natnump (decf n)) (setq score (funcall gnus-decay-score-function score))) - (setcdr kill (cons score + (setcdr kill (cons score (cdr (cdr kill))))))))) ;; Return whether this score file needs to be saved. By Je-haysuss! updated))