(shr-tag-li): Get <li> indentation right.
[gnus] / lisp / nnir.el
index e57b7d8..de304bf 100644 (file)
@@ -1,7 +1,7 @@
 ;;; nnir.el --- search mail with various search engines -*- coding: iso-8859-1 -*-
 
 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-;;   2007, 2008 Free Software Foundation, Inc.
+;;   2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Kai Großjohann <grossjohann@ls6.cs.uni-dortmund.de>
 ;; Swish-e and Swish++ backends by:
@@ -52,7 +52,7 @@
 
 
 ;; The most recent version of this can always be fetched from the Gnus
-;; CVS repository.  See http://www.gnus.org/ for more information.
+;; repository.  See http://www.gnus.org/ for more information.
 
 ;; This code is still in the development stage but I'd like other
 ;; people to have a look at it.  Please do not hesitate to contact me
 
 ;; I have tried to make the code expandable.  Basically, it is divided
 ;; into two layers.  The upper layer is somewhat like the `nnvirtual'
-;; or `nnkiboze' backends: given a specification of what articles to
-;; show from another backend, it creates a group containing exactly
-;; those articles.  The lower layer issues a query to a search engine
-;; and produces such a specification of what articles to show from the
+;; backend: given a specification of what articles to show from
+;; another backend, it creates a group containing exactly those
+;; articles.  The lower layer issues a query to a search engine and
+;; produces such a specification of what articles to show from the
 ;; other backend.
 
 ;; The interface between the two layers consists of the single
 (require 'gnus-sum)
 (require 'message)
 (require 'gnus-util)
-(eval-and-compile
+(eval-when-compile
   (require 'cl))
 
 (nnoo-declare nnir)
 (gnus-declare-backend "nnir" 'mail)
 
 (defvar nnir-imap-search-field "TEXT"
-  "The IMAP search item when doing an nnir search")
+  "The IMAP search item when doing an nnir search. To use raw
+  imap queries by default set this to \"\"")
 
 (defvar nnir-imap-search-arguments
   '(("Whole message" . "TEXT")
     ("Subject" . "SUBJECT")
     ("To" . "TO")
     ("From" . "FROM")
-    (nil . "HEADER \"%s\""))
+    ("Head" . "HEADER \"%s\"")
+    (nil . ""))
   "Mapping from user readable strings to IMAP search items for use in nnir")
 
 (defvar nnir-imap-search-argument-history ()
   "The history for querying search options in nnir")
 
+(defvar nnir-get-article-nov-override-function nil
+  "If non-nil, a function that will be passed each search result.  This
+should return a message's headers in NOV format.
+
+If this variable is nil, or if the provided function returns nil for a search
+result, `gnus-retrieve-headers' will be called instead.")
+
+
 ;;; Developer Extension Variable:
 
 (defvar nnir-engines
@@ -505,7 +515,7 @@ that it is for swish++, not Wais."
 ;; `nnir-swish-e-additional-switches'
 
 (make-obsolete-variable 'nnir-swish-e-index-file
-                       'nnir-swish-e-index-files)
+                       'nnir-swish-e-index-files "Emacs 23.1")
 (defcustom nnir-swish-e-index-file
   (expand-file-name "~/Mail/index.swish-e")
   "*Index file for swish-e.
@@ -658,6 +668,11 @@ that it is for Namazu, not Wais."
            gnus-current-window-configuration)
      nil)))
 
+(eval-when-compile
+  (when (featurep 'xemacs)
+    ;; The `kbd' macro requires that the `read-kbd-macro' macro is available.
+    (require 'edmacro)))
+
 (defun nnir-group-mode-hook ()
   (define-key gnus-group-mode-map (kbd "G G")
     'gnus-group-make-nnir-group))
@@ -685,7 +700,7 @@ The returned format is as `gnus-server-to-method' needs it.  See
 and show thread that contains this article."
   (interactive)
   (unless (eq 'nnir (car (gnus-find-method-for-group gnus-newsgroup-name)))
-    (error "Can't execute this command unless in nnir group."))
+    (error "Can't execute this command unless in nnir group"))
   (let* ((cur (gnus-summary-article-number))
          (group (nnir-artlist-artitem-group nnir-artlist cur))
          (backend-number (nnir-artlist-artitem-number nnir-artlist cur))
@@ -720,7 +735,7 @@ and show thread that contains this article."
   ;; Just set the server variables appropriately.
   (nnoo-change-server 'nnir server definitions))
 
-(deffoo nnir-request-group (group &optional server fast)
+(deffoo nnir-request-group (group &optional server fast info)
   "GROUP is the query string."
   (nnir-possibly-change-server server)
   ;; Check for cache and return that if appropriate.
@@ -731,8 +746,7 @@ and show thread that contains this article."
       nnir-artlist
     ;; Cache miss.
     (setq nnir-artlist (nnir-run-query group)))
-  (save-excursion
-    (set-buffer nntp-server-buffer)
+  (with-current-buffer nntp-server-buffer
     (if (zerop (length nnir-artlist))
        (progn
          (setq nnir-current-query nil
@@ -774,25 +788,31 @@ and show thread that contains this article."
        (nnir-possibly-change-server server)
         (let ((gnus-override-method
               (gnus-server-to-method server)))
-         (case (setq foo (gnus-retrieve-headers (list artno) artfullgroup nil))
-           (nov
-            (goto-char (point-min))
-            (setq novitem (nnheader-parse-nov))
-            (unless novitem
-              (pop-to-buffer nntp-server-buffer)
-              (error
-               "nnheader-parse-nov returned nil for article %s in group %s"
-               artno artfullgroup)))
-           (headers
-            (goto-char (point-min))
-            (setq novitem (nnheader-parse-head))
-            (unless novitem
-              (pop-to-buffer nntp-server-buffer)
-              (error
-               "nnheader-parse-head returned nil for article %s in group %s"
-               artno artfullgroup)))
-           (t (error "Unknown header type %s while requesting article %s of group %s"
-                     foo artno artfullgroup))))
+         ;; if nnir-get-article-nov-override-function is set, use it
+         (if nnir-get-article-nov-override-function
+             (setq novitem (funcall nnir-get-article-nov-override-function
+                                    artitem))
+         ;; else, set novitem through nnheader-parse-nov/nnheader-parse-head
+           (case (setq foo (gnus-retrieve-headers (list artno)
+                                                  artfullgroup nil))
+             (nov
+              (goto-char (point-min))
+              (setq novitem (nnheader-parse-nov))
+              (unless novitem
+                (pop-to-buffer nntp-server-buffer)
+                (error
+                 "nnheader-parse-nov returned nil for article %s in group %s"
+                 artno artfullgroup)))
+             (headers
+              (goto-char (point-min))
+              (setq novitem (nnheader-parse-head))
+              (unless novitem
+                (pop-to-buffer nntp-server-buffer)
+                (error
+                 "nnheader-parse-head returned nil for article %s in group %s"
+                 artno artfullgroup)))
+             (t (error "Unknown header type %s while requesting article %s of group %s"
+                       foo artno artfullgroup)))))
        ;; replace article number in original group with article number
         ;; in nnir group
         (mail-header-set-number novitem art)
@@ -861,7 +881,9 @@ ready to be added to the list of search results."
   (when (file-readable-p (concat prefix dirnam article))
     ;; remove trailing slash and, for nnmaildir, cur/new/tmp
     (setq dirnam
-         (substring dirnam 0 (if (string= server "nnmaildir:") -5 -1)))
+         (substring dirnam 0
+                    (if (string= (gnus-group-server server) "nnmaildir")
+                        -5 -1)))
 
     ;; Set group to dirnam without any leading dots or slashes,
     ;; and with all subsequent slashes replaced by dots
@@ -870,7 +892,7 @@ ready to be added to the list of search results."
                  "[/\\]" "." t)))
 
     (vector (nnir-group-full-name group server)
-           (if (string= server "nnmaildir:")
+           (if (string= (gnus-group-server server) "nnmaildir")
                (nnmaildir-base-name-to-article-number
                 (substring article 0 (string-match ":" article))
                 group nil)
@@ -884,7 +906,7 @@ ready to be added to the list of search results."
   "Run given query agains waissearch.  Returns vector of (group name, file name)
 pairs (also vectors, actually)."
   (when group
-    (error "The freeWAIS-sf backend cannot search specific groups."))
+    (error "The freeWAIS-sf backend cannot search specific groups"))
   (save-excursion
     (let ((qstring (cdr (assq 'query query)))
          (prefix (nnir-read-server-parm 'nnir-wais-remove-prefix server))
@@ -911,17 +933,18 @@ pairs (also vectors, actually)."
         (unless (string-match prefix dirnam)
           (nnheader-report 'nnir "Dir name %s doesn't contain prefix %s"
                            dirnam prefix))
-        (setq group (substitute ?. ?/ (replace-match "" t t dirnam)))
+        (setq group (gnus-replace-in-string
+                     (replace-match "" t t dirnam) "/" "."))
         (push (vector (nnir-group-full-name group server)
                       (string-to-number artno)
                       (string-to-number score))
               artlist))
       (message "Massaging waissearch output...done")
       (apply 'vector
-             (sort* artlist
-                    (function (lambda (x y)
-                                (> (nnir-artitem-rsv x)
-                                   (nnir-artitem-rsv y)))))))))
+             (sort artlist
+                   (function (lambda (x y)
+                               (> (nnir-artitem-rsv x)
+                                  (nnir-artitem-rsv y)))))))))
 
 ;; IMAP interface.
 ;; todo:
@@ -937,6 +960,11 @@ pairs (also vectors, actually)."
 (autoload 'imap-search "imap")
 (autoload 'imap-quote-specials "imap")
 
+(eval-when-compile
+  (autoload 'nnimap-buffer "nnimap")
+  (autoload 'nnimap-command "nnimap")
+  (autoload 'nnimap-possibly-change-group "nnimap"))
+
 (defun nnir-run-imap (query srv &optional group-option)
   "Run a search against an IMAP back-end server.
 This uses a custom query language parser; see `nnir-imap-make-query' for
@@ -948,23 +976,30 @@ details on the language and supported extensions"
          (defs (caddr (gnus-server-to-method srv)))
          (criteria (or (cdr (assq 'criteria query))
                        nnir-imap-search-field))
-         artlist buf)
+         (gnus-inhibit-demon t)
+         artlist)
       (message "Opening server %s" server)
       (condition-case ()
-         (when (nnimap-open-server server defs) ;; xxx
-           (setq buf nnimap-server-buffer) ;; xxx
-           (message "Searching %s..." group)
-            (let ((arts 0)
-                  (mbx (gnus-group-real-name group)))
-              (when (imap-mailbox-select mbx nil buf)
-                (mapc
-                 (lambda (artnum)
-                   (push (vector group artnum 1) artlist)
-                   (setq arts (1+ arts)))
-                 (imap-search (nnir-imap-make-query criteria qstring) buf))
-                (message "Searching %s... %d matches" mbx arts)))
-            (message "Searching %s...done" group))
-        (quit nil))
+         (when (nnimap-possibly-change-group (gnus-group-short-name group) server)
+           (with-current-buffer (nnimap-buffer)
+             (message "Searching %s..." group)
+             (let ((arts 0)
+                   (result
+                    (nnimap-command "UID SEARCH  %s"
+                                    (if (string= criteria "")
+                                        qstring
+                                      (nnir-imap-make-query criteria qstring)
+                                      ))))
+               (mapc
+                (lambda (artnum)
+                  (push (vector group artnum 1) artlist)
+                  (setq arts (1+ arts)))
+                (and (car result)
+                     (delete 0 (mapcar #'string-to-number
+                                       (cdr (assoc "SEARCH" (cdr result)))))))
+               (message "Searching %s... %d matches" group arts)))
+           (message "Searching %s...done" group))
+       (quit nil))
       (reverse artlist))))
 
 (defun nnir-imap-make-query (criteria qstring)
@@ -1156,7 +1191,7 @@ Tested with swish++ 4.7 on GNU/Linux and with swish++ 5.0b2 on
 Windows NT 4.0."
 
   (when group
-    (error "The swish++ backend cannot search specific groups."))
+    (error "The swish++ backend cannot search specific groups"))
 
   (save-excursion
     (let ( (qstring (cdr (assq 'query query)))
@@ -1167,13 +1202,13 @@ Windows NT 4.0."
           ;; is sufficient.  Note that we can't only use the value of
           ;; nnml-use-compressed-files because old articles might have been
           ;; saved with a different value.
-          (article-pattern (if (string= server "nnmaildir:")
+          (article-pattern (if (string= (gnus-group-server server) "nnmaildir")
                                ":[0-9]+"
                              "^[0-9]+\\(\\.[a-z0-9]+\\)?$"))
            score artno dirnam filenam)
 
       (when (equal "" qstring)
-        (error "swish++: You didn't enter anything."))
+        (error "swish++: You didn't enter anything"))
 
       (set-buffer (get-buffer-create nnir-tmp-buffer))
       (erase-buffer)
@@ -1230,10 +1265,10 @@ Windows NT 4.0."
 
       ;; Sort by score
       (apply 'vector
-             (sort* artlist
-                    (function (lambda (x y)
-                                (> (nnir-artitem-rsv x)
-                                   (nnir-artitem-rsv y)))))))))
+             (sort artlist
+                   (function (lambda (x y)
+                               (> (nnir-artitem-rsv x)
+                                  (nnir-artitem-rsv y)))))))))
 
 ;; Swish-E interface.
 (defun nnir-run-swish-e (query server &optional group)
@@ -1245,7 +1280,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
 
   ;; swish-e crashes with empty parameter to "-w" on commandline...
   (when group
-    (error "The swish-e backend cannot search specific groups."))
+    (error "The swish-e backend cannot search specific groups"))
 
   (save-excursion
     (let ((qstring (cdr (assq 'query query)))
@@ -1255,7 +1290,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
           artlist score artno dirnam group )
 
       (when (equal "" qstring)
-        (error "swish-e: You didn't enter anything."))
+        (error "swish-e: You didn't enter anything"))
 
       (set-buffer (get-buffer-create nnir-tmp-buffer))
       (erase-buffer)
@@ -1311,9 +1346,9 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
            ;; eliminate all ".", "/", "\" from beginning. Always matches.
             (string-match "^[./\\]*\\(.*\\)$" dirnam)
             ;; "/" -> "."
-            (setq group (substitute ?. ?/ (match-string 1 dirnam)))
+            (setq group (gnus-replace-in-string (match-string 1 dirnam) "/" "."))
             ;; Windows "\\" -> "."
-            (setq group (substitute ?. ?\\ group))
+            (setq group (gnus-replace-in-string group "\\\\" "."))
 
             (push (vector (nnir-group-full-name group server)
                           (string-to-number artno)
@@ -1324,10 +1359,10 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
 
       ;; Sort by score
       (apply 'vector
-             (sort* artlist
-                    (function (lambda (x y)
-                                (> (nnir-artitem-rsv x)
-                                   (nnir-artitem-rsv y)))))))))
+             (sort artlist
+                   (function (lambda (x y)
+                               (> (nnir-artitem-rsv x)
+                                  (nnir-artitem-rsv y)))))))))
 
 ;; HyREX interface
 (defun nnir-run-hyrex (query server &optional group)
@@ -1392,19 +1427,20 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
              score (match-string 3))
        (when (string-match prefix dirnam)
          (setq dirnam (replace-match "" t t dirnam)))
-       (push (vector (nnir-group-full-name (substitute ?. ?/ dirnam) server)
+       (push (vector (nnir-group-full-name
+                       (gnus-replace-in-string dirnam "/" ".") server)
                      (string-to-number artno)
                      (string-to-number score))
              artlist))
       (message "Massaging hyrex-search output...done.")
       (apply 'vector
-            (sort* artlist
-                   (function (lambda (x y)
-                               (if (string-lessp (nnir-artitem-group x)
-                                                 (nnir-artitem-group y))
-                                   t
-                                 (< (nnir-artitem-number x)
-                                    (nnir-artitem-number y)))))))
+            (sort artlist
+                   (function (lambda (x y)
+                               (if (string-lessp (nnir-artitem-group x)
+                                                 (nnir-artitem-group y))
+                                   t
+                                 (< (nnir-artitem-number x)
+                                    (nnir-artitem-number y)))))))
       )))
 
 ;; Namazu interface
