* elmo2.el (elmo-generic-list-folder-unread): Rewrite.
authorteranisi <teranisi>
Fri, 29 Sep 2000 02:25:00 +0000 (02:25 +0000)
committerteranisi <teranisi>
Fri, 29 Sep 2000 02:25:00 +0000 (02:25 +0000)
(elmo-list-folder-unread): Rewrite.
(elmo-list-folder-important): Ditto.

* elmo-util.el (elmo-filter-get-spec): Rewrite.
(elmo-condition-parse-error): New inline function.
(elmo-read-search-condition): New function.
(elmo-read-search-condition-internal): Ditto.
(elmo-parse-search-condition): Rewrite.
(elmo-condition-parse): New function.
(elmo-condition-parse-or-expr): Ditto.
(elmo-condition-parse-and-expr): Ditto.
(elmo-condition-parse-primitive): Ditto.
(elmo-condition-parse-search-value): Ditto.
(elmo-buffer-field-primitive-condition-match): Ditto.
(elmo-buffer-field-condition-match): Rewrite.
(elmo-file-field-condition-match): Ditto.

* elmo-msgdb.el (elmo-msgdb-expand-path): Use
`elmo-replace-msgid-as-filename' instead of `elmo-safe-filename'.

* elmo-internal.el (elmo-internal-search): Set `number' and
`number-list' argument of `elmo-file-field-condition-match'.

* elmo-maildir.el (elmo-maildir-search): Ditto.

* elmo-imap4.el (elmo-imap4-list-folder-unread): Use
`msgdb' as argument instead of `mark-alist'.
(elmo-imap4-list-folder-important): Use `msgdb' as argument instead of
`overview'.

* elmo-multi.el: Likewise.

* elmo-pipe.el: Likewise.

* elmo-imap4.el (elmo-imap4-search-internal-primitive): New function.
(elmo-imap4-search-internal): Rewrite.
(elmo-imap4-search): Ditto.

* elmo-filter.el (elmo-filter-list-folder): Rewrite.
(elmo-filter-list-folder-unread): Ditto.
(elmo-filter-list-folder-important): Ditto.
(elmo-filter-search): Rewrite.

* elmo-cache.el (elmo-cache-search-all): Set `number' and
`number-list' argument of `elmo-file-field-condition-match'.
(elmo-cache-search): Ditto.

* elmo-localdir.el: Likewise.

