Sync with wl-2_4.
[elisp/wanderlust.git] / wl / wl-message.el
1 ;;; wl-message.el -- Message displaying modules for Wanderlust.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
9
10 ;; This program 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 ;; This program 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
26 ;;; Commentary:
27 ;; 
28
29 ;;; Code:
30 ;; 
31
32 (require 'wl-vars)
33 (require 'wl-highlight)
34
35 (eval-when-compile
36   (if wl-use-semi
37       (progn
38         (require 'wl-mime)
39         (require 'mime-view)
40         (require 'mmelmo-imap4))
41     (require 'tm-wl))
42   (defalias-maybe 'event-window 'ignore)
43   (defalias-maybe 'posn-window 'ignore)
44   (defalias-maybe 'event-start 'ignore)
45   (defalias-maybe 'mime-open-entity 'ignore))
46
47 (defvar wl-original-buf-name "*Message*")
48 (defvar wl-message-buf-name "Message")
49 (defvar wl-message-buffer-cur-summary-buffer nil)
50 (defvar wl-message-buffer-cur-folder nil)
51 (defvar wl-message-buffer-cur-number nil)
52
53 (defvar wl-original-buffer-cur-folder nil)
54 (defvar wl-original-buffer-cur-number nil)
55 (defvar wl-original-buffer-cur-msgdb  nil)
56
57 (defvar mmelmo-imap4-skipped-parts)
58
59 (make-variable-buffer-local 'wl-message-buffer-cur-folder)
60 (make-variable-buffer-local 'wl-message-buffer-cur-number)
61
62 (defvar wl-fixed-window-configuration nil)
63
64 (defun wl-message-buffer-window ()
65   (let* ((mes-buf (concat "^" (default-value 'wl-message-buf-name)))
66          (start-win (selected-window))
67          (cur-win start-win))
68     (catch 'found
69       (while (progn
70                (setq cur-win (next-window cur-win))
71                (if (string-match mes-buf (buffer-name (window-buffer cur-win)))
72                    (throw 'found cur-win))
73                (not (eq cur-win start-win)))))))
74
75 (defun wl-select-buffer (buffer)
76   (let ((gbw (or (get-buffer-window buffer)
77                  (wl-message-buffer-window)))
78         (sum (car wl-message-window-size))
79         (mes (cdr wl-message-window-size))
80         whi)
81     (when (and gbw
82                (not (eq (save-excursion (set-buffer (window-buffer gbw))
83                                         wl-message-buffer-cur-summary-buffer)
84                         (current-buffer))))
85       (delete-window gbw)
86       (run-hooks 'wl-message-window-deleted-hook)
87       (setq gbw nil))
88     (if gbw
89         (select-window gbw)
90 ;;;   (if (or (null mes)
91 ;;;           wl-stay-folder-window)
92 ;;;       (delete-other-windows))
93       (when wl-fixed-window-configuration
94         (delete-other-windows)
95         (and wl-stay-folder-window
96              (wl-summary-toggle-disp-folder)))
97       (setq whi (1- (window-height)))
98       (if mes
99           (progn
100             (let ((total (+ sum mes)))
101               (setq sum (max window-min-height (/ (* whi sum) total)))
102               (setq mes (max window-min-height (/ (* whi mes) total))))
103             (if (< whi (+ sum mes))
104                 (enlarge-window (- (+ sum mes) whi)))))
105       (split-window (get-buffer-window (current-buffer)) sum)
106       (other-window 1))
107     (switch-to-buffer buffer)))
108
109 ;;
110 ;; called by wl-summary-mode buffer
111 ;;
112 (defvar wl-message-func-called-hook nil)
113
114 (defun wl-message-scroll-down (amount)
115   (let ((view-message-buffer (get-buffer-create wl-message-buf-name))
116         (cur-buf (current-buffer)))
117     (wl-select-buffer view-message-buffer)
118     (if (bobp)
119         ()
120       (scroll-down))
121     (select-window (get-buffer-window cur-buf))))
122
123 (defun wl-message-scroll-up (amount)
124   (let ((view-message-buffer (get-buffer-create wl-message-buf-name))
125         (cur-buf (current-buffer)))
126     (wl-select-buffer view-message-buffer)
127     (save-excursion
128       (save-restriction
129         (widen)
130         (forward-page 1)
131         (if (pos-visible-in-window-p (point))
132             (wl-message-narrow-to-page 1)))) ; Go to next page.
133     (if (eobp)
134         ()
135       (scroll-up))
136     (select-window (get-buffer-window cur-buf))))
137   
138 (defun wl-message-follow-current-entity (buffer)
139   "Follow to current message."
140   (wl-draft-reply (wl-message-get-original-buffer)
141                   nil wl-message-buffer-cur-summary-buffer) ; reply to all
142   (let ((mail-reply-buffer buffer))
143     (wl-draft-yank-from-mail-reply-buffer nil)))
144
145 (defun wl-message-original-mode ()
146   (setq major-mode 'wl-message-original-mode)
147   (setq mode-name "Original")
148   (setq buffer-read-only t)
149   (if (fboundp 'set-buffer-file-coding-system)
150       (set-buffer-file-coding-system wl-cs-noconv)))
151
152 (defun wl-message-mode ()
153   (interactive)
154   (setq major-mode 'wl-message-mode)
155   (setq buffer-read-only t)
156   (setq mode-name "Message"))
157
158 (defun wl-message-get-buffer-create ()
159   (let ((buf-name wl-message-buf-name))
160     (or (get-buffer buf-name)
161         (save-excursion
162           (set-buffer (get-buffer-create buf-name))
163           (wl-message-mode)
164           (run-hooks 'wl-message-buffer-created-hook)
165           (get-buffer buf-name)))))
166
167 (defun wl-message-original-get-buffer-create ()
168   (or (get-buffer wl-original-buf-name)
169       (save-excursion
170         (set-buffer (get-buffer-create wl-original-buf-name))
171         (wl-message-original-mode)
172         (get-buffer wl-original-buf-name))))
173   
174 (defun wl-message-exit ()
175   (interactive)
176   (let (summary-buf summary-win)
177     (if (setq summary-buf wl-message-buffer-cur-summary-buffer)
178         (if (setq summary-win (get-buffer-window summary-buf))
179             (select-window summary-win)
180           (switch-to-buffer summary-buf)
181           (wl-select-buffer wl-message-buf-name)
182           (select-window (get-buffer-window summary-buf))))
183     (run-hooks 'wl-message-exit-hook)))
184
185 (defvar wl-message-mode-map nil)
186 (if wl-message-mode-map
187     ()
188   (setq wl-message-mode-map (make-sparse-keymap))
189   (define-key wl-message-mode-map "q" 'wl-message-exit)
190   (define-key wl-message-mode-map "n" 'wl-message-exit)
191   (define-key wl-message-mode-map "p" 'wl-message-exit))
192
193 (defun wl-message-decode (outbuf inbuf flag)
194   (cond
195    ((eq flag 'all-header)
196     (save-excursion
197       (set-buffer inbuf)
198       (let ((buffer-read-only nil))
199         (decode-mime-charset-region (point-min)
200                                     (save-excursion
201                                       (goto-char (point-min))
202                                       (re-search-forward "^$" nil t)
203                                       (point))
204                                     wl-mime-charset)))
205     (wl-message-decode-with-all-header outbuf inbuf))
206    ((eq flag 'no-mime)
207     (save-excursion
208       (set-buffer inbuf)
209       (let ((buffer-read-only nil))
210         (save-excursion
211           (set-buffer outbuf)
212           (elmo-set-buffer-multibyte nil))
213         (copy-to-buffer outbuf (point-min) (point-max))
214         (set-buffer outbuf)
215         (use-local-map wl-message-mode-map)
216         (elmo-set-buffer-multibyte default-enable-multibyte-characters)
217 ;;;     (decode-mime-charset-region (point-min) (point-max) wl-mime-charset)
218         ;; we can call decode-coding-region() directly, because multibyte flag is t.
219         (decode-coding-region (point-min) (point-max) wl-cs-autoconv)
220         (wl-highlight-message (point-min)
221                               (save-excursion
222                                 (goto-char (point-min))
223                                 (re-search-forward "^$" nil t)) nil))))
224    (t                                   ; normal
225     (save-excursion
226       (set-buffer inbuf)
227       (let ((buffer-read-only nil))
228         (decode-mime-charset-region (point-min)
229                                     (save-excursion
230                                       (goto-char (point-min))
231                                       (re-search-forward "^$" nil t)
232                                       (point))
233                                     wl-mime-charset)))
234     (wl-message-decode-mode outbuf inbuf))))
235
236 (defun wl-message-prev-page (&optional lines)
237   "Scroll down this message.  Returns non-nil if top of message."
238   (interactive)
239   (let ((cur-buf (current-buffer))
240         (view-message-buffer (get-buffer-create wl-message-buf-name))
241         ret-val)
242     (wl-select-buffer view-message-buffer)
243     (move-to-window-line 0)
244     (if (and wl-break-pages
245              (bobp)
246              (not (save-restriction (widen) (bobp))))
247         (progn
248           (wl-message-narrow-to-page -1)
249           (goto-char (point-max))
250           (recenter -1))
251       (if (not (bobp))
252           (scroll-down lines)
253         (setq ret-val t)))
254     (select-window (get-buffer-window cur-buf))
255     ret-val))
256
257 (static-if (fboundp 'luna-make-entity)
258     (defsubst wl-message-make-mime-entity (backend number backend folder msgdb)
259       (luna-make-entity (mm-expand-class-name 'elmo)
260                         :location (get-buffer-create
261                                    (concat mmelmo-entity-buffer-name "0"))
262                         :imap (eq backend 'elmo-imap4)
263                         :folder folder
264                         :number number
265                         :msgdb msgdb :size 0))
266   (defsubst wl-message-make-mime-entity (backend number backend folder msgdb)
267     (mime-open-entity backend (list folder number msgdb nil))))
268
269 (defun wl-message-next-page (&optional lines)
270   "Scroll up this message.  Returns non-nil if bottom of message."
271   (interactive)
272   (let ((cur-buf (current-buffer))
273         (view-message-buffer (get-buffer-create wl-message-buf-name))
274         ret-val)
275     (wl-select-buffer view-message-buffer)
276     (move-to-window-line -1)
277     (if (save-excursion
278           (end-of-line)
279           (and (pos-visible-in-window-p)
280                (eobp)))
281         (if (or (null wl-break-pages)
282                 (save-excursion
283                   (save-restriction
284                     (widen) (forward-line) (eobp))))
285             (setq ret-val t)
286           (wl-message-narrow-to-page 1)
287           (setq ret-val nil))
288       (condition-case ()
289           (scroll-up lines)
290         (end-of-buffer
291          (goto-char (point-max))))
292       (setq ret-val nil))
293     (select-window (get-buffer-window cur-buf))
294     ret-val
295     ))
296
297 (defun wl-message-narrow-to-page (&optional arg)
298   (interactive "P")
299   (setq arg (if arg (prefix-numeric-value arg) 0))
300   (save-excursion
301     (condition-case ()
302         (forward-page -1)               ; Beginning of current page.
303       (beginning-of-buffer
304        (goto-char (point-min))))
305     (forward-char 1)  ; for compatibility with emacs-19.28 and emacs-19.29
306     (widen)
307     (cond
308      ((> arg 0) (forward-page arg))
309      ((< arg 0) (forward-page (1- arg))))
310     (forward-page)
311     (if wl-break-pages
312         (narrow-to-region (point)
313                           (progn
314                             (forward-page -1)
315                             (if (and (eolp) (not (bobp)))
316                                 (forward-line))
317                             (point)))) ))
318
319 (defun wl-message-toggle-disp-summary ()
320   (interactive)
321   (let ((summary-buf (get-buffer wl-message-buffer-cur-summary-buffer))
322         summary-win)
323     (if (and summary-buf
324              (buffer-live-p summary-buf))
325         (if (setq summary-win (get-buffer-window summary-buf))
326             (delete-window summary-win)
327           (switch-to-buffer summary-buf)
328           (wl-select-buffer wl-message-buf-name))
329       (wl-summary-goto-folder-subr wl-message-buffer-cur-folder 'no-sync
330                                    nil nil t)
331                                         ; no summary-buf
332       (let ((sum-buf (current-buffer)))
333         (wl-select-buffer wl-message-buf-name)
334         (setq wl-message-buffer-cur-summary-buffer sum-buf)))))
335
336 (defun wl-message-normal-get-original-buffer ()
337   (let ((ret-val (get-buffer wl-original-buf-name)))
338     (if (not ret-val)
339         (save-excursion
340           (set-buffer (setq ret-val
341                             (get-buffer-create wl-original-buf-name)))
342           (wl-message-original-mode)))
343     ret-val))
344
345
346 (if wl-use-semi
347     (defalias 'wl-message-get-original-buffer
348       'mmelmo-get-original-buffer)
349   (defalias 'wl-message-get-original-buffer
350     'wl-message-normal-get-original-buffer))
351
352 (defvar wl-message-redisplay-func 'wl-normal-message-redisplay)
353 (defvar wl-message-cache-used nil) ;whether cache is used or not.
354
355 (defun wl-message-redisplay (folder number flag msgdb &optional force-reload)
356   (let ((default-mime-charset wl-mime-charset)
357         (buffer-read-only nil))
358     (setq wl-message-cache-used nil)
359     (if wl-message-redisplay-func
360         (funcall wl-message-redisplay-func
361                  folder number flag msgdb force-reload))))
362
363 ;; nil means don't fetch all.
364 (defun wl-message-decide-backend (folder number message-id size)
365   (let ((dont-do-that (and
366                        (not (setq wl-message-cache-used
367                                   (or
368                                    (elmo-buffer-cache-hit
369                                     (list folder number message-id))
370                                    (elmo-cache-exists-p message-id
371                                                         folder number))))
372                        (integerp size)
373                        (not (elmo-local-file-p folder number))
374                        wl-fetch-confirm-threshold
375                        (>= size wl-fetch-confirm-threshold)
376                        (not (y-or-n-p
377                              (format "Fetch entire message? (%dbytes)"
378                                      size))))))
379     (message "")
380     (cond ((and dont-do-that
381                 (eq (elmo-folder-number-get-type folder number) 'imap4)
382                 (not (and (elmo-use-cache-p folder number)
383                           (elmo-cache-exists-p message-id folder number))))
384            'elmo-imap4)
385           (t (if (not dont-do-that) 'elmo)))))
386
387 (defmacro wl-message-original-buffer-folder ()
388   wl-original-buffer-cur-folder)
389
390 (defmacro wl-message-original-buffer-number ()
391   wl-original-buffer-cur-number)
392
393 (defun wl-message-set-original-buffer-information (folder number)
394   (when (or (not (string= folder (or wl-original-buffer-cur-folder "")))
395             (not (eq number (or wl-original-buffer-cur-number 0))))
396     (setq wl-original-buffer-cur-folder folder)
397     (setq wl-original-buffer-cur-number number)))
398
399 ;; Works on FLIM-1.9.0/SEMI-1.8.2 or later (maybe).
400 (defun wl-mmelmo-message-redisplay (folder number flag msgdb
401                                            &optional force-reload)
402   (let* ((cur-buf (current-buffer))
403          (view-message-buffer (wl-message-get-buffer-create))
404          (message-id (cdr (assq number
405                                 (elmo-msgdb-get-number-alist msgdb))))
406          (size (elmo-msgdb-overview-entity-get-size
407                 (elmo-msgdb-overview-get-entity number msgdb)))
408          (backend (wl-message-decide-backend folder number message-id size))
409          cur-entity ret-val header-end real-fld-num summary-win)
410     (require 'mmelmo)
411     (wl-select-buffer view-message-buffer)
412     (set-buffer view-message-buffer)
413     (unwind-protect
414         (progn
415           (setq wl-message-buffer-cur-summary-buffer cur-buf)
416           (setq wl-message-buffer-cur-folder folder)
417           (setq wl-message-buffer-cur-number number)
418           (setq buffer-read-only nil)
419           (erase-buffer)
420           (if backend
421               (let (mime-display-header-hook ;; bind to nil...
422                     (wl-message-ignored-field-list
423                      (if (eq flag 'all-header)
424                          nil
425                        wl-message-ignored-field-list))
426                     (mmelmo-force-reload force-reload)
427                     (mmelmo-imap4-threshold wl-fetch-confirm-threshold))
428                 (setq real-fld-num (elmo-get-real-folder-number
429                                     folder number))
430                 (setq cur-entity
431                       (wl-message-make-mime-entity
432                        backend
433                        (if (eq backend 'elmo-imap4)
434                            (cdr real-fld-num)
435                          number)
436                        backend
437                        (if (eq backend 'elmo-imap4)
438                            (car real-fld-num)
439                          folder)
440                        msgdb))
441                 (setq mmelmo-imap4-skipped-parts nil)
442                 ;; mime-display-message sets buffer-read-only variable as t.
443                 ;; which makes buffer read-only status confused...
444                 (mime-display-message cur-entity view-message-buffer
445                                       nil nil 'mmelmo-original-mode)
446                 (if mmelmo-imap4-skipped-parts
447                     (progn
448                       (message "Skipped fetching of %s."
449                                (mapconcat
450                                 (lambda (x)
451                                   (format "[%s]" x))
452                                 mmelmo-imap4-skipped-parts ","))))
453                 (if (and (eq backend 'elmo-imap4)
454                          (null mmelmo-imap4-skipped-parts))
455                     (message "No required part was skipped."))
456                 (setq ret-val (not (eq backend 'elmo-imap4))))
457             (message "Skipped fetching.")
458             (setq ret-val nil)))
459       (setq buffer-read-only nil)
460       (wl-message-set-original-buffer-information folder number)
461       (wl-message-overload-functions)
462       ;; highlight body
463       (when wl-highlight-body-too
464         (wl-highlight-body))
465       (condition-case ()
466           (wl-message-narrow-to-page)
467         (error nil));; ignore errors.
468       (setq mode-line-buffer-identification
469             (format "Wanderlust: << %s / %s >>"
470                     (if (memq 'modeline wl-use-folder-petname)
471                         (wl-folder-get-petname folder)
472                       folder) number))
473       (goto-char (point-min))
474       (unwind-protect
475           (save-excursion
476             (run-hooks 'wl-message-redisplay-hook))
477         ;; go back to summary mode
478         (set-buffer-modified-p nil)
479         (setq buffer-read-only t)
480         (set-buffer cur-buf)
481         (setq summary-win (get-buffer-window cur-buf))
482         (if (window-live-p summary-win)
483             (select-window summary-win))))
484     ret-val
485     ))
486
487 (defun wl-normal-message-redisplay (folder number flag msgdb
488                                            &optional force-reload)
489   (interactive)
490   (let* ((cur-buf (current-buffer))
491          (original-message-buffer (wl-message-get-original-buffer))
492          (view-message-buffer (wl-message-get-buffer-create))
493          (message-id (cdr (assq number
494                                 (elmo-msgdb-get-number-alist msgdb))))
495          (size (elmo-msgdb-overview-entity-get-size
496                 (elmo-msgdb-overview-get-entity number msgdb)))
497          header-end ret-val summary-win)
498     (wl-select-buffer view-message-buffer)
499     (unwind-protect
500         (progn
501           (setq wl-message-buffer-cur-summary-buffer cur-buf)
502           (setq wl-message-buffer-cur-folder folder)
503           (setq wl-message-buffer-cur-number number)
504           (setq buffer-read-only nil)
505           (erase-buffer)
506           (if (or (eq (elmo-folder-number-get-type folder number) 'localdir)
507                   (not (and (integerp size)
508                             wl-fetch-confirm-threshold
509                             (>= size wl-fetch-confirm-threshold)
510                             (not (elmo-cache-exists-p message-id
511                                                       folder number))
512                             (not (y-or-n-p
513                                   (format "Fetch entire message? (%dbytes)"
514                                           size))))))
515               (progn
516                 (save-excursion
517                   (set-buffer original-message-buffer)
518                   (let ((buffer-read-only nil))
519                     (elmo-read-msg-with-buffer-cache
520                      folder number original-message-buffer msgdb force-reload)))
521                 ;; decode MIME message.
522                 (wl-message-decode
523                  view-message-buffer
524                  original-message-buffer flag)
525                 (setq ret-val t))
526             (save-excursion
527               (set-buffer view-message-buffer)
528               (insert "\n\n"))))
529       (setq buffer-read-only nil)
530       (wl-message-set-original-buffer-information folder number)
531       (wl-message-overload-functions)
532       ;; highlight body
533       (and wl-highlight-body-too (wl-highlight-body))
534       (condition-case ()
535           (wl-message-narrow-to-page)
536         (error nil)) ; ignore errors.
537       (setq mode-line-buffer-identification
538             (format "Wanderlust: << %s / %s >>"
539                     (if (memq 'modeline wl-use-folder-petname)
540                         (wl-folder-get-petname folder)
541                       folder)
542                     number))
543       (goto-char (point-min))
544       (unwind-protect
545           (run-hooks 'wl-message-redisplay-hook)
546         ;; go back to summary mode
547         (set-buffer-modified-p nil)
548         (setq buffer-read-only t)
549         (set-buffer cur-buf)
550         (setq summary-win (get-buffer-window cur-buf))
551         (if (window-live-p summary-win)
552             (select-window summary-win)))
553       ret-val
554       )))
555
556 (defvar wl-message-button-map (make-sparse-keymap))
557
558 (defun wl-message-add-button (from to function &optional data)
559   "Create a button between FROM and TO with callback FUNCTION and DATA."
560   (add-text-properties
561    from to
562    (nconc (list 'wl-message-button-callback function)
563           (if data
564               (list 'wl-message-button-data data))))
565   (let ((ov (make-overlay from to)))
566     (overlay-put ov 'mouse-face 'highlight)
567     (overlay-put ov 'local-map wl-message-button-map)
568     (overlay-put ov 'evaporate t)))
569
570 (defun wl-message-button-dispatcher (event)
571   "Select the button under point."
572   (interactive "@e")
573   (mouse-set-point event)
574   (let ((callback (get-text-property (point) 'wl-message-button-callback))
575         (data (get-text-property (point) 'wl-message-button-data)))
576     (if callback
577         (funcall callback data)
578       (wl-message-button-dispatcher-internal event))))
579
580 (defun wl-message-button-refer-article (data)
581   "Read article specified by Message-ID DATA at point."
582   (switch-to-buffer-other-window
583    wl-message-buffer-cur-summary-buffer)
584   (if (wl-summary-jump-to-msg-by-message-id data)
585       (wl-summary-redisplay)))
586
587 (defun wl-message-refer-article-or-url (e)
588   "Read article specified by message-id around point.
589 If failed, attempt to execute button-dispatcher."
590   (interactive "e")
591   (let ((window (get-buffer-window (current-buffer)))
592         mouse-window point beg end msg-id)
593     (unwind-protect
594         (progn
595           (mouse-set-point e)
596           (setq mouse-window (get-buffer-window (current-buffer)))
597           (setq point (point))
598           (setq beg (save-excursion (beginning-of-line) (point)))
599           (setq end (save-excursion (end-of-line) (point)))
600           (search-forward ">" end t)      ;Move point to end of "<....>".
601           (if (and (re-search-backward "\\(<[^<> \t\n]+@[^<> \t\n]+>\\)"
602                                        beg t)
603                    (not (string-match "mailto:"
604                                       (setq msg-id (wl-match-buffer 1)))))
605               (progn
606                 (goto-char point)
607                 (switch-to-buffer-other-window
608                  wl-message-buffer-cur-summary-buffer)
609                 (if (wl-summary-jump-to-msg-by-message-id msg-id)
610                     (wl-summary-redisplay)))
611             (wl-message-button-dispatcher-internal e)))
612       (if (eq mouse-window (get-buffer-window (current-buffer)))
613           (select-window window)))))
614
615 (defun wl-message-uu-substring (buf outbuf &optional first last)
616   (save-excursion
617     (set-buffer buf)
618     (search-forward "\n\n")
619     (let ((sp (point))
620           ep filename case-fold-search)
621       (catch 'done
622         (if first
623             (progn
624               (if (re-search-forward "^begin[ \t]+[0-9]+[ \t]+\\([^ ].*\\)" nil t)
625                   (setq filename (buffer-substring (match-beginning 1)(match-end 1)))
626                 (throw 'done nil)))
627           (re-search-forward "^M.*$" nil t)) ; uuencoded string
628         (beginning-of-line)
629         (setq sp (point))
630         (goto-char (point-max))
631         (if last
632             (re-search-backward "^end" sp t)
633           (re-search-backward "^M.*$" sp t)) ; uuencoded string
634         (forward-line 1)
635         (setq ep (point))
636         (set-buffer outbuf)
637         (goto-char (point-max))
638         (insert-buffer-substring buf sp ep)
639         (set-buffer buf)
640         filename))))
641
642 (require 'product)
643 (product-provide (provide 'wl-message) (require 'wl-version))
644
645 ;;; wl-message.el ends here