;;; nnml.el --- mail spool access for Gnus
-;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2003
;; Free Software Foundation, Inc.
;; Author: Simon Josefsson <simon@josefsson.org> (adding MARKS)
;; Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
+;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Keywords: news, mail
;; This file is part of GNU Emacs.
(require 'nnoo)
(eval-when-compile (require 'cl))
+(eval-and-compile
+ (autoload 'gnus-article-unpropagatable-p "gnus-sum"))
+
(nnoo-declare nnml)
(defvoo nnml-directory message-directory
- "Spool directory for the nnml mail backend.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "Spool directory for the nnml mail backend.")
(defvoo nnml-active-file
(expand-file-name "active" nnml-directory)
- "Mail active file.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "Mail active file.")
(defvoo nnml-newsgroups-file
(expand-file-name "newsgroups" nnml-directory)
- "Mail newsgroups description file.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "Mail newsgroups description file.")
(defvoo nnml-get-new-mail t
- "If non-nil, nnml will check the incoming mail file and split the mail.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "If non-nil, nnml will check the incoming mail file and split the mail.")
(defvoo nnml-nov-is-evil nil
"If non-nil, Gnus will never generate and use nov databases for mail spools.
set this to t, and want to set it to nil again, you should always run
the `nnml-generate-nov-databases' command. The function will go
through all nnml directories and generate nov databases for them
-all. This may very well take some time.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+all. This may very well take some time.")
(defvoo nnml-marks-is-evil nil
"If non-nil, Gnus will never generate and use marks file for mail spools.
to t, and want to set it to nil again, you should always remove the
corresponding marks file (usually named `.marks' in the nnml group
directory, but see `nnml-marks-file-name') for the group. Then the
-marks file will be regenerated properly by Gnus.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+marks file will be regenerated properly by Gnus.")
(defvoo nnml-prepare-save-mail-hook nil
- "Hook run narrowed to an article before saving.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "Hook run narrowed to an article before saving.")
(defvoo nnml-inhibit-expiry nil
- "If non-nil, inhibit expiry.
-
-This variable is a virtual server slot. See the Gnus manual for details.")
+ "If non-nil, inhibit expiry.")
+(defvoo nnml-use-compressed-files nil
+ "If non-nil, allow using compressed message files.")
\f
(defvoo nnml-marks nil)
-\f
+(defvar nnml-marks-modtime (gnus-make-hashtable))
+\f
;;; Interface functions.
(nnoo-define-basics nnml)
(setq beg (point))
(nnheader-insert-head file)
(goto-char beg)
- (if (search-forward "\n\n" nil t)
+ (if (re-search-forward "\n\r?\n" nil t)
(forward-char -1)
(goto-char (point-max))
(insert "\n\n"))
(when (and (setq group-num (nnml-find-group-number id))
(cdr
(assq (cdr group-num)
- (nnml-article-to-file-alist
+ (nnheader-article-to-file-alist
(setq gpath
(nnmail-group-pathname
(car group-num)
(setq articles (gnus-sorted-intersection articles active-articles))
(while (and articles is-old)
- (if (and (setq article (nnml-article-to-file (setq number (pop articles))))
+ (if (and (setq article (nnml-article-to-file
+ (setq number (pop articles))))
(setq mod-time (nth 5 (file-attributes article)))
(nnml-deletable-article-p group number)
(setq is-old (nnmail-expired-article-p group mod-time force
(let (nnml-current-directory
nnml-current-group
nnml-article-file-alist)
- (nnmail-expiry-target-group nnmail-expiry-target group))))
+ (nnmail-expiry-target-group nnmail-expiry-target group)))
+ ;; Maybe directory is changed during nnmail-expiry-target-group.
+ (nnml-possibly-change-directory group server))
(nnheader-message 5 "Deleting article %s in %s"
number group)
(condition-case ()
(nnmail-check-syntax)
(let (result)
(when nnmail-cache-accepted-message-ids
- (nnmail-cache-insert (nnmail-fetch-field "message-id")))
+ (nnmail-cache-insert (nnmail-fetch-field "message-id") group))
(if (stringp group)
(and
(nnmail-activate 'nnml)
(if (or (looking-at art)
(search-forward (concat "\n" art) nil t))
;; Delete the old NOV line.
- (delete-region (progn (beginning-of-line) (point))
- (progn (forward-line 1) (point)))
+ (gnus-delete-line)
;; The line isn't here, so we have to find out where
;; we should insert it. (This situation should never
;; occur, but one likes to make sure...)
;; We move the articles file by file instead of renaming
;; the directory -- there may be subgroups in this group.
;; One might be more clever, I guess.
- (let ((files (nnml-article-to-file-alist old-dir)))
+ (let ((files (nnheader-article-to-file-alist old-dir)))
(while files
(rename-file
(concat old-dir (cdar files))
(defun nnml-article-to-file (article)
(nnml-update-file-alist)
(let (file)
- (if (setq file (cdr (assq article nnml-article-file-alist)))
+ (if (setq file
+ (if nnml-use-compressed-files
+ (cdr (assq article nnml-article-file-alist))
+ (number-to-string article)))
(expand-file-name file nnml-current-directory)
- (if (not nnheader-directory-files-is-safe)
- ;; Just to make sure nothing went wrong when reading over NFS --
- ;; check once more.
- (when (file-exists-p
- (setq file (expand-file-name (number-to-string article)
- nnml-current-directory)))
- (nnml-update-file-alist t)
- file)))))
+ (when (not nnheader-directory-files-is-safe)
+ ;; Just to make sure nothing went wrong when reading over NFS --
+ ;; check once more.
+ (when (file-exists-p
+ (setq file (expand-file-name (number-to-string article)
+ nnml-current-directory)))
+ (nnml-update-file-alist t)
+ file)))))
(defun nnml-deletable-article-p (group article)
"Say whether ARTICLE in GROUP can be deleted."
(unless nnml-article-file-alist
(setq nnml-article-file-alist
(sort
- (nnml-article-to-file-alist nnml-current-directory)
+ (nnml-current-group-article-to-file-alist)
'car-less-than-car)))
(setq active
(if nnml-article-file-alist
(nnheader-insert-nov headers)))
(defsubst nnml-header-value ()
- (buffer-substring (match-end 0) (progn (end-of-line) (point))))
+ (buffer-substring (match-end 0) (gnus-point-at-eol)))
(defun nnml-parse-head (chars &optional number)
"Parse the head of the current buffer."
(unless (zerop (buffer-size))
(narrow-to-region
(goto-char (point-min))
- (if (search-forward "\n\n" nil t) (1- (point)) (point-max))))
- ;; Fold continuation lines.
- (goto-char (point-min))
- (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
- (replace-match " " t t))
- ;; Remove any tabs; they are too confusing.
- (subst-char-in-region (point-min) (point-max) ?\t ? )
- (let ((headers (nnheader-parse-head t)))
+ (if (re-search-forward "\n\r?\n" nil t)
+ (1- (point))
+ (point-max))))
+ (let ((headers (nnheader-parse-naked-head)))
(mail-header-set-chars headers chars)
(mail-header-set-number headers number)
headers))))
(when (buffer-name (cdar nnml-nov-buffer-alist))
(set-buffer (cdar nnml-nov-buffer-alist))
(when (buffer-modified-p)
- (nnmail-write-region 1 (point-max) nnml-nov-buffer-file-name
- nil 'nomesg))
+ (nnmail-write-region (point-min) (point-max)
+ nnml-nov-buffer-file-name nil 'nomesg))
(set-buffer-modified-p nil)
(kill-buffer (current-buffer)))
(setq nnml-nov-buffer-alist (cdr nnml-nov-buffer-alist)))))
(narrow-to-region
(goto-char (point-min))
(progn
- (search-forward "\n\n" nil t)
+ (re-search-forward "\n\r?\n" nil t)
(setq chars (- (point-max) (point)))
- (max 1 (1- (point)))))
+ (max (point-min) (1- (point)))))
(unless (zerop (buffer-size))
(goto-char (point-min))
(setq headers (nnml-parse-head chars (caar files)))
(setq files (cdr files)))
(save-excursion
(set-buffer nov-buffer)
- (nnmail-write-region 1 (point-max) nov nil 'nomesg)
+ (nnmail-write-region (point-min) (point-max) nov nil 'nomesg)
(kill-buffer (current-buffer))))))
(defun nnml-nov-delete-article (group article)
t))
(defun nnml-update-file-alist (&optional force)
- (when (or (not nnml-article-file-alist)
- force)
- (setq nnml-article-file-alist
- (nnml-article-to-file-alist nnml-current-directory))))
+ (when nnml-use-compressed-files
+ (when (or (not nnml-article-file-alist)
+ force)
+ (setq nnml-article-file-alist
+ (nnml-current-group-article-to-file-alist)))))
(defun nnml-directory-articles (dir)
"Return a list of all article files in a directory.
(forward-line 1))
list))))
-(defun nnml-article-to-file-alist (dir)
- "Return an alist of article/file pairs in DIR.
-Use the nov database for that directory if available."
- (if (or gnus-nov-is-evil nnml-nov-is-evil
+(defun nnml-current-group-article-to-file-alist ()
+ "Return an alist of article/file pairs in the current group.
+Use the nov database for the current group if available."
+ (if (or gnus-nov-is-evil
+ nnml-nov-is-evil
(not (file-exists-p
(expand-file-name nnml-nov-file-name
nnml-current-directory))))
;; build list from .overview if available
(save-excursion
(let ((alist nil)
- art
- (buffer (nnml-get-nov-buffer nnml-current-group)))
+ (buffer (nnml-get-nov-buffer nnml-current-group))
+ art)
(set-buffer buffer)
(goto-char (point-min))
(while (not (eobp))
(deffoo nnml-request-update-info (group info &optional server)
(nnml-possibly-change-directory group server)
- (unless nnml-marks-is-evil
+ (when (and (not nnml-marks-is-evil) (nnml-marks-changed-p group))
(nnheader-message 8 "Updating marks for %s..." group)
(nnml-open-marks group server)
;; Update info using `nnml-marks'.
(mapcar (lambda (pred)
- (gnus-info-set-marks
- info
- (gnus-update-alist-soft
- (cdr pred)
- (cdr (assq (cdr pred) nnml-marks))
- (gnus-info-marks info))
- t))
+ (unless (memq (cdr pred) gnus-article-unpropagated-mark-lists)
+ (gnus-info-set-marks
+ info
+ (gnus-update-alist-soft
+ (cdr pred)
+ (cdr (assq (cdr pred) nnml-marks))
+ (gnus-info-marks info))
+ t)))
gnus-article-mark-lists)
(let ((seen (cdr (assq 'read nnml-marks))))
(gnus-info-set-read info
(if (and (integerp (car seen))
(null (cdr seen)))
(list (cons (car seen) (car seen)))
- seen))))
+ seen)))
+ (nnheader-message 8 "Updating marks for %s...done" group))
info)
+(defun nnml-marks-changed-p (group)
+ (let ((file (expand-file-name nnml-marks-file-name
+ (nnmail-group-pathname group nnml-directory))))
+ (if (null (gnus-gethash file nnml-marks-modtime))
+ t ;; never looked at marks file, assume it has changed
+ (not (equal (gnus-gethash file nnml-marks-modtime)
+ (nth 5 (file-attributes file)))))))
+
(defun nnml-save-marks (group server)
(let ((file-name-coding-system nnmail-pathname-coding-system)
(file (expand-file-name nnml-marks-file-name
(nnml-possibly-create-directory group)
(with-temp-file file
(erase-buffer)
- (princ nnml-marks (current-buffer))
- (insert "\n")))
+ (gnus-prin1 nnml-marks)
+ (insert "\n"))
+ (gnus-sethash file
+ (nth 5 (file-attributes file))
+ nnml-marks-modtime))
(error (or (gnus-yes-or-no-p
(format "Could not write to %s (%s). Continue? " file err))
(error "Cannot write to %s (%s)" err))))))
(defun nnml-open-marks (group server)
- (let ((file (expand-file-name
- nnml-marks-file-name
+ (let ((file (expand-file-name
+ nnml-marks-file-name
(nnmail-group-pathname group nnml-directory))))
(if (file-exists-p file)
- (setq nnml-marks (condition-case err
- (with-temp-buffer
- (nnheader-insert-file-contents file)
- (read (current-buffer)))
- (error (or (gnus-yes-or-no-p
- (format "Error reading nnml marks file %s (%s). Continuing will use marks from .newsrc.eld. Continue? " file err))
- (error "Cannot read nnml marks file %s (%s)" file err)))))
+ (condition-case err
+ (with-temp-buffer
+ (gnus-sethash file (nth 5 (file-attributes file))
+ nnml-marks-modtime)
+ (nnheader-insert-file-contents file)
+ (setq nnml-marks (read (current-buffer)))
+ (dolist (el gnus-article-unpropagated-mark-lists)
+ (setq nnml-marks (gnus-remassoc el nnml-marks))))
+ (error (or (gnus-yes-or-no-p
+ (format "Error reading nnml marks file %s (%s). Continuing will use marks from .newsrc.eld. Continue? " file err))
+ (error "Cannot read nnml marks file %s (%s)" file err))))
;; User didn't have a .marks file. Probably first time
;; user of the .marks stuff. Bootstrap it from .newsrc.eld.
(let ((info (gnus-get-info
(nnheader-message 7 "Bootstrapping marks for %s..." group)
(setq nnml-marks (gnus-info-marks info))
(push (cons 'read (gnus-info-read info)) nnml-marks)
- (nnml-save-marks group server)))))
+ (dolist (el gnus-article-unpropagated-mark-lists)
+ (setq nnml-marks (gnus-remassoc el nnml-marks)))
+ (nnml-save-marks group server)
+ (nnheader-message 7 "Bootstrapping marks for %s...done" group)))))
(provide 'nnml)