Merge from emacs--devo--0
[gnus] / contrib / nnir.el
index 0c5f236..54d7ef7 100644 (file)
@@ -1,14 +1,20 @@
 ;;; nnir.el --- search mail with various search engines -*- coding: iso-8859-1 -*-
-;; Copyright (C) 1998 Kai Großjohann
+
+;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006, 2007 Free Software Foundation, Inc.
 
 ;; Author: Kai Großjohann <grossjohann@ls6.cs.uni-dortmund.de>
-;; Keywords: news, mail, searching, ir, glimpse, wais, hyrex
+;; Swish-e and Swish++ backends by: 
+;;   Christoph Conrad <christoph.conrad@gmx.de>.
+;; Imap backend by: Simon Josefsson <jas@pdc.kth.se>.
+
+;; Keywords: news mail searching ir
 
-;; This file is not part of GNU Emacs.
+;; This file is part of GNU Emacs.
 
 ;; This 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)
+;; the Free Software Foundation; either version 3, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
@@ -18,8 +24,8 @@
 
 ;; 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.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -31,7 +37,7 @@
 ;; with your ideas.
 
 ;; What does it do?  Well, it allows you to index your mail using some
-;; search engine (freeWAIS-sf, Glimpse and others -- see later),
+;; search engine (freeWAIS-sf, swish-e and others -- see later),
 ;; then type `G G' in the Group buffer and issue a query to the search
 ;; engine.  You will then get a buffer which shows all articles
 ;; matching the query, sorted by Retrieval Status Value (score).
@@ -55,7 +61,6 @@
 ;;   others doesn't support nnfolder.
 ;; * It can only search the mail backend's which are supported by one
 ;;   search engine, because of different query languages.
-;; * There are restrictions to the Glimpse setup.
 ;; * There are restrictions to the Wais setup.
 ;; * There are restrictions to the imap setup.
 ;; * gnus-summary-nnir-goto-thread: Fetches whole group first, before
 ;; this prefix.  It defaults to `$HOME/Mail/' (note the trailing
 ;; slash).
 
-;; 2. Glimpse
-;;
-;; The code expects you to have one Glimpse index which contains all
-;; your mail files.  The Lisp setup involves setting the
-;; `nnir-glimpse-*' variables.  The most difficult to understand
-;; variable is probably `nnir-glimpse-remove-prefix', it corresponds
-;; to `nnir-wais-remove-prefix', see above.  The `nnir-glimpse-home'
-;; variable should be set to the value of the `-H' option which allows
-;; one to search this Glimpse index.  I have indexed my whole home
-;; directory with Glimpse, so I assume a default of `$HOME'.
-
-;; 3. Namazu
+;; 2. Namazu
 ;;
 ;; The Namazu backend requires you to have one directory containing all
 ;; index files, this is controlled by the `nnir-namazu-index-directory'
 ;; For maximum searching efficiency I have a cron job set to run this
 ;; command every four hours.
 
-;; 4. HyREX
+;; 3. HyREX
 ;;
 ;; The HyREX backend requires you to have one directory from where all
 ;; your relative paths are to, if you use them. This directory must be
 ;; To function the `nnir-hyrex-remove-prefix' variable must also be
 ;; correct, see the documentation for `nnir-wais-remove-prefix' above.
 
+;; 4. find-grep
+;;
+;; The find-grep engine simply runs find(1) to locate eligible
+;; articles and searches them with grep(1).  This, of course, is much
+;; slower than using a proper search engine but OTOH doesn't require
+;; maintenance of an index and is still faster than using any built-in
+;; means for searching.  The method specification of the server to
+;; search must include a directory for this engine to work (E.g.,
+;; `nnml-directory').  The tools must be POSIX compliant.  GNU Find
+;; prior to version 4.2.12 (4.2.26 on Linux due to incorrect ARG_MAX
+;; handling) does not work.
+;; ,----
+;; |    ;; find-grep configuration for searching the Gnus Cache
+;; |
+;; |   (nnml "cache"
+;; |          (nnml-get-new-mail nil)
+;; |          (nnir-search-engine find-grep)
+;; |          (nnml-directory "~/News/cache/")
+;; |          (nnml-active-file "~/News/cache/active"))
+;; `----
+
 ;; Developer information:
 
 ;; I have tried to make the code expandable.  Basically, it is divided
 ;; the second element should be the article number, and the third
 ;; element should be the Retrieval Status Value (RSV) as returned from
 ;; the search engine.  An RSV is the score assigned to the document by
-;; the search engine.  For Boolean search engines like Glimpse, the
+;; the search engine.  For Boolean search engines, the
 ;; RSV is always 1000 (or 1 or 100, or whatever you like).
 
 ;; The sorting order of the articles in the summary buffer created by
 
 ;; Todo, or future ideas:
 
-;; * Make it so that Glimpse can also be called without `-F'.
-;;
 ;; * It should be possible to restrict search to certain groups.
 ;;
 ;; * There is currently no error checking.
 ;; * Support compressed mail files.  Probably, just stripping off the
 ;;   `.gz' or `.Z' file name extension is sufficient.
 ;;
