1 ;;; elmo-search.el --- Search by external program interface for ELMO.
3 ;; Copyright (C) 2005 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Hiroya Murata <lapis-lazuli@pop06.odn.ne.jp>
7 ;; Keywords: mail, net news
9 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
32 (eval-when-compile (require 'cl
))
38 (defcustom elmo-search-use-drive-letter
39 (memq system-type
'(OS/2 emx windows-nt
))
40 "*If non-nil, do a drive letter conversion (e.g. /a|/ => a:/)."
41 :type
'(choice (const :tag
"Not use" nil
)
45 (defvar elmo-search-engine-alist nil
46 "*An alist of search engines.
47 Each element looks like (ENGINE CLASS PROPERTIES...)
48 ENGINE is a symbol, the name of the search engine.
49 CLASS is a symbol, the class name that performs a search.
50 PROPERTIES is a plist, it configure an engine with the CLASS.")
52 (defcustom elmo-search-default-engine
'namazu
53 "*Default search engine for elmo-search folder."
58 (defconst elmo-search-folder-name-syntax
`(pattern (?\
] param
(?
! engine
))))
63 (luna-define-class elmo-search-engine
() (param))
64 (luna-define-internal-accessors 'elmo-search-engine
))
66 (luna-define-generic elmo-search-engine-do-search
(engine pattern
)
67 "Search messages which is match PATTERN by ENGINE.")
69 (luna-define-generic elmo-search-engine-create-message-entity
(engine
72 "Create msgdb entity for the message in the FOLDER with NUMBER.")
74 (luna-define-generic elmo-search-engine-fetch-message
(engine location
)
75 "Fetch a message into current buffer.
76 ENGINE is the ELMO search engine structure.
77 LOCATION is the location of the message.
78 Returns non-nil if fetching was succeed.")
80 (defun elmo-make-search-engine (type &optional param
)
81 (let ((spec (or (cdr (assq type elmo-search-engine-alist
))
82 (error "Undefined search engine `%s'" type
))))
83 (require (intern (format "elmo-search-%s" (car spec
))))
84 (apply 'luna-make-entity
85 (intern (format "elmo-search-engine-%s" (car spec
)))
89 (defun elmo-search-register-engine (name class
&rest properties
)
90 (let ((cell (assq name elmo-search-engine-alist
))
91 (spec (cons class properties
)))
94 (setq elmo-search-engine-alist
95 (cons (cons name spec
) elmo-search-engine-alist
)))))
99 (luna-define-class elmo-search-folder
(elmo-map-folder)
101 (luna-define-internal-accessors 'elmo-search-folder
))
103 (luna-define-method elmo-folder-initialize
((folder elmo-search-folder
)
105 (when (> (length name
) 0)
106 (let* ((tokens (car (elmo-parse-separated-tokens
108 elmo-search-folder-name-syntax
)))
109 (engine (cdr (assq 'engine tokens
))))
110 (elmo-search-folder-set-engine-internal
112 (elmo-make-search-engine (if (> (length engine
) 0)
114 elmo-search-default-engine
)
115 (cdr (assq 'param tokens
))))
116 (elmo-search-folder-set-pattern-internal
118 (cdr (assq 'pattern tokens
)))))
121 (luna-define-method elmo-folder-expand-msgdb-path
((folder elmo-search-folder
))
123 (elmo-replace-string-as-filename
124 (elmo-folder-name-internal folder
))
125 (expand-file-name "search" elmo-msgdb-directory
)))
127 (luna-define-method elmo-folder-msgdb-create
((folder elmo-search-folder
)
129 (let ((new-msgdb (elmo-make-msgdb))
131 (elmo-with-progress-display (elmo-folder-msgdb-create (length numbers
))
133 (dolist (number numbers
)
134 (setq entity
(elmo-search-engine-create-message-entity
135 (elmo-search-folder-engine-internal folder
)
136 (elmo-msgdb-message-entity-handler new-msgdb
)
139 (elmo-msgdb-append-entity new-msgdb entity
'(new unread
)))
140 (elmo-progress-notify 'elmo-folder-msgdb-create
)))
143 (luna-define-method elmo-folder-message-file-p
((folder elmo-search-folder
))
146 (defun elmo-search-location-to-filename (location)
147 (when (string-match "^file://" location
)
148 (let ((filename (substring location
(match-end 0))))
150 (if (and elmo-search-use-drive-letter
151 (string-match "^/\\([A-Za-z]\\)[:|]/\\(.*\\)$" filename
))
152 (replace-match "\\1:/\\2" t nil filename
)
155 (luna-define-method elmo-message-file-name
((folder elmo-search-folder
)
157 (elmo-search-location-to-filename
158 (elmo-map-message-location folder number
)))
160 (luna-define-method elmo-folder-message-make-temp-file-p
161 ((folder elmo-search-folder
))
164 (luna-define-method elmo-folder-diff
((folder elmo-search-folder
))
167 (luna-define-method elmo-folder-message-make-temp-files
((folder
172 (let ((temp-dir (elmo-folder-make-temporary-directory folder
))
173 (cur-number (or start-number
0)))
174 (dolist (number numbers
)
176 (elmo-message-file-name folder number
)
178 (number-to-string (if start-number cur-number number
))
183 (luna-define-method elmo-map-message-fetch
((folder elmo-search-folder
)
185 &optional section unseen
)
186 (elmo-search-engine-fetch-message
187 (elmo-search-folder-engine-internal folder
)
190 (luna-define-method elmo-map-folder-list-message-locations
191 ((folder elmo-search-folder
))
192 (elmo-search-engine-do-search
193 (elmo-search-folder-engine-internal folder
)
194 (elmo-search-folder-pattern-internal folder
)))
196 (luna-define-method elmo-folder-exists-p
((folder elmo-search-folder
))
197 (elmo-search-folder-pattern-internal folder
))
199 (luna-define-method elmo-folder-have-subfolder-p
((folder elmo-search-folder
))
200 (null (elmo-search-folder-pattern-internal folder
)))
202 (luna-define-method elmo-folder-list-subfolders
((folder elmo-search-folder
)
205 (lambda (name) (elmo-recover-string-from-filename name
))
206 (directory-files (expand-file-name "search" elmo-msgdb-directory
)
208 (concat "^" (regexp-quote
209 (elmo-folder-prefix-internal folder
))))))
211 (luna-define-method elmo-folder-delete-messages
((folder elmo-search-folder
)
213 (elmo-folder-kill-messages folder numbers
)
219 ;; external program search engine
221 (luna-define-class elmo-search-engine-extprog
(elmo-search-engine)
222 (prog args charset parser
))
223 (luna-define-internal-accessors 'elmo-search-engine-extprog
))
225 (luna-define-method elmo-search-engine-do-search
226 ((engine elmo-search-engine-extprog
) pattern
)
228 (let ((charset (elmo-search-engine-extprog-charset-internal engine
))
229 (parser (or (elmo-search-engine-extprog-parser-internal engine
)
230 #'elmo-search-parse-filename-list
)))
232 (elmo-search-engine-extprog-prog-internal engine
)
239 (cond ((stringp arg
) arg
)
242 (encode-mime-charset-string pattern charset
)
246 (funcall arg engine pattern
)
247 (wrong-number-of-arguments
248 (funcall arg engine
))))
251 (symbol-value arg
))))
252 (elmo-search-engine-extprog-args-internal engine
)))))
255 ;; search engine for local files
257 (luna-define-class elmo-search-engine-local-file
258 (elmo-search-engine-extprog))
259 (luna-define-internal-accessors 'elmo-search-engine-local-file
))
261 (defun elmo-search-parse-filename-list ()
263 (goto-char (point-min))
266 (when (and elmo-search-use-drive-letter
267 (looking-at "^\\([A-Za-z]\\)[:|]/"))
268 (replace-match "/\\1:/")
270 (unless (looking-at "^file://")
275 (setq locations
(cons (buffer-substring bol
(point)) locations
))
277 (nreverse locations
)))
279 (luna-define-method elmo-search-engine-create-message-entity
280 ((engine elmo-search-engine-local-file
) handler folder number
)
281 (let ((filename (elmo-message-file-name folder number
))
284 (setq entity
(elmo-msgdb-create-message-entity-from-file
285 handler number filename
)))
286 (unless (or (elmo-message-entity-field entity
'to
)
287 (elmo-message-entity-field entity
'cc
)
288 (not (string= (elmo-message-entity-field entity
'subject
)
290 (elmo-message-entity-set-field entity
'subject
291 (file-name-nondirectory filename
))
292 (setq uid
(nth 2 (file-attributes filename
)))
293 (elmo-message-entity-set-field entity
'from
296 " <"(user-login-name uid
) "@"
300 (luna-define-method elmo-search-engine-fetch-message
301 ((engine elmo-search-engine-local-file
) location
)
302 (let ((filename (elmo-search-location-to-filename location
)))
303 (when (and filename
(file-exists-p filename
))
305 (insert-file-contents-as-binary filename
)
306 (unless (or (std11-field-body "To")
307 (std11-field-body "Cc")
308 (std11-field-body "Subject"))
309 (let (charset guess uid
)
311 (set-buffer-multibyte t
)
312 (insert-file-contents filename
)
313 (setq charset
(detect-mime-charset-region (point-min)
315 (goto-char (point-min))
316 (setq guess
(mime-find-file-type filename
))
317 (setq uid
(nth 2 (file-attributes filename
)))
318 (insert "From: " (concat (user-full-name uid
)
319 " <"(user-login-name uid
) "@"
320 (system-name) ">") "\n")
321 (insert "Subject: " filename
"\n")
322 (insert "Content-Type: "
323 (concat (nth 0 guess
) "/" (nth 1 guess
))
324 "; charset=" (upcase (symbol-name charset
))
325 "\nMIME-Version: 1.0\n\n")
326 (encode-mime-charset-region (point-min) (point-max) charset
)
327 (set-buffer-multibyte nil
)))))))
329 (provide 'elmo-search-local-file
)
332 (defcustom elmo-search-namazu-default-index-path
"~/Mail"
333 "*Default index path for namazu.
334 If the value is a list, all elements are used as index paths for namazu."
335 :type
'(choice (directory :tag
"Index Path")
336 (repeat (directory :tag
"Index Path")))
339 (defcustom elmo-search-namazu-index-alias-alist nil
340 "*Alist of ALIAS and INDEX-PATH."
341 :type
'(repeat (cons (string :tag
"Alias Name")
342 (choice (directory :tag
"Index Path")
343 (repeat (directory :tag
"Index Path")))))
346 (defun elmo-search-namazu-index (engine pattern
)
347 (let* ((param (elmo-search-engine-param-internal engine
))
349 ((cdr (assoc param elmo-search-namazu-index-alias-alist
)))
350 ((and param
(> (length param
) 0))
353 elmo-search-namazu-default-index-path
))))
355 (mapcar 'expand-file-name index
)
356 (expand-file-name index
))))
360 (defun elmo-search-grep-target (engine pattern
)
361 (let ((dirname (expand-file-name (elmo-search-engine-param-internal engine
)))
362 (files (list null-device
)))
363 (dolist (filename (directory-files dirname
))
364 (unless (string-match "^\\.\\.?" filename
)
365 (setq files
(cons (expand-file-name filename dirname
) files
))))
369 ;;; Setup `elmo-search-engine-alist'
370 (unless noninteractive
371 (or (assq 'namazu elmo-search-engine-alist
)
372 (elmo-search-register-engine
375 :args
'("--all" "--list" "--early" pattern elmo-search-namazu-index
)
376 :charset
'iso-2022-jp
))
377 (or (assq 'grep elmo-search-engine-alist
)
378 (elmo-search-register-engine
381 :args
'("-l" "-e" pattern elmo-search-grep-target
))))
384 (product-provide (provide 'elmo-search
) (require 'elmo-version
))
386 ;;; elmo-search.el ends here