(require 'eword-decode)
(require 'utf7)
(require 'poem)
+(require 'emu)
-(defmacro elmo-set-buffer-multibyte (flag)
- "Set the multibyte flag of the current buffer to FLAG."
- (cond ((boundp 'MULE)
- (list 'setq 'mc-flag flag))
- ((featurep 'xemacs)
- flag)
- ((and (boundp 'emacs-major-version) (>= emacs-major-version 20))
- (list 'set-buffer-multibyte flag))
- (t
- flag)))
+(eval-and-compile
+ (autoload 'md5 "md5"))
(defvar elmo-work-buf-name " *elmo work*")
(defvar elmo-temp-buf-name " *elmo temp*")
"Execute BODY on work buffer. Work buffer remains."
(` (save-excursion
(set-buffer (get-buffer-create elmo-work-buf-name))
- (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+ (set-buffer-multibyte default-enable-multibyte-characters)
(erase-buffer)
(,@ body))))
+(put 'elmo-set-work-buf 'lisp-indent-function 0)
+(def-edebug-spec elmo-set-work-buf t)
+
(defmacro elmo-bind-directory (dir &rest body)
"Set current directory DIR and execute BODY."
(` (let ((default-directory (file-name-as-directory (, dir))))
(,@ body))))
+(put 'elmo-bind-directory 'lisp-indent-function 1)
+(def-edebug-spec elmo-bind-directory
+ (form &rest form))
+
(defun elmo-object-load (filename &optional mime-charset no-err)
"Load OBJECT from the file specified by FILENAME.
File content is decoded with MIME-CHARSET."
(as-binary-input-file
(insert-file-contents filename))
(when mime-charset
- (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+ (set-buffer-multibyte default-enable-multibyte-characters)
(decode-mime-charset-region (point-min) (point-max) mime-charset))
(condition-case nil
(read (current-buffer))
(if (file-writable-p filename)
(progn
(when mime-charset
-;;; (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+;;; (set-buffer-multibyte default-enable-multibyte-characters)
(encode-mime-charset-region (point-min) (point-max) mime-charset))
(as-binary-output-file
(write-region (point-min) (point-max) filename nil 'no-msg)))
Directory of the file is created if it doesn't exist.
File content is encoded with MIME-CHARSET."
(elmo-set-work-buf
- (prin1 object (current-buffer))
+ (let (print-length print-level)
+ (prin1 object (current-buffer)))
;;;(princ "\n" (current-buffer))
(elmo-save-buffer filename mime-charset)))
(format "%s (%s): " prompt default)
(mapcar 'list
(append '("AND" "OR"
- "Last" "First"
+ "Last" "First" "Flag"
"From" "Subject" "To" "Cc" "Body"
"Since" "Before" "ToCc"
"!From" "!Subject" "!To" "!Cc" "!Body"
elmo-date-descriptions)))
(concat (downcase field) ":"
(if (equal value "") default value))))
+ ((string= field "Flag")
+ (setq value (completing-read
+ (format "Value for '%s': " field)
+ (mapcar 'list
+ '("unread" "important" "answered" "digest" "any"))))
+ (unless (string-match (concat "^" elmo-condition-atom-regexp "$")
+ value)
+ (setq value (prin1-to-string value)))
+ (concat (downcase field) ":" value))
(t
(setq value (read-from-minibuffer (format "Value for '%s': " field)))
(unless (string-match (concat "^" elmo-condition-atom-regexp "$")
(goto-char (match-end 0))))
;; search-key ::= [A-Za-z-]+
;; ;; "since" / "before" / "last" / "first" /
-;; ;; "body" / field-name
+;; ;; "body" / "flag" / field-name
((looking-at "\\(!\\)? *\\([A-Za-z-]+\\) *: *")
(goto-char (match-end 0))
(let ((search-key (vector
(cond
((looking-at "\"")
(read (current-buffer)))
- ((or (looking-at "yesterday") (looking-at "lastweek")
+ ((or (looking-at elmo-condition-atom-regexp)
+ (looking-at "yesterday") (looking-at "lastweek")
(looking-at "lastmonth") (looking-at "lastyear")
(looking-at "[0-9]+ *daysago")
(looking-at "[0-9]+-[A-Za-z]+-[0-9]+")
(looking-at "[0-9]+-[0-9]+-[0-9]+")
- (looking-at "[0-9]+")
- (looking-at elmo-condition-atom-regexp))
+ (looking-at "[0-9]+"))
(prog1 (elmo-match-buffer 0)
(goto-char (match-end 0))))
(t (error "Syntax error '%s'" (buffer-string)))))
(elmo-set-work-buf
(let ((coding-system-for-read 'no-conversion)
(coding-system-for-write 'no-conversion))
- (if unibyte (elmo-set-buffer-multibyte nil))
+ (if unibyte (set-buffer-multibyte nil))
(insert string)
(goto-char (point-min))
(while (search-forward (char-to-string char) nil t)
(replace-match "\n"))
(buffer-string))))
-(defun elmo-uniq-list (lst)
+(defun elmo-uniq-list (lst &optional delete-function)
"Distractively uniqfy elements of LST."
+ (setq delete-function (or delete-function #'delete))
(let ((tmp lst))
- (while tmp (setq tmp
- (setcdr tmp
- (and (cdr tmp)
- (delete (car tmp)
- (cdr tmp)))))))
+ (while tmp
+ (setq tmp
+ (setcdr tmp
+ (and (cdr tmp)
+ (funcall delete-function
+ (car tmp)
+ (cdr tmp)))))))
lst)
+(defun elmo-uniq-sorted-list (list &optional equal-function)
+ "Distractively uniqfy elements of sorted LIST."
+ (setq equal-function (or equal-function #'equal))
+ (let ((list list))
+ (while list
+ (while (funcall equal-function (car list) (cadr list))
+ (setcdr list (cddr list)))
+ (setq list (cdr list))))
+ list)
+
(defun elmo-list-insert (list element after)
(let* ((match (memq after list))
(rest (and match (cdr (memq after list)))))
(nconc list rest))
(nconc list (list element)))))
-(defun elmo-string-partial-p (string)
- (and (stringp string) (string-match "message/partial" string)))
-
(defun elmo-get-file-string (filename &optional remove-final-newline)
(elmo-set-work-buf
(let (insert-file-contents-pre-hook ; To avoid autoconv-xmas...
(defun elmo-passwd-alist-clear ()
"Clear password cache."
(interactive)
+ (dolist (pair elmo-passwd-alist)
+ (when (stringp (cdr-safe pair))
+ (fillarray (cdr pair) 0)))
(setq elmo-passwd-alist nil))
(defun elmo-passwd-alist-save ()
(save-excursion
(let ((filename (expand-file-name elmo-passwd-alist-file-name
elmo-msgdb-directory))
- (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*")))
+ (tmp-buffer (get-buffer-create " *elmo-passwd-alist-tmp*"))
+ print-length print-level)
(set-buffer tmp-buffer)
(erase-buffer)
(prin1 elmo-passwd-alist tmp-buffer)
(defun elmo-remove-passwd (key)
"Remove password from password pool (for failure)."
(let (pass-cons)
- (if (setq pass-cons (assoc key elmo-passwd-alist))
- (progn
- (unwind-protect
- (fillarray (cdr pass-cons) 0))
- (setq elmo-passwd-alist
- (delete pass-cons elmo-passwd-alist))))))
+ (while (setq pass-cons (assoc key elmo-passwd-alist))
+ (unwind-protect
+ (fillarray (cdr pass-cons) 0)
+ (setq elmo-passwd-alist
+ (delete pass-cons elmo-passwd-alist))))))
(defmacro elmo-read-char-exclusive ()
(cond ((featurep 'xemacs)
))))
(defun elmo-list-filter (l1 l2)
- "L1 is filter."
- (if (eq l1 t)
- ;; t means filter all.
- nil
- (if l1
- (elmo-delete-if (lambda (x) (not (memq x l1))) l2)
- ;; filter is nil
- l2)))
+ "Rerurn a list from L2 in which each element is a member of L1."
+ (elmo-delete-if (lambda (x) (not (memq x l1))) l2))
(defsubst elmo-list-delete-if-smaller (list number)
(let ((ret-val (copy-sequence list)))
(defun elmo-list-diff (list1 list2 &optional mes)
(if mes
- (message mes))
+ (message "%s" mes))
(let ((clist1 (copy-sequence list1))
(clist2 (copy-sequence list2)))
(while list2
(setq clist2 (delq (car list1) clist2))
(setq list1 (cdr list1)))
(if mes
- (message (concat mes "done")))
+ (message "%sdone" mes))
(list clist1 clist2)))
(defun elmo-list-bigger-diff (list1 list2 &optional mes)
(setq result (search-forward (elmo-filter-value condition)
nil t))))
(t
- (let ((fval (std11-field-body (elmo-filter-key condition))))
+ (dolist (fval (elmo-multiple-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))))))
+ (setq result (or result
+ (and fval (string-match
+ (elmo-filter-value condition) fval)))))))
(if (eq (elmo-filter-type condition) 'unmatch)
(setq result (not result)))
result))
(t
(elmo-set-work-buf
(as-binary-input-file (insert-file-contents file))
- (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+ (set-buffer-multibyte default-enable-multibyte-characters)
;; Should consider charset?
(decode-mime-charset-region (point-min)(point-max) elmo-mime-charset)
(setq result
file (nth 2 condition) number number-list)))))
(defmacro elmo-get-hash-val (string hashtable)
- `(and (stringp ,string)
- (let ((sym (intern-soft ,string ,hashtable)))
- (if (boundp sym)
- (symbol-value sym)))))
+ (static-if (fboundp 'unintern)
+ `(symbol-value (intern-soft ,string ,hashtable))
+ `(let ((sym (intern-soft ,string ,hashtable)))
+ (and (boundp sym)
+ (symbol-value sym)))))
(defmacro elmo-set-hash-val (string value hashtable)
- (list 'set (list 'intern string hashtable) value))
+ `(set (intern ,string ,hashtable) ,value))
(defmacro elmo-clear-hash-val (string hashtable)
(static-if (fboundp 'unintern)
"Normalize MIME encoded STRING."
(and string
(elmo-set-work-buf
- (elmo-set-buffer-multibyte default-enable-multibyte-characters)
+ (set-buffer-multibyte default-enable-multibyte-characters)
(setq string
(encode-mime-charset-string
(eword-decode-and-unfold-unstructured-field-body
string)
elmo-mime-charset))
- (elmo-set-buffer-multibyte nil)
+ (set-buffer-multibyte nil)
string)))
(defsubst elmo-collect-field (beg end downcase-field-name)
(let (result)
(while lst
(unless (funcall pred (car lst))
- (setq result (nconc result (list (car lst)))))
+ (setq result (cons (car lst) result)))
(setq lst (cdr lst)))
- result))
+ (nreverse result)))
(defun elmo-list-delete (list1 list2 &optional delete-function)
"Delete by side effect any occurrences equal to elements of LIST1 from LIST2.
(when (>= new-rate 100)
(elmo-progress-clear label))))))
+(put 'elmo-with-progress-display 'lisp-indent-function '2)
+(def-edebug-spec elmo-with-progress-display
+ (form (symbolp form &optional form) &rest form))
+
+(defmacro elmo-with-progress-display (condition spec &rest body)
+ "Evaluate BODY with progress gauge if CONDITION is non-nil.
+SPEC is a list as followed (LABEL MAX-VALUE [FORMAT])."
+ (let ((label (car spec))
+ (max-value (cadr spec))
+ (fmt (caddr spec)))
+ `(unwind-protect
+ (progn
+ (when ,condition
+ (elmo-progress-set (quote ,label) ,max-value ,fmt))
+ ,@body)
+ (elmo-progress-clear (quote ,label)))))
+
(defun elmo-time-expire (before-time diff-time)
(let* ((current (current-time))
(rest (when (< (nth 1 current) (nth 1 before-time))
(defalias 'elmo-field-body 'std11-fetch-field) ;;no narrow-to-region
(defalias 'elmo-field-body 'std11-field-body))
+(defun elmo-unfold-field-body (name)
+ (let ((value (elmo-field-body name)))
+ (and value
+ (std11-unfold-string value))))
+
+(defun elmo-decoded-field-body (field-name &optional mode)
+ (let ((field-body (elmo-field-body field-name)))
+ (and field-body
+ (elmo-set-work-buf
+ (mime-decode-field-body field-body field-name mode)))))
+
(defun elmo-address-quote-specials (word)
"Make quoted string of WORD if needed."
(let ((lal (std11-lexical-analyze word)))
(y-or-n-p prompt)))
(defun elmo-string-member (string slist)
- "Return t if STRING is a member of the SLIST."
(catch 'found
(while slist
(if (and (stringp (car slist))
(throw 'found t))
(setq slist (cdr slist)))))
+(static-cond ((fboundp 'member-ignore-case)
+ (defalias 'elmo-string-member-ignore-case 'member-ignore-case))
+ ((fboundp 'compare-strings)
+ (defun elmo-string-member-ignore-case (elt list)
+ "Like `member', but ignores differences in case and text representation.
+ELT must be a string. Upper-case and lower-case letters are treated as equal.
+Unibyte strings are converted to multibyte for comparison."
+ (while (and list (not (eq t (compare-strings elt 0 nil (car list) 0 nil t))))
+ (setq list (cdr list)))
+ list))
+ (t
+ (defun elmo-string-member-ignore-case (elt list)
+ "Like `member', but ignores differences in case and text representation.
+ELT must be a string. Upper-case and lower-case letters are treated as equal."
+ (let ((str (downcase elt)))
+ (while (and list (not (string= str (downcase (car list)))))
+ (setq list (cdr list)))
+ list))))
+
(defun elmo-string-match-member (str list &optional case-ignore)
(let ((case-fold-search case-ignore))
(catch 'member
(setq number-set-1 (nconc number-set-1 (list number))))
number-set-1))
+(defun elmo-make-number-list (beg end)
+ (let (number-list i)
+ (setq i end)
+ (while (>= i beg)
+ (setq number-list (cons i number-list))
+ (setq i (1- i)))
+ number-list))
+
(defun elmo-number-set-to-number-list (number-set)
"Return a number list which corresponds to NUMBER-SET."
- (let (number-list elem i)
+ (let ((number-list (list 'dummy))
+ elem)
(while number-set
(setq elem (car number-set))
(cond
((consp elem)
- (setq i (car elem))
- (while (<= i (cdr elem))
- (setq number-list (cons i number-list))
- (incf i)))
+ (nconc number-list (elmo-make-number-list (car elem) (cdr elem))))
((integerp elem)
- (setq number-list (cons elem number-list))))
+ (nconc number-list (list elem))))
(setq number-set (cdr number-set)))
- (nreverse number-list)))
+ (cdr number-list)))
(defcustom elmo-list-subdirectories-ignore-regexp "^\\(\\.\\.?\\|[0-9]+\\)$"
"*Regexp to filter subfolders."
(nth (% (/ sum 16) 2) chars)
(nth (% sum 16) chars))))
+;;;
(defun elmo-file-cache-get-path (msgid &optional section)
"Get cache path for MSGID.
If optional argument SECTION is specified, partial cache path is returned."
(method (completing-read (format "Expire by (%s): "
elmo-cache-expire-default-method)
'(("size" . "size")
- ("age" . "age")))))
- (if (string= method "")
- (setq method elmo-cache-expire-default-method))
+ ("age" . "age"))
+ nil t)))
+ (when (string= method "")
+ (setq method elmo-cache-expire-default-method))
(funcall (intern (concat "elmo-cache-expire-by-" method)))))
(defun elmo-read-float-value-from-minibuffer (prompt &optional initial)
(elmo-resque-obsolete-variable (cdr pair)
(car pair))))
+(defsubst elmo-msgdb-get-last-message-id (string)
+ (if string
+ (save-match-data
+ (let (beg)
+ (elmo-set-work-buf
+ (insert string)
+ (goto-char (point-max))
+ (when (search-backward "<" nil t)
+ (setq beg (point))
+ (if (search-forward ">" nil t)
+ (elmo-replace-in-string
+ (buffer-substring beg (point)) "\n[ \t]*" ""))))))))
+
+(defun elmo-msgdb-get-message-id-from-buffer ()
+ (let ((msgid (elmo-field-body "message-id")))
+ (if msgid
+ (if (string-match "<\\(.+\\)>$" msgid)
+ msgid
+ (concat "<" msgid ">")) ; Invaild message-id.
+ ;; no message-id, so put dummy msgid.
+ (concat "<"
+ (if (elmo-unfold-field-body "date")
+ (timezone-make-date-sortable (elmo-unfold-field-body "date"))
+ (md5 (string-as-unibyte (buffer-string))))
+ (nth 1 (eword-extract-address-components
+ (or (elmo-field-body "from") "nobody"))) ">"))))
+
+(defsubst elmo-msgdb-insert-file-header (file)
+ "Insert the header of the article."
+ (let ((beg 0)
+ insert-file-contents-pre-hook ; To avoid autoconv-xmas...
+ insert-file-contents-post-hook
+ format-alist)
+ (when (file-exists-p file)
+ ;; Read until header separator is found.
+ (while (and (eq elmo-msgdb-file-header-chop-length
+ (nth 1
+ (insert-file-contents-as-binary
+ file nil beg
+ (incf beg elmo-msgdb-file-header-chop-length))))
+ (prog1 (not (search-forward "\n\n" nil t))
+ (goto-char (point-max))))))))
+
+;;
+;; overview handling
+;;
+(defun elmo-multiple-field-body (name &optional boundary)
+ (save-excursion
+ (save-restriction
+ (std11-narrow-to-header boundary)
+ (goto-char (point-min))
+ (let ((case-fold-search t)
+ (field-body nil))
+ (while (re-search-forward (concat "^" name ":[ \t]*") nil t)
+ (setq field-body
+ (nconc field-body
+ (list (buffer-substring-no-properties
+ (match-end 0) (std11-field-end))))))
+ field-body))))
+
;;; Queue.
(defvar elmo-dop-queue-filename "queue"
"*Disconnected operation queue is saved in this file.")
elmo-msgdb-directory)
elmo-dop-queue))
+(if (and (fboundp 'regexp-opt)
+ (not (featurep 'xemacs)))
+ (defalias 'elmo-regexp-opt 'regexp-opt)
+ (defun elmo-regexp-opt (strings &optional paren)
+ "Return a regexp to match a string in STRINGS.
+Each string should be unique in STRINGS and should not contain any regexps,
+quoted or not. If optional PAREN is non-nil, ensure that the returned regexp
+is enclosed by at least one regexp grouping construct."
+ (let ((open-paren (if paren "\\(" "")) (close-paren (if paren "\\)" "")))
+ (concat open-paren (mapconcat 'regexp-quote strings "\\|")
+ close-paren))))
+
(require 'product)
(product-provide (provide 'elmo-util) (require 'elmo-version))