* elmo-archive.el (elmo-archive-field-condition-match): Added argument
number-list and pass it to `elmo-buffer-field-condition-match'.
(elmo-archive-field-condition-match): Pass `number-list' to
`elmo-archive-field-condition-match'

13 files changed:
elmo/ChangeLog
elmo/elmo-archive.el
elmo/elmo-cache.el
elmo/elmo-filter.el
elmo/elmo-imap4.el
elmo/elmo-internal.el
elmo/elmo-localdir.el
elmo/elmo-maildir.el
elmo/elmo-msgdb.el
elmo/elmo-multi.el
elmo/elmo-pipe.el
elmo/elmo-util.el
elmo/elmo2.el

index 9da5e25..6a74f79 100644 (file)
@@ -1,3 +1,60 @@
+2000-09-29  Yuuichi Teranishi  <teranisi@gohome.org>
+
+       * elmo2.el (elmo-generic-list-folder-unread): Rewrite.
+       (elmo-list-folder-unread): Rewrite.
+       (elmo-list-folder-important): Ditto.
+
+       * elmo-util.el (elmo-filter-get-spec): Rewrite.
+       (elmo-condition-parse-error): New inline function.
+       (elmo-read-search-condition): New function.
+       (elmo-read-search-condition-internal): Ditto.
+       (elmo-parse-search-condition): Rewrite.
+       (elmo-condition-parse): New function.
+       (elmo-condition-parse-or-expr): Ditto.
+       (elmo-condition-parse-and-expr): Ditto.
+       (elmo-condition-parse-primitive): Ditto.
+       (elmo-condition-parse-search-value): Ditto.
+       (elmo-buffer-field-primitive-condition-match): Ditto.
+       (elmo-buffer-field-condition-match): Rewrite.
+       (elmo-file-field-condition-match): Ditto.
+
+       * elmo-msgdb.el (elmo-msgdb-expand-path): Use
+       `elmo-replace-msgid-as-filename' instead of `elmo-safe-filename'.
+
+       * elmo-internal.el (elmo-internal-search): Set `number' and
+       `number-list' argument of `elmo-file-field-condition-match'.
+
+       * elmo-maildir.el (elmo-maildir-search): Ditto.
+
+       * elmo-imap4.el (elmo-imap4-list-folder-unread): Use
+       `msgdb' as argument instead of `mark-alist'.
+       (elmo-imap4-list-folder-important): Use `msgdb' as argument instead of
+       `overview'.
+
+       * elmo-multi.el: Likewise.
+
+       * elmo-pipe.el: Likewise.
+
+       * elmo-imap4.el (elmo-imap4-search-internal-primitive): New function.
+       (elmo-imap4-search-internal): Rewrite.
+       (elmo-imap4-search): Ditto.
+
+       * elmo-filter.el (elmo-filter-list-folder): Rewrite.
+       (elmo-filter-list-folder-unread): Ditto.
+       (elmo-filter-list-folder-important): Ditto.
+       (elmo-filter-search): Rewrite.
+
+       * elmo-cache.el (elmo-cache-search-all): Set `number' and
+       `number-list' argument of `elmo-file-field-condition-match'.
+       (elmo-cache-search): Ditto.
+
+       * elmo-localdir.el: Likewise.
+
+       * elmo-archive.el (elmo-archive-field-condition-match): Added argument
+       number-list and pass it to `elmo-buffer-field-condition-match'.
+       (elmo-archive-field-condition-match): Pass `number-list' to
+       `elmo-archive-field-condition-match'
+
 2000-09-25  Yuuichi Teranishi  <teranisi@gohome.org>
 
        * elmo-nntp.el (elmo-nntp-list-folder): Set current group after
index dab0504..c96b003 100644 (file)
@@ -971,7 +971,8 @@ TYPE specifies the archiver's symbol."
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Search functions
 
-(defsubst elmo-archive-field-condition-match (spec number condition prefix)
+(defsubst elmo-archive-field-condition-match (spec number number-list
+                                                  condition prefix)
   (save-excursion
     (let* ((type (nth 2 spec))
           (arc (elmo-archive-get-archive-name (nth 1 spec) type spec))
@@ -983,7 +984,7 @@ TYPE specifies the archiver's symbol."
          (elmo-archive-call-method method args t))
         (elmo-set-buffer-multibyte default-enable-multibyte-characters)
         (decode-mime-charset-region (point-min)(point-max) elmo-mime-charset)
-        (elmo-buffer-field-condition-match condition))))))
+        (elmo-buffer-field-condition-match condition number number-list))))))
 
 (defun elmo-archive-search (spec condition &optional from-msgs)
   (let* (;;(args (elmo-string-to-list key))
@@ -996,7 +997,7 @@ TYPE specifies the archiver's symbol."
         (case-fold-search nil)
         ret-val)
     (while msgs
-      (if (elmo-archive-field-condition-match spec (car msgs)
+      (if (elmo-archive-field-condition-match spec (car msgs) msgs
                                              condition
                                              (nth 3 spec))
          (setq ret-val (cons (car msgs) ret-val)))
index 53b7823..4f81749 100644 (file)
@@ -280,29 +280,33 @@ If KBYTES is kilo bytes (This value must be float)."
                ;; not directory.
                path))))))
 
-(defun elmo-cache-search-all (folder condition from-msgs)
+(defun elmo-cache-search-all (folder condition)
   (let* ((number-alist (elmo-msgdb-number-load
                        (elmo-msgdb-expand-path folder)))
-        (nalist number-alist)
+        (number-list (mapcar 'car number-alist))
         (num (length number-alist))
         cache-file
         ret-val
         case-fold-search msg
         percent i)
     (setq i 0)
-    (while nalist
-      (if (and (setq cache-file (elmo-cache-exists-p (cdr (car nalist))
+    (while number-alist
+      (if (and (setq cache-file (elmo-cache-exists-p (cdr (car
+                                                          number-alist))
                                                     folder
-                                                    (car (car nalist))))
-              (elmo-file-field-condition-match cache-file condition))
-         (setq ret-val (append ret-val (list (caar nalist)))))
+                                                    (car (car
+                                                          number-alist))))
+              (elmo-file-field-condition-match cache-file condition
+                                               (car (car number-alist))
+                                               number-list))
+         (setq ret-val (append ret-val (list (caar number-alist)))))
       (when (> num elmo-display-progress-threshold)
        (setq i (1+ i))
        (setq percent (/ (* i 100) num))
        (elmo-display-progress
         'elmo-cache-search-all "Searching..."
         percent))
-      (setq nalist (cdr nalist)))
+      (setq number-alist (cdr number-alist)))
     ret-val))
 
 (defun elmo-cache-collect-sub-directories (init dir &optional recursively)
@@ -696,7 +700,9 @@ Returning its cache buffer."
            (elmo-msgid-to-cache
             (cdr (assq (car msgs) number-alist)))
            (elmo-cache-get-folder-directory spec))
-                                           condition)
+          condition
+          (car msgs)
+          msgs)
          (setq ret-val (cons (car msgs) ret-val)))
       (when (> num elmo-display-progress-threshold)
        (setq i (1+ i))
index 14b2731..2490bc6 100644 (file)
   (elmo-call-func (nth 2 spec) "delete-msgs" msgs))
 
 (defun elmo-filter-list-folder (spec)
-  (let ((filter (nth 1 spec))
-       (folder (nth 2 spec))
-       numbers)
-    (cond
-     ((vectorp filter)
-      (cond ((string= (elmo-filter-key filter)
-                     "last")
-            (setq numbers (elmo-list-folder folder))
-            (nthcdr (max (- (length numbers)
-                            (string-to-int (elmo-filter-value filter)))
-                         0)
-                    numbers))
-           ((string= (elmo-filter-key filter)
-                     "first")
-            (setq numbers (elmo-list-folder folder))
-            (let ((rest (nthcdr (string-to-int (elmo-filter-value filter) )
-                                numbers)))
-              (mapcar '(lambda (x)
-                         (delete x numbers)) rest))
-            numbers)))
-     ((listp filter)
-      (elmo-search folder filter)))))
-
-(defun elmo-filter-list-folder-unread (spec mark-alist unread-marks)
-  (let ((filter (nth 1 spec))
-       (folder (nth 2 spec))
-       msgs pair)
-    (cond
-     ((vectorp filter)
-      (cond ((string= (elmo-filter-key filter)
-                     "last")
-            (setq msgs (elmo-list-folder-unread folder mark-alist
-                                                unread-marks))
-            (nthcdr (max (- (length msgs)
-                            (string-to-int (elmo-filter-value filter)))
-                         0)
-                    msgs))
-           ((string= (elmo-filter-key filter)
-                     "first")
-            (setq msgs (elmo-list-folder-unread folder
-                                                mark-alist
-                                                unread-marks))
-            (let ((rest (nthcdr (string-to-int (elmo-filter-value filter) )
-                                msgs)))
-              (mapcar '(lambda (x)
-                         (delete x msgs)) rest))
-            msgs)))
-     ((listp filter)
-      (elmo-list-filter
-       (elmo-search folder filter)
-       (elmo-list-folder-unread folder mark-alist unread-marks))))))
-
-(defun elmo-filter-list-folder-important (spec overview)
-  (let ((filter (nth 1 spec))
-       (folder (nth 2 spec))
-       msgs pair)
-    (cond
-     ((vectorp filter)
-      (cond ((string= (elmo-filter-key filter)
-                     "last")
-            (setq msgs (elmo-list-folder-important folder overview))
-            (nthcdr (max (- (length msgs)
-                            (string-to-int (elmo-filter-value filter)))
-                         0)
-                    msgs))
-           ((string= (elmo-filter-key filter)
-                     "first")
-            (setq msgs (elmo-list-folder-important folder overview))
-            (let ((rest (nthcdr (string-to-int (elmo-filter-value filter) )
-                                msgs)))
-              (mapcar '(lambda (x)
-                         (delete x msgs)) rest))
-            msgs)))
-     ((listp filter)
-      (elmo-list-filter
-       (mapcar
-       '(lambda (x) (elmo-msgdb-overview-entity-get-number x))
-       overview)
-       (elmo-list-folder-important folder overview))))))
+  (elmo-search (nth 2 spec) (nth 1 spec)))
+
+(defun elmo-filter-list-folder-unread (spec msgdb unread-marks)
+  (elmo-list-filter
+   (mapcar 'car (elmo-msgdb-get-number-alist msgdb))
+   (elmo-list-folder-unread
+    (nth 2 spec) msgdb unread-marks)))
+
+(defun elmo-filter-list-folder-important (spec msgdb)
+  (elmo-list-filter
+   (mapcar 'car (elmo-msgdb-get-number-alist msgdb))
+   (elmo-list-folder-important
+    (nth 2 spec)
+    msgdb)))
 
 (defun elmo-filter-max-of-folder (spec)
   (elmo-max-of-folder (nth 2 spec)))
 (defun elmo-filter-create-folder (spec)
   (elmo-create-folder (nth 2 spec)))
 
-(defun elmo-filter-search (spec condition &optional numlist)
+(defun elmo-filter-search (spec condition &optional from-msgs)
   ;; search from messages in this folder
   (elmo-list-filter
-   numlist
-   (elmo-call-func (nth 2 spec) "search" condition
-                  (elmo-filter-list-folder spec))))
+   from-msgs
+   (elmo-search (nth 2 spec) condition
+               (elmo-filter-list-folder spec))))
 
 (defun elmo-filter-use-cache-p (spec number)
   (elmo-call-func (nth 2 spec) "use-cache-p" number))
index 4f9e9b8..66b8dc1 100644 (file)
@@ -57,7 +57,7 @@
   (defun-maybe sasl-digest-md5-digest-response
     (digest-challenge username passwd serv-type host &optional realm))
   (defun-maybe starttls-negotiate (a))
-  (defun-maybe elmo-generic-list-folder-unread (spec mark-alist unread-marks))
+  (defun-maybe elmo-generic-list-folder-unread (spec msgdb unread-marks))
   (defsubst-maybe utf7-decode-string (string &optional imap) string))
 
 (defvar elmo-imap4-use-lock t
@@ -730,12 +730,12 @@ BUFFER must be a single-byte buffer."
                      numbers))
       numbers)))
 
-(defun elmo-imap4-list-folder-unread (spec mark-alist unread-marks)
+(defun elmo-imap4-list-folder-unread (spec msgdb unread-marks)
   (if (elmo-imap4-use-flag-p spec)
       (elmo-imap4-list spec "unseen")
-    (elmo-generic-list-folder-unread spec mark-alist unread-marks)))
+    (elmo-generic-list-folder-unread spec msgdb unread-marks)))
 
-(defun elmo-imap4-list-folder-important (spec overview)
+(defun elmo-imap4-list-folder-important (spec msgdb)
   (and (elmo-imap4-use-flag-p spec)
        (elmo-imap4-list spec "flagged")))
 
@@ -744,10 +744,23 @@ BUFFER must be a single-byte buffer."
        (insert (, string))
        (detect-mime-charset-region (point-min) (point-max)))))
 
-(defun elmo-imap4-search-internal (session filter)
+(defun elmo-imap4-search-internal-primitive (spec session filter from-msgs)
   (let ((search-key (elmo-filter-key filter))
+       (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to"))
        charset)
     (cond
+     ((string= "last" search-key)
+      (let ((numbers (or from-msgs (elmo-imap4-list-folder spec))))
+       (nthcdr (max (- (length numbers)
+                       (string-to-int (elmo-filter-value filter)))
+                    0)
+               numbers)))
+     ((string= "first" search-key)
+      (let* ((numbers (or from-msgs (elmo-imap4-list-folder spec)))
+            (rest (nthcdr (string-to-int (elmo-filter-value filter) )
+                          numbers)))
+       (mapcar '(lambda (x) (delete x numbers)) rest)
+       numbers))
      ((or (string= "since" search-key)
          (string= "before" search-key))
       (setq search-key (concat "sent" search-key))
@@ -755,16 +768,29 @@ BUFFER must be a single-byte buffer."
        (elmo-imap4-send-command-wait session
                                     (format
                                      (if elmo-imap4-use-uid
-                                         "uid search %s %s"
-                                       " search %s %s")
+                                         "uid search %s %s %s %s"
+                                       " search %s %s %s %s")
+                                     (if from-msgs
+                                         (concat
+                                          (unless elmo-imap4-use-uid "uid ")
+                                          (cdr
+                                           (elmo-imap4-make-number-set-list
+                                            from-msgs)))
+                                       "")
+                                     (if (eq (elmo-filter-type filter)
+                                             'unmatch)
+                                         "not" "")
                                      search-key
                                      (elmo-date-get-description
                                       (elmo-date-get-datevec
                                        (elmo-filter-value filter)))))
        'search))
      (t
-      (setq charset (elmo-imap4-detect-search-charset
-                    (elmo-filter-value filter)))
+      (setq charset
+           (if (eq (length (elmo-filter-value filter)) 0)
+               (setq charset 'us-ascii)
+             (elmo-imap4-detect-search-charset
+              (elmo-filter-value filter))))
       (elmo-imap4-response-value
        (elmo-imap4-send-command-wait session
                                     (list
@@ -773,33 +799,58 @@ BUFFER must be a single-byte buffer."
                                        "search CHARSET ")
                                      (elmo-imap4-astring
                                       (symbol-name charset))
+                                     (if from-msgs
+                                         (concat
+                                          (unless elmo-imap4-use-uid "uid ")
+                                          (cdr
+                                           (elmo-imap4-make-number-set-list
+                                            from-msgs)))
+                                       "")
                                      (if (eq (elmo-filter-type filter)
                                              'unmatch)
                                          " not " " ")
-                                     (format "%s "
+                                     (format "%s%s "
+                                             (if (member
+                                                  (elmo-filter-key filter)
+                                                  imap-search-keys)
+                                                 ""
+                                               "header ")
                                              (elmo-filter-key filter))
                                      (elmo-imap4-astring
                                       (encode-mime-charset-string
                                        (elmo-filter-value filter) charset))))
        'search)))))
 
+(defun elmo-imap4-search-internal (spec session condition from-msgs)
+  (let (result)
+    (cond
+     ((vectorp condition)
+      (setq result (elmo-imap4-search-internal-primitive
+                   spec session condition from-msgs)))
+     ((eq (car condition) 'and)
+      (setq result (elmo-imap4-search-internal spec session (nth 1 condition)
+                                              from-msgs)
+           result (elmo-list-filter result
+                                    (elmo-imap4-search-internal
+                                     spec session (nth 2 condition)
+                                     from-msgs))))
+     ((eq (car condition) 'or)
+      (setq result (elmo-imap4-search-internal
+                   spec session (nth 1 condition) from-msgs)
+           result (elmo-uniq-list
+                   (nconc result
+                          (elmo-imap4-search-internal
+                           spec session (nth 2 condition) from-msgs)))
+           result (sort result '<))))))
+    
+
 (defun elmo-imap4-search (spec condition &optional from-msgs)
   (save-excursion
-    (let* ((session (elmo-imap4-get-session spec))
-          response matched)
+    (let ((session (elmo-imap4-get-session spec)))
       (elmo-imap4-session-select-mailbox
        session
        (elmo-imap4-spec-mailbox spec))
-      (while condition
-       (setq response (elmo-imap4-search-internal session
-                                                  (car condition)))
-       (setq matched (nconc matched response))
-       (setq condition (cdr condition)))
-      (if from-msgs
-         (elmo-list-filter
-          from-msgs
-          (elmo-uniq-list (sort matched '<)))
-       (elmo-uniq-list (sort matched '<))))))
+      (elmo-imap4-search-internal spec session condition from-msgs))))
 
 (defun elmo-imap4-use-flag-p (spec)
   (not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
index aa297bf..1d69b68 100644 (file)
         (loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
                      (elmo-msgdb-location-load (elmo-msgdb-expand-path
                                                 nil spec))))
+        (number-list (mapcar 'car loc-alist))
         cache-file
         ret-val
         case-fold-search msg
     (while loc-alist
       (if (and (setq cache-file (elmo-cache-exists-p (cdr (car loc-alist))))
               (elmo-file-field-condition-match cache-file
-                                               condition))
+                                               condition
+                                               (car (car loc-alist))
+                                               number-list))
          (setq ret-val (append ret-val (list (car (car loc-alist))))))
       (setq i (1+ i))
       (setq percent (/ (* i 100) num))
index af33d0e..c7865e2 100644 (file)
        (rename-file old new)
        t))))
 
-(defsubst elmo-localdir-field-condition-match (spec number condition)
+(defsubst elmo-localdir-field-condition-match (spec condition
+                                                   number number-list)
   (elmo-file-field-condition-match
    (expand-file-name (int-to-string number)
                     (elmo-localdir-get-folder-directory spec))
-   condition))
+   condition
+   number number-list))
 
 (defun elmo-localdir-search (spec condition &optional from-msgs)
   (let* ((msgs (or from-msgs (elmo-localdir-list-folder spec)))
         (num (length msgs))
         (i 0) case-fold-search ret-val)
     (while msgs
-      (if (elmo-localdir-field-condition-match spec (car msgs)
-                                              condition)
+      (if (elmo-localdir-field-condition-match spec condition
+                                              (car msgs) msgs)
          (setq ret-val (cons (car msgs) ret-val)))
       (when (> num elmo-display-progress-threshold)
        (setq i (1+ i))
index 4ca4610..10a61a6 100644 (file)
@@ -440,7 +440,7 @@ file name for maildir directories."
        (if (elmo-file-field-condition-match
             (elmo-maildir-number-to-filename
              dir (car msgs) loc-alist)
-            condition)
+            condition (car msgs) msgs)
            (setq ret-val (append ret-val (list msg-num))))
        (setq i (1+ i))
        (setq percent (/ (* i 100) num))
index 0e43a2f..c012ce0 100644 (file)
@@ -78,7 +78,7 @@
                                           elmo-msgdb-dir)))
       ((eq type 'filter)
        (expand-file-name
-       (elmo-safe-filename folder)
+       (elmo-replace-msgid-as-filename folder)
        (expand-file-name "filter"
                          elmo-msgdb-dir)))
       ((eq type 'archive)
index 0fb6397..06bf4fe 100644 (file)
       (setq result (nconc result (list one-alist))))
     result))
 
-(defun elmo-multi-list-folder-unread (spec mark-alist unread-marks)
+(defun elmo-multi-list-folder-unread (spec msgdb unread-marks)
   (let* ((flds (cdr spec))
         (cur-number 0)
+        (mark-alist (elmo-msgdb-get-mark-alist msgdb))
         mark-alist-list
         ret-val)
     (setq mark-alist-list (elmo-multi-mark-alist-list mark-alist))
       (setq flds (cdr flds)))
     ret-val))
 
-(defun elmo-multi-list-folder-important (spec overview)
+(defun elmo-multi-list-folder-important (spec msgdb)
   (let* ((flds (cdr spec))
         (cur-number 0)
         ret-val)
                       (lambda (x)
                         (+
                          (* elmo-multi-divide-number cur-number) x)))
-                     (elmo-list-folder-important (car flds) overview))))
+                     (elmo-list-folder-important (car flds)
+                                                 msgdb))))
       (setq flds (cdr flds)))
     ret-val))
 
index eb07743..fce64b7 100644 (file)
                      numbers))
       numbers)))
 
-(defun elmo-pipe-list-folder-unread (spec mark-alist unread-marks)
-  (elmo-list-folder-unread (elmo-pipe-spec-dst spec) mark-alist unread-marks))
+(defun elmo-pipe-list-folder-unread (spec msgdb unread-marks)
+  (elmo-list-folder-unread (elmo-pipe-spec-dst spec)
+                          msgdb unread-marks))
   
-(defun elmo-pipe-list-folder-important (spec overview)
-  (elmo-list-folder-important (elmo-pipe-spec-dst spec) overview))
+(defun elmo-pipe-list-folder-important (spec msgdb)
+  (elmo-list-folder-important (elmo-pipe-spec-dst spec) msgdb))
 
 (defun elmo-pipe-max-of-folder (spec)
   (let* (elmo-pop3-use-uidl
index 9af7aa9..174198a 100644 (file)
@@ -446,23 +446,13 @@ File content is encoded with MIME-CHARSET."
               ",")))))
 
 (defun elmo-filter-get-spec (folder)
-  (save-match-data
-    (when (string-match
-          "^\\(/\\)\\(.*\\)$"
-          folder)
-      (let ((spec (elmo-match-string 2 folder))
-           filter)
-       (when (string-match "\\([^/]+\\)/" spec)
-         (setq filter (elmo-match-string 1 spec))
-         (setq spec (substring spec (match-end 0))))
-       (cond
-        ((string-match "^\\(last\\|first\\):\\(.*\\)$" filter) ; partial
-         (setq filter (vector 'partial
-                              (elmo-match-string 1 filter)
-                              (elmo-match-string 2 filter))))
-        (t
-         (setq filter (elmo-parse-search-condition filter))))
-       (list 'filter filter spec)))))
+  (when (string-match "^\\(/\\)\\(.*\\)$" folder)
+    (let ((folder (elmo-match-string 2 folder))
+         pair)
+      (setq pair (elmo-parse-search-condition folder))
+      (if (string-match "^ */\\(.*\\)$" (cdr pair))
+         (list 'filter (car pair) (elmo-match-string 1 (cdr pair)))
+       (error "Folder syntax error `%s'" folder)))))
 
 (defun elmo-pipe-get-spec (folder)
   (when (string-match "^\\(|\\)\\([^|]*\\)|\\(.*\\)$" folder)
@@ -478,46 +468,145 @@ File content is encoded with MIME-CHARSET."
                 folder)
       (error "%s is not supported folder type" folder))))
 
+;;; Search Condition
+(defun elmo-read-search-condition (default)
+  "Read search condition string interactively"
+  (elmo-read-search-condition-internal "Search by" default))
+
+(defun elmo-read-search-condition-internal (prompt default)
+  (let* ((completion-ignore-case t)
+        (field (completing-read
+                (format "%s (%s): " prompt default)
+                (mapcar 'list
+                        (append '("AND" "OR"
+                                  "Last" "First"
+                                  "From" "Subject" "To" "Cc" "Body"
+                                  "Since" "Before" "ToCc"
+                                  "!From" "!Subject" "!To" "!Cc" "!Body"
+                                  "!Since" "!Before" "!ToCc")
+                                elmo-msgdb-extra-fields)))))
+    (setq field (if (string= field "")
+                   (setq field default)
+                 field))
+    (cond
+     ((or (string= field "AND") (string= field "OR"))
+      (concat "("
+             (elmo-read-search-condition-internal
+              (concat field "(1) Search by") default)
+             (if (string= field "AND") "&" "|")
+             (elmo-read-search-condition-internal
+              (concat field "(2) Search by") default)
+             ")"))
+     ((string-match "Since\\|Before" field)
+      (concat
+       (downcase field) ":"
+       (completing-read (format "Value for '%s': " field)
+                       (mapcar (function
+                                (lambda (x)
+                                  (list (format "%s" (car x)))))
+                               elmo-date-descriptions))))
+     (t
+      (concat
+       (downcase field) ":"
+       (prin1-to-string
+       (read-from-minibuffer
+        (format "Value for '%s': " field))))))))
+
+(defsubst elmo-condition-parse-error ()
+  (error "Syntax error in '%s'" (buffer-string)))
+
 (defun elmo-parse-search-condition (condition)
-  (let ((terms (split-string condition "|")) ; split by OR
-       term ret-val)
-    (while terms
-      (setq term (car terms))
-      (cond
-       ((string-match "^\\([a-zA-Z\\-]+\\)=\\(.*\\)$" term)
-       (if (save-match-data
-             (string-match "tocc" (elmo-match-string 1 term))) ;; syntax sugar
-           (setq ret-val (nconc
-                          ret-val
-                          (list (vector 'match "to"
-                                        (elmo-match-string 2 term))
-                                (vector 'match "cc"
-                                        (elmo-match-string 2 term)))))
-         (setq ret-val (cons (vector 'match
-                                     (elmo-match-string 1 term)
-                                     (elmo-match-string 2 term))
-                             ret-val))))
-       ((string-match "^\\([a-zA-Z\\-]+\\)!=\\(.*\\)$" term)
-       (if (save-match-data
-             (string-match "tocc" (elmo-match-string 1 term))) ;; syntax sugar
-           (setq ret-val (nconc
-                          ret-val
-                          (list (vector 'unmatch "to"
-                                        (elmo-match-string 2 term))
-                                (vector 'unmatch "cc"
-                                        (elmo-match-string 2 term)))))
-         (setq ret-val (cons (vector 'unmatch
-                                     (elmo-match-string 1 term)
-                                     (elmo-match-string 2 term))
-                             ret-val))))
-       ((string-match "^\\(since\\|before\\):\\(.*\\)$" term)
-       (setq ret-val (cons (vector 'date
-                                   (elmo-match-string 1 term)
-                                   (elmo-match-string 2 term))
-                           ret-val))))
-      (setq terms (cdr terms)))
-    ret-val))
+  "Parse CONDITION.
+Return value is a cons cell of (STRUCTURE . REST)"
+  (with-temp-buffer
+    (insert condition)
+    (goto-char (point-min))
+    (cons (elmo-condition-parse) (buffer-substring (point) (point-max)))))
+
+;; condition    ::= or-expr
+(defun elmo-condition-parse ()
+  (or (elmo-condition-parse-or-expr)
+      (elmo-condition-parse-error)))
+
+;; or-expr      ::= and-expr /
+;;                 and-expr "|" or-expr
+(defun elmo-condition-parse-or-expr ()
+  (let ((left (elmo-condition-parse-and-expr)))
+    (if (looking-at "| *")
+       (progn
+         (goto-char (match-end 0))
+         (list 'or left (elmo-condition-parse-or-expr)))
+      left)))
+
+;; and-expr     ::= primitive /
+;;                  primitive "&" and-expr
+(defun elmo-condition-parse-and-expr ()
+  (let ((left (elmo-condition-parse-primitive)))
+    (if (looking-at "& *")
+       (progn
+         (goto-char (match-end 0))
+         (list 'and left (elmo-condition-parse-and-expr)))
+      left)))
+
+;; primitive    ::= "(" expr ")" /
+;;                  ["!"] search-key SPACE* ":" SPACE* search-value
+(defun elmo-condition-parse-primitive ()
+  (cond 
+   ((looking-at "( *")
+    (goto-char (match-end 0))
+    (prog1 (elmo-condition-parse)
+      (unless (looking-at ") *")
+       (elmo-condition-parse-error))
+      (goto-char (match-end 0))))
+;; search-key   ::= [A-Za-z-]+
+;;                 ;; "since" / "before" / "last" / "first" /
+;;                 ;; "body" / field-name
+   ((looking-at "\\(!\\)? *\\([A-Za-z-]+\\) *: *")
+    (goto-char (match-end 0))
+    (let ((search-key (vector
+                      (if (match-beginning 1) 'unmatch 'match)
+                      (elmo-match-buffer 2)
+                      (elmo-condition-parse-search-value))))
+      ;; syntax sugar.
+      (if (string= (aref search-key 1) "tocc")
+         (if (eq (aref search-key 0) 'match)
+             (list 'or
+                   (vector 'match "to" (aref search-key 2))
+                   (vector 'match "cc" (aref search-key 2)))
+           (list 'and
+                 (vector 'unmatch "to" (aref search-key 2))
+                 (vector 'unmatch "cc" (aref search-key 2))))
+       search-key)))))
+
+;; search-value ::= quoted / time / number / atom
+;; quoted       ::= <elisp string expression>
+;; time         ::= "yesterday" / "lastweek" / "lastmonth" / "lastyear" /
+;;                   number SPACE* "daysago" /
+;;                   number "-" month "-" number  ; ex. 10-May-2000
+;; number       ::= [0-9]+
+;; month        ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
+;;                  "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
+;; atom         ::= ATOM_CHARS*
+;; SPACE        ::= <ascii space character, 0x20>
+;; ATOM_CHARS   ::= <any character except specials>
+;; specials     ::= SPACE / <"> / </> / <)> / <|> / <&>
+;;                  ;; These characters should be quoted.
+(defun elmo-condition-parse-search-value ()
+  (cond
+   ((looking-at "\"")
+    (read (current-buffer)))
+   ((or (looking-at "yesterday") (looking-at "lastweek")
+       (looking-at "lastmonth") (looking-at "lastyear")
+       (looking-at "[0-9]+ *daysago")
+       (looking-at "[0-9]+")
+       (looking-at "[0-9]+-[A-Za-z]+-[0-9]+")
+       (looking-at "[^/ \")|&]*") ; atom* (except quoted specials).
+       )
+    (prog1 (elmo-match-buffer 0)
+      (goto-char (match-end 0))))
+   (t (error "Syntax error '%s'" (buffer-string)))))
 
+;;;
 (defun elmo-multi-get-real-folder-number (folder number)
   (let* ((spec (elmo-folder-get-spec folder))
         (flds (cdr spec))
@@ -1120,73 +1209,80 @@ Otherwise treat \\ in NEWTEXT string as special:
 (defmacro elmo-filter-value (filter)
   (` (aref (, filter) 2)))
 
-(defsubst elmo-buffer-field-condition-match (condition)
-  (let (term)
-    (catch 'done
-      (while condition
-       (goto-char (point-min))
-       (setq term (car condition))
-       (cond
-        ((and (eq (elmo-filter-type term) 'date)
-              (string= (elmo-filter-key term) "since"))
-         (let ((date (elmo-date-get-datevec (elmo-filter-value term))))
-           (if (string<
-                (timezone-make-sortable-date (aref date 0)
-                                             (aref date 1)
-                                             (aref date 2)
-                                             (timezone-make-time-string
-                                              (aref date 3)
-                                              (aref date 4)
-                                              (aref date 5)))
-                (timezone-make-date-sortable (std11-field-body "date")))
-               (throw 'done t))))
-        ((and (eq (elmo-filter-type term) 'date)
-              (string= (elmo-filter-key term) "before"))
-         (let ((date (elmo-date-get-datevec (elmo-filter-value term))))
-           (if (string<
-                (timezone-make-date-sortable (std11-field-body "date"))
-                (timezone-make-sortable-date (aref date 0)
-                                             (aref date 1)
-                                             (aref date 2)
-                                             (timezone-make-time-string
-                                              (aref date 3)
-                                              (aref date 4)
-                                              (aref date 5))))
-               (throw 'done t))))
-        ((eq (elmo-filter-type term) 'match)
-         (if (string= "body" (elmo-filter-key term))
-             (progn
-               (re-search-forward "^$" nil t)     ; goto body
-               (if (search-forward (elmo-filter-value term) nil t)
-                   (throw 'done t)))
-           (let ((fval (eword-decode-string
-                        (or (std11-field-body (elmo-filter-key term)) ""))))
-             (if (and fval (string-match (elmo-filter-value term)
-                                         fval))
-                 (throw 'done t)))))
-        ((eq (elmo-filter-type term) 'unmatch)
-         (if (string= "body" (elmo-filter-key term))
-             (progn
-               (re-search-forward "^$" nil t)     ; goto body
-               (if (not (search-forward (elmo-filter-value term) nil t))
-                   (throw 'done t)))
-           (let ((fval (eword-decode-string
-                        (or (std11-field-body (elmo-filter-key term)) ""))))
-             (if fval
-                 (if (not (string-match (elmo-filter-value term)
-                                        fval))
-                     (throw 'done t))
-               (throw 'done t)))))) ; OK?
-       (setq condition (cdr condition)))
-      (throw 'done nil))))
-
-(defsubst elmo-file-field-condition-match (file condition)
+(defsubst elmo-buffer-field-primitive-condition-match (condition
+                                                      number
+                                                      number-list)
+  (let (result)
+    (goto-char (point-min))
+    (cond
+     ((string= (elmo-filter-key condition) "last")
+      (setq result (> (length (memq number number-list))
+                     (string-to-int (elmo-filter-value condition)))))
+     ((string= (elmo-filter-key condition) "first")
+      (setq result (< (- (length number-list)
+                        (length (memq number number-list)))
+                     (string-to-int (elmo-filter-value condition)))))
+     ((string= (elmo-filter-key condition) "since")
+      (let ((date (elmo-date-get-datevec (elmo-filter-value condition))))
+       (setq result
+             (string<
+              (timezone-make-sortable-date (aref date 0)
+                                           (aref date 1)
+                                           (aref date 2)
+                                           (timezone-make-time-string
+                                            (aref date 3)
+                                            (aref date 4)
+                                            (aref date 5)))
+              (timezone-make-date-sortable (std11-field-body "date"))))))
+     ((string= (elmo-filter-key condition) "before")
+      (let ((date (elmo-date-get-datevec (elmo-filter-value condition))))
+       (setq result
+             (string<
+              (timezone-make-date-sortable (std11-field-body "date"))
+              (timezone-make-sortable-date (aref date 0)
+                                           (aref date 1)
+                                           (aref date 2)
+                                           (timezone-make-time-string
+                                            (aref date 3)
+                                            (aref date 4)
+                                            (aref date 5)))))))
+     ((string= (elmo-filter-key condition) "body")
+      (and (re-search-forward "^$" nil t)         ; goto body
+          (setq result (search-forward (elmo-filter-value condition)
+                                       nil t))))
+     (t
+      (let ((fval (std11-field-body (elmo-filter-key condition))))
+       (if (eq (length fval) 0) (setq fval nil))
+       (if fval (setq fval (eword-decode-string fval)))
+       (setq result (and fval (string-match
+                               (elmo-filter-value condition) fval))))))
+    (if (eq (elmo-filter-type condition) 'unmatch)
+       (setq result (not result)))
+    result))
+
+(defun elmo-buffer-field-condition-match (condition number number-list)
+  (cond
+   ((vectorp condition)
+    (elmo-buffer-field-primitive-condition-match
+     condition number number-list))
+   ((eq (car condition) 'and)
+    (and (elmo-buffer-field-condition-match
+         (nth 1 condition) number number-list)
+        (elmo-buffer-field-condition-match
+         (nth 2 condition) number number-list)))
+   ((eq (car condition) 'or)
+    (or (elmo-buffer-field-condition-match
+        (nth 1 condition) number number-list)
+       (elmo-buffer-field-condition-match
+        (nth 2 condition) number number-list)))))
+
+(defsubst elmo-file-field-condition-match (file condition number number-list)
   (elmo-set-work-buf
-   (as-binary-input-file
-    (insert-file-contents file))
+   (as-binary-input-file (insert-file-contents file))
    (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+   ;; Should consider charset?
    (decode-mime-charset-region (point-min)(point-max) elmo-mime-charset)
-   (elmo-buffer-field-condition-match condition)))
+   (elmo-buffer-field-condition-match condition number number-list)))
 
 (defun elmo-cross-device-link-error-p (err)
   (let ((errobj err)
index 17abb1e..ff71a73 100644 (file)
@@ -386,14 +386,10 @@ without cacheing."
       (elmo-call-func folder "delete-msgs" msgs)
     (elmo-dop-delete-msgs folder msgs msgdb)))
 
-;;
-;; Server side search.
-;;
 (defun elmo-search (folder condition &optional from-msgs)
-  (let ((type (elmo-folder-get-type folder)))
-    (if (elmo-folder-plugged-p folder)
-       (elmo-call-func folder "search" condition from-msgs)
-      (elmo-cache-search-all folder condition from-msgs))))
+  (if (elmo-folder-plugged-p folder)
+      (elmo-call-func folder "search" condition from-msgs)
+    (elmo-cache-search-all folder condition from-msgs)))
 
 (defun elmo-msgdb-create (folder numlist new-mark already-mark
                                 seen-mark important-mark seen-list)
@@ -578,16 +574,17 @@ without cacheing."
   "Just return number-alist."
   number-alist)
 
-(defun elmo-generic-list-folder-unread (spec mark-alist unread-marks)
-  (elmo-delete-if
-   'null
-   (mapcar
-    (function (lambda (x)
-               (if (member (cadr (assq (car x) mark-alist)) unread-marks)
-                   (car x))))
-    mark-alist)))
-
-(defun elmo-generic-list-folder-important (spec overview)
+(defun elmo-generic-list-folder-unread (spec msgdb unread-marks)
+  (let ((mark-alist (elmo-msgdb-get-mark-alist msgdb)))
+    (elmo-delete-if
+     'null
+     (mapcar
+      (function (lambda (x)
+                 (if (member (cadr (assq (car x) mark-alist)) unread-marks)
+                     (car x))))
+      mark-alist))))
+
+(defun elmo-generic-list-folder-important (spec msgdb)
   nil)
 
 (defun elmo-update-number (folder msgdb)
@@ -632,22 +629,22 @@ without cacheing."
                0)))
          (length in-folder))))
 
-(defun elmo-list-folder-unread (folder mark-alist unread-marks)
-  (elmo-call-func folder "list-folder-unread" mark-alist unread-marks))
+(defun elmo-list-folder-unread (folder msgdb unread-marks)
+  (elmo-call-func folder "list-folder-unread" msgdb unread-marks))
 
-(defun elmo-list-folder-important (folder overview)
-  (let (importants)
+(defun elmo-list-folder-important (folder msgdb)
+  (let (importants 
+       (overview (elmo-msgdb-get-overview msgdb)))
     ;; server side importants...(append only.)
     (if (elmo-folder-plugged-p folder)
        (setq importants (elmo-call-func folder "list-folder-important"
-                                        overview)))
+                                        msgdb)))
     (or elmo-msgdb-global-mark-alist
        (setq elmo-msgdb-global-mark-alist
              (elmo-object-load (expand-file-name
                                 elmo-msgdb-global-mark-filename
                                 elmo-msgdb-dir))))
     (while overview
-      (car overview)
       (if (assoc (elmo-msgdb-overview-entity-get-id (car overview))
                 elmo-msgdb-global-mark-alist)
          (setq importants (cons
@@ -695,8 +692,11 @@ Currently works on IMAP4 folder only."
           (elmo-multi-folder-diff fld))
          ((and (eq type 'filter)
                (or (elmo-multi-p fld)
-                   (not
-                    (vectorp (nth 1 (elmo-folder-get-spec fld)))))
+                   (not (and (vectorp (nth 1 (elmo-folder-get-spec fld)))
+                             (string-match
+                              "^first$\\|^last$"
+                              (elmo-filter-key
+                               (nth 1 (elmo-folder-get-spec fld)))))))
                ;; not partial...unsync number is unknown.
                (cons nil
                      (cdr (elmo-folder-diff