55428047cc31ee3dbf476c99d1430c2186d4e33e
[gnus] / lisp / nnbabyl.el
1 ;;; nnbabyl.el --- rmail mbox access for Gnus
2 ;; Copyright (C) 1995,96 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;;      Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6 ;; Keywords: news, mail
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; For an overview of what the interface functions do, please see the
28 ;; Gnus sources.  
29
30 ;;; Code:
31
32 (require 'nnheader)
33 (require 'rmail)
34 (require 'nnmail)
35 (require 'nnoo)
36 (eval-when-compile (require 'cl))
37
38 (nnoo-declare nnbabyl)
39
40 (defvoo nnbabyl-mbox-file (expand-file-name "~/RMAIL")
41   "The name of the rmail box file in the users home directory.")
42
43 (defvoo nnbabyl-active-file (expand-file-name "~/.rmail-active")
44   "The name of the active file for the rmail box.")
45
46 (defvoo nnbabyl-get-new-mail t
47   "If non-nil, nnbabyl will check the incoming mail file and split the mail.")
48
49 (defvoo nnbabyl-prepare-save-mail-hook nil
50   "Hook run narrowed to an article before saving.")
51
52 \f
53
54 (defvar nnbabyl-mail-delimiter "\^_")
55
56 (defconst nnbabyl-version "nnbabyl 1.0"
57   "nnbabyl version.")
58
59 (defvoo nnbabyl-mbox-buffer nil)
60 (defvoo nnbabyl-current-group nil)
61 (defvoo nnbabyl-status-string "")
62 (defvoo nnbabyl-group-alist nil)
63 (defvoo nnbabyl-active-timestamp nil)
64
65 (defvoo nnbabyl-previous-buffer-mode nil)
66
67 (eval-and-compile
68   (autoload 'gnus-set-text-properties "gnus-ems"))
69
70 \f
71
72 ;;; Interface functions
73
74 (nnoo-define-basics nnbabyl)
75
76 (deffoo nnbabyl-retrieve-headers (articles &optional group server fetch-old)
77   (save-excursion
78     (set-buffer nntp-server-buffer)
79     (erase-buffer)
80     (let ((number (length articles))
81           (count 0)
82           (delim (concat "^" nnbabyl-mail-delimiter))
83           article art-string start stop)
84       (nnbabyl-possibly-change-newsgroup group server)
85       (while (setq article (pop articles))
86         (setq art-string (nnbabyl-article-string article))
87         (set-buffer nnbabyl-mbox-buffer)
88         (beginning-of-line)
89         (when (or (search-forward art-string nil t)
90                   (search-backward art-string nil t))
91           (re-search-backward delim nil t)
92           (while (and (not (looking-at ".+:"))
93                       (zerop (forward-line 1))))
94           (setq start (point))
95           (search-forward "\n\n" nil t)
96           (setq stop (1- (point)))
97           (set-buffer nntp-server-buffer)
98           (insert "221 ")
99           (princ article (current-buffer))
100           (insert " Article retrieved.\n")
101           (insert-buffer-substring nnbabyl-mbox-buffer start stop)
102           (goto-char (point-max))
103           (insert ".\n"))
104         (and (numberp nnmail-large-newsgroup)
105              (> number nnmail-large-newsgroup)
106              (zerop (% (incf count) 20))
107              (nnheader-message 5 "nnbabyl: Receiving headers... %d%%"
108                                (/ (* count 100) number))))
109
110       (and (numberp nnmail-large-newsgroup)
111            (> number nnmail-large-newsgroup)
112            (nnheader-message 5 "nnbabyl: Receiving headers...done"))
113
114       (set-buffer nntp-server-buffer)
115       (nnheader-fold-continuation-lines)
116       'headers)))
117
118 (deffoo nnbabyl-open-server (server &optional defs)
119   (nnoo-change-server 'nnbabyl server defs)
120   (nnbabyl-create-mbox)
121   (cond 
122    ((not (file-exists-p nnbabyl-mbox-file))
123     (nnbabyl-close-server)
124     (nnheader-report 'nnbabyl "No such file: %s" nnbabyl-mbox-file))
125    ((file-directory-p nnbabyl-mbox-file)
126     (nnbabyl-close-server)
127     (nnheader-report 'nnbabyl "Not a regular file: %s" nnbabyl-mbox-file))
128    (t
129     (nnheader-report 'nnbabyl "Opened server %s using mbox %s" server
130                      nnbabyl-mbox-file)
131     t)))
132
133 (deffoo nnbabyl-close-server (&optional server)
134   ;; Restore buffer mode.
135   (when (and (nnbabyl-server-opened)
136              nnbabyl-previous-buffer-mode)
137     (save-excursion
138       (set-buffer nnbabyl-mbox-buffer)
139       (narrow-to-region
140        (caar nnbabyl-previous-buffer-mode)
141        (cdar nnbabyl-previous-buffer-mode))
142       (funcall (cdr nnbabyl-previous-buffer-mode))))
143   (nnoo-close-server 'nnbabyl server)
144   (setq nnbabyl-mbox-buffer nil)
145   t)
146
147 (deffoo nnbabyl-server-opened (&optional server)
148   (and (nnoo-current-server-p 'nnbabyl server)
149        nnbabyl-mbox-buffer
150        (buffer-name nnbabyl-mbox-buffer)
151        nntp-server-buffer
152        (buffer-name nntp-server-buffer)))
153
154 (deffoo nnbabyl-request-article (article &optional newsgroup server buffer)
155   (nnbabyl-possibly-change-newsgroup newsgroup server)
156   (save-excursion
157     (set-buffer nnbabyl-mbox-buffer)
158     (goto-char (point-min))
159     (when (search-forward (nnbabyl-article-string article) nil t)
160       (let (start stop summary-line)
161         (re-search-backward (concat "^" nnbabyl-mail-delimiter) nil t)
162         (while (and (not (looking-at ".+:"))
163                     (zerop (forward-line 1))))
164         (setq start (point))
165         (or (when (re-search-forward 
166                    (concat "^" nnbabyl-mail-delimiter) nil t)
167               (beginning-of-line)
168               t)
169             (goto-char (point-max)))
170         (setq stop (point))
171         (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
172           (set-buffer nntp-server-buffer)
173           (erase-buffer)
174           (insert-buffer-substring nnbabyl-mbox-buffer start stop)
175           (goto-char (point-min))
176           ;; If there is an EOOH header, then we have to remove some
177           ;; duplicated headers. 
178           (setq summary-line (looking-at "Summary-line:"))
179           (when (search-forward "\n*** EOOH ***" nil t)
180             (if summary-line
181                 ;; The headers to be deleted are located before the
182                 ;; EOOH line...
183                 (delete-region (point-min) (progn (forward-line 1)
184                                                   (point)))
185               ;; ...or after.
186               (delete-region (progn (beginning-of-line) (point))
187                              (or (search-forward "\n\n" nil t)
188                                  (point)))))
189           (if (numberp article)
190               (cons nnbabyl-current-group article)
191             (nnbabyl-article-group-number)))))))
192
193 (deffoo nnbabyl-request-group (group &optional server dont-check)
194   (let ((active (cadr (assoc group nnbabyl-group-alist))))
195     (save-excursion
196       (cond 
197        ((or (null active)
198             (null (nnbabyl-possibly-change-newsgroup group server)))
199         (nnheader-report 'nnbabyl "No such group: %s" group))
200        (dont-check
201         (nnheader-report 'nnbabyl "Selected group %s" group)
202         (nnheader-insert ""))
203        (t
204         (nnheader-report 'nnbabyl "Selected group %s" group)
205         (nnheader-insert "211 %d %d %d %s\n" 
206                          (1+ (- (cdr active) (car active)))
207                          (car active) (cdr active) group))))))
208
209 (deffoo nnbabyl-request-scan (&optional group server)
210   (nnbabyl-possibly-change-newsgroup group server)
211   (nnbabyl-read-mbox)
212   (nnmail-get-new-mail 
213    'nnbabyl 
214    (lambda ()
215      (save-excursion
216        (set-buffer nnbabyl-mbox-buffer)
217        (save-buffer)))
218    nnbabyl-mbox-file group
219    (lambda ()
220      (save-excursion
221        (let ((in-buf (current-buffer)))
222          (goto-char (point-min))
223          (while (search-forward "\n\^_\n" nil t)
224            (delete-char -1))
225          (set-buffer nnbabyl-mbox-buffer)
226          (goto-char (point-max))
227          (search-backward "\n\^_" nil t)
228          (goto-char (match-end 0))
229          (insert-buffer-substring in-buf)))
230      (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))))
231
232 (deffoo nnbabyl-close-group (group &optional server)
233   t)
234
235 (deffoo nnbabyl-request-create-group (group &optional server args)
236   (nnmail-activate 'nnbabyl)
237   (unless (assoc group nnbabyl-group-alist)
238     (push (list group (cons 1 0))
239                                     nnbabyl-group-alist)
240     (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))
241   t)
242
243 (deffoo nnbabyl-request-list (&optional server)
244   (save-excursion
245     (nnmail-find-file nnbabyl-active-file)
246     (setq nnbabyl-group-alist (nnmail-get-active))
247     t))
248
249 (deffoo nnbabyl-request-newgroups (date &optional server)
250   (nnbabyl-request-list server))
251
252 (deffoo nnbabyl-request-list-newsgroups (&optional server)
253   (nnheader-report 'nnbabyl "nnbabyl: LIST NEWSGROUPS is not implemented."))
254
255 (deffoo nnbabyl-request-expire-articles
256   (articles newsgroup &optional server force)
257   (nnbabyl-possibly-change-newsgroup newsgroup server)
258   (let* ((is-old t)
259          rest)
260     (nnmail-activate 'nnbabyl)
261
262     (save-excursion 
263       (set-buffer nnbabyl-mbox-buffer)
264       (gnus-set-text-properties (point-min) (point-max) nil)
265       (while (and articles is-old)
266         (goto-char (point-min))
267         (when (search-forward (nnbabyl-article-string (car articles)) nil t)
268           (if (setq is-old
269                     (nnmail-expired-article-p
270                      newsgroup
271                      (buffer-substring 
272                       (point) (progn (end-of-line) (point))) force))
273               (progn
274                 (nnheader-message 5 "Deleting article %d in %s..." 
275                                   (car articles) newsgroup)
276                 (nnbabyl-delete-mail))
277             (push (car articles) rest)))
278         (setq articles (cdr articles)))
279       (save-buffer)
280       ;; Find the lowest active article in this group.
281       (let ((active (nth 1 (assoc newsgroup nnbabyl-group-alist))))
282         (goto-char (point-min))
283         (while (and (not (search-forward
284                           (nnbabyl-article-string (car active)) nil t))
285                     (<= (car active) (cdr active)))
286           (setcar active (1+ (car active)))
287           (goto-char (point-min))))
288       (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
289       (nconc rest articles))))
290
291 (deffoo nnbabyl-request-move-article 
292   (article group server accept-form &optional last)
293   (let ((buf (get-buffer-create " *nnbabyl move*"))
294         result)
295     (and 
296      (nnbabyl-request-article article group server)
297      (save-excursion
298        (set-buffer buf)
299        (insert-buffer-substring nntp-server-buffer)
300        (goto-char (point-min))
301        (while (re-search-forward 
302                "^X-Gnus-Newsgroup:" 
303                (save-excursion (search-forward "\n\n" nil t) (point)) t)
304          (delete-region (progn (beginning-of-line) (point))
305                         (progn (forward-line 1) (point))))
306        (setq result (eval accept-form))
307        (kill-buffer (current-buffer))
308        result)
309      (save-excursion
310        (nnbabyl-possibly-change-newsgroup group server)
311        (set-buffer nnbabyl-mbox-buffer)
312        (goto-char (point-min))
313        (if (search-forward (nnbabyl-article-string article) nil t)
314            (nnbabyl-delete-mail))
315        (and last (save-buffer))))
316     result))
317
318 (deffoo nnbabyl-request-accept-article (group &optional server last)
319   (nnbabyl-possibly-change-newsgroup group server)
320   (nnmail-check-syntax)
321   (let ((buf (current-buffer))
322         result beg)
323     (and 
324      (nnmail-activate 'nnbabyl)
325      (save-excursion
326        (goto-char (point-min))
327        (search-forward "\n\n" nil t)
328        (forward-line -1)
329        (save-excursion
330          (while (re-search-backward "^X-Gnus-Newsgroup: " beg t)
331            (delete-region (point) (progn (forward-line 1) (point)))))
332        (setq result (car (nnbabyl-save-mail
333                           (if (stringp group)
334                               (list (cons group (nnbabyl-active-number group)))
335                             (nnmail-article-group 'nnbabyl-active-number)))))
336        (set-buffer nnbabyl-mbox-buffer)
337        (goto-char (point-max))
338        (search-backward "\n\^_")
339        (goto-char (match-end 0))
340        (insert-buffer-substring buf)
341        (when last
342          (save-buffer)
343          (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))
344        result))))
345
346 (deffoo nnbabyl-request-replace-article (article group buffer)
347   (nnbabyl-possibly-change-newsgroup group)
348   (save-excursion
349     (set-buffer nnbabyl-mbox-buffer)
350     (goto-char (point-min))
351     (if (not (search-forward (nnbabyl-article-string article) nil t))
352         nil
353       (nnbabyl-delete-mail t t)
354       (insert-buffer-substring buffer)
355       (save-buffer)
356       t)))
357
358 (deffoo nnbabyl-request-delete-group (group &optional force server)
359   (nnbabyl-possibly-change-newsgroup group server)
360   ;; Delete all articles in GROUP.
361   (if (not force)
362       ()                                ; Don't delete the articles.
363     (save-excursion
364       (set-buffer nnbabyl-mbox-buffer)
365       (goto-char (point-min))
366       ;; Delete all articles in this group.
367       (let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":"))
368             found)
369         (while (search-forward ident nil t)
370           (setq found t)
371           (nnbabyl-delete-mail))
372         (when found
373           (save-buffer)))))
374   ;; Remove the group from all structures.
375   (setq nnbabyl-group-alist 
376         (delq (assoc group nnbabyl-group-alist) nnbabyl-group-alist)
377         nnbabyl-current-group nil)
378   ;; Save the active file.
379   (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
380   t)
381
382 (deffoo nnbabyl-request-rename-group (group new-name &optional server)
383   (nnbabyl-possibly-change-newsgroup group server)
384   (save-excursion
385     (set-buffer nnbabyl-mbox-buffer)
386     (goto-char (point-min))
387     (let ((ident (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":"))
388           (new-ident (concat "\nX-Gnus-Newsgroup: " new-name ":"))
389           found)
390       (while (search-forward ident nil t)
391         (replace-match new-ident t t)
392         (setq found t))
393       (when found
394         (save-buffer))))
395   (let ((entry (assoc group nnbabyl-group-alist)))
396     (and entry (setcar entry new-name))
397     (setq nnbabyl-current-group nil)
398     ;; Save the new group alist.
399     (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
400     t))
401
402 \f
403 ;;; Internal functions.
404
405 ;; If FORCE, delete article no matter how many X-Gnus-Newsgroup
406 ;; headers there are.  If LEAVE-DELIM, don't delete the Unix mbox
407 ;; delimiter line.
408 (defun nnbabyl-delete-mail (&optional force leave-delim)
409   ;; Delete the current X-Gnus-Newsgroup line.
410   (unless force
411     (delete-region
412      (progn (beginning-of-line) (point))
413      (progn (forward-line 1) (point))))
414   ;; Beginning of the article.
415   (save-excursion
416     (save-restriction
417       (widen)
418       (narrow-to-region
419        (save-excursion
420          (re-search-backward (concat "^" nnbabyl-mail-delimiter) nil t)
421          (if leave-delim (progn (forward-line 1) (point))
422            (match-beginning 0)))
423        (progn
424          (forward-line 1)
425          (or (and (re-search-forward (concat "^" nnbabyl-mail-delimiter)
426                                      nil t)
427                   (match-beginning 0))
428              (point-max))))
429       (goto-char (point-min))
430       ;; Only delete the article if no other groups owns it as well.
431       (when (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
432         (delete-region (point-min) (point-max))))))
433
434 (defun nnbabyl-possibly-change-newsgroup (newsgroup &optional server)
435   (when (and server 
436              (not (nnbabyl-server-opened server)))
437     (nnbabyl-open-server server))
438   (when (or (not nnbabyl-mbox-buffer)
439             (not (buffer-name nnbabyl-mbox-buffer)))
440     (save-excursion (nnbabyl-read-mbox)))
441   (unless nnbabyl-group-alist
442     (nnmail-activate 'nnbabyl))
443   (if newsgroup
444       (if (assoc newsgroup nnbabyl-group-alist)
445           (setq nnbabyl-current-group newsgroup)
446         (nnheader-report 'nnbabyl "No such group in file"))
447     t))
448
449 (defun nnbabyl-article-string (article)
450   (if (numberp article)
451       (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":" 
452               (int-to-string article) " ")
453     (concat "\nMessage-ID: " article)))
454
455 (defun nnbabyl-article-group-number ()
456   (save-excursion
457     (goto-char (point-min))
458     (when (re-search-forward "^X-Gnus-Newsgroup: +\\([^:]+\\):\\([0-9]+\\) "
459                              nil t)
460       (cons (buffer-substring (match-beginning 1) (match-end 1))
461             (string-to-int
462              (buffer-substring (match-beginning 2) (match-end 2)))))))
463
464 (defun nnbabyl-insert-lines ()
465   "Insert how many lines and chars there are in the body of the mail."
466   (let (lines chars)
467     (save-excursion
468       (goto-char (point-min))
469       (when (search-forward "\n\n" nil t)
470         ;; There may be an EOOH line here...
471         (when (looking-at "\\*\\*\\* EOOH \\*\\*\\*")
472           (search-forward "\n\n" nil t))
473         (setq chars (- (point-max) (point))
474               lines (max (- (count-lines (point) (point-max)) 1) 0))
475         ;; Move back to the end of the headers. 
476         (goto-char (point-min))
477         (search-forward "\n\n" nil t)
478         (forward-char -1)
479         (save-excursion
480           (when (re-search-backward "^Lines: " nil t)
481             (delete-region (point) (progn (forward-line 1) (point)))))
482         (insert (format "Lines: %d\n" lines))
483         chars))))
484
485 (defun nnbabyl-save-mail (group-art)
486   ;; Called narrowed to an article.
487   (nnbabyl-insert-lines)
488   (nnmail-insert-xref group-art)
489   (nnbabyl-insert-newsgroup-line group-art)
490   (run-hooks 'nnbabyl-prepare-save-mail-hook)
491   group-art)
492
493 (defun nnbabyl-insert-newsgroup-line (group-art)
494   (save-excursion
495     (goto-char (point-min))
496     (while (looking-at "From ")
497       (replace-match "Mail-from: From " t t)
498       (forward-line 1))
499     ;; If there is a C-l at the beginning of the narrowed region, this
500     ;; isn't really a "save", but rather a "scan".
501     (goto-char (point-min))
502     (unless (looking-at "\^L")
503       (save-excursion
504         (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
505         (goto-char (point-max))
506         (insert "\^_\n")))
507     (when (search-forward "\n\n" nil t)
508       (forward-char -1)
509       (while group-art
510         (insert (format "X-Gnus-Newsgroup: %s:%d   %s\n" 
511                         (caar group-art) (cdar group-art)
512                         (current-time-string)))
513         (setq group-art (cdr group-art))))
514     t))
515
516 (defun nnbabyl-active-number (group)
517   ;; Find the next article number in GROUP.
518   (let ((active (cadr (assoc group nnbabyl-group-alist))))
519     (if active
520         (setcdr active (1+ (cdr active)))
521       ;; This group is new, so we create a new entry for it.
522       ;; This might be a bit naughty... creating groups on the drop of
523       ;; a hat, but I don't know...
524       (push (list group (setq active (cons 1 1)))
525             nnbabyl-group-alist))
526     (cdr active)))
527
528 (defun nnbabyl-create-mbox ()
529   (unless (file-exists-p nnbabyl-mbox-file)
530     ;; Create a new, empty RMAIL mbox file.
531     (save-excursion
532       (set-buffer (setq nnbabyl-mbox-buffer
533                         (create-file-buffer nnbabyl-mbox-file)))
534       (setq buffer-file-name nnbabyl-mbox-file)
535       (insert "BABYL OPTIONS:\n\n\^_")
536       (nnmail-write-region
537        (point-min) (point-max) nnbabyl-mbox-file t 'nomesg))))
538
539 (defun nnbabyl-read-mbox ()
540   (nnmail-activate 'nnbabyl)
541   (nnbabyl-create-mbox)
542
543   (if (and nnbabyl-mbox-buffer
544            (buffer-name nnbabyl-mbox-buffer)
545            (save-excursion
546              (set-buffer nnbabyl-mbox-buffer)
547              (= (buffer-size) (nnheader-file-size nnbabyl-mbox-file))))
548       ()                                ; This buffer hasn't changed since we read it last.  Possibly.
549     (save-excursion
550       (let ((delim (concat "^" nnbabyl-mail-delimiter))
551             (alist nnbabyl-group-alist)
552             start end number)
553         (set-buffer (setq nnbabyl-mbox-buffer 
554                           (nnheader-find-file-noselect 
555                            nnbabyl-mbox-file nil 'raw)))
556         ;; Save previous buffer mode.
557         (setq nnbabyl-previous-buffer-mode 
558               (cons (cons (point-min) (point-max))
559                     major-mode))
560
561         (buffer-disable-undo (current-buffer))
562         (widen)
563         (setq buffer-read-only nil)
564         (fundamental-mode)
565
566         ;; Go through the group alist and compare against
567         ;; the rmail file.
568         (while alist
569           (goto-char (point-max))
570           (when (and (re-search-backward
571                       (format "^X-Gnus-Newsgroup: %s:\\([0-9]+\\) "
572                               (caar alist))
573                       nil t)
574                      (> (setq number
575                               (string-to-number 
576                                (buffer-substring
577                                 (match-beginning 1) (match-end 1))))
578                         (cdadar alist)))
579             (setcdr (cadar alist) (1+ number)))
580           (setq alist (cdr alist)))
581         
582         ;; We go through the mbox and make sure that each and 
583         ;; every mail belongs to some group or other.
584         (goto-char (point-min))
585         (re-search-forward delim nil t)
586         (setq start (match-end 0))
587         (while (re-search-forward delim nil t)
588           (setq end (match-end 0))
589           (unless (search-backward "\nX-Gnus-Newsgroup: " start t)
590             (goto-char end)
591             (save-excursion
592               (save-restriction
593                 (narrow-to-region (goto-char start) end)
594                 (nnbabyl-save-mail 
595                  (nnmail-article-group 'nnbabyl-active-number))
596                 (setq end (point-max)))))
597           (goto-char (setq start end)))
598         (when (buffer-modified-p (current-buffer))
599           (save-buffer))
600         (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)))))
601
602 (defun nnbabyl-remove-incoming-delims ()
603   (goto-char (point-min))
604   (while (search-forward "\^_" nil t)
605     (replace-match "?" t t)))
606
607 (defun nnbabyl-check-mbox ()
608   "Go through the nnbabyl mbox and make sure that no article numbers are reused."
609   (interactive)
610   (let ((idents (make-vector 1000 0))
611         id)
612     (save-excursion
613       (when (or (not nnbabyl-mbox-buffer)
614                 (not (buffer-name nnbabyl-mbox-buffer)))
615         (nnbabyl-read-mbox))
616       (set-buffer nnbabyl-mbox-buffer)
617       (goto-char (point-min))
618       (while (re-search-forward "^X-Gnus-Newsgroup: \\([^ ]+\\) "  nil t)
619         (if (intern-soft (setq id (match-string 1)) idents)
620             (progn
621               (delete-region (progn (beginning-of-line) (point))
622                              (progn (forward-line 1) (point)))
623               (nnheader-message 7 "Moving %s..." id)
624               (nnbabyl-save-mail
625                (nnmail-article-group 'nnbabyl-active-number)))
626           (intern id idents)))
627       (when (buffer-modified-p (current-buffer))
628         (save-buffer))
629       (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
630       (message ""))))
631
632 (provide 'nnbabyl)
633
634 ;;; nnbabyl.el ends here