@@ -1416,7 +1452,7 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
   (when group
     (error "The Namazu backend cannot search specific groups"))
   (save-excursion
-    (let ((article-pattern (if (string= server "nnmaildir:")
+    (let ((article-pattern (if (string= (gnus-group-server server) "nnmaildir")
                               ":[0-9]+"
                             "^[0-9]+$"))
           artlist
@@ -1471,10 +1507,10 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
 
       ;; sort artlist by score
       (apply 'vector
-             (sort* artlist
-                    (function (lambda (x y)
-                                (> (nnir-artitem-rsv x)
-                                   (nnir-artitem-rsv y)))))))))
+             (sort artlist
+                   (function (lambda (x y)
+                               (> (nnir-artitem-rsv x)
+                                  (nnir-artitem-rsv y)))))))))
 
 (defun nnir-run-find-grep (query server &optional group)
   "Run find and grep to obtain matching articles."
@@ -1500,11 +1536,14 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
                 "."
               ;; Try accessing the group literally as well as
               ;; interpreting dots as directory separators so the
-              ;; engine works with plain nnml as well as the Gnus
-              ;; Cache.
-              (find-if 'file-directory-p
-               (let ((group (gnus-group-real-name group)))
-                 (list group (gnus-replace-in-string group "\\." "/" t)))))))
+              ;; engine works with plain nnml as well as the Gnus Cache.
+               (let ((group (gnus-group-real-name group)))
+                 ;; Replace cl-func find-if.
+                 (if (file-directory-p group)
+                     group
+                   (if (file-directory-p
+                        (setq group (gnus-replace-in-string group "\\." "/" t)))
+                       group))))))
        (unless group
          (error "Cannot locate directory for group"))
        (save-excursion
@@ -1512,7 +1551,8 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
           'call-process "find" nil t
           "find" group "-type" "f" "-name" "[0-9]*" "-exec"
           "grep"
-          `("-l" ,@(and grep-options (split-string grep-options "\\s-" t))
+          `("-l" ,@(and grep-options
+                        (split-string grep-options "\\s-" t))
             "-e" ,regexp "{}" "+"))))
 
       ;; Translate relative paths to group names.