-;; * Support a find/grep combination.
-;;
 ;; * At least for imap, the query is performed twice.
 ;;
 
 
 ;;; Setup Code:
 
-(defconst nnir-version "$Id: nnir.el,v 1.2 2003/09/30 21:30:34 kaig Exp $"
-  "Version of NNIR.")
-
 (require 'cl)
 (require 'nnoo)
 (require 'gnus-group)
 (require 'gnus-sum)
+(require 'message)
 (eval-and-compile
   (require 'gnus-util))
 (eval-when-compile
 ;;; Developer Extension Variable:
 
 (defvar nnir-engines
-  `((glimpse nnir-run-glimpse
-             ((group . "Group spec: ")))
-    (wais    nnir-run-waissearch
+  `((wais    nnir-run-waissearch
              ())
-    (excite  nnir-run-excite-search
-            ())
     (imap    nnir-run-imap
              ((criteria 
               "Search in: "                      ; Prompt
     (namazu  nnir-run-namazu
              ())
     (hyrex   nnir-run-hyrex
-            ((group . "Group spec: "))))
+            ((group . "Group spec: ")))
+  (find-grep nnir-run-find-grep
+            ((grep-options . "Grep options: "))))
   "Alist of supported search engines.
 Each element in the alist is a three-element list (ENGINE FUNCTION ARGS).
 ENGINE is a symbol designating the searching engine.  FUNCTION is also
@@ -379,7 +386,8 @@ Add an entry here when adding a new search engine.")
 ;;; User Customizable Variables:
 
 (defgroup nnir nil
-  "Search nnmh and nnml groups in Gnus with Glimpse, freeWAIS-sf, or EWS.")
+  "Search nnmh and nnml groups in Gnus with swish-e, freeWAIS-sf, or EWS."
+  :group 'gnus)
 
 ;; Mail backend.
 
@@ -409,47 +417,6 @@ settings of `nnir-search-engine'."
   :type '(sexp)
   :group 'nnir)
 
-;; Glimpse engine.
-
-(defcustom nnir-glimpse-program "glimpse"
-  "*Name of Glimpse executable."
-  :type '(string)
-  :group 'nnir)
-
-(defcustom nnir-glimpse-home (getenv "HOME")
-  "*Value of `-H' glimpse option.
-`~' and environment variables must be expanded, see the functions
-`expand-file-name' and `substitute-in-file-name'."
-  :type '(directory)
-  :group 'nnir)
-
-(defcustom nnir-glimpse-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "*The prefix to remove from each file name returned by Glimpse
-in order to get a group name (albeit with / instead of .).  This is a
-regular expression.
-
-For example, suppose that Glimpse returns file names such as
-\"/home/john/Mail/mail/misc/42\".  For this example, use the following
-setting:  (setq nnir-glimpse-remove-prefix \"/home/john/Mail/\")
-Note the trailing slash.  Removing this prefix gives \"mail/misc/42\".
-`nnir' knows to remove the \"/42\" and to replace \"/\" with \".\" to
-arrive at the correct group name, \"mail.misc\"."
-  :type '(regexp)
-  :group 'nnir)
-
-(defcustom nnir-glimpse-additional-switches '("-i")
-  "*A list of strings, to be given as additional arguments to glimpse.
-The switches `-H', `-W', `-l' and `-y' are always used -- calling
-glimpse without them does not make sense in our situation.
-Suggested elements to put here are `-i' and `-w'.
-
-Note that this should be a list.  Ie, do NOT use the following:
-    (setq nnir-glimpse-additional-switches \"-i -w\") ; wrong!
-Instead, use this:
-    (setq nnir-glimpse-additional-switches '(\"-i\" \"-w\"))"
-  :type '(repeat (string))
-  :group 'nnir)
-
 ;; freeWAIS-sf.
 
 (defcustom nnir-wais-program "waissearch"
@@ -472,37 +439,15 @@ The string given here is passed to `waissearch -d' as-is."
 in order to get a group name (albeit with / instead of .).  This is a
 regular expression.
 
-This variable is similar to `nnir-glimpse-remove-prefix', only for Wais,
-not Glimpse."
-  :type '(regexp)
-  :group 'nnir)
-
-;; EWS (Excite for Web Servers) engine.
-
-(defcustom nnir-excite-aquery-program "aquery.pl"
-  "*Name of the EWS query program.  Should be `aquery.pl' or a path to same."
-  :type '(string)
-  :group 'nnir)
-
-(defcustom nnir-excite-collection "Mail"
-  "*Name of the EWS collection to search."
-  :type '(string)
-  :group 'nnir)
-
-(defcustom nnir-excite-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "*The prefix to remove from each file name returned by EWS
-in order to get a group name (albeit with / instead of .).  This is a
-regular expression.
-
-This variable is very similar to `nnir-glimpse-remove-prefix', except
-that it is for EWS, not Glimpse."
+For example, suppose that Wais returns file names such as
+\"/home/john/Mail/mail/misc/42\".  For this example, use the following
+setting:  (setq nnir-wais-remove-prefix \"/home/john/Mail/\")
+Note the trailing slash.  Removing this prefix gives \"mail/misc/42\".
+`nnir' knows to remove the \"/42\" and to replace \"/\" with \".\" to
+arrive at the correct group name, \"mail.misc\"."
   :type '(regexp)
   :group 'nnir)
 
-;; Swish++.  Next three variables Copyright (C) 2000, 2001 Christoph
-;; Conrad <christoph.conrad@gmx.de>.
-;; Swish++ home page: http://homepage.mac.com/pauljlucas/software/swish/
-
 (defcustom nnir-swish++-configuration-file
   (expand-file-name "~/Mail/swish++.conf")
   "*Configuration file for swish++."
@@ -529,23 +474,35 @@ Instead, use this:
 in order to get a group name (albeit with / instead of .).  This is a
 regular expression.
 
-This variable is very similar to `nnir-glimpse-remove-prefix', except
-that it is for swish++, not Glimpse."
+This variable is very similar to `nnir-wais-remove-prefix', except
+that it is for swish++, not Wais."
   :type '(regexp)
   :group 'nnir)
 
-;; Swish-E.  Next three variables Copyright (C) 2000 Christoph Conrad
-;; <christoph.conrad@gmx.de>.
+;; Swish-E.
 ;; URL: http://sunsite.berkeley.edu/SWISH-E/
 ;; New version: http://www.boe.es/swish-e
+;; Variables `nnir-swish-e-index-file', `nnir-swish-e-program' and
+;; `nnir-swish-e-additional-switches'
 
+(make-obsolete-variable 'nnir-swish-e-index-file
+                       'nnir-swish-e-index-files)
 (defcustom nnir-swish-e-index-file
   (expand-file-name "~/Mail/index.swish-e")
   "*Index file for swish-e.
-This could be a server parameter."
+This could be a server parameter.
+It is never consulted once `nnir-swish-e-index-files', which should be
+used instead, has been customized."
   :type '(file)
   :group 'nnir)
 
+(defcustom nnir-swish-e-index-files
+  (list nnir-swish-e-index-file)
+  "*List of index files for swish-e.
+This could be a server parameter."
+  :type '(repeat (file))
+  :group 'nnir)
+
 (defcustom nnir-swish-e-program "swish-e"
   "*Name of swish-e search executable.
 This cannot be a server parameter."
@@ -569,8 +526,8 @@ This could be a server parameter."
 in order to get a group name (albeit with / instead of .).  This is a
 regular expression.
 
-This variable is very similar to `nnir-glimpse-remove-prefix', except
-that it is for swish-e, not Glimpse.
+This variable is very similar to `nnir-wais-remove-prefix', except
+that it is for swish-e, not Wais.
 
 This could be a server parameter."
   :type '(regexp)
@@ -638,8 +595,8 @@ Instead, use this:
   "*The prefix to remove from each file name returned by Namazu
 in order to get a group name (albeit with / instead of .).
 
-This variable is very similar to `nnir-glimpse-remove-prefix', except
-that it is for Namazu, not Glimpse."
+This variable is very similar to `nnir-wais-remove-prefix', except
+that it is for Namazu, not Wais."
   :type '(directory)
   :group 'nnir)
 
@@ -675,6 +632,7 @@ that it is for Namazu, not Glimpse."
     (if extra-parms
         (setq parms (nnir-read-parms query))
       (setq parms (list (cons 'query query))))
+    (add-to-list 'parms (cons 'unique-id (message-unique-id)) t)
     (gnus-group-read-ephemeral-group
      (concat "nnir:" (prin1-to-string parms)) '(nnir "") t
      (cons (current-buffer)
@@ -692,7 +650,18 @@ that it is for Namazu, not Glimpse."
     'gnus-group-make-nnir-group))
 (add-hook 'gnus-group-mode-hook 'nnir-group-mode-hook)
 
-
+(defmacro nnir-group-server (group)
+  "Return the server for a newsgroup GROUP.
+The returned format is as `gnus-server-to-method' needs it.  See
+`gnus-group-real-prefix' and `gnus-group-real-name'."
+  `(let ((gname ,group))
+     (if (string-match "^\\([^:]+\\):" gname)
+        (progn
+          (setq gname (match-string 1 gname))
+          (if (string-match "^\\([^+]+\\)\\+\\(.+\\)$" gname)
+              (format "%s:%s" (match-string 1 gname) (match-string 2 gname))
+            (concat gname ":")))
+       "native")))
 
 ;; Summary mode commands.
 
@@ -769,7 +738,6 @@ and show thread that contains this article."
 (deffoo nnir-retrieve-headers (articles &optional group server fetch-old)
   (save-excursion
     (let ((artlist (copy-sequence articles))
-          (idx 1)
           (art nil)
           (artitem nil)
           (artgroup nil) (artno nil)
@@ -796,27 +764,30 @@ and show thread that contains this article."
         ;; NOV data and prepend to `novdata'
         (set-buffer nntp-server-buffer)
        (nnir-possibly-change-server 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 (nnheader-report 'nnir "Don't support header type %s." foo)))
-       ;; replace article number in original group with article number
+        (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))))
+       ;; replace article number in original group with article number
         ;; in nnir group
-        (mail-header-set-number novitem idx)
+        (mail-header-set-number novitem art)
         (mail-header-set-from novitem
                               (mail-header-from novitem))
         (mail-header-set-subject
@@ -826,8 +797,7 @@ and show thread that contains this article."
                  (mail-header-subject novitem)))
         ;;-(mail-header-set-extra novitem nil)
         (push novitem novdata)
-        (setq artlist (cdr artlist))
-        (setq idx (1+ idx)))
+        (setq artlist (cdr artlist)))
       (setq novdata (nreverse novdata))
       (set-buffer nntp-server-buffer) (erase-buffer)
       (mapcar 'nnheader-insert-nov novdata)
@@ -835,99 +805,33 @@ and show thread that contains this article."
 
 (deffoo nnir-request-article (article
                               &optional group server to-buffer)
-  (save-excursion
-    (let* ((artitem (nnir-artlist-article nnir-artlist
-                                          article))
-           (artfullgroup (nnir-artitem-group artitem))
-           (artno (nnir-artitem-number artitem))
-           ;; Bug?
-           ;; Why must we bind nntp-server-buffer here?  It won't
-           ;; work if `buf' is used, say.  (Of course, the set-buffer
-           ;; line below must then be updated, too.)
-           (nntp-server-buffer (or to-buffer nntp-server-buffer)))
-      (set-buffer nntp-server-buffer)
-      (erase-buffer)
-      (message "Requesting article %d from group %s"
-               artno artfullgroup)
-      (gnus-request-article artno artfullgroup nntp-server-buffer)
-      (cons artfullgroup artno))))
+  (if (stringp article)
+      (nnheader-report
+       'nnir
+       "nnir-retrieve-headers doesn't grok message ids: %s"
+       article)
+    (save-excursion
+      (let* ((artitem (nnir-artlist-article nnir-artlist
+                                           article))
+            (artfullgroup (nnir-artitem-group artitem))
+            (artno (nnir-artitem-number artitem))
+            ;; Bug?
+            ;; Why must we bind nntp-server-buffer here?  It won't
+            ;; work if `buf' is used, say.  (Of course, the set-buffer
+            ;; line below must then be updated, too.)
+            (nntp-server-buffer (or to-buffer nntp-server-buffer)))
+       (set-buffer nntp-server-buffer)
+       (erase-buffer)
+       (message "Requesting article %d from group %s"
+                artno artfullgroup)
+       (gnus-request-article artno artfullgroup nntp-server-buffer)
+       (cons artfullgroup artno)))))
 
 
 (nnoo-define-skeleton nnir)
 
 ;;; Search Engine Interfaces:
 
-;; Glimpse interface.
-(defun nnir-run-glimpse (query server &optional group)
-  "Run given query against glimpse.  Returns a vector of (group name, file name)
-pairs (also vectors, actually)."
-  (save-excursion
-    (let ((artlist nil)
-          (groupspec (cdr (assq 'group query)))
-          (qstring (cdr (assq 'query query)))
-         (prefix (nnir-read-server-parm 'nnir-glimps-remove-prefix server))
-         artno dirnam)
-      (when (and group groupspec)
-        (error (concat "It does not make sense to use a group spec"
-                       " with process-marked groups.")))
-      (when group
-        (setq groupspec (gnus-group-real-name group)))
-      (set-buffer (get-buffer-create nnir-tmp-buffer))
-      (erase-buffer)
-      (if groupspec
-          (message "Doing glimpse query %s on %s..." query groupspec)
-        (message "Doing glimpse query %s..." query))
-      (let* ((cp-list
-              `( ,nnir-glimpse-program
-                 nil                    ; input from /dev/null
-                 t                      ; output
-                 nil                    ; don't redisplay
-                 "-H" ,(nnir-read-server-parm 'nnir-glimpse-home server) ; search home dir
-                 "-W"                   ; match pattern in file
-                 "-l" "-y"              ; misc options
-                 ,@(nnir-read-server-parm 'nnir-glimpse-additional-switches server)
-                 "-F" ,prefix           ; restrict output to mail
-                 ,qstring               ; the query, in glimpse format
-                ))
-             (exitstatus
-              (progn
-                (message "%s args: %s" nnir-glimpse-program
-                         (mapconcat 'identity (cddddr cp-list) " "))
-                (apply 'call-process cp-list))))
-        (unless (or (null exitstatus)
-                    (zerop exitstatus))
-          (nnheader-report 'nnir "Couldn't run glimpse: %s" exitstatus)
-          ;; Glimpse failure reason is in this buffer, show it if
-          ;; the user wants it.
-          (when (> gnus-verbose 6)
-            (display-buffer nnir-tmp-buffer))))
-      (when groupspec
-        (keep-lines groupspec))
-      (if groupspec
-          (message "Doing glimpse query %s on %s...done" query groupspec)
-        (message "Doing glimpse query %s...done" query))
-      (sit-for 0)
-      ;; remove superfluous stuff from glimpse output
-      (goto-char (point-min))
-      (delete-non-matching-lines "/[0-9]+$")
-      ;;(delete-matching-lines "\\.overview~?$")
-      (goto-char (point-min))
-      (while (re-search-forward (concat "^" prefix "\\(.+\\)" "/\\([0-9]\\)+$") nil t)
-       ;; replace / with . in group names
-        (setq dirnam (substitute ?. ?/ (match-string 1))
-             artno  (match-string 2))
-       (push (vector (nnir-group-full-name dirnam server)
-                     (string-to-int artno)) artlist))
-
-      (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))))))
-      )))
-
 ;; freeWAIS-sf interface.
 (defun nnir-run-waissearch (query server &optional group)
   "Run given query agains waissearch.  Returns vector of (group name, file name)
@@ -973,50 +877,7 @@ pairs (also vectors, actually)."
                                 (> (nnir-artitem-rsv x)
                                    (nnir-artitem-rsv y)))))))))
 
-;; EWS (Excite for Web Servers) interface
-(defun nnir-run-excite-search (query server &optional group)
-  "Run a given query against EWS.  Returns vector of (group name, file name)
-pairs (also vectors, actually)."
-  (when group
-    (error "Searching specific groups not implemented for EWS."))
-  (save-excursion
-    (let ((qstring (cdr (assq 'query query)))
-         (prefix (nnir-read-server-parm 'nnir-excite-remove-prefix server))
-         artlist group article-num article)
-      (setq nnir-current-query query)
-      (set-buffer (get-buffer-create nnir-tmp-buffer))
-      (erase-buffer)
-      (message "Doing EWS query %s..." qstring)
-      (call-process nnir-excite-aquery-program
-                   nil                 ; input from /dev/null
-                   t                   ; output to current buffer
-                   nil                 ; don't redisplay
-                   (nnir-read-server-parm 'nnir-excite-collection server)
-                   (if (string= (substring qstring 0 1) "(")
-                       qstring
-                     (format "(concept %s)" qstring)))
-      (message "Gathering query output...")
-
-      (goto-char (point-min))
-      (while (re-search-forward
-             "^[0-9]+\\s-[0-9]+\\s-[0-9]+\\s-\\(\\S-*\\)" nil t)
-       (setq article (match-string 1))
-       (unless (string-match
-                (concat "^" (regexp-quote prefix)
-                        "\\(.*\\)/\\([0-9]+\\)") article)
-         (nnheader-report 'nnir "Dir name %s doesn't contain prefix %s"
-                          article prefix))
-       (setq group (substitute ?. ?/ (match-string 1 article)))
-       (setq group (nnir-group-full-name group server))
-       (setq article-num (match-string 2 article))
-       (setq artlist (vconcat artlist (vector (vector group
-                                                      (string-to-int article-num)
-                                                      1000)))))
-      (message "Gathering query output...done")
-      artlist)))
-
-;; IMAP interface.  The following function is Copyright (C) 1998 Simon
-;; Josefsson <jas@pdc.kth.se>.
+;; IMAP interface.
 ;; todo:
 ;; nnir invokes this two (2) times???!
 ;; we should not use nnimap at all but open our own server connection
@@ -1053,8 +914,7 @@ pairs (also vectors, actually)."
         (quit nil))
       (reverse artlist))))
 
-;; Swish++ interface.  The following function is Copyright (C) 2000,
-;; 2001 Christoph Conrad <christoph.conrad@gmx.de>.
+;; Swish++ interface.
 ;; -cc- Todo
 ;; Search by
 ;; - group
@@ -1126,7 +986,11 @@ Windows NT 4.0."
               dirnam (file-name-directory (match-string 2)))
 
         ;; don't match directories
-        (when (string-match "^[0-9]+$" artno)
+        (when (string-match "^[0-9]+\\(\\.[a-z0-9]+\\)?$" artno)
+         ;; nnml-use-compressed-files might be any string, but probably this
+         ;; 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.
           (when (not (null dirnam))
 
            ;; maybe limit results to matching groups.
@@ -1160,8 +1024,7 @@ Windows NT 4.0."
                                 (> (nnir-artitem-rsv x)
                                    (nnir-artitem-rsv y)))))))))
 
-;; Swish-E interface.  The following function is Copyright (C) 2000,
-;; 2001 by Christoph Conrad <christoph.conrad@gmx.de>.
+;; Swish-E interface.
 (defun nnir-run-swish-e (query server &optional group)
   "Run given query against swish-e.
 Returns a vector of (group name, file name) pairs (also vectors,
@@ -1188,18 +1051,18 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
       (erase-buffer)
 
       (message "Doing swish-e query %s..." query)
-      (let* ((index-file
+      (let* ((index-files
              (or (nnir-read-server-parm
-                  'nnir-swish-e-index-file server)
-                 (error "Missing parameter `nnir-swish-e-index-file'")))
+                  'nnir-swish-e-index-files server)
+                 (error "Missing parameter `nnir-swish-e-index-files'")))
             (additional-switches
              (nnir-read-server-parm
-              'nnir-swish++-additional-switches server))
+              'nnir-swish-e-additional-switches server))
             (cp-list `(,nnir-swish-e-program
                        nil             ; input from /dev/null
                        t               ; output
                        nil             ; don't redisplay
-                       "-f" ,index-file
+                       "-f" ,@index-files
                        ,@additional-switches
                        "-w"
                        ,qstring        ; the query, in swish-e format
@@ -1413,6 +1276,59 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
                                 (> (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."
+  (let* ((method (gnus-server-to-method server))
+        (sym (intern
+              (concat (symbol-name (car method)) "-directory")))
+        (directory (cadr (assoc sym (cddr method))))
+        (regexp (cdr (assoc 'query query)))
+        (grep-options (cdr (assoc 'grep-options query)))
+        artlist)
+    (unless directory
+      (error "No directory found in method specification of server %s"
+            server))
+    (message "Searching %s using find-grep..." (or group server))
+    (save-window-excursion
+      (set-buffer (get-buffer-create nnir-tmp-buffer))
+      (erase-buffer)
+      (if (> gnus-verbose 6)
+         (pop-to-buffer (current-buffer)))
+      (cd directory) ; Using relative paths simplifies postprocessing.
+      (let ((group
+            (if (not group)
+                "."
+              ;; 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)))))))
+       (unless group
+         (error "Cannot locate directory for group"))
+       (save-excursion
+         (apply
+          'call-process "find" nil t
+          "find" group "-type" "f" "-name" "[0-9]*" "-exec"
+          "grep"
+          `("-l" ,@(and grep-options (split-string grep-options "\\s-" t))
+            "-e" ,regexp "{}" "+"))))
+
+      ;; Translate relative paths to group names.
+      (while (not (eobp))
+       (let* ((path (split-string
+                     (buffer-substring (point) (line-end-position)) "/" t))
+              (art (string-to-number (car (last path)))))
+         (while (string= "." (car path))
+           (setq path (cdr path)))
+         (let ((group (mapconcat 'identity (subseq path 0 -1) ".")))
+           (push (vector (nnir-group-full-name group server) art 0)
+                 artlist))
+         (forward-line 1)))
+      (message "Searching %s using find-grep...done" (or group server))
+      artlist)))
+
 ;;; Util Code:
 
 (defun nnir-read-parms (query)
@@ -1474,9 +1390,7 @@ form 'backend:name'."
     (cond ((and method (assq key (cddr method)))
           (nth 1 (assq key (cddr method))))
          ((and nnir-mail-backend
-               (gnus-method-equal method nnir-mail-backend))
-          (symbol-value key))
-         ((null nnir-mail-backend)
+               (gnus-server-equal method nnir-mail-backend))
           (symbol-value key))
          (t nil))))
 ;;     (if method
@@ -1486,17 +1400,6 @@ form 'backend:name'."
 ;;       (symbol-value key))
 ;;     ))
 
-(defmacro nnir-group-server (group)
-  "Returns the server for a foreign newsgroup in the format as gnus-server-to-method needs it. Compare to gnus-group-real-prefix and gnus-group-real-name."
-  `(let ((gname ,group))
-    (if (string-match "^\\([^:]+\\):" gname)
-       (setq gname (match-string 1 gname))
-      nil)
-    (if (string-match "^\\([^+]+\\)\\+\\(.+\\)$" gname)
-       (format "%s:%s" (match-string 1 gname) (match-string 2 gname))
-      (concat gname ":"))
-    ))
-
 (defun nnir-group-full-name (shortname server)
   "For the given group name, return a full Gnus group name.
 The Gnus backend/server information is added."
@@ -1539,7 +1442,7 @@ The Gnus backend/server information is added."
 
 (defun nnir-artlist-artitem-rsv (artlist n)
   "Returns from ARTLIST the Retrieval Status Value of the Nth artitem
-(counting from 1)."
+\(counting from 1)."
   (nnir-artitem-rsv (nnir-artlist-article artlist n)))
 
 ;; unused?
@@ -1557,3 +1460,5 @@ The Gnus backend/server information is added."
 
 ;; The end.
 (provide 'nnir)
+
+;;; arch-tag: 9b3fecf8-4397-4bbb-bf3c-6ac3cbbc6664