comparison lisp/mh-e/mh-index.el @ 56673:e9a6cbc8ca5e

Upgraded to MH-E version 7.4.80. See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
author Bill Wohler <wohler@newt.com>
date Sun, 15 Aug 2004 22:00:06 +0000
parents d36b00b98db0
children 4f4f410e6fe8 d8411455de48
comparison
equal deleted inserted replaced
56672:83ab2b01744a 56673:e9a6cbc8ca5e
29 ;;; (1) The following search engines are supported: 29 ;;; (1) The following search engines are supported:
30 ;;; swish++ 30 ;;; swish++
31 ;;; swish-e 31 ;;; swish-e
32 ;;; mairix 32 ;;; mairix
33 ;;; namazu 33 ;;; namazu
34 ;;; glimpse
35 ;;; grep 34 ;;; grep
36 ;;; 35 ;;;
37 ;;; (2) To use this package, you first have to build an index. Please read 36 ;;; (2) To use this package, you first have to build an index. Please read
38 ;;; the documentation for `mh-index-search' to get started. That 37 ;;; the documentation for `mh-index-search' to get started. That
39 ;;; documentation will direct you to the specific instructions for your 38 ;;; documentation will direct you to the specific instructions for your
41 40
42 ;;; Change Log: 41 ;;; Change Log:
43 42
44 ;;; Code: 43 ;;; Code:
45 44
46 (require 'mh-utils) 45 (eval-when-compile (require 'mh-acros))
47 (mh-require-cl) 46 (mh-require-cl)
48 (require 'mh-e) 47 (require 'mh-e)
49 (require 'mh-mime) 48 (require 'mh-mime)
50 (require 'mh-pick) 49 (require 'mh-pick)
51 50
64 (mairix 63 (mairix
65 mh-mairix-binary mh-mairix-execute-search mh-mairix-next-result 64 mh-mairix-binary mh-mairix-execute-search mh-mairix-next-result
66 mh-mairix-regexp-builder) 65 mh-mairix-regexp-builder)
67 (namazu 66 (namazu
68 mh-namazu-binary mh-namazu-execute-search mh-namazu-next-result nil) 67 mh-namazu-binary mh-namazu-execute-search mh-namazu-next-result nil)
69 (glimpse
70 mh-glimpse-binary mh-glimpse-execute-search mh-glimpse-next-result nil)
71 (pick 68 (pick
72 mh-pick-binary mh-pick-execute-search mh-pick-next-result 69 mh-pick-binary mh-pick-execute-search mh-pick-next-result
73 mh-pick-regexp-builder) 70 mh-pick-regexp-builder)
74 (grep 71 (grep
75 mh-grep-binary mh-grep-execute-search mh-grep-next-result nil)) 72 mh-grep-binary mh-grep-execute-search mh-grep-next-result nil))
198 (omsg (cdr intermediate))) 195 (omsg (cdr intermediate)))
199 ;; This is most probably a duplicate. So eliminate it. 196 ;; This is most probably a duplicate. So eliminate it.
200 (call-process "rm" nil nil nil 197 (call-process "rm" nil nil nil
201 (format "%s%s/%s" mh-user-path 198 (format "%s%s/%s" mh-user-path
202 (substring mh-current-folder 1) msg)) 199 (substring mh-current-folder 1) msg))
203 (remhash omsg (gethash ofolder mh-index-data)))) 200 (when (gethash ofolder mh-index-data)
201 (remhash omsg (gethash ofolder mh-index-data)))))
204 (t 202 (t
205 (setf (gethash msg mh-index-msg-checksum-map) checksum) 203 (setf (gethash msg mh-index-msg-checksum-map) checksum)
206 (when origin-map 204 (when origin-map
207 (setf (gethash checksum mh-index-checksum-origin-map) 205 (setf (gethash checksum mh-index-checksum-origin-map)
208 (gethash msg origin-map)))))) 206 (gethash msg origin-map))))))
299 (dolist (msg (mh-translate-range mh-current-folder "all")) 297 (dolist (msg (mh-translate-range mh-current-folder "all"))
300 (let* ((checksum (gethash msg mh-index-msg-checksum-map)) 298 (let* ((checksum (gethash msg mh-index-msg-checksum-map))
301 (pair (gethash checksum mh-index-checksum-origin-map)) 299 (pair (gethash checksum mh-index-checksum-origin-map))
302 (ofolder (car pair)) 300 (ofolder (car pair))
303 (omsg (cdr pair))) 301 (omsg (cdr pair)))
304 (loop for seq in (gethash omsg (gethash ofolder seq-hash)) 302 (loop for seq in (ignore-errors
303 (gethash omsg (gethash ofolder seq-hash)))
305 do (if (assoc seq seq-list) 304 do (if (assoc seq seq-list)
306 (push msg (cdr (assoc seq seq-list))) 305 (push msg (cdr (assoc seq seq-list)))
307 (push (list seq msg) seq-list))))) 306 (push (list seq msg) seq-list)))))
308 (loop for seq in seq-list 307 (loop for seq in seq-list
309 do (apply #'mh-exec-cmd "mark" mh-current-folder 308 do (apply #'mh-exec-cmd "mark" mh-current-folder
372 371
373 - `mh-swish++-execute-search' 372 - `mh-swish++-execute-search'
374 - `mh-swish-execute-search' 373 - `mh-swish-execute-search'
375 - `mh-mairix-execute-search' 374 - `mh-mairix-execute-search'
376 - `mh-namazu-execute-search' 375 - `mh-namazu-execute-search'
377 - `mh-glimpse-execute-search'
378 376
379 If none of these programs are present then we use pick. If desired grep can be 377 If none of these programs are present then we use pick. If desired grep can be
380 used instead. Details about these methods can be found in: 378 used instead. Details about these methods can be found in:
381 379
382 - `mh-pick-execute-search' 380 - `mh-pick-execute-search'
434 (let ((buffer-name (buffer-name (current-buffer)))) 432 (let ((buffer-name (buffer-name (current-buffer))))
435 (mh-process-or-undo-commands buffer-name) 433 (mh-process-or-undo-commands buffer-name)
436 (save-excursion (mh-exec-cmd-quiet nil "rmf" buffer-name)) 434 (save-excursion (mh-exec-cmd-quiet nil "rmf" buffer-name))
437 (mh-exec-cmd-quiet nil "folder" "-create" "-fast" buffer-name) 435 (mh-exec-cmd-quiet nil "folder" "-create" "-fast" buffer-name)
438 (setq index-folder buffer-name)) 436 (setq index-folder buffer-name))
439 (setq index-folder (mh-index-new-folder index-folder))) 437 (setq index-folder (mh-index-new-folder index-folder search-regexp)))
440 438
441 (let ((folder-path (format "%s%s" mh-user-path (substring folder 1))) 439 (let ((folder-path (format "%s%s" mh-user-path (substring folder 1)))
442 (folder-results-map (make-hash-table :test #'equal)) 440 (folder-results-map (make-hash-table :test #'equal))
443 (origin-map (make-hash-table :test #'equal))) 441 (origin-map (make-hash-table :test #'equal)))
444 ;; Run search program... 442 ;; Run search program...
584 (pattern (funcall mh-index-regexp-builder regexp-list))) 582 (pattern (funcall mh-index-regexp-builder regexp-list)))
585 (if pattern 583 (if pattern
586 (mh-index-search nil mh-current-folder pattern 584 (mh-index-search nil mh-current-folder pattern
587 mh-previous-window-config) 585 mh-previous-window-config)
588 (error "No search terms")))) 586 (error "No search terms"))))
589
590 (defun mh-replace-string (old new)
591 "Replace all occurrences of OLD with NEW in the current buffer."
592 (goto-char (point-min))
593 (let ((case-fold-search t))
594 (while (search-forward old nil t)
595 (replace-match new t t))))
596 587
597 ;;;###mh-autoload 588 ;;;###mh-autoload
598 (defun mh-index-parse-search-regexp (input-string) 589 (defun mh-index-parse-search-regexp (input-string)
599 "Construct parse tree for INPUT-STRING. 590 "Construct parse tree for INPUT-STRING.
600 All occurrences of &, |, ! and ~ in INPUT-STRING are replaced by AND, OR and 591 All occurrences of &, |, ! and ~ in INPUT-STRING are replaced by AND, OR and
737 728
738 (defun mh-msg-exists-p (msg folder) 729 (defun mh-msg-exists-p (msg folder)
739 "Check if MSG exists in FOLDER." 730 "Check if MSG exists in FOLDER."
740 (file-exists-p (format "%s%s/%s" mh-user-path (substring folder 1) msg))) 731 (file-exists-p (format "%s%s/%s" mh-user-path (substring folder 1) msg)))
741 732
742 (defun mh-index-new-folder (name) 733 (defun mh-index-new-folder (name search-regexp)
743 "Create and return an MH folder name based on NAME. 734 "Return a folder name based on NAME for search results of SEARCH-REGEXP.
744 If the folder NAME already exists then check if NAME<2> exists. If it doesn't 735
745 then it is created and returned. Otherwise try NAME<3>. This is repeated till 736 If folder NAME already exists and was generated for the same SEARCH-REGEXP
746 we find a new folder name." 737 then it is reused.
738
739 Otherwise if the folder NAME was generated from a different search then check
740 if NAME<2> can be used. Otherwise try NAME<3>. This is repeated till we find a
741 new folder name.
742
743 If the folder returned doesn't exist then it is created."
747 (unless (mh-folder-name-p name) 744 (unless (mh-folder-name-p name)
748 (error "The argument should be a valid MH folder name")) 745 (error "The argument should be a valid MH folder name"))
749 (let ((chosen-name name)) 746 (let ((chosen-name
750 (block unique-name 747 (loop for i from 1
751 (unless (mh-folder-exists-p name) 748 for candidate = (if (equal i 1) name (format "%s<%s>" name i))
752 (return-from unique-name)) 749 when (or (not (mh-folder-exists-p candidate))
753 (loop for index from 2 750 (equal (mh-index-folder-search-regexp candidate)
754 do (let ((new-name (format "%s<%s>" name index))) 751 search-regexp))
755 (unless (mh-folder-exists-p new-name) 752 return candidate)))
756 (setq chosen-name new-name) 753 ;; Do pending refiles/deletes...
757 (return-from unique-name))))) 754 (when (get-buffer chosen-name)
755 (mh-process-or-undo-commands chosen-name))
756 ;; Recreate folder...
757 (save-excursion (mh-exec-cmd-quiet nil "rmf" chosen-name))
758 (mh-exec-cmd-quiet nil "folder" "-create" "-fast" chosen-name) 758 (mh-exec-cmd-quiet nil "folder" "-create" "-fast" chosen-name)
759 (mh-remove-from-sub-folders-cache chosen-name) 759 (mh-remove-from-sub-folders-cache chosen-name)
760 (when (boundp 'mh-speed-folder-map) 760 (when (boundp 'mh-speed-folder-map)
761 (mh-speed-add-folder chosen-name)) 761 (mh-speed-add-folder chosen-name))
762 chosen-name)) 762 chosen-name))
763
764 (defun mh-index-folder-search-regexp (folder)
765 "If FOLDER was created by a index search, return the search regexp.
766 Return nil if FOLDER doesn't exist or the .mhe_index file is garbled."
767 (ignore-errors
768 (with-temp-buffer
769 (insert-file-contents
770 (format "%s%s/%s" mh-user-path (substring folder 1) mh-index-data-file))
771 (goto-char (point-min))
772 (forward-list 3)
773 (cadr (read (current-buffer))))))
763 774
764 ;;;###mh-autoload 775 ;;;###mh-autoload
765 (defun mh-index-insert-folder-headers () 776 (defun mh-index-insert-folder-headers ()
766 "Annotate the search results with original folder names." 777 "Annotate the search results with original folder names."
767 (let ((cur-msg (mh-get-msg-num nil)) 778 (let ((cur-msg (mh-get-msg-num nil))
775 mh-index-checksum-origin-map))) 786 mh-index-checksum-origin-map)))
776 (when (and current-folder (not (equal current-folder last-folder))) 787 (when (and current-folder (not (equal current-folder last-folder)))
777 (insert (if last-folder "\n" "") current-folder "\n") 788 (insert (if last-folder "\n" "") current-folder "\n")
778 (setq last-folder current-folder)) 789 (setq last-folder current-folder))
779 (forward-line)) 790 (forward-line))
780 (when cur-msg (mh-goto-msg cur-msg t)) 791 (when cur-msg
781 (set-buffer-modified-p old-buffer-modified-flag))) 792 (mh-notate-cur)
793 (mh-goto-msg cur-msg t))
794 (set-buffer-modified-p old-buffer-modified-flag))
795 (mh-index-create-imenu-index))
796
797 ;;;###mh-autoload
798 (defun mh-index-create-imenu-index ()
799 "Create alist of folder names and positions in index folder buffers."
800 (save-excursion
801 (setq which-func-mode t)
802 (let ((alist ()))
803 (goto-char (point-min))
804 (while (re-search-forward "^+" nil t)
805 (save-excursion
806 (beginning-of-line)
807 (push (cons (buffer-substring-no-properties
808 (point) (line-end-position))
809 (set-marker (make-marker) (point)))
810 alist)))
811 (setq imenu--index-alist (nreverse alist)))))
782 812
783 ;;;###mh-autoload 813 ;;;###mh-autoload
784 (defun mh-index-group-by-folder () 814 (defun mh-index-group-by-folder ()
785 "Partition the messages based on source folder. 815 "Partition the messages based on source folder.
786 Returns an alist with the the folder names in the car and the cdr being the 816 Returns an alist with the the folder names in the car and the cdr being the
834 (when (or (not (get-buffer folder)) 864 (when (or (not (get-buffer folder))
835 (y-or-n-p (format "Reuse buffer displaying %s? " folder))) 865 (y-or-n-p (format "Reuse buffer displaying %s? " folder)))
836 (mh-visit-folder 866 (mh-visit-folder
837 folder (loop for x being the hash-keys of (gethash folder mh-index-data) 867 folder (loop for x being the hash-keys of (gethash folder mh-index-data)
838 when (mh-msg-exists-p x folder) collect x))))) 868 when (mh-msg-exists-p x folder) collect x)))))
839
840 ;;;###mh-autoload
841 (defun mh-index-update-unseen (msg)
842 "Remove counterpart of MSG in source folder from `mh-unseen-seq'.
843 Also `mh-update-unseen' is called in the original folder, if we have it open."
844 (let* ((checksum (gethash msg mh-index-msg-checksum-map))
845 (folder-msg-pair (gethash checksum mh-index-checksum-origin-map))
846 (orig-folder (car folder-msg-pair))
847 (orig-msg (cdr folder-msg-pair)))
848 (when (mh-index-match-checksum orig-msg orig-folder checksum)
849 (when (get-buffer orig-folder)
850 (save-excursion
851 (set-buffer orig-folder)
852 (unless (member orig-msg mh-seen-list) (push orig-msg mh-seen-list))
853 (mh-update-unseen)))
854 (mh-exec-cmd-daemon "mark" #'ignore orig-folder (format "%s" orig-msg)
855 "-sequence" (symbol-name mh-unseen-seq) "-del"))))
856 869
857 (defun mh-index-match-checksum (msg folder checksum) 870 (defun mh-index-match-checksum (msg folder checksum)
858 "Check if MSG in FOLDER has X-MHE-Checksum header value of CHECKSUM." 871 "Check if MSG in FOLDER has X-MHE-Checksum header value of CHECKSUM."
859 (with-temp-buffer 872 (with-temp-buffer
860 (mh-exec-cmd-output mh-scan-prog nil "-width" "80" 873 (mh-exec-cmd-output mh-scan-prog nil "-width" "80"
971 (mh-index-matching-source-msgs msgs)) 984 (mh-index-matching-source-msgs msgs))
972 folders)))) 985 folders))))
973 986
974 987
975 988
976 ;; Glimpse interface
977
978 (defvar mh-glimpse-binary (executable-find "glimpse"))
979 (defvar mh-glimpse-directory ".glimpse")
980
981 ;;;###mh-autoload
982 (defun mh-glimpse-execute-search (folder-path search-regexp)
983 "Execute glimpse and read the results.
984
985 In the examples below, replace /home/user/Mail with the path to your MH
986 directory.
987
988 First create the directory /home/user/Mail/.glimpse. Then create the file
989 /home/user/Mail/.glimpse/.glimpse_exclude with the following contents:
990
991 */.*
992 */#*
993 */,*
994 */*~
995 ^/home/user/Mail/.glimpse
996 ^/home/user/Mail/mhe-index
997
998 If there are any directories you would like to ignore, append lines like the
999 following to .glimpse_exclude:
1000
1001 ^/home/user/Mail/scripts
1002
1003 You do not want to index the folders that hold the results of your searches
1004 since they tend to be ephemeral and the original messages are indexed anyway.
1005 The configuration file above assumes that the results are found in sub-folders
1006 of `mh-index-folder' which is +mhe-index by default.
1007
1008 Use the following command line to generate the glimpse index. Run this
1009 daily from cron:
1010
1011 glimpseindex -H /home/user/Mail/.glimpse /home/user/Mail
1012
1013 FOLDER-PATH is the directory in which SEARCH-REGEXP is used to search."
1014 (set-buffer (get-buffer-create mh-index-temp-buffer))
1015 (erase-buffer)
1016 (call-process mh-glimpse-binary nil '(t nil) nil
1017 ;(format "-%s" fuzz)
1018 "-i" "-y"
1019 "-H" (format "%s%s" mh-user-path mh-glimpse-directory)
1020 "-F" (format "^%s" folder-path)
1021 search-regexp)
1022 (goto-char (point-min)))
1023
1024 (defun mh-glimpse-next-result ()
1025 "Read the next result.
1026 Parse it and return the message folder, message index and the match. If no
1027 other matches left then return nil. If the current record is invalid return
1028 'error."
1029 (prog1
1030 (block nil
1031 (when (eobp)
1032 (return nil))
1033 (let ((eol-pos (line-end-position))
1034 (bol-pos (line-beginning-position))
1035 folder-start msg-end)
1036 (goto-char bol-pos)
1037 (unless (search-forward mh-user-path eol-pos t)
1038 (return 'error))
1039 (setq folder-start (point))
1040 (unless (search-forward ": " eol-pos t)
1041 (return 'error))
1042 (let ((match (buffer-substring-no-properties (point) eol-pos)))
1043 (forward-char -2)
1044 (setq msg-end (point))
1045 (unless (search-backward "/" folder-start t)
1046 (return 'error))
1047 (list (format "+%s" (buffer-substring-no-properties
1048 folder-start (point)))
1049 (let ((val (ignore-errors (read-from-string
1050 (buffer-substring-no-properties
1051 (1+ (point)) msg-end)))))
1052 (if (and (consp val) (integerp (car val)))
1053 (car val)
1054 (return 'error)))
1055 match))))
1056 (forward-line)))
1057
1058
1059
1060 ;; Pick interface 989 ;; Pick interface
1061 990
1062 (defvar mh-index-pick-folder) 991 (defvar mh-index-pick-folder)
1063 (defvar mh-pick-binary "pick") 992 (defvar mh-pick-binary "pick")
1064 993
1317 (point-min) (point-max) "sh" nil (get-buffer mh-index-temp-buffer)))) 1246 (point-min) (point-max) "sh" nil (get-buffer mh-index-temp-buffer))))
1318 1247
1319 ;;;###mh-autoload 1248 ;;;###mh-autoload
1320 (defun mh-index-sequenced-messages (folders sequence) 1249 (defun mh-index-sequenced-messages (folders sequence)
1321 "Display messages from FOLDERS in SEQUENCE. 1250 "Display messages from FOLDERS in SEQUENCE.
1322 By default the folders specified by `mh-index-new-messages-folders' are 1251 All messages in the sequence you provide from the folders in
1323 searched. With a prefix argument, enter a space-separated list of folders, or 1252 `mh-index-new-messages-folders' are listed. With a prefix argument, enter a
1324 nothing to search all folders. 1253 space-separated list of folders, or nothing to search all folders."
1325
1326 Argument SEQUENCE defaults to `mh-unseen-seq' and is the sequence that the
1327 function searches for in each of the FOLDERS. With a prefix argument, enter a
1328 sequence to use."
1329 (interactive 1254 (interactive
1330 (list (if current-prefix-arg 1255 (list (if current-prefix-arg
1331 (split-string (read-string "Search folder(s) [all]? ")) 1256 (split-string (read-string "Search folder(s): [all] "))
1332 mh-index-new-messages-folders) 1257 mh-index-new-messages-folders)
1333 (mh-read-seq-default "Search" nil))) 1258 (mh-read-seq-default "Search" nil)))
1334 (unless sequence (setq sequence mh-unseen-seq)) 1259 (unless sequence (setq sequence mh-unseen-seq))
1335 (let* ((mh-flists-search-folders folders) 1260 (let* ((mh-flists-search-folders folders)
1336 (mh-flists-sequence sequence) 1261 (mh-flists-sequence sequence)
1365 (when (stringp message) (message message)))) 1290 (when (stringp message) (message message))))
1366 1291
1367 ;;;###mh-autoload 1292 ;;;###mh-autoload
1368 (defun mh-index-new-messages (folders) 1293 (defun mh-index-new-messages (folders)
1369 "Display unseen messages. 1294 "Display unseen messages.
1370 All messages in the `unseen' sequence from FOLDERS are displayed. 1295 If you use a program such as `procmail' to use `rcvstore' to file your
1371 By default the folders specified by `mh-index-new-messages-folders' 1296 incoming mail automatically, you can display new, unseen, messages using this
1372 are searched. With a prefix argument, enter a space-separated list of 1297 command. All messages in the `unseen' sequence from the folders in
1373 folders, or nothing to search all folders." 1298 `mh-index-new-messages-folders' are listed. With a prefix argument, enter a
1299 space-separated list of FOLDERS, or nothing to search all folders."
1374 (interactive 1300 (interactive
1375 (list (if current-prefix-arg 1301 (list (if current-prefix-arg
1376 (split-string (read-string "Search folder(s) [all]? ")) 1302 (split-string (read-string "Search folder(s): [all] "))
1377 mh-index-new-messages-folders))) 1303 mh-index-new-messages-folders)))
1378 (mh-index-sequenced-messages folders mh-unseen-seq)) 1304 (mh-index-sequenced-messages folders mh-unseen-seq))
1379 1305
1380 ;;;###mh-autoload 1306 ;;;###mh-autoload
1381 (defun mh-index-ticked-messages (folders) 1307 (defun mh-index-ticked-messages (folders)
1382 "Display ticked messages. 1308 "Display ticked messages.
1383 All messages in the `tick' sequence from FOLDERS are displayed. 1309 All messages in `mh-tick-seq' from the folders in
1384 By default the folders specified by `mh-index-ticked-messages-folders' 1310 `mh-index-ticked-messages-folders' are listed. With a prefix argument, enter a
1385 are searched. With a prefix argument, enter a space-separated list of 1311 space-separated list of FOLDERS, or nothing to search all folders."
1386 folders, or nothing to search all folders."
1387 (interactive 1312 (interactive
1388 (list (if current-prefix-arg 1313 (list (if current-prefix-arg
1389 (split-string (read-string "Search folder(s) [all]? ")) 1314 (split-string (read-string "Search folder(s): [all] "))
1390 mh-index-ticked-messages-folders))) 1315 mh-index-ticked-messages-folders)))
1391 (mh-index-sequenced-messages folders mh-tick-seq)) 1316 (mh-index-sequenced-messages folders mh-tick-seq))
1392 1317
1393 1318
1394 1319