@@ -1522,7 +1562,14 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
               (art (string-to-number (car (last path)))))
          (while (string= "." (car path))
            (setq path (cdr path)))
-         (let ((group (mapconcat 'identity (subseq path 0 -1) ".")))
+         (let ((group (mapconcat 'identity
+                                  ;; Replace cl-func: (subseq path 0 -1)
+                                  (let ((end (1- (length path)))
+                                        res)
+                                    (while (>= (setq end (1- end)) 0)
+                                      (push (pop path) res))
+                                    (nreverse res))
+                                  ".")))
            (push (vector (nnir-group-full-name group server) art 0)
                  artlist))
          (forward-line 1)))
@@ -1543,7 +1590,7 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
   (let ((sym (car parmspec))
         (prompt (cdr parmspec)))
     (if (listp prompt)
-       (let* ((result (apply 'completing-read prompt))
+       (let* ((result (gnus-completing-read prompt nil))
               (mapping (or (assoc result nnir-imap-search-arguments)
                            (assoc nil nnir-imap-search-arguments))))
          (cons sym (format (cdr mapping) result)))
@@ -1661,5 +1708,4 @@ The Gnus backend/server information is added."
 ;; The end.
 (provide 'nnir)
 
-;; arch-tag: 9b3fecf8-4397-4bbb-bf3c-6ac3cbbc6664
 ;;; nnir.el ends here