view lisp/gnus/gnus-logic.el @ 38717:152fbfce2115

(start_display): Don't try to determine the continuation lines width if lines are truncated. It's superfluous and leads to incorrect results if POS is not visible in the window after reseating the iterator at the previous line start.
author Gerd Moellmann <gerd@gnu.org>
date Tue, 07 Aug 2001 11:36:43 +0000
parents a26d9b55abb6
children 695cf19ef79e d7ddb3e565de
line wrap: on
line source

;;; gnus-logic.el --- advanced scoring code for Gnus
;; Copyright (C) 1996, 1997, 1998, 1999, 2000
;;        Free Software Foundation, Inc.

;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: news

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;;; Code:

(eval-when-compile (require 'cl))

(require 'gnus)
(require 'gnus-score)
(require 'gnus-util)

;;; Internal variables.

(defvar gnus-advanced-headers nil)

;; To avoid having 8-bit characters in the source file.
(defvar gnus-advanced-not (intern (format "%c" 172)))

(defconst gnus-advanced-index
  ;; Name to index alist.
  '(("number" 0 gnus-advanced-integer)
    ("subject" 1 gnus-advanced-string)
    ("from" 2 gnus-advanced-string)
    ("date" 3 gnus-advanced-date)
    ("message-id" 4 gnus-advanced-string)
    ("references" 5 gnus-advanced-string)
    ("chars" 6 gnus-advanced-integer)
    ("lines" 7 gnus-advanced-integer)
    ("xref" 8 gnus-advanced-string)
    ("head" nil gnus-advanced-body)
    ("body" nil gnus-advanced-body)
    ("all" nil gnus-advanced-body)))

(eval-and-compile
  (autoload 'parse-time-string "parse-time"))

(defun gnus-score-advanced (rule &optional trace)
  "Apply advanced scoring RULE to all the articles in the current group."
  (let ((headers gnus-newsgroup-headers)
	gnus-advanced-headers score)
    (while (setq gnus-advanced-headers (pop headers))
      (when (gnus-advanced-score-rule (car rule))
	;; This rule was successful, so we add the score to
	;; this article.
	(if (setq score (assq (mail-header-number gnus-advanced-headers)
			      gnus-newsgroup-scored))
	    (setcdr score
		    (+ (cdr score)
		       (or (nth 1 rule)
			   gnus-score-interactive-default-score)))
	  (push (cons (mail-header-number gnus-advanced-headers)
		      (or (nth 1 rule)
			  gnus-score-interactive-default-score))
		gnus-newsgroup-scored)
	  (when trace
	    (push (cons "A file" rule)
		  gnus-score-trace)))))))

(defun gnus-advanced-score-rule (rule)
  "Apply RULE to `gnus-advanced-headers'."
  (let ((type (car rule)))
    (cond
     ;; "And" rule.
     ((or (eq type '&) (eq type 'and))
      (pop rule)
      (if (not rule)
	  t				; Empty rule is true.
	(while (and rule
		    (gnus-advanced-score-rule (car rule)))
	  (pop rule))
	;; If all the rules were true, then `rule' should be nil.
	(not rule)))
     ;; "Or" rule.
     ((or (eq type '|) (eq type 'or))
      (pop rule)
      (if (not rule)
	  nil
	(while (and rule
		    (not (gnus-advanced-score-rule (car rule))))
	  (pop rule))
	;; If one of the rules returned true, then `rule' should be non-nil.
	rule))
     ;; "Not" rule.
     ((or (eq type '!) (eq type 'not) (eq type gnus-advanced-not))
      (not (gnus-advanced-score-rule (nth 1 rule))))
     ;; This is a `1-'-type redirection rule.
     ((and (symbolp type)
	   (string-match "^[0-9]+-$\\|^\\^+$" (symbol-name type)))
      (let ((gnus-advanced-headers
	     (gnus-parent-headers
	      gnus-advanced-headers
	      (if (string-match "^\\([0-9]+\\)-$" (symbol-name type))
		  ;; 1- type redirection.
		  (string-to-number
		   (substring (symbol-name type)
			      (match-beginning 0) (match-end 0)))
		;; ^^^ type redirection.
		(length (symbol-name type))))))
	(when gnus-advanced-headers
	  (gnus-advanced-score-rule (nth 1 rule)))))
     ;; Plain scoring rule.
     ((stringp type)
      (gnus-advanced-score-article rule))
     ;; Bug-out time!
     (t
      (error "Unknown advanced score type: %s" rule)))))

(defun gnus-advanced-score-article (rule)
  ;; `rule' is a semi-normal score rule, so we find out
  ;; what function that's supposed to do the actual
  ;; processing.
  (let* ((header (car rule))
	 (func (assoc (downcase header) gnus-advanced-index)))
    (if (not func)
	(error "No such header: %s" rule)
      ;; Call the score function.
      (funcall (caddr func) (or (cadr func) header)
	       (cadr rule) (caddr rule)))))

(defun gnus-advanced-string (index match type)
  "See whether string MATCH of TYPE matches `gnus-advanced-headers' in INDEX."
  (let* ((type (or type 's))
	 (case-fold-search (not (eq (downcase (symbol-name type))
				    (symbol-name type))))
	 (header (or (aref gnus-advanced-headers index) "")))
    (cond
     ((memq type '(r R regexp Regexp))
      (string-match match header))
     ((memq type '(s S string String))
      (string-match (regexp-quote match) header))
     ((memq type '(e E exact Exact))
      (string= match header))
     ((memq type '(f F fuzzy Fuzzy))
      (string-match (regexp-quote (gnus-simplify-subject-fuzzy match))
		    header))
     (t
      (error "No such string match type: %s" type)))))

(defun gnus-advanced-integer (index match type)
  (if (not (memq type '(< > <= >= =)))
      (error "No such integer score type: %s" type)
    (funcall type match (or (aref gnus-advanced-headers index) 0))))

(defun gnus-advanced-date (index match type)
  (let ((date (apply 'encode-time (parse-time-string
				   (aref gnus-advanced-headers index))))
	(match (apply 'encode-time (parse-time-string match))))
    (cond
     ((eq type 'at)
      (equal date match))
     ((eq type 'before)
      (time-less-p match date))
     ((eq type 'after)
      (time-less-p date match))
     (t
      (error "No such date score type: %s" type)))))

(defun gnus-advanced-body (header match type)
  (when (string= header "all")
    (setq header "article"))
  (save-excursion
    (set-buffer nntp-server-buffer)
    (let* ((request-func (cond ((string= "head" header)
				'gnus-request-head)
			       ((string= "body" header)
				'gnus-request-body)
			       (t 'gnus-request-article)))
	   ofunc article)
      ;; Not all backends support partial fetching.  In that case,
      ;; we just fetch the entire article.
      (unless (gnus-check-backend-function
	       (intern (concat "request-" header))
	       gnus-newsgroup-name)
	(setq ofunc request-func)
	(setq request-func 'gnus-request-article))
      (setq article (mail-header-number gnus-advanced-headers))
      (gnus-message 7 "Scoring article %s..." article)
      (when (funcall request-func article gnus-newsgroup-name)
	(goto-char (point-min))
	;; If just parts of the article is to be searched and the
	;; backend didn't support partial fetching, we just narrow
	;; to the relevant parts.
	(when ofunc
	  (if (eq ofunc 'gnus-request-head)
	      (narrow-to-region
	       (point)
	       (or (search-forward "\n\n" nil t) (point-max)))
	    (narrow-to-region
	     (or (search-forward "\n\n" nil t) (point))
	     (point-max))))
	(let* ((case-fold-search (not (eq (downcase (symbol-name type))
					  (symbol-name type))))
	       (search-func
		(cond ((memq type '(r R regexp Regexp))
		       're-search-forward)
		      ((memq type '(s S string String))
		       'search-forward)
		      (t
		       (error "Invalid match type: %s" type)))))
	  (goto-char (point-min))
	  (prog1
	      (funcall search-func match nil t)
	    (widen)))))))

(provide 'gnus-logic)

;;; gnus-logic.el ends here