comparison lisp/gnus/nnsoup.el @ 70:131b0175ea99 r20-0b30

Import from CVS: tag r20-0b30
author cvs
date Mon, 13 Aug 2007 09:02:59 +0200
parents ec9a17fef872
children 0d2f883870bc
comparison
equal deleted inserted replaced
69:804d1389bcd6 70:131b0175ea99
1 ;;; nnsoup.el --- SOUP access for Gnus 1 ;;; nnsoup.el --- SOUP access for Gnus
2 ;; Copyright (C) 1995,96,97 Free Software Foundation, Inc. 2 ;; Copyright (C) 1995,96 Free Software Foundation, Inc.
3 3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no> 4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet> 5 ;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6 ;; Keywords: news, mail 6 ;; Keywords: news, mail
7 7
54 "Active file.") 54 "Active file.")
55 55
56 (defvoo nnsoup-packer "tar cf - %s | gzip > $HOME/Soupin%d.tgz" 56 (defvoo nnsoup-packer "tar cf - %s | gzip > $HOME/Soupin%d.tgz"
57 "Format string command for packing a SOUP packet. 57 "Format string command for packing a SOUP packet.
58 The SOUP files will be inserted where the %s is in the string. 58 The SOUP files will be inserted where the %s is in the string.
59 This string MUST contain both %s and %d. The file number will be 59 This string MUST contain both %s and %d. The file number will be
60 inserted where %d appears.") 60 inserted where %d appears.")
61 61
62 (defvoo nnsoup-unpacker "gunzip -c %s | tar xvf -" 62 (defvoo nnsoup-unpacker "gunzip -c %s | tar xvf -"
63 "*Format string command for unpacking a SOUP packet. 63 "*Format string command for unpacking a SOUP packet.
64 The SOUP packet file name will be inserted at the %s.") 64 The SOUP packet file name will be inserted at the %s.")
79 (defvoo nnsoup-current-prefix 0) 79 (defvoo nnsoup-current-prefix 0)
80 (defvoo nnsoup-replies-list nil) 80 (defvoo nnsoup-replies-list nil)
81 (defvoo nnsoup-buffers nil) 81 (defvoo nnsoup-buffers nil)
82 (defvoo nnsoup-current-group nil) 82 (defvoo nnsoup-current-group nil)
83 (defvoo nnsoup-group-alist-touched nil) 83 (defvoo nnsoup-group-alist-touched nil)
84 (defvoo nnsoup-article-alist nil)
85 84
86 85
87 86
88 ;;; Interface functions. 87 ;;; Interface functions.
89 88
111 ;; This is a useful area. 110 ;; This is a useful area.
112 (push (car areas) useful-areas) 111 (push (car areas) useful-areas)
113 (setq this-area-seq nil) 112 (setq this-area-seq nil)
114 ;; We take note whether this MSG has a corresponding IDX 113 ;; We take note whether this MSG has a corresponding IDX
115 ;; for later use. 114 ;; for later use.
116 (when (or (= (gnus-soup-encoding-index 115 (when (or (= (gnus-soup-encoding-index
117 (gnus-soup-area-encoding (nth 1 (car areas)))) ?n) 116 (gnus-soup-area-encoding (nth 1 (car areas)))) ?n)
118 (not (file-exists-p 117 (not (file-exists-p
119 (nnsoup-file 118 (nnsoup-file
120 (gnus-soup-area-prefix (nth 1 (car areas))))))) 119 (gnus-soup-area-prefix (nth 1 (car areas)))))))
121 (setq use-nov nil)) 120 (setq use-nov nil))
126 (setq sequence (cdr sequence))) 125 (setq sequence (cdr sequence)))
127 (setcar useful-areas (cons (nreverse this-area-seq) 126 (setcar useful-areas (cons (nreverse this-area-seq)
128 (car useful-areas))))) 127 (car useful-areas)))))
129 128
130 ;; We now have a list of article numbers and corresponding 129 ;; We now have a list of article numbers and corresponding
131 ;; areas. 130 ;; areas.
132 (setq useful-areas (nreverse useful-areas)) 131 (setq useful-areas (nreverse useful-areas))
133 132
134 ;; Two different approaches depending on whether all the MSG 133 ;; Two different approaches depending on whether all the MSG
135 ;; files have corresponding IDX files. If they all do, we 134 ;; files have corresponding IDX files. If they all do, we
136 ;; simply return the relevant IDX files and let Gnus sort out 135 ;; simply return the relevant IDX files and let Gnus sort out
161 (while useful-areas 160 (while useful-areas
162 (setq articles (caar useful-areas) 161 (setq articles (caar useful-areas)
163 useful-areas (cdr useful-areas)) 162 useful-areas (cdr useful-areas))
164 (while articles 163 (while articles
165 (when (setq msg-buf 164 (when (setq msg-buf
166 (nnsoup-narrow-to-article 165 (nnsoup-narrow-to-article
167 (car articles) (cdar useful-areas) 'head)) 166 (car articles) (cdar useful-areas) 'head))
168 (goto-char (point-max)) 167 (goto-char (point-max))
169 (insert (format "221 %d Article retrieved.\n" (car articles))) 168 (insert (format "221 %d Article retrieved.\n" (car articles)))
170 (insert-buffer-substring msg-buf) 169 (insert-buffer-substring msg-buf)
171 (goto-char (point-max)) 170 (goto-char (point-max))
179 (nnoo-change-server 'nnsoup server defs) 178 (nnoo-change-server 'nnsoup server defs)
180 (when (not (file-exists-p nnsoup-directory)) 179 (when (not (file-exists-p nnsoup-directory))
181 (condition-case () 180 (condition-case ()
182 (make-directory nnsoup-directory t) 181 (make-directory nnsoup-directory t)
183 (error t))) 182 (error t)))
184 (cond 183 (cond
185 ((not (file-exists-p nnsoup-directory)) 184 ((not (file-exists-p nnsoup-directory))
186 (nnsoup-close-server) 185 (nnsoup-close-server)
187 (nnheader-report 'nnsoup "Couldn't create directory: %s" nnsoup-directory)) 186 (nnheader-report 'nnsoup "Couldn't create directory: %s" nnsoup-directory))
188 ((not (file-directory-p (file-truename nnsoup-directory))) 187 ((not (file-directory-p (file-truename nnsoup-directory)))
189 (nnsoup-close-server) 188 (nnsoup-close-server)
223 (insert-buffer-substring buf) 222 (insert-buffer-substring buf)
224 t)))) 223 t))))
225 224
226 (deffoo nnsoup-request-group (group &optional server dont-check) 225 (deffoo nnsoup-request-group (group &optional server dont-check)
227 (nnsoup-possibly-change-group group) 226 (nnsoup-possibly-change-group group)
228 (if dont-check 227 (if dont-check
229 t 228 t
230 (let ((active (cadr (assoc group nnsoup-group-alist)))) 229 (let ((active (cadr (assoc group nnsoup-group-alist))))
231 (if (not active) 230 (if (not active)
232 (nnheader-report 'nnsoup "No such group: %s" group) 231 (nnheader-report 'nnsoup "No such group: %s" group)
233 (nnheader-insert 232 (nnheader-insert
234 "211 %d %d %d %s\n" 233 "211 %d %d %d %s\n"
235 (max (1+ (- (cdr active) (car active))) 0) 234 (max (1+ (- (cdr active) (car active))) 0)
236 (car active) (cdr active) group))))) 235 (car active) (cdr active) group)))))
237 236
238 (deffoo nnsoup-request-type (group &optional article) 237 (deffoo nnsoup-request-type (group &optional article)
239 (nnsoup-possibly-change-group group) 238 (nnsoup-possibly-change-group group)
240 ;; Try to guess the type based on the first articl ein the group.
241 (when (not article)
242 (setq article
243 (cdaar (cddr (assoc group nnsoup-group-alist)))))
244 (if (not article) 239 (if (not article)
245 'unknown 240 'unknown
246 (let ((kind (gnus-soup-encoding-kind 241 (let ((kind (gnus-soup-encoding-kind
247 (gnus-soup-area-encoding 242 (gnus-soup-area-encoding
248 (nth 1 (nnsoup-article-to-area 243 (nth 1 (nnsoup-article-to-area
249 article nnsoup-current-group)))))) 244 article nnsoup-current-group))))))
250 (cond ((= kind ?m) 'mail) 245 (cond ((= kind ?m) 'mail)
251 ((= kind ?n) 'news) 246 ((= kind ?n) 'news)
310 (and (or (setq mod-time (nth 5 (file-attributes 305 (and (or (setq mod-time (nth 5 (file-attributes
311 (nnsoup-file prefix)))) 306 (nnsoup-file prefix))))
312 (setq mod-time (nth 5 (file-attributes 307 (setq mod-time (nth 5 (file-attributes
313 (nnsoup-file prefix t))))) 308 (nnsoup-file prefix t)))))
314 (gnus-sublist-p articles range-list) 309 (gnus-sublist-p articles range-list)
315 ;; This file is old enough. 310 ;; This file is old enough.
316 (nnmail-expired-article-p group mod-time force)) 311 (nnmail-expired-article-p group mod-time force))
317 ;; Ok, we delete this file. 312 ;; Ok, we delete this file.
318 (when (ignore-errors 313 (when (condition-case nil
319 (nnheader-message 314 (progn
320 5 "Deleting %s in group %s..." (nnsoup-file prefix) 315 (nnheader-message
321 group) 316 5 "Deleting %s in group %s..." (nnsoup-file prefix)
322 (when (file-exists-p (nnsoup-file prefix)) 317 group)
323 (delete-file (nnsoup-file prefix))) 318 (when (file-exists-p (nnsoup-file prefix))
324 (nnheader-message 319 (delete-file (nnsoup-file prefix)))
325 5 "Deleting %s in group %s..." (nnsoup-file prefix t) 320 (nnheader-message
326 group) 321 5 "Deleting %s in group %s..." (nnsoup-file prefix t)
327 (when (file-exists-p (nnsoup-file prefix t)) 322 group)
328 (delete-file (nnsoup-file prefix t))) 323 (when (file-exists-p (nnsoup-file prefix t))
329 t) 324 (delete-file (nnsoup-file prefix t)))
325 t)
326 (error nil))
330 (setcdr (cdr total-infolist) (delq info (cddr total-infolist))) 327 (setcdr (cdr total-infolist) (delq info (cddr total-infolist)))
331 (setq articles (gnus-sorted-complement articles range-list)))) 328 (setq articles (gnus-sorted-complement articles range-list))))
332 (when (not mod-time) 329 (when (not mod-time)
333 (setcdr (cdr total-infolist) (delq info (cddr total-infolist))))) 330 (setcdr (cdr total-infolist) (delq info (cddr total-infolist)))))
334 (if (cddr total-infolist) 331 (if (cddr total-infolist)
340 337
341 338
342 ;;; Internal functions 339 ;;; Internal functions
343 340
344 (defun nnsoup-possibly-change-group (group &optional force) 341 (defun nnsoup-possibly-change-group (group &optional force)
345 (when (and group 342 (if group
346 (not (equal nnsoup-current-group group))) 343 (setq nnsoup-current-group group)
347 (setq nnsoup-article-alist nil) 344 t))
348 (setq nnsoup-current-group group))
349 t)
350 345
351 (defun nnsoup-read-active-file () 346 (defun nnsoup-read-active-file ()
352 (setq nnsoup-group-alist nil) 347 (setq nnsoup-group-alist nil)
353 (when (file-exists-p nnsoup-active-file) 348 (when (file-exists-p nnsoup-active-file)
354 (ignore-errors 349 (condition-case ()
355 (load nnsoup-active-file t t t)) 350 (load nnsoup-active-file t t t)
351 (error nil))
356 ;; Be backwards compatible. 352 ;; Be backwards compatible.
357 (when (and nnsoup-group-alist 353 (when (and nnsoup-group-alist
358 (not (atom (caadar nnsoup-group-alist)))) 354 (not (atom (caadar nnsoup-group-alist))))
359 (let ((alist nnsoup-group-alist) 355 (let ((alist nnsoup-group-alist)
360 entry e min max) 356 entry e min max)
367 (setq nnsoup-group-alist-touched t)) 363 (setq nnsoup-group-alist-touched t))
368 nnsoup-group-alist)) 364 nnsoup-group-alist))
369 365
370 (defun nnsoup-write-active-file (&optional force) 366 (defun nnsoup-write-active-file (&optional force)
371 (when (and nnsoup-group-alist 367 (when (and nnsoup-group-alist
372 (or force 368 (or force
373 nnsoup-group-alist-touched)) 369 nnsoup-group-alist-touched))
374 (setq nnsoup-group-alist-touched nil) 370 (setq nnsoup-group-alist-touched nil)
375 (nnheader-temp-write nnsoup-active-file 371 (nnheader-temp-write nnsoup-active-file
376 (gnus-prin1 `(setq nnsoup-group-alist ',nnsoup-group-alist)) 372 (let ((standard-output (current-buffer)))
377 (insert "\n") 373 (prin1 `(setq nnsoup-group-alist ',nnsoup-group-alist))
378 (gnus-prin1 `(setq nnsoup-current-prefix ,nnsoup-current-prefix)) 374 (insert "\n")
379 (insert "\n")))) 375 (prin1 `(setq nnsoup-current-prefix ,nnsoup-current-prefix))
376 (insert "\n")))))
380 377
381 (defun nnsoup-next-prefix () 378 (defun nnsoup-next-prefix ()
382 "Return the next free prefix." 379 "Return the next free prefix."
383 (let (prefix) 380 (let (prefix)
384 (while (or (file-exists-p 381 (while (or (file-exists-p
385 (nnsoup-file (setq prefix (int-to-string 382 (nnsoup-file (setq prefix (int-to-string
386 nnsoup-current-prefix)))) 383 nnsoup-current-prefix))))
387 (file-exists-p (nnsoup-file prefix t))) 384 (file-exists-p (nnsoup-file prefix t)))
388 (incf nnsoup-current-prefix)) 385 (incf nnsoup-current-prefix))
389 (incf nnsoup-current-prefix) 386 (incf nnsoup-current-prefix)
390 prefix)) 387 prefix))
391 388
392 (defun nnsoup-file-name (dir file)
393 "Return the full path of FILE (in any case) in DIR."
394 (let* ((case-fold-search t)
395 (files (directory-files dir t))
396 (regexp (concat (regexp-quote file) "$")))
397 (car (delq nil
398 (mapcar
399 (lambda (file)
400 (if (string-match regexp file)
401 file
402 nil))
403 files)))))
404
405 (defun nnsoup-read-areas () 389 (defun nnsoup-read-areas ()
406 (let ((areas-file (nnsoup-file-name nnsoup-tmp-directory "areas"))) 390 (save-excursion
407 (when areas-file 391 (set-buffer nntp-server-buffer)
408 (save-excursion 392 (let ((areas (gnus-soup-parse-areas (concat nnsoup-tmp-directory "AREAS")))
409 (set-buffer nntp-server-buffer) 393 entry number area lnum cur-prefix file)
410 (let ((areas (gnus-soup-parse-areas areas-file)) 394 ;; Go through all areas in the new AREAS file.
411 entry number area lnum cur-prefix file) 395 (while (setq area (pop areas))
412 ;; Go through all areas in the new AREAS file. 396 ;; Change the name to the permanent name and move the files.
413 (while (setq area (pop areas)) 397 (setq cur-prefix (nnsoup-next-prefix))
414 ;; Change the name to the permanent name and move the files. 398 (message "Incorporating file %s..." cur-prefix)
415 (setq cur-prefix (nnsoup-next-prefix)) 399 (when (file-exists-p
416 (message "Incorporating file %s..." cur-prefix) 400 (setq file (concat nnsoup-tmp-directory
417 (when (file-exists-p 401 (gnus-soup-area-prefix area) ".IDX")))
418 (setq file (concat nnsoup-tmp-directory 402 (rename-file file (nnsoup-file cur-prefix)))
419 (gnus-soup-area-prefix area) ".IDX"))) 403 (when (file-exists-p
420 (rename-file file (nnsoup-file cur-prefix))) 404 (setq file (concat nnsoup-tmp-directory
421 (when (file-exists-p 405 (gnus-soup-area-prefix area) ".MSG")))
422 (setq file (concat nnsoup-tmp-directory 406 (rename-file file (nnsoup-file cur-prefix t))
423 (gnus-soup-area-prefix area) ".MSG"))) 407 (gnus-soup-set-area-prefix area cur-prefix)
424 (rename-file file (nnsoup-file cur-prefix t)) 408 ;; Find the number of new articles in this area.
425 (gnus-soup-set-area-prefix area cur-prefix) 409 (setq number (nnsoup-number-of-articles area))
426 ;; Find the number of new articles in this area. 410 (if (not (setq entry (assoc (gnus-soup-area-name area)
427 (setq number (nnsoup-number-of-articles area)) 411 nnsoup-group-alist)))
428 (if (not (setq entry (assoc (gnus-soup-area-name area) 412 ;; If this is a new area (group), we just add this info to
429 nnsoup-group-alist))) 413 ;; the group alist.
430 ;; If this is a new area (group), we just add this info to 414 (push (list (gnus-soup-area-name area)
431 ;; the group alist. 415 (cons 1 number)
432 (push (list (gnus-soup-area-name area) 416 (list (cons 1 number) area))
433 (cons 1 number) 417 nnsoup-group-alist)
434 (list (cons 1 number) area)) 418 ;; There are already articles in this group, so we add this
435 nnsoup-group-alist) 419 ;; info to the end of the entry.
436 ;; There are already articles in this group, so we add this 420 (nconc entry (list (list (cons (1+ (setq lnum (cdadr entry)))
437 ;; info to the end of the entry. 421 (+ lnum number))
438 (nconc entry (list (list (cons (1+ (setq lnum (cdadr entry))) 422 area)))
439 (+ lnum number)) 423 (setcdr (cadr entry) (+ lnum number))))))
440 area))) 424 (nnsoup-write-active-file t)
441 (setcdr (cadr entry) (+ lnum number)))))) 425 (delete-file (concat nnsoup-tmp-directory "AREAS"))))
442 (nnsoup-write-active-file t)
443 (delete-file areas-file)))))
444 426
445 (defun nnsoup-number-of-articles (area) 427 (defun nnsoup-number-of-articles (area)
446 (save-excursion 428 (save-excursion
447 (cond 429 (cond
448 ;; If the number is in the area info, we just return it. 430 ;; If the number is in the area info, we just return it.
449 ((gnus-soup-area-number area) 431 ((gnus-soup-area-number area)
450 (gnus-soup-area-number area)) 432 (gnus-soup-area-number area))
451 ;; If there is an index file, we just count the lines. 433 ;; If there is an index file, we just count the lines.
452 ((/= (gnus-soup-encoding-index (gnus-soup-area-encoding area)) ?n) 434 ((/= (gnus-soup-encoding-index (gnus-soup-area-encoding area)) ?n)
453 (set-buffer (nnsoup-index-buffer (gnus-soup-area-prefix area))) 435 (set-buffer (nnsoup-index-buffer (gnus-soup-area-prefix area)))
454 (count-lines (point-min) (point-max))) 436 (count-lines (point-min) (point-max)))
455 ;; We do it the hard way - re-searching through the message 437 ;; We do it the hard way - re-searching through the message
456 ;; buffer. 438 ;; buffer.
457 (t 439 (t
458 (set-buffer (nnsoup-message-buffer (gnus-soup-area-prefix area))) 440 (set-buffer (nnsoup-message-buffer (gnus-soup-area-prefix area)))
459 (unless (assoc (gnus-soup-area-prefix area) nnsoup-article-alist) 441 (goto-char (point-min))
460 (nnsoup-dissect-buffer area)) 442 (let ((regexp (nnsoup-header (gnus-soup-encoding-format
461 (length (cdr (assoc (gnus-soup-area-prefix area) 443 (gnus-soup-area-encoding area))))
462 nnsoup-article-alist))))))) 444 (num 0))
463 445 (while (re-search-forward regexp nil t)
464 (defun nnsoup-dissect-buffer (area) 446 (setq num (1+ num)))
465 (let ((mbox-delim (concat "^" message-unix-mail-delimiter)) 447 num)))))
466 (format (gnus-soup-encoding-format (gnus-soup-area-encoding area)))
467 (i 0)
468 alist len)
469 (goto-char (point-min))
470 (cond
471 ;; rnews batch format
472 ((= format ?n)
473 (while (looking-at "^#! *rnews \\(+[0-9]+\\) *$")
474 (forward-line 1)
475 (push (list
476 (incf i) (point)
477 (progn
478 (forward-char (string-to-number (match-string 1)))
479 (point)))
480 alist)))
481 ;; Unix mbox format
482 ((= format ?m)
483 (while (looking-at mbox-delim)
484 (forward-line 1)
485 (push (list
486 (incf i) (point)
487 (progn
488 (if (re-search-forward mbox-delim nil t)
489 (beginning-of-line)
490 (goto-char (point-max)))
491 (point)))
492 alist)))
493 ;; MMDF format
494 ((= format ?M)
495 (while (looking-at "\^A\^A\^A\^A\n")
496 (forward-line 1)
497 (push (list
498 (incf i) (point)
499 (progn
500 (if (search-forward "\n\^A\^A\^A\^A\n" nil t)
501 (beginning-of-line)
502 (goto-char (point-max)))
503 (point)))
504 alist)))
505 ;; Binary format
506 ((or (= format ?B) (= format ?b))
507 (while (not (eobp))
508 (setq len (+ (* (char-after (point)) (expt 2.0 24))
509 (* (char-after (+ (point) 1)) (expt 2 16))
510 (* (char-after (+ (point) 2)) (expt 2 8))
511 (char-after (+ (point) 3))))
512 (push (list
513 (incf i) (+ (point) 4)
514 (progn
515 (forward-char (floor (+ len 4)))
516 (point)))
517 alist)))
518 (t
519 (error "Unknown format: %c" format)))
520 (push (cons (gnus-soup-area-prefix area) alist) nnsoup-article-alist)))
521 448
522 (defun nnsoup-index-buffer (prefix &optional message) 449 (defun nnsoup-index-buffer (prefix &optional message)
523 (let* ((file (concat prefix (if message ".MSG" ".IDX"))) 450 (let* ((file (concat prefix (if message ".MSG" ".IDX")))
524 (buffer-name (concat " *nnsoup " file "*"))) 451 (buffer-name (concat " *nnsoup " file "*")))
525 (or (get-buffer buffer-name) ; File already loaded. 452 (or (get-buffer buffer-name) ; File aready loaded.
526 (when (file-exists-p (concat nnsoup-directory file)) 453 (when (file-exists-p (concat nnsoup-directory file))
527 (save-excursion ; Load the file. 454 (save-excursion ; Load the file.
528 (set-buffer (get-buffer-create buffer-name)) 455 (set-buffer (get-buffer-create buffer-name))
529 (buffer-disable-undo (current-buffer)) 456 (buffer-disable-undo (current-buffer))
530 (push (cons nnsoup-current-group (current-buffer)) nnsoup-buffers) 457 (push (cons nnsoup-current-group (current-buffer)) nnsoup-buffers)
531 (nnheader-insert-file-contents (concat nnsoup-directory file)) 458 (insert-file-contents (concat nnsoup-directory file))
532 (current-buffer)))))) 459 (current-buffer))))))
533 460
534 (defun nnsoup-file (prefix &optional message) 461 (defun nnsoup-file (prefix &optional message)
535 (expand-file-name 462 (expand-file-name
536 (concat nnsoup-directory prefix (if message ".MSG" ".IDX")))) 463 (concat nnsoup-directory prefix (if message ".MSG" ".IDX"))))
543 (let ((packets (directory-files 470 (let ((packets (directory-files
544 nnsoup-packet-directory t nnsoup-packet-regexp)) 471 nnsoup-packet-directory t nnsoup-packet-regexp))
545 packet) 472 packet)
546 (while (setq packet (pop packets)) 473 (while (setq packet (pop packets))
547 (message "nnsoup: unpacking %s..." packet) 474 (message "nnsoup: unpacking %s..." packet)
548 (if (not (gnus-soup-unpack-packet 475 (if (not (gnus-soup-unpack-packet
549 nnsoup-tmp-directory nnsoup-unpacker packet)) 476 nnsoup-tmp-directory nnsoup-unpacker packet))
550 (message "Couldn't unpack %s" packet) 477 (message "Couldn't unpack %s" packet)
551 (delete-file packet) 478 (delete-file packet)
552 (nnsoup-read-areas) 479 (nnsoup-read-areas)
553 (message "Unpacking...done"))))) 480 (message "Unpacking...done")))))
561 (save-excursion 488 (save-excursion
562 (cond 489 (cond
563 ;; There is no MSG file. 490 ;; There is no MSG file.
564 ((null msg-buf) 491 ((null msg-buf)
565 nil) 492 nil)
566 ;; We use the index file to find out where the article 493
567 ;; begins and ends. 494 ;; We use the index file to find out where the article begins and ends.
568 ((and (= (gnus-soup-encoding-index 495 ((and (= (gnus-soup-encoding-index
569 (gnus-soup-area-encoding (nth 1 area))) 496 (gnus-soup-area-encoding (nth 1 area)))
570 ?c) 497 ?c)
571 (file-exists-p (nnsoup-file prefix))) 498 (file-exists-p (nnsoup-file prefix)))
572 (set-buffer (nnsoup-index-buffer prefix)) 499 (set-buffer (nnsoup-index-buffer prefix))
573 (widen) 500 (widen)
581 (set-buffer msg-buf) 508 (set-buffer msg-buf)
582 (widen) 509 (widen)
583 (let ((format (gnus-soup-encoding-format 510 (let ((format (gnus-soup-encoding-format
584 (gnus-soup-area-encoding (nth 1 area))))) 511 (gnus-soup-area-encoding (nth 1 area)))))
585 (goto-char end) 512 (goto-char end)
586 (when (or (= format ?n) (= format ?m)) 513 (if (or (= format ?n) (= format ?m))
587 (setq end (progn (forward-line -1) (point)))))) 514 (setq end (progn (forward-line -1) (point))))))
588 (set-buffer msg-buf)) 515 (set-buffer msg-buf))
589 (widen) 516 (widen)
590 (narrow-to-region beg (or end (point-max)))) 517 (narrow-to-region beg (or end (point-max))))
591 (t 518 (t
592 (set-buffer msg-buf) 519 (set-buffer msg-buf)
593 (widen) 520 (widen)
594 (unless (assoc (gnus-soup-area-prefix (nth 1 area)) 521 (goto-char (point-min))
595 nnsoup-article-alist) 522 (let ((header (nnsoup-header
596 (nnsoup-dissect-buffer (nth 1 area))) 523 (gnus-soup-encoding-format
597 (let ((entry (assq article (cdr (assoc (gnus-soup-area-prefix 524 (gnus-soup-area-encoding (nth 1 area))))))
598 (nth 1 area)) 525 (re-search-forward header nil t (- article (caar area)))
599 nnsoup-article-alist))))) 526 (narrow-to-region
600 (when entry 527 (match-beginning 0)
601 (narrow-to-region (cadr entry) (caddr entry)))))) 528 (if (re-search-forward header nil t)
529 (match-beginning 0)
530 (point-max))))))
602 (goto-char (point-min)) 531 (goto-char (point-min))
603 (if (not head) 532 (if (not head)
604 () 533 ()
605 (narrow-to-region 534 (narrow-to-region
606 (point-min) 535 (point-min)
607 (if (search-forward "\n\n" nil t) 536 (if (search-forward "\n\n" nil t)
608 (1- (point)) 537 (1- (point))
609 (point-max)))) 538 (point-max))))
610 msg-buf)))) 539 msg-buf))))
611 540
541 (defun nnsoup-header (format)
542 (cond
543 ((= format ?n)
544 "^#! *rnews +[0-9]+ *$")
545 ((= format ?m)
546 (concat "^" message-unix-mail-delimiter))
547 ((= format ?M)
548 "^\^A\^A\^A\^A\n")
549 (t
550 (error "Unknown format: %c" format))))
551
612 ;;;###autoload 552 ;;;###autoload
613 (defun nnsoup-pack-replies () 553 (defun nnsoup-pack-replies ()
614 "Make an outbound package of SOUP replies." 554 "Make an outbound package of SOUP replies."
615 (interactive) 555 (interactive)
616 (unless (file-exists-p nnsoup-replies-directory)
617 (message "No such directory: %s" nnsoup-replies-directory))
618 ;; Write all data buffers. 556 ;; Write all data buffers.
619 (gnus-soup-save-areas) 557 (gnus-soup-save-areas)
620 ;; Write the active file. 558 ;; Write the active file.
621 (nnsoup-write-active-file) 559 (nnsoup-write-active-file)
622 ;; Write the REPLIES file. 560 ;; Write the REPLIES file.
623 (nnsoup-write-replies) 561 (nnsoup-write-replies)
624 ;; Check whether there is anything here.
625 (when (null (directory-files nnsoup-replies-directory nil "\\.MSG$"))
626 (error "No files to pack."))
627 ;; Pack all these files into a SOUP packet. 562 ;; Pack all these files into a SOUP packet.
628 (gnus-soup-pack nnsoup-replies-directory nnsoup-packer)) 563 (gnus-soup-pack nnsoup-replies-directory nnsoup-packer))
629 564
630 (defun nnsoup-write-replies () 565 (defun nnsoup-write-replies ()
631 "Write the REPLIES file." 566 "Write the REPLIES file."
660 (defun nnsoup-store-reply (kind) 595 (defun nnsoup-store-reply (kind)
661 ;; Mostly stolen from `message.el'. 596 ;; Mostly stolen from `message.el'.
662 (require 'mail-utils) 597 (require 'mail-utils)
663 (let ((tembuf (generate-new-buffer " message temp")) 598 (let ((tembuf (generate-new-buffer " message temp"))
664 (case-fold-search nil) 599 (case-fold-search nil)
600 (news (message-news-p))
601 (resend-to-addresses (mail-fetch-field "resent-to"))
665 delimline 602 delimline
666 (mailbuf (current-buffer))) 603 (mailbuf (current-buffer)))
667 (unwind-protect 604 (unwind-protect
668 (save-excursion 605 (save-excursion
669 (save-restriction 606 (save-restriction
681 (message-remove-header message-ignored-mail-headers t)) 618 (message-remove-header message-ignored-mail-headers t))
682 (goto-char (point-max)) 619 (goto-char (point-max))
683 ;; require one newline at the end. 620 ;; require one newline at the end.
684 (or (= (preceding-char) ?\n) 621 (or (= (preceding-char) ?\n)
685 (insert ?\n)) 622 (insert ?\n))
623 (when (and news
624 (equal kind "mail")
625 (or (mail-fetch-field "cc")
626 (mail-fetch-field "to")))
627 (message-insert-courtesy-copy))
686 (let ((case-fold-search t)) 628 (let ((case-fold-search t))
687 ;; Change header-delimiter to be what sendmail expects. 629 ;; Change header-delimiter to be what sendmail expects.
688 (goto-char (point-min)) 630 (goto-char (point-min))
689 (re-search-forward 631 (re-search-forward
690 (concat "^" (regexp-quote mail-header-separator) "\n")) 632 (concat "^" (regexp-quote mail-header-separator) "\n"))
695 ;; Sun's bug that swallows newlines. 637 ;; Sun's bug that swallows newlines.
696 (goto-char (1+ delimline)) 638 (goto-char (1+ delimline))
697 (when (eval message-mailer-swallows-blank-line) 639 (when (eval message-mailer-swallows-blank-line)
698 (newline)) 640 (newline))
699 (let ((msg-buf 641 (let ((msg-buf
700 (gnus-soup-store 642 (gnus-soup-store
701 nnsoup-replies-directory 643 nnsoup-replies-directory
702 (nnsoup-kind-to-prefix kind) nil nnsoup-replies-format-type 644 (nnsoup-kind-to-prefix kind) nil nnsoup-replies-format-type
703 nnsoup-replies-index-type)) 645 nnsoup-replies-index-type))
704 (num 0)) 646 (num 0))
705 (when (and msg-buf (bufferp msg-buf)) 647 (when (and msg-buf (bufferp msg-buf))
706 (save-excursion 648 (save-excursion
713 (kill-buffer tembuf)))))) 655 (kill-buffer tembuf))))))
714 656
715 (defun nnsoup-kind-to-prefix (kind) 657 (defun nnsoup-kind-to-prefix (kind)
716 (unless nnsoup-replies-list 658 (unless nnsoup-replies-list
717 (setq nnsoup-replies-list 659 (setq nnsoup-replies-list
718 (gnus-soup-parse-replies 660 (gnus-soup-parse-replies
719 (concat nnsoup-replies-directory "REPLIES")))) 661 (concat nnsoup-replies-directory "REPLIES"))))
720 (let ((replies nnsoup-replies-list)) 662 (let ((replies nnsoup-replies-list))
721 (while (and replies 663 (while (and replies
722 (not (string= kind (gnus-soup-reply-kind (car replies))))) 664 (not (string= kind (gnus-soup-reply-kind (car replies)))))
723 (setq replies (cdr replies))) 665 (setq replies (cdr replies)))
724 (if replies 666 (if replies
725 (gnus-soup-reply-prefix (car replies)) 667 (gnus-soup-reply-prefix (car replies))
726 (push (vector (gnus-soup-unique-prefix nnsoup-replies-directory) 668 (setq nnsoup-replies-list
727 kind 669 (cons (vector (gnus-soup-unique-prefix nnsoup-replies-directory)
728 (format "%c%c%c" 670 kind
729 nnsoup-replies-format-type 671 (format "%c%c%c"
730 nnsoup-replies-index-type 672 nnsoup-replies-format-type
731 (if (string= kind "news") 673 nnsoup-replies-index-type
732 ?n ?m))) 674 (if (string= kind "news")
733 nnsoup-replies-list) 675 ?n ?m)))
676 nnsoup-replies-list))
734 (gnus-soup-reply-prefix (car nnsoup-replies-list))))) 677 (gnus-soup-reply-prefix (car nnsoup-replies-list)))))
735 678
736 (defun nnsoup-make-active () 679 (defun nnsoup-make-active ()
737 "(Re-)create the SOUP active file." 680 "(Re-)create the SOUP active file."
738 (interactive) 681 (interactive)
746 (set-buffer (get-buffer-create " *nnsoup work*")) 689 (set-buffer (get-buffer-create " *nnsoup work*"))
747 (buffer-disable-undo (current-buffer)) 690 (buffer-disable-undo (current-buffer))
748 (while files 691 (while files
749 (message "Doing %s..." (car files)) 692 (message "Doing %s..." (car files))
750 (erase-buffer) 693 (erase-buffer)
751 (nnheader-insert-file-contents (car files)) 694 (insert-file-contents (car files))
752 (goto-char (point-min)) 695 (goto-char (point-min))
753 (if (not (re-search-forward "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t *\\(Xref: \\)? *[^ ]* \\([^ ]+\\):[0-9]" nil t)) 696 (if (not (re-search-forward "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t *\\(Xref: \\)? *[^ ]* \\([^ ]+\\):[0-9]" nil t))
754 (setq group "unknown") 697 (setq group "unknown")
755 (setq group (match-string 2))) 698 (setq group (match-string 2)))
756 (setq lines (count-lines (point-min) (point-max))) 699 (setq lines (count-lines (point-min) (point-max)))
757 (setq ident (progn (string-match 700 (setq ident (progn (string-match
758 "/\\([0-9]+\\)\\." (car files)) 701 "/\\([0-9]+\\)\\." (car files))
759 (substring 702 (substring
760 (car files) (match-beginning 1) 703 (car files) (match-beginning 1)
761 (match-end 1)))) 704 (match-end 1))))
762 (if (not (setq elem (assoc group active))) 705 (if (not (setq elem (assoc group active)))
763 (push (list group (cons 1 lines) 706 (push (list group (cons 1 lines)
764 (list (cons 1 lines) 707 (list (cons 1 lines)
765 (vector ident group "ncm" "" lines))) 708 (vector ident group "ncm" "" lines)))
766 active) 709 active)
767 (nconc elem 710 (nconc elem
768 (list 711 (list
769 (list (cons (1+ (setq min (cdadr elem))) 712 (list (cons (1+ (setq min (cdadr elem)))
776 (nnsoup-write-active-file t))) 719 (nnsoup-write-active-file t)))
777 720
778 (defun nnsoup-delete-unreferenced-message-files () 721 (defun nnsoup-delete-unreferenced-message-files ()
779 "Delete any *.MSG and *.IDX files that aren't known by nnsoup." 722 "Delete any *.MSG and *.IDX files that aren't known by nnsoup."
780 (interactive) 723 (interactive)
781 (let* ((known (apply 'nconc (mapcar 724 (let* ((known (apply 'nconc (mapcar
782 (lambda (ga) 725 (lambda (ga)
783 (mapcar 726 (mapcar
784 (lambda (area) 727 (lambda (area)
785 (gnus-soup-area-prefix (cadr area))) 728 (gnus-soup-area-prefix (cadr area)))
786 (cddr ga))) 729 (cddr ga)))