* gnus-art.el (gnus-article-truncate-lines): New variable.
[gnus] / lisp / nnml.el
index 7fa42ce..61d1987 100644 (file)
@@ -1,7 +1,7 @@
 ;;; nnml.el --- mail spool access for Gnus
 
 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-;;   2004, 2005 Free Software Foundation, Inc.
+;;   2004, 2005, 2006 Free Software Foundation, Inc.
 
 ;; Authors: Didier Verna <didier@xemacs.org> (adding compaction)
 ;;     Simon Josefsson <simon@josefsson.org> (adding MARKS)
@@ -41,7 +41,8 @@
 (eval-when-compile (require 'cl))
 
 (eval-and-compile
-  (autoload 'gnus-article-unpropagatable-p "gnus-sum"))
+  (autoload 'gnus-article-unpropagatable-p "gnus-sum")
+  (autoload 'gnus-backlog-remove-article "gnus-bcklg"))
 
 (nnoo-declare nnml)
 
@@ -84,7 +85,12 @@ marks file will be regenerated properly by Gnus.")
   "If non-nil, inhibit expiry.")
 
 (defvoo nnml-use-compressed-files nil
-  "If non-nil, allow using compressed message files.")
+  "If non-nil, allow using compressed message files.
+
+If it is a string, use it as the file extension which specifies
+the comression program.  You can set it to \".bz2\" if your Emacs
+supports auto-compression using the bzip2 program.  A value of t
+is equivalent to \".gz\".")
 
 (defvoo nnml-compressed-files-size-threshold 1000
   "Default size threshold for compressed message files.
@@ -630,7 +636,9 @@ non-nil.")
     (setq extension
          (and nnml-use-compressed-files
              (> chars nnml-compressed-files-size-threshold)
-              ".gz"))
+             (if (stringp nnml-use-compressed-files)
+                 nnml-use-compressed-files
+               ".gz")))
     (nnmail-insert-xref group-art)
     (run-hooks 'nnmail-prepare-save-mail-hook)
     (run-hooks 'nnml-prepare-save-mail-hook)
@@ -762,12 +770,14 @@ non-nil.")
     (nnml-open-server server))
   (setq nnml-directory (expand-file-name nnml-directory))
   ;; Recurse down the directories.
-  (nnml-generate-nov-databases-1 nnml-directory nil t)
+  (nnml-generate-nov-databases-directory nnml-directory nil t)
   ;; Save the active file.
   (nnmail-save-active nnml-group-alist nnml-active-file))
 
-(defun nnml-generate-nov-databases-1 (dir &optional seen no-active)
-  "Regenerate the NOV database in DIR."
+(defun nnml-generate-nov-databases-directory (dir &optional seen no-active)
+  "Regenerate the NOV database in DIR.
+
+Unless no-active is non-nil, update the active file too."
   (interactive "DRegenerate NOV in: ")
   (setq dir (file-name-as-directory dir))
   ;; Only scan this sub-tree if we haven't been here yet.
@@ -777,7 +787,7 @@ non-nil.")
     (dolist (dir (directory-files dir t nil t))
       (when (and (not (string-match "^\\." (file-name-nondirectory dir)))
                 (file-directory-p dir))
-       (nnml-generate-nov-databases-1 dir seen)))
+       (nnml-generate-nov-databases-directory dir seen)))
     ;; Do this directory.
     (let ((files (sort (nnheader-article-to-file-alist dir)
                       'car-less-than-car)))
@@ -1018,9 +1028,26 @@ Use the nov database for the current group if available."
 
 
 ;;;
-;;; Group and server compaction
+;;; Group and server compaction. -- dvl
 ;;;
 
+;; #### FIXME: this function handles self Xref: entry correctly, but I don't
+;; #### know how to handle external cross-references. I actually don't know if
+;; #### this is handled correctly elsewhere. For instance, what happens if you
+;; #### move all articles to a new group (that's what people do for manual
+;; #### compaction) ?
+
+;; #### NOTE: the function below handles the article backlog. This is
+;; #### conceptually the wrong place to do it because the backend is at a
+;; #### lower level. However, this is the only place where we have the needed
+;; #### information to do the job. Ideally, this function should not handle
+;; #### the backlog by itself, but return a list of moved groups / articles to
+;; #### the caller. This will become important to avoid code duplication when
+;; #### other backends get a compaction feature. Also, note that invalidating
+;; #### the "original article buffer" is already done at an upper level.
+
+;; Shouldn't `nnml-request-compact-group' be interactive? --rsteib
+
 (defun nnml-request-compact-group (group &optional server save)
   (nnml-possibly-change-directory group server)
   (unless nnml-article-file-alist
@@ -1044,58 +1071,95 @@ Use the nov database for the current group if available."
          (let ((old-number (car article)))
            (when (> old-number new-number)
              ;; There is a gap here:
-             (setq compacted t)
-             ;; #### NOTE: `nnml-article-to-file' calls
-             ;; #### `nnml-update-file-alist'  (which in turn calls
-             ;; #### `nnml-current-group-article-to-file-alist', which might
-             ;; #### use the NOV database). This might turn out to be
-             ;; #### inefficient. In that case, we will do the work manually.
-             ;; 1/ Move the article to a new file:
-             (let* ((oldfile (nnml-article-to-file old-number))
-                    (newfile
-                     (gnus-replace-in-string
-                      oldfile (concat "\\("
-                                      (int-to-string old-number)
-                                      "\\)\\(\\(\\.gz\\)?\\)$")
-                      (concat (int-to-string new-number) "\\2"))))
-               (with-current-buffer nntp-server-buffer
-                 (nnmail-find-file oldfile)
-                 (nnmail-write-region (point-min) (point-max) newfile))
-               (funcall nnmail-delete-file-function oldfile))
-             ;; 2/ Update all marks for this article:
-             ;; #### NOTE: it is possible that the new article number already
-             ;; #### belongs to a range, whereas the corresponding article
-             ;; #### doesn't exist (for example, if you delete an article).
-             ;; #### For that reason, it is important to update the ranges
-             ;; #### (meaning remove inexistant articles) before doing
-             ;; anything on them.
-             ;; 2 a/ read articles:
-             (let ((read (gnus-info-read info)))
-               (setq read (gnus-remove-from-range read (list new-number)))
-               (when (gnus-member-of-range old-number read)
-                 (setq read (gnus-remove-from-range read (list old-number)))
-                 (setq read (gnus-add-to-range read (list new-number))))
-               (gnus-info-set-read info read))
-             ;; 2 b/ marked articles:
-             (let ((oldmarks (gnus-info-marks info))
-                   mark newmarks)
-               (while (setq mark (pop oldmarks))
-                 (setcdr mark (gnus-remove-from-range (cdr mark)
-                                                      (list new-number)))
-                 (when (gnus-member-of-range old-number (cdr mark))
+             (let ((old-number-string (int-to-string old-number))
+                   (new-number-string (int-to-string new-number)))
+               (setq compacted t)
+               ;; #### NOTE: `nnml-article-to-file' calls
+               ;; #### `nnml-update-file-alist'  (which in turn calls
+               ;; #### `nnml-current-group-article-to-file-alist', which
+               ;; #### might use the NOV database). This might turn out to be
+               ;; #### inefficient. In that case, we will do the work
+               ;; #### manually.
+               ;; 1/ Move the article to a new file:
+               (let* ((oldfile (nnml-article-to-file old-number))
+                      (newfile
+                       (gnus-replace-in-string
+                        oldfile
+                        ;; nnml-use-compressed-files might be any string, but
+                        ;; probably it's sufficient to take into account only
+                        ;; "\\.[a-z0-9]+".  Note that we can't only use the
+                        ;; value of nnml-use-compressed-files because old
+                        ;; articles might have been saved with a different
+                        ;; value.
+                        (concat
+                         "\\(" old-number-string "\\)\\(\\(\\.[a-z0-9]+\\)?\\)$")
+                        (concat new-number-string "\\2"))))
+                 (with-current-buffer nntp-server-buffer
+                   (nnmail-find-file oldfile)
+                   ;; Update the Xref header in the article itself:
+                   (when (and (re-search-forward "^Xref: [^ ]+ " nil t)
+                              (re-search-forward
+                               (concat "\\<"
+                                       (regexp-quote
+                                        (concat group ":" old-number-string))
+                                       "\\>")
+                               (point-at-eol) t))
+                     (replace-match
+                      (concat group ":" new-number-string)))
+                   ;; Save to the new file:
+                   (nnmail-write-region (point-min) (point-max) newfile))
+                 (funcall nnmail-delete-file-function oldfile))
+               ;; 2/ Update all marks for this article:
+               ;; #### NOTE: it is possible that the new article number
+               ;; #### already belongs to a range, whereas the corresponding
+               ;; #### article doesn't exist (for example, if you delete an
+               ;; #### article). For that reason, it is important to update
+               ;; #### the ranges (meaning remove inexistant articles) before
+               ;; #### doing anything on them.
+               ;; 2 a/ read articles:
+               (let ((read (gnus-info-read info)))
+                 (setq read (gnus-remove-from-range read (list new-number)))
+                 (when (gnus-member-of-range old-number read)
+                   (setq read (gnus-remove-from-range read (list old-number)))
+                   (setq read (gnus-add-to-range read (list new-number))))
+                 (gnus-info-set-read info read))
+               ;; 2 b/ marked articles:
+               (let ((oldmarks (gnus-info-marks info))
+                     mark newmarks)
+                 (while (setq mark (pop oldmarks))
                    (setcdr mark (gnus-remove-from-range (cdr mark)
-                                                        (list old-number)))
-                   (setcdr mark (gnus-add-to-range (cdr mark)
-                                                   (list new-number))))
-                 (push mark newmarks))
-               (gnus-info-set-marks info newmarks))
-             ;; 3/ Update the NOV entry for this article:
-             (unless nnml-nov-is-evil
-               (save-excursion
-                 (set-buffer (nnml-open-nov group))
-                 (when (nnheader-find-nov-line old-number)
-                   (looking-at (int-to-string old-number))
-                   (replace-match (int-to-string new-number) nil t)))))
+                                                        (list new-number)))
+                   (when (gnus-member-of-range old-number (cdr mark))
+                     (setcdr mark (gnus-remove-from-range (cdr mark)
+                                                          (list old-number)))
+                     (setcdr mark (gnus-add-to-range (cdr mark)
+                                                     (list new-number))))
+                   (push mark newmarks))
+                 (gnus-info-set-marks info newmarks))
+               ;; 3/ Update the NOV entry for this article:
+               (unless nnml-nov-is-evil
+                 (save-excursion
+                   (set-buffer (nnml-open-nov group))
+                   (when (nnheader-find-nov-line old-number)
+                     ;; Replace the article number:
+                     (looking-at old-number-string)
+                     (replace-match new-number-string nil t)
+                     ;; Update the Xref header:
+                     (when (re-search-forward
+                            (concat "\\(Xref:[^\t\n]* \\)\\<"
+                                    (regexp-quote
+                                     (concat group ":" old-number-string))
+                                    "\\>")
+                            (point-at-eol) t)
+                       (replace-match
+                        (concat "\\1" group ":" new-number-string))))))
+               ;; 4/ Possibly remove the article from the backlog:
+               (when gnus-keep-backlog
+                 ;; #### NOTE: instead of removing the article, we could
+                 ;; #### modify the backlog to reflect the numbering change,
+                 ;; #### but I don't think it's worth it.
+                 (gnus-backlog-remove-article group-full-name old-number)
+                 (gnus-backlog-remove-article group-full-name new-number))))
            (setq new-number (1+ new-number)))))
       (if (not compacted)
          ;; No compaction had to be done: