Fixed.
[elisp/wanderlust.git] / elmo / elmo-cache.el
1 ;;; elmo-cache.el -- Cache modules for Elmo.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 2000 Kenichi OKADA <okada@opaopa.org>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;;      Kenichi OKADA <okada@opaopa.org>
8 ;; Keywords: mail, net news
9
10 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16 ;;
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21 ;;
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27
28 ;;; Commentary:
29 ;; 
30
31 ;;; Code:
32 ;; 
33 (require 'elmo-vars)
34 (require 'elmo-util)
35
36 (defsubst elmo-cache-to-msgid (filename)
37   (concat "<" (elmo-recover-string-from-filename filename) ">"))
38
39 ;;; File cache.
40
41 (defun elmo-file-cache-get-path (msgid &optional section)
42   "Get cache path for MSGID.
43 If optional argument SECTION is specified, partial cache path is returned."
44   (if (setq msgid (elmo-msgid-to-cache msgid))
45       (expand-file-name
46        (if section
47            (format "%s/%s/%s/%s/%s"
48                    elmo-msgdb-dir
49                    elmo-cache-dirname
50                    (elmo-cache-get-path-subr msgid)
51                    msgid
52                    section)
53          (format "%s/%s/%s/%s"
54                  elmo-msgdb-dir
55                  elmo-cache-dirname
56                  (elmo-cache-get-path-subr msgid)
57                  msgid)))))
58
59 (defmacro elmo-file-cache-expand-path (path section)
60   "Return file name for the file-cache corresponds to the section.
61 PATH is the file-cache path.
62 SECTION is the section string."
63   (` (expand-file-name (or (, section) "") (, path))))
64
65 (defun elmo-file-cache-delete (path)
66   "Delete a cache on PATH."
67   (let (files)
68     (when (file-exists-p path)
69       (if (file-directory-p path)
70           (progn
71             (setq files (directory-files path t "^[^\\.]"))
72             (while files
73               (delete-file (car files))
74               (setq files (cdr files)))
75             (delete-directory path))
76         (delete-file path)))))
77
78 (defun elmo-file-cache-exists-p (msgid)
79   "Returns 'section or 'entire if a cache which corresponds to MSGID exists."
80   (elmo-file-cache-status (elmo-file-cache-get msgid)))
81
82 (defun elmo-file-cache-save (cache-path section)
83   "Save current buffer as cache on PATH."
84   (let ((path (if section (expand-file-name section cache-path) cache-path))
85         files dir)
86     (if (and (null section)
87              (file-directory-p path))
88         (progn
89           (setq files (directory-files path t "^[^\\.]"))
90           (while files
91             (delete-file (car files))
92             (setq files (cdr files)))
93           (delete-directory path))
94       (if (and section
95                (not (file-directory-p cache-path)))
96           (delete-file cache-path)))
97     (when path
98       (setq dir (directory-file-name (file-name-directory path)))
99       (if (not (file-exists-p dir))
100           (elmo-make-directory dir))
101       (write-region-as-binary (point-min) (point-max)
102                               path nil 'no-msg))))
103
104 (defmacro elmo-make-file-cache (path status)
105   "PATH is the cache file name.
106 STATUS is one of 'section, 'entire or nil.
107  nil means no cache exists.
108 'section means partial section cache exists.
109 'entire means entire cache exists.
110 If the cache is partial file-cache, TYPE is 'partial."
111   (` (cons (, path) (, status))))
112
113 (defmacro elmo-file-cache-path (file-cache)
114   "Returns the file path of the FILE-CACHE."
115   (` (car (, file-cache))))
116
117 (defmacro elmo-file-cache-status (file-cache)
118   "Returns the status of the FILE-CACHE."
119   (` (cdr (, file-cache))))
120
121 (defun elmo-file-cache-get (msgid &optional section)
122   "Returns the current file-cache object associated with MSGID.
123 MSGID is the message-id of the message.
124 If optional argument SECTION is specified, get partial file-cache object
125 associated with SECTION."
126   (if msgid
127       (let ((path (elmo-cache-get-path msgid)))
128         (if (and path (file-exists-p path))
129             (if (file-directory-p path)
130                 (if section
131                     (if (file-exists-p (setq path (expand-file-name
132                                                    section path)))
133                         (cons path 'section))
134                   ;; section is not specified but sectional.
135                   (cons path 'section))
136               ;; not directory.
137               (unless section
138                 (cons path 'entire)))
139           ;; no cache.
140           (cons path nil)))))
141
142 ;;;
143 (defun elmo-cache-expire ()
144   (interactive)
145   (let* ((completion-ignore-case t)
146          (method (completing-read (format "Expire by (%s): "
147                                           elmo-cache-expire-default-method)
148                                   '(("size" . "size")
149                                     ("age" . "age")))))
150     (if (string= method "")
151         (setq method elmo-cache-expire-default-method))
152     (funcall (intern (concat "elmo-cache-expire-by-" method)))))
153
154 (defun elmo-read-float-value-from-minibuffer (prompt &optional initial)
155   (let ((str (read-from-minibuffer prompt initial)))
156     (cond
157      ((string-match "[0-9]*\\.[0-9]+" str)
158       (string-to-number str))
159      ((string-match "[0-9]+" str)
160       (string-to-number (concat str ".0")))
161      (t (error "%s is not number" str)))))
162
163 (defun elmo-cache-expire-by-size (&optional kbytes)
164   "Expire cache file by size.
165 If KBYTES is kilo bytes (This value must be float)."
166   (interactive)
167   (let ((size (or kbytes
168                   (and (interactive-p)
169                        (elmo-read-float-value-from-minibuffer
170                         "Enter cache disk size (Kbytes): "
171                         (number-to-string
172                          (if (integerp elmo-cache-expire-default-size)
173                              (float elmo-cache-expire-default-size)
174                            elmo-cache-expire-default-size))))
175                   (if (integerp elmo-cache-expire-default-size)
176                       (float elmo-cache-expire-default-size))))
177         (locked (elmo-dop-lock-list-load))
178         (count 0)
179         (Kbytes 1024)
180         total beginning)
181     (message "Checking disk usage...")
182     (setq total (/ (elmo-disk-usage
183                     (expand-file-name
184                      elmo-cache-dirname elmo-msgdb-dir)) Kbytes))
185     (setq beginning total)
186     (message "Checking disk usage...done")
187     (let ((cfl (elmo-cache-get-sorted-cache-file-list))
188           (deleted 0)
189           oldest
190           cur-size cur-file)
191       (while (and (<= size total)
192                   (setq oldest (elmo-cache-get-oldest-cache-file-entity cfl)))
193         (setq cur-file (expand-file-name (car (cdr oldest)) (car oldest)))
194         (setq cur-size (/ (elmo-disk-usage cur-file) Kbytes))
195         (when (elmo-cache-force-delete cur-file locked)
196           (setq count (+ count 1))
197           (message "%d cache(s) are expired." count))
198         (setq deleted (+ deleted cur-size))
199         (setq total (- total cur-size)))
200       (message "%d cache(s) are expired from disk (%d Kbytes/%d Kbytes)."
201                count deleted beginning))))
202
203 (defun elmo-cache-make-file-entity (filename path)
204   (cons filename (elmo-get-last-accessed-time filename path)))
205
206 (defun elmo-cache-get-oldest-cache-file-entity (cache-file-list)
207   (let ((cfl cache-file-list)
208         flist firsts oldest-entity wonlist)
209     (while cfl
210       (setq flist (cdr (car cfl)))
211       (setq firsts (append firsts (list
212                                    (cons (car (car cfl))
213                                          (car flist)))))
214       (setq cfl (cdr cfl)))
215 ;;; (prin1 firsts)
216     (while firsts
217       (if (and (not oldest-entity)
218                (cdr (cdr (car firsts))))
219           (setq oldest-entity (car firsts)))
220       (if (and (cdr (cdr (car firsts)))
221                (cdr (cdr oldest-entity))
222                (> (cdr (cdr oldest-entity)) (cdr (cdr (car firsts)))))
223           (setq oldest-entity (car firsts)))
224       (setq firsts (cdr firsts)))
225     (setq wonlist (assoc (car oldest-entity) cache-file-list))
226     (and wonlist
227          (setcdr wonlist (delete (car (cdr wonlist)) (cdr wonlist))))
228     oldest-entity))
229
230 (defun elmo-cache-get-sorted-cache-file-list ()
231   (let ((dirs (directory-files
232                (expand-file-name elmo-cache-dirname elmo-msgdb-dir)
233                t "^[^\\.]"))
234         (i 0) num
235         elist
236         ret-val)
237     (setq num (length dirs))
238     (message "Collecting cache info...")
239     (while dirs
240       (setq elist (mapcar (lambda (x)
241                             (elmo-cache-make-file-entity x (car dirs)))
242                           (directory-files (car dirs) nil "^[^\\.]")))
243       (setq ret-val (append ret-val
244                             (list (cons
245                                    (car dirs)
246                                    (sort
247                                     elist
248                                     (lambda (x y)
249                                       (< (cdr x)
250                                          (cdr y))))))))
251       (when (> num elmo-display-progress-threshold)
252         (setq i (+ i 1))
253         (elmo-display-progress
254          'elmo-cache-get-sorted-cache-file-list "Collecting cache info..."
255          (/ (* i 100) num)))
256       (setq dirs (cdr dirs)))
257     (message "Collecting cache info...done")
258     ret-val))
259
260 (defun elmo-cache-expire-by-age (&optional days)
261   (let ((age (or (and days (int-to-string days))
262                  (and (interactive-p)
263                       (read-from-minibuffer
264                        (format "Enter days (%s): "
265                                elmo-cache-expire-default-age)))
266                  (int-to-string elmo-cache-expire-default-age)))
267         (dirs (directory-files
268                (expand-file-name elmo-cache-dirname elmo-msgdb-dir)
269                t "^[^\\.]"))
270         (locked (elmo-dop-lock-list-load))
271         (count 0)
272         curtime)
273     (if (string= age "")
274         (setq age elmo-cache-expire-default-age)
275       (setq age (string-to-int age)))
276     (setq curtime (current-time))
277     (setq curtime (+ (* (nth 0 curtime)
278                         (float 65536)) (nth 1 curtime)))
279     (while dirs
280       (let ((files (directory-files (car dirs) t "^[^\\.]"))
281             (limit-age (* age 86400)))
282         (while files
283           (when (> (- curtime (elmo-get-last-accessed-time (car files)))
284                    limit-age)
285             (when (elmo-cache-force-delete (car files) locked)
286               (setq count (+ 1 count))
287               (message "%d cache file(s) are expired." count)))
288           (setq files (cdr files))))
289       (setq dirs (cdr dirs)))))
290
291 (defun elmo-cache-search-all (folder condition from-msgs)
292   (let* ((number-alist (elmo-msgdb-number-load
293                         (elmo-msgdb-expand-path folder)))
294          (number-list (or from-msgs (mapcar 'car number-alist)))
295          (num (length number-alist))
296          cache-file
297          ret-val
298          case-fold-search msg
299          percent i)
300     (setq i 0)
301     (while number-alist
302       (if (and (memq (car (car number-alist)) number-list)
303                (setq cache-file (elmo-cache-exists-p (cdr (car
304                                                            number-alist))
305                                                      folder
306                                                      (car (car
307                                                            number-alist))))
308                (elmo-file-field-condition-match cache-file condition
309                                                 (car (car number-alist))
310                                                 number-list))
311           (setq ret-val (append ret-val (list (caar number-alist)))))
312       (when (> num elmo-display-progress-threshold)
313         (setq i (1+ i))
314         (setq percent (/ (* i 100) num))
315         (elmo-display-progress
316          'elmo-cache-search-all "Searching..."
317          percent))
318       (setq number-alist (cdr number-alist)))
319     ret-val))
320
321 (defun elmo-cache-collect-sub-directories (init dir &optional recursively)
322   "Collect subdirectories under DIR."
323   (let ((dirs
324          (delete (expand-file-name elmo-cache-dirname
325                                    elmo-msgdb-dir)
326                  (directory-files dir t "^[^\\.]")))
327         ret-val)
328     (setq dirs (elmo-delete-if (lambda (x) (not (file-directory-p x))) dirs))
329     (setq ret-val (append init dirs))
330     (while (and recursively dirs)
331       (setq ret-val
332             (elmo-cache-collect-sub-directories
333              ret-val
334              (car dirs) recursively))
335       (setq dirs (cdr dirs)))
336     ret-val))
337
338 (defun elmo-msgid-to-cache (msgid)
339   (when (and msgid
340              (string-match "<\\(.+\\)>$" msgid))
341     (elmo-replace-string-as-filename (elmo-match-string 1 msgid))))
342
343 (defun elmo-cache-get-path (msgid &optional folder number)
344   "Get path for cache file associated with MSGID, FOLDER, and NUMBER."
345   (if (setq msgid (elmo-msgid-to-cache msgid))
346       (expand-file-name
347        (expand-file-name
348         (if folder
349             (format "%s/%s/%s@%s"
350                     (elmo-cache-get-path-subr msgid)
351                     msgid
352                     (or number "")
353                     (elmo-safe-filename folder))
354           (format "%s/%s"
355                   (elmo-cache-get-path-subr msgid)
356                   msgid))
357         (expand-file-name elmo-cache-dirname
358                           elmo-msgdb-dir)))))
359
360 (defsubst elmo-cache-get-path-subr (msgid)
361   (let ((chars '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F))
362         (clist (string-to-char-list msgid))
363         (sum 0))
364     (while clist
365       (setq sum (+ sum (car clist)))
366       (setq clist (cdr clist)))
367     (format "%c%c"
368             (nth (% (/ sum 16) 2) chars)
369             (nth (% sum 16) chars))))
370   
371
372 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
373 ;; 
374 ;;
375 ;; cache backend by Kenichi OKADA <okada@opaopa.org>
376 ;;
377
378 (defsubst elmo-cache-get-folder-directory (spec)
379   (if (file-name-absolute-p (nth 1 spec))
380       (nth 1 spec) ; already full path.
381     (expand-file-name (nth 1 spec)
382                       (expand-file-name elmo-cache-dirname elmo-msgdb-dir))))
383
384 (defun elmo-cache-msgdb-expand-path (spec)
385   (let ((fld-name (nth 1 spec)))
386     (expand-file-name fld-name
387                       (expand-file-name "internal/cache"
388                                         elmo-msgdb-dir))))
389
390 (defun elmo-cache-number-to-filename (spec number)
391   (let ((number-alist
392          (elmo-cache-list-folder-subr spec nil t)))
393     (elmo-msgid-to-cache
394      (cdr (assq number number-alist)))))
395
396 (if (boundp 'nemacs-version)
397     (defsubst elmo-cache-insert-header (file)
398       "Insert the header of the article (Does not work on nemacs)."
399       (as-binary-input-file
400        (insert-file-contents file)))
401   (defsubst elmo-cache-insert-header (file)
402     "Insert the header of the article."
403     (let ((beg 0)
404           insert-file-contents-pre-hook ; To avoid autoconv-xmas...
405           insert-file-contents-post-hook
406           format-alist)
407       (when (file-exists-p file)
408         ;; Read until header separator is found.
409         (while (and (eq elmo-localdir-header-chop-length
410                         (nth 1
411                              (as-binary-input-file
412                               (insert-file-contents
413                                file nil beg
414                                (incf beg elmo-localdir-header-chop-length)))))
415                     (prog1 (not (search-forward "\n\n" nil t))
416                       (goto-char (point-max)))))))))
417
418 (defsubst elmo-cache-msgdb-create-overview-entity-from-file (number file)
419   (save-excursion
420     (let ((tmp-buffer (get-buffer-create " *ELMO Cache Temp*"))
421           insert-file-contents-pre-hook   ; To avoid autoconv-xmas...
422           insert-file-contents-post-hook header-end
423           (attrib (file-attributes file))
424           ret-val size mtime)
425       (set-buffer tmp-buffer)
426       (erase-buffer)
427       (if (not (file-exists-p file))
428           ()
429         (setq size (nth 7 attrib))
430         (setq mtime (timezone-make-date-arpa-standard
431                      (current-time-string (nth 5 attrib)) (current-time-zone)))
432         ;; insert header from file.
433         (catch 'done
434           (condition-case nil
435               (elmo-cache-insert-header file)
436             (error (throw 'done nil)))
437           (goto-char (point-min))
438           (setq header-end
439                 (if (re-search-forward "\\(^--.*$\\)\\|\\(\n\n\\)" nil t)
440                     (point)
441                   (point-max)))
442           (narrow-to-region (point-min) header-end)
443           (setq ret-val (elmo-msgdb-create-overview-from-buffer number size mtime))
444           (kill-buffer tmp-buffer))
445         ret-val))))
446
447 (defun elmo-cache-msgdb-create-as-numlist (spec numlist new-mark
448                                                    already-mark seen-mark
449                                                    important-mark seen-list)
450   (when numlist
451     (let ((dir (elmo-cache-get-folder-directory spec))
452           (nalist (elmo-cache-list-folder-subr spec nil t))
453           overview number-alist mark-alist entity message-id
454           i percent len num seen gmark)
455       (setq len (length numlist))
456       (setq i 0)
457       (message "Creating msgdb...")
458       (while numlist
459         (setq entity
460               (elmo-cache-msgdb-create-overview-entity-from-file
461                (car numlist)
462                (expand-file-name
463                 (elmo-msgid-to-cache
464                  (setq message-id (cdr (assq (car numlist) nalist)))) dir)))
465         (if (null entity)
466             ()
467           (setq num (elmo-msgdb-overview-entity-get-number entity))
468           (setq overview
469                 (elmo-msgdb-append-element
470                  overview entity))
471           (setq number-alist
472                 (elmo-msgdb-number-add number-alist num message-id))
473           (setq seen (member message-id seen-list))
474           (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
475                               (if seen
476                                   nil
477                                 new-mark)))
478               (setq mark-alist
479                     (elmo-msgdb-mark-append
480                      mark-alist
481                      num
482                      gmark))))
483         (when (> len elmo-display-progress-threshold)
484           (setq i (1+ i))
485           (setq percent (/ (* i 100) len))
486           (elmo-display-progress
487            'elmo-cache-msgdb-create-as-numlist "Creating msgdb..."
488            percent))
489         (setq numlist (cdr numlist)))
490       (message "Creating msgdb...done")
491       (list overview number-alist mark-alist))))
492
493 (defalias 'elmo-cache-msgdb-create 'elmo-cache-msgdb-create-as-numlist)
494
495 (defun elmo-cache-list-folders (spec &optional hierarchy)
496   (let ((folder (concat "'cache" (nth 1 spec))))
497     (elmo-cache-list-folders-subr folder hierarchy)))
498
499 (defun elmo-cache-list-folders-subr (folder &optional hierarchy)
500   (let ((case-fold-search t)
501         folders curdir dirent relpath abspath attr
502         subprefix subfolder)
503     (condition-case ()
504         (progn
505           (setq curdir
506                 (expand-file-name
507                  (nth 1 (elmo-folder-get-spec folder))
508                  (expand-file-name elmo-cache-dirname elmo-msgdb-dir)))
509           (if (string-match "^[+=$!]$" folder) ; localdir, archive, localnews
510               (setq subprefix folder)
511             (setq subprefix (concat folder elmo-path-sep)))
512             ;; include parent
513             ;(setq folders (list folder)))
514           (setq dirent (directory-files curdir nil "^[01][0-9A-F]$"))
515           (catch 'done
516            (while dirent
517             (setq relpath (car dirent))
518             (setq dirent (cdr dirent))
519             (setq abspath (expand-file-name relpath curdir))
520             (and
521              (eq (nth 0 (setq attr (file-attributes abspath))) t)
522              (setq subfolder (concat subprefix relpath))
523              (setq folders (nconc folders (list subfolder))))))
524           folders)
525       (file-error folders))))
526
527 (defsubst elmo-cache-list-folder-subr (spec &optional nonsort nonalist)
528   (let* ((dir (elmo-cache-get-folder-directory spec))
529          (flist (mapcar 'file-name-nondirectory
530                         (elmo-delete-if 'file-directory-p
531                                         (directory-files
532                                          dir t "^[^@]+@[^@]+$" t))))
533          (folder (concat "'cache/" (nth 1 spec)))
534          (number-alist (or (elmo-msgdb-number-load
535                             (elmo-msgdb-expand-path folder))
536                            (list nil)))
537          nlist)
538     (setq nlist
539           (mapcar '(lambda (filename)
540                      (elmo-cache-filename-to-number filename number-alist))
541                   flist))
542     (if nonalist
543         number-alist
544       (if nonsort
545           (cons (or (elmo-max-of-list nlist) 0) (length nlist))
546         (sort nlist '<)))))
547
548 (defsubst elmo-cache-filename-to-number (filename number-alist)
549   (let* ((msgid (elmo-cache-to-msgid filename))
550          number)
551     (or (car (rassoc msgid number-alist))
552         (prog1
553             (setq number (+ (or (caar (last number-alist))
554                                 0) 1))
555           (if (car number-alist)
556               (nconc number-alist
557                      (list (cons number msgid)))
558             (setcar number-alist (cons number msgid)))))))
559
560 (defun elmo-cache-append-msg (spec string message-id &optional msg no-see)
561   (let ((dir (elmo-cache-get-folder-directory spec))
562         (tmp-buffer (get-buffer-create " *ELMO Temp buffer*"))
563         filename)
564     (save-excursion
565       (set-buffer tmp-buffer)
566       (erase-buffer)
567       (setq filename (expand-file-name (elmo-msgid-to-cache message-id) dir))
568       (unwind-protect
569           (if (file-writable-p filename)
570               (progn
571                 (insert string)
572                 (as-binary-output-file
573                  (write-region (point-min) (point-max) filename nil 'no-msg))
574                 t)
575             nil)
576         (kill-buffer tmp-buffer)))))
577
578 (defun elmo-cache-delete-msg (spec number locked)
579   (let* ((dir (elmo-cache-get-folder-directory spec))
580          (file (expand-file-name
581                 (elmo-cache-number-to-filename spec number) dir)))
582     ;; return nil if failed.
583     (elmo-cache-force-delete file locked)))
584
585 (defun elmo-cache-read-msg (spec number outbuf &optional set-mark)
586   (save-excursion
587     (let* ((dir (elmo-cache-get-folder-directory spec))
588            (file (expand-file-name
589                   (elmo-cache-number-to-filename spec number) dir)))
590       (set-buffer outbuf)
591       (erase-buffer)
592       (when (file-exists-p file)
593         (as-binary-input-file (insert-file-contents file))
594         (elmo-delete-cr-get-content-type)))))
595
596 (defun elmo-cache-delete-msgs (spec msgs)
597   (let ((locked (elmo-dop-lock-list-load)))
598     (not (memq nil
599                (mapcar '(lambda (msg) (elmo-cache-delete-msg spec msg locked))
600                        msgs)))))
601
602 (defun elmo-cache-list-folder (spec)    ; called by elmo-cache-search()
603   (let ((killed (and elmo-use-killed-list
604                      (elmo-msgdb-killed-list-load
605                       (elmo-msgdb-expand-path spec))))
606         numbers)
607     (setq numbers (elmo-cache-list-folder-subr spec))
608     (elmo-living-messages numbers killed)))
609
610 (defun elmo-cache-max-of-folder (spec)
611   (elmo-cache-list-folder-subr spec t))
612
613 (defun elmo-cache-check-validity (spec validity-file)
614   t)
615
616 (defun elmo-cache-sync-validity (spec validity-file)
617   t)
618
619 (defun elmo-cache-folder-exists-p (spec)
620   (file-directory-p (elmo-cache-get-folder-directory spec)))
621
622 (defun elmo-cache-folder-creatable-p (spec)
623   nil)
624
625 (defun elmo-cache-create-folder (spec)
626   nil)
627
628 (defun elmo-cache-search (spec condition &optional from-msgs)
629   (let* ((number-alist (elmo-cache-list-folder-subr spec nil t))
630          (msgs (or from-msgs (mapcar 'car number-alist)))
631          (num (length msgs))
632          (i 0) case-fold-search ret-val)
633     (while msgs
634       (if (elmo-file-field-condition-match
635            (expand-file-name
636             (elmo-msgid-to-cache
637              (cdr (assq (car msgs) number-alist)))
638             (elmo-cache-get-folder-directory spec))
639            condition
640            (car msgs)
641            msgs)
642           (setq ret-val (cons (car msgs) ret-val)))
643       (when (> num elmo-display-progress-threshold)
644         (setq i (1+ i))
645         (elmo-display-progress
646          'elmo-cache-search "Searching..."
647          (/ (* i 100) num)))
648       (setq msgs (cdr msgs)))
649     (nreverse ret-val)))
650
651 ;;; (localdir, maildir, localnews) -> cache
652 (defun elmo-cache-copy-msgs (dst-spec msgs src-spec
653                                       &optional loc-alist same-number)
654   (let ((dst-dir
655          (elmo-cache-get-folder-directory dst-spec))
656         (next-num (1+ (car (elmo-cache-list-folder-subr dst-spec t))))
657         (number-alist
658          (elmo-msgdb-number-load
659           (elmo-msgdb-expand-path src-spec))))
660     (if same-number (error "Not implemented"))
661     (while msgs
662       (elmo-copy-file
663        ;; src file
664        (elmo-call-func src-spec "get-msg-filename" (car msgs) loc-alist)
665        ;; dst file
666        (expand-file-name
667         (elmo-msgid-to-cache
668          (cdr (assq (if same-number (car msgs) next-num) number-alist)))
669         dst-dir))
670       (if (and (setq msgs (cdr msgs))
671                (not same-number))
672           (setq next-num (1+ next-num))))
673     t))
674
675 (defun elmo-cache-use-cache-p (spec number)
676   nil)
677
678 (defun elmo-cache-local-file-p (spec number)
679   t)
680
681 (defun elmo-cache-get-msg-filename (spec number &optional loc-alist)
682   (expand-file-name
683    (elmo-cache-number-to-filename spec number)
684    (elmo-cache-get-folder-directory spec)))
685
686 (defalias 'elmo-cache-sync-number-alist
687   'elmo-generic-sync-number-alist)
688 (defalias 'elmo-cache-list-folder-unread
689   'elmo-generic-list-folder-unread)
690 (defalias 'elmo-cache-list-folder-important
691   'elmo-generic-list-folder-important)
692 (defalias 'elmo-cache-commit 'elmo-generic-commit)
693 (defalias 'elmo-cache-folder-diff 'elmo-generic-folder-diff)
694
695 (require 'product)
696 (product-provide (provide 'elmo-cache) (require 'elmo-version))
697
698 ;;; elmo-cache.el ends here