]> code.delx.au - gnu-emacs/blob - lisp/mh-e/mh-comp.el
782537aee2d791f91c3611e8908c997e08018868
[gnu-emacs] / lisp / mh-e / mh-comp.el
1 ;;; mh-comp.el --- MH-E functions for composing and sending messages
2
3 ;; Copyright (C) 1993, 1995, 1997, 2000-2013 Free Software Foundation, Inc.
4
5 ;; Author: Bill Wohler <wohler@newt.com>
6 ;; Maintainer: Bill Wohler <wohler@newt.com>
7 ;; Keywords: mail
8 ;; See: mh-e.el
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24
25 ;;; Commentary:
26
27 ;; This file includes the functions in the MH-Folder maps that get us
28 ;; into MH-Letter mode, as well the functions in the MH-Letter mode
29 ;; that are used to send the mail. Other that those, functions that
30 ;; are needed in mh-letter.el should be found there.
31
32 ;;; Change Log:
33
34 ;;; Code:
35
36 (require 'mh-e)
37 (require 'mh-gnus) ;needed because mh-gnus.el not compiled
38 (require 'mh-scan)
39
40 (require 'sendmail)
41
42 (autoload 'easy-menu-add "easymenu")
43 (autoload 'mml-insert-tag "mml")
44
45 \f
46
47 ;;; Site Customization
48
49 (defvar mh-send-prog "send"
50 "Name of the MH send program.
51 Some sites need to change this because of a name conflict.")
52
53 (defvar mh-send-uses-spost-flag nil
54 "Non-nil means \"send\" uses \"spost\" to submit messages.
55
56 If the value of \"postproc:\" is \"spost\", you may need to set
57 this variable to t to tell MH-E to avoid using features of
58 \"post\" that are not supported by \"spost\". You'll know that
59 you'll need to do this if sending mail fails with an error of
60 \"spost: -msgid unknown\".")
61
62 (defvar mh-redist-background nil
63 "If non-nil redist will be done in background like send.
64 This allows transaction log to be visible if -watch, -verbose or
65 -snoop are used.")
66
67 \f
68
69 ;;; Variables
70
71 (defvar mh-comp-formfile "components"
72 "Name of file to be used as a skeleton for composing messages.
73
74 Default is \"components\".
75
76 If not an absolute file name, the file is searched for first in the
77 user's MH directory, then in the system MH lib directory.")
78
79 (defvar mh-repl-formfile "replcomps"
80 "Name of file to be used as a skeleton for replying to messages.
81
82 Default is \"replcomps\".
83
84 If not an absolute file name, the file is searched for first in the
85 user's MH directory, then in the system MH lib directory.")
86
87 (defvar mh-repl-group-formfile "replgroupcomps"
88 "Name of file to be used as a skeleton for replying to messages.
89
90 Default is \"replgroupcomps\".
91
92 This file is used to form replies to the sender and all recipients of
93 a message. Only used if `(mh-variant-p 'nmh)' is non-nil.
94 If not an absolute file name, the file is searched for first in the
95 user's MH directory, then in the system MH lib directory.")
96
97 (defvar mh-rejected-letter-start
98 (format "^%s$"
99 (regexp-opt
100 '("Content-Type: message/rfc822" ;MIME MDN
101 "------ This is a copy of the message, including all the headers. ------";from exim
102 "--- Below this line is a copy of the message."; from qmail
103 " ----- Unsent message follows -----" ;from sendmail V5
104 " --------Unsent Message below:" ; from sendmail at BU
105 " ----- Original message follows -----" ;from sendmail V8
106 "------- Unsent Draft" ;from MH itself
107 "---------- Original Message ----------" ;from zmailer
108 " --- The unsent message follows ---" ;from AIX mail system
109 " Your message follows:" ;from MMDF-II
110 "Content-Description: Returned Content" ;1993 KJ sendmail
111 ))))
112
113 (defvar mh-new-draft-cleaned-headers
114 "^Date:\\|^Received:\\|^Message-Id:\\|^From:\\|^Sender:\\|^Errors-To:\\|^Delivery-Date:\\|^Return-Path:"
115 "Regexp of header lines to remove before offering a message as a new draft\\<mh-folder-mode-map>.
116 Used by the \\[mh-edit-again] and \\[mh-extract-rejected-mail] commands.")
117
118 (defvar mh-letter-mode-syntax-table
119 (let ((syntax-table (make-syntax-table text-mode-syntax-table)))
120 (modify-syntax-entry ?% "." syntax-table)
121 syntax-table)
122 "Syntax table used by MH-E while in MH-Letter mode.")
123
124 (defvar mh-regexp-in-field-syntax-table nil
125 "Specify a syntax table for `mh-regexp-in-field-p' to use.")
126
127 (defvar mh-fcc-syntax-table
128 (let ((syntax-table (make-syntax-table text-mode-syntax-table)))
129 (modify-syntax-entry ?+ "w" syntax-table)
130 (modify-syntax-entry ?/ "w" syntax-table)
131 syntax-table)
132 "Syntax table used by MH-E while searching an Fcc field.")
133
134 (defvar mh-addr-syntax-table
135 (let ((syntax-table (make-syntax-table text-mode-syntax-table)))
136 (modify-syntax-entry ?! "w" syntax-table)
137 (modify-syntax-entry ?# "w" syntax-table)
138 (modify-syntax-entry ?$ "w" syntax-table)
139 (modify-syntax-entry ?% "w" syntax-table)
140 (modify-syntax-entry ?& "w" syntax-table)
141 (modify-syntax-entry ?' "w" syntax-table)
142 (modify-syntax-entry ?* "w" syntax-table)
143 (modify-syntax-entry ?+ "w" syntax-table)
144 (modify-syntax-entry ?- "w" syntax-table)
145 (modify-syntax-entry ?/ "w" syntax-table)
146 (modify-syntax-entry ?= "w" syntax-table)
147 (modify-syntax-entry ?? "w" syntax-table)
148 (modify-syntax-entry ?^ "w" syntax-table)
149 (modify-syntax-entry ?_ "w" syntax-table)
150 (modify-syntax-entry ?` "w" syntax-table)
151 (modify-syntax-entry ?{ "w" syntax-table)
152 (modify-syntax-entry ?| "w" syntax-table)
153 (modify-syntax-entry ?} "w" syntax-table)
154 (modify-syntax-entry ?~ "w" syntax-table)
155 (modify-syntax-entry ?. "w" syntax-table)
156 (modify-syntax-entry ?@ "w" syntax-table)
157 syntax-table)
158 "Syntax table used by MH-E while searching an address field.")
159
160 (defvar mh-send-args ""
161 "Extra args to pass to \"send\" command.")
162
163 (defvar mh-annotate-char nil
164 "Character to use to annotate `mh-sent-from-msg'.")
165
166 (defvar mh-annotate-field nil
167 "Field name for message annotation.")
168
169 (defvar mh-annotate-list nil
170 "Messages annotated, either a sequence name or a list of message numbers.
171 This variable can be used by `mh-annotate-msg-hook'.")
172
173 (defvar mh-insert-auto-fields-done-local nil
174 "Buffer-local variable set when `mh-insert-auto-fields' called successfully.")
175 (make-variable-buffer-local 'mh-insert-auto-fields-done-local)
176
177 \f
178
179 ;;; MH-E Entry Points
180
181 ;;;###autoload
182 (defun mh-smail ()
183 "Compose a message with the MH mail system.
184 See `mh-send' for more details on composing mail."
185 (interactive)
186 (mh-find-path)
187 (call-interactively 'mh-send))
188
189 ;;;###autoload
190 (defun mh-smail-other-window ()
191 "Compose a message with the MH mail system in other window.
192 See `mh-send' for more details on composing mail."
193 (interactive)
194 (mh-find-path)
195 (call-interactively 'mh-send-other-window))
196
197 (defun mh-send-other-window (to cc subject)
198 "Compose a message in another window.
199
200 See `mh-send' for more information and a description of how the
201 TO, CC, and SUBJECT arguments are used."
202 (interactive (list
203 (mh-interactive-read-address "To: ")
204 (mh-interactive-read-address "Cc: ")
205 (mh-interactive-read-string "Subject: ")))
206 (let ((pop-up-windows t))
207 (mh-send-sub to cc subject (current-window-configuration))))
208
209 (defvar mh-error-if-no-draft nil) ;raise error over using old draft
210
211 ;;;###autoload
212 (defun mh-smail-batch (&optional to subject other-headers &rest ignored)
213 "Compose a message with the MH mail system.
214
215 This function does not prompt the user for any header fields, and
216 thus is suitable for use by programs that want to create a mail
217 buffer. Users should use \\[mh-smail] to compose mail.
218
219 Optional arguments for setting certain fields include TO,
220 SUBJECT, and OTHER-HEADERS. Additional arguments are IGNORED.
221
222 This function remains for Emacs 21 compatibility. New
223 applications should use `mh-user-agent-compose'."
224 (mh-find-path)
225 (let ((mh-error-if-no-draft t))
226 (mh-send (or to "") "" (or subject ""))))
227
228 ;;;###autoload
229 (define-mail-user-agent 'mh-e-user-agent
230 'mh-user-agent-compose 'mh-send-letter 'mh-fully-kill-draft
231 'mh-before-send-letter-hook)
232
233 ;;;###autoload
234 (defun mh-user-agent-compose (&optional to subject other-headers continue
235 switch-function yank-action
236 send-actions return-action
237 &rest ignored)
238 "Set up mail composition draft with the MH mail system.
239 This is the `mail-user-agent' entry point to MH-E. This function
240 conforms to the contract specified by `define-mail-user-agent'
241 which means that this function should accept the same arguments
242 as `compose-mail'.
243
244 The optional arguments TO and SUBJECT specify recipients and the
245 initial Subject field, respectively.
246
247 OTHER-HEADERS is an alist specifying additional header fields.
248 Elements look like (HEADER . VALUE) where both HEADER and VALUE
249 are strings.
250
251 CONTINUE, SWITCH-FUNCTION, YANK-ACTION, SEND-ACTIONS, and
252 RETURN-ACTION and any additional arguments are IGNORED."
253 (mh-find-path)
254 (let ((mh-error-if-no-draft t))
255 (mh-send to "" subject)
256 (while other-headers
257 (mh-insert-fields (concat (car (car other-headers)) ":")
258 (cdr (car other-headers)))
259 (setq other-headers (cdr other-headers)))))
260
261 ;; Shush compiler.
262 (mh-do-in-xemacs
263 (defvar sendmail-coding-system))
264
265 ;;;###autoload
266 (defun mh-send-letter (&optional arg)
267 "Save draft and send message.
268
269 When you are all through editing a message, you send it with this
270 command. You can give a prefix argument ARG to monitor the first stage
271 of the delivery\; this output can be found in a buffer called \"*MH-E
272 Mail Delivery*\".
273
274 The hook `mh-before-send-letter-hook' is run at the beginning of
275 this command. For example, if you want to check your spelling in
276 your message before sending, add the function `ispell-message'.
277
278 Unless `mh-insert-auto-fields' had previously been called
279 manually, the function `mh-insert-auto-fields' is called to
280 insert fields based upon the recipients. If fields are added, you
281 are given a chance to see and to confirm these fields before the
282 message is actually sent. You can do away with this confirmation
283 by turning off the option `mh-auto-fields-prompt-flag'.
284
285 In case the MH \"send\" program is installed under a different name,
286 use `mh-send-prog' to tell MH-E the name.
287
288 The hook `mh-annotate-msg-hook' is run after annotating the
289 message and scan line."
290 (interactive "P")
291 (run-hooks 'mh-before-send-letter-hook)
292 (if (and (mh-insert-auto-fields t)
293 mh-auto-fields-prompt-flag
294 (goto-char (point-min)))
295 (if (not (y-or-n-p "Auto fields inserted, send? "))
296 (error "Send aborted")))
297 (cond ((mh-mh-directive-present-p)
298 (mh-mh-to-mime))
299 ((or (mh-mml-tag-present-p) (not (mh-ascii-buffer-p)))
300 (mh-mml-to-mime)))
301 (save-buffer)
302 (message "Sending...")
303 (let ((draft-buffer (current-buffer))
304 (file-name buffer-file-name)
305 (config mh-previous-window-config)
306 (coding-system-for-write
307 (if (and (local-variable-p 'buffer-file-coding-system
308 (current-buffer)) ;XEmacs needs two args
309 ;; We're not sure why, but buffer-file-coding-system
310 ;; tends to get set to undecided-unix.
311 (not (memq buffer-file-coding-system
312 '(undecided undecided-unix undecided-dos))))
313 buffer-file-coding-system
314 (or (and (boundp 'sendmail-coding-system) sendmail-coding-system)
315 (and (default-boundp 'buffer-file-coding-system)
316 (default-value 'buffer-file-coding-system))
317 'iso-latin-1))))
318 ;; Older versions of spost do not support -msgid and -mime.
319 (unless mh-send-uses-spost-flag
320 ;; Adding a Message-ID field looks good, makes it easier to search for
321 ;; message in your +outbox, and best of all doesn't break threading for
322 ;; the recipient if you reply to a message in your +outbox.
323 (setq mh-send-args (concat "-msgid " mh-send-args))
324 ;; The default BCC encapsulation will make a MIME message unreadable.
325 ;; With nmh use the -mime arg to prevent this.
326 (if (and (mh-variant-p 'nmh)
327 (mh-goto-header-field "Bcc:")
328 (mh-goto-header-field "Content-Type:"))
329 (setq mh-send-args (concat "-mime " mh-send-args))))
330 (cond (arg
331 (pop-to-buffer mh-mail-delivery-buffer)
332 (erase-buffer)
333 (mh-exec-cmd-output mh-send-prog t
334 "-nodraftfolder" "-watch" "-nopush"
335 (split-string mh-send-args) file-name)
336 (goto-char (point-max)) ; show the interesting part
337 (recenter -1)
338 (set-buffer draft-buffer)) ; for annotation below
339 (t
340 (mh-exec-cmd-daemon mh-send-prog nil
341 "-nodraftfolder" "-noverbose"
342 (split-string mh-send-args) file-name)))
343 (if mh-annotate-char
344 (mh-annotate-msg mh-sent-from-msg
345 mh-sent-from-folder
346 mh-annotate-char
347 "-component" mh-annotate-field
348 "-text" (format "\"%s %s\""
349 (mh-get-header-field "To:")
350 (mh-get-header-field "Cc:"))))
351
352 (cond ((or (not arg)
353 (y-or-n-p "Kill draft buffer? "))
354 (kill-buffer draft-buffer)
355 (if config
356 (set-window-configuration config))))
357 (if arg
358 (message "Sending...done")
359 (message "Sending...backgrounded"))))
360
361 ;;;###autoload
362 (defun mh-fully-kill-draft ()
363 "Quit editing and delete draft message.
364
365 If for some reason you are not happy with the draft, you can use
366 this command to kill the draft buffer and delete the draft
367 message. Use the command \\[kill-buffer] if you don't want to
368 delete the draft message."
369 (interactive)
370 (if (y-or-n-p "Kill draft message? ")
371 (let ((config mh-previous-window-config))
372 (if (file-exists-p buffer-file-name)
373 (delete-file buffer-file-name))
374 (set-buffer-modified-p nil)
375 (kill-buffer (buffer-name))
376 (message "")
377 (if config
378 (set-window-configuration config)))
379 (error "Message not killed")))
380
381 \f
382
383 ;;; MH-Folder Commands
384
385 ;; Alphabetical.
386
387 ;;;###mh-autoload
388 (defun mh-edit-again (message)
389 "Edit a MESSAGE to send it again.
390
391 If you don't complete a draft for one reason or another, and if
392 the draft buffer is no longer available, you can pick your draft
393 up again with this command. If you don't use a draft folder, your
394 last \"draft\" file will be used. If you use draft folders,
395 you'll need to visit the draft folder with \"\\[mh-visit-folder]
396 drafts <RET>\", use \\[mh-next-undeleted-msg] to move to the
397 appropriate message, and then use \\[mh-edit-again] to prepare
398 the message for editing.
399
400 This command can also be used to take messages that were sent to
401 you and to send them to more people.
402
403 Don't use this command to re-edit a message from a Mailer-Daemon
404 who complained that your mail wasn't posted for some reason or
405 another (see `mh-extract-rejected-mail').
406
407 The default message is the current message.
408
409 See also `mh-send'."
410 (interactive (list (mh-get-msg-num t)))
411 (let* ((from-folder mh-current-folder)
412 (config (current-window-configuration))
413 (draft
414 (cond ((and mh-draft-folder (equal from-folder mh-draft-folder))
415 (pop-to-buffer (find-file-noselect (mh-msg-filename message))
416 t)
417 (rename-buffer (format "draft-%d" message))
418 ;; Make buffer writable...
419 (setq buffer-read-only nil)
420 ;; If buffer was being used to display the message reinsert
421 ;; from file...
422 (when (eq major-mode 'mh-show-mode)
423 (erase-buffer)
424 (insert-file-contents buffer-file-name))
425 (buffer-name))
426 (t
427 (mh-read-draft "clean-up" (mh-msg-filename message) nil)))))
428 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)
429 (mh-insert-header-separator)
430 ;; Merge in components
431 (mh-mapc
432 (function
433 (lambda (header-field)
434 (let ((field (car header-field))
435 (value (cdr header-field))
436 (case-fold-search t))
437 (cond
438 ;; Address field
439 ((string-match field "^To$\\|^Cc$\\|^From$")
440 (cond
441 ((not (mh-goto-header-field (concat field ":")))
442 ;; Header field does not exist, add it
443 (mh-goto-header-end 0)
444 (insert field ": " value "\n"))
445 ((string-equal value "")
446 ;; Header field already exists and no value
447 )
448 (t
449 ;; Header field exists and we have a value
450 (let (address mailbox (alias (mh-alias-expand value)))
451 (and alias
452 (setq address (ietf-drums-parse-address alias))
453 (setq mailbox (car address)))
454 ;; XXX - Need to parse all addresses out of field
455 (if (and
456 (not (mh-regexp-in-field-p
457 (concat "\\b" (regexp-quote value) "\\b") field))
458 mailbox
459 (not (mh-regexp-in-field-p
460 (concat "\\b" (regexp-quote mailbox) "\\b") field)))
461 (insert " " value ","))
462 ))))
463 ((string-match field "^Fcc$")
464 ;; Folder reference
465 (mh-modify-header-field field value))
466 ;; Text field, that's an easy case
467 (t
468 (mh-modify-header-field field value))))))
469 (mh-components-to-list (mh-find-components)))
470 (goto-char (point-min))
471 (save-buffer)
472 (mh-compose-and-send-mail
473 draft "" from-folder nil nil nil nil nil nil config)
474 (mh-letter-mode-message)
475 (mh-letter-adjust-point)))
476
477 (defun mh-extract-header-field ()
478 "Extract field name and field value from the field at point.
479 Returns a list of field name and value (which may be null)."
480 (let ((end (save-excursion (mh-header-field-end)
481 (point))))
482 (if (looking-at mh-letter-header-field-regexp)
483 (save-excursion
484 (goto-char (match-end 1))
485 (forward-char 1)
486 (skip-chars-forward " \t")
487 (cons (match-string-no-properties 1) (buffer-substring-no-properties (point) end))))))
488
489
490 (defun mh-components-to-list (components)
491 "Convert the COMPONENTS file to a list of field names and values."
492 (with-current-buffer (get-buffer-create mh-temp-buffer)
493 (erase-buffer)
494 (insert-file-contents components)
495 (goto-char (point-min))
496 (let
497 ((header-fields nil))
498 (while (mh-in-header-p)
499 (setq header-fields (append header-fields (list (mh-extract-header-field))))
500 (mh-header-field-end)
501 (forward-char 1)
502 )
503 header-fields)))
504
505 ;;;###mh-autoload
506 (defun mh-extract-rejected-mail (message)
507 "Edit a MESSAGE that was returned by the mail system.
508
509 This command prepares the message for editing by removing the
510 Mailer-Daemon envelope and unneeded header fields. Fix whatever
511 addressing problem you had, and send the message again with
512 \\[mh-send-letter].
513
514 The default message is the current message.
515
516 See also `mh-send'."
517 (interactive (list (mh-get-msg-num t)))
518 (let ((from-folder mh-current-folder)
519 (config (current-window-configuration))
520 (draft (mh-read-draft "extraction" (mh-msg-filename message) nil)))
521 (goto-char (point-min))
522 (cond ((re-search-forward mh-rejected-letter-start nil t)
523 (skip-chars-forward " \t\n")
524 (delete-region (point-min) (point))
525 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil))
526 (t
527 (message "Does not appear to be a rejected letter")))
528 (mh-insert-header-separator)
529 (goto-char (point-min))
530 (save-buffer)
531 (mh-compose-and-send-mail draft "" from-folder message
532 (mh-get-header-field "To:")
533 (mh-get-header-field "From:")
534 (mh-get-header-field "Cc:")
535 nil nil config)
536 (mh-letter-mode-message)))
537
538 ;;;###mh-autoload
539 (defun mh-forward (to cc &optional range)
540 "Forward message.
541
542 You are prompted for the TO and CC recipients. You are given a
543 draft to edit that looks like it would if you had run the MH
544 command \"forw\". You can then add some text.
545
546 You can forward several messages by using a RANGE. All of the
547 messages in the range are inserted into your draft. Check the
548 documentation of `mh-interactive-range' to see how RANGE is read
549 in interactive use.
550
551 The hook `mh-forward-hook' is called on the draft.
552
553 See also `mh-compose-forward-as-mime-flag',
554 `mh-forward-subject-format', and `mh-send'."
555 (interactive (list (mh-interactive-read-address "To: ")
556 (mh-interactive-read-address "Cc: ")
557 (mh-interactive-range "Forward")))
558 (let* ((folder mh-current-folder)
559 (msgs (mh-range-to-msg-list range))
560 (config (current-window-configuration))
561 (fwd-msg-file (mh-msg-filename (car msgs) folder))
562 ;; forw always leaves file in "draft" since it doesn't have -draft
563 (draft-name (expand-file-name "draft" mh-user-path))
564 (draft (cond ((or (not (file-exists-p draft-name))
565 (y-or-n-p "The file draft exists; discard it? "))
566 (mh-exec-cmd "forw" "-build"
567 (if (and (mh-variant-p 'nmh)
568 mh-compose-forward-as-mime-flag)
569 "-mime")
570 mh-current-folder
571 (mh-coalesce-msg-list msgs))
572 (prog1
573 (mh-read-draft "" draft-name t)
574 (mh-insert-fields "To:" to "Cc:" cc)
575 (save-buffer)))
576 (t
577 (mh-read-draft "" draft-name nil)))))
578 (let (orig-from
579 orig-subject)
580 (with-current-buffer (get-buffer-create mh-temp-buffer)
581 (erase-buffer)
582 (insert-file-contents fwd-msg-file)
583 (setq orig-from (mh-get-header-field "From:"))
584 (setq orig-subject (mh-get-header-field "Subject:")))
585 (let ((forw-subject
586 (mh-forwarded-letter-subject orig-from orig-subject)))
587 (mh-insert-fields "Subject:" forw-subject)
588 (goto-char (point-min))
589 ;; Set the local value of mh-mail-header-separator according to what is
590 ;; present in the buffer...
591 (set (make-local-variable 'mh-mail-header-separator)
592 (save-excursion
593 (goto-char (mh-mail-header-end))
594 (buffer-substring-no-properties (point) (mh-line-end-position))))
595 (set (make-local-variable 'mail-header-separator) mh-mail-header-separator) ;override sendmail.el
596 ;; If using MML, translate MH-style directive
597 (if (equal mh-compose-insertion 'mml)
598 (save-excursion
599 (goto-char (mh-mail-header-end))
600 (while
601 (re-search-forward
602 "^#forw \\[\\([^]]+\\)\\] \\(+\\S-+\\) \\(.*\\)$"
603 (point-max) t)
604 (let ((description (if (equal (match-string 1)
605 "forwarded messages")
606 "forwarded message %d"
607 (match-string 1)))
608 (msgs (split-string (match-string 3)))
609 (i 0))
610 (beginning-of-line)
611 (delete-region (point) (progn (forward-line 1) (point)))
612 (dolist (msg msgs)
613 (setq i (1+ i))
614 (mh-mml-forward-message (format description i)
615 folder msg)
616 ;; Was inserted before us, move to end of file to preserve order
617 (goto-char (point-max)))))))
618 ;; Position just before forwarded message.
619 (if (re-search-forward "^------- Forwarded Message" nil t)
620 (forward-line -1)
621 (goto-char (mh-mail-header-end))
622 (forward-line 1))
623 (delete-other-windows)
624 (mh-add-msgs-to-seq msgs 'forwarded t)
625 (mh-compose-and-send-mail draft "" folder msgs
626 to forw-subject cc
627 mh-note-forw "Forwarded:"
628 config)
629 (mh-letter-mode-message)
630 (mh-letter-adjust-point)
631 (run-hooks 'mh-forward-hook)))))
632
633 (defun mh-forwarded-letter-subject (from subject)
634 "Return a Subject suitable for a forwarded message.
635 Original message has headers FROM and SUBJECT."
636 (let ((addr-start (string-match "<" from))
637 (comment (string-match "(" from)))
638 (cond ((and addr-start (> addr-start 0))
639 ;; Full Name <luser@host>
640 (setq from (substring from 0 (1- addr-start))))
641 (comment
642 ;; luser@host (Full Name)
643 (setq from (substring from (1+ comment) (1- (length from)))))))
644 (format mh-forward-subject-format from subject))
645
646 ;;;###mh-autoload
647 (defun mh-redistribute (to cc &optional message)
648 "Redistribute a message.
649
650 This command is similar in function to forwarding mail, but it
651 does not allow you to edit the message, nor does it add your name
652 to the \"From\" header field. It appears to the recipient as if
653 the message had come from the original sender. When you run this
654 command, you are prompted for the TO and CC recipients. The
655 default MESSAGE is the current message.
656
657 Also investigate the command \\[mh-edit-again] for another way to
658 redistribute messages.
659
660 See also `mh-redist-full-contents-flag'.
661
662 The hook `mh-annotate-msg-hook' is run after annotating the
663 message and scan line."
664 (interactive (list (mh-read-address "Redist-To: ")
665 (mh-read-address "Redist-Cc: ")
666 (mh-get-msg-num t)))
667 (or message
668 (setq message (mh-get-msg-num t)))
669 (save-window-excursion
670 (let ((folder mh-current-folder)
671 (draft (mh-read-draft "redistribution"
672 (if mh-redist-full-contents-flag
673 (mh-msg-filename message)
674 nil)
675 nil)))
676 (mh-goto-header-end 0)
677 (insert "Resent-To: " to "\n")
678 (if (not (equal cc "")) (insert "Resent-cc: " cc "\n"))
679 (mh-clean-msg-header
680 (point-min)
681 "^Message-Id:\\|^Received:\\|^Return-Path:\\|^Sender:\\|^Date:\\|^From:"
682 nil)
683 (save-buffer)
684 (message "Redistributing...")
685 (let ((env "mhdist=1"))
686 ;; Setup environment...
687 (setq env (concat env " mhaltmsg="
688 (if mh-redist-full-contents-flag
689 buffer-file-name
690 (mh-msg-filename message folder))))
691 (unless mh-redist-full-contents-flag
692 (setq env (concat env " mhannotate=1")))
693 ;; Redistribute...
694 (if mh-redist-background
695 (mh-exec-cmd-env-daemon env mh-send-prog nil buffer-file-name)
696 (mh-exec-cmd-error env mh-send-prog "-push" buffer-file-name))
697 ;; Annotate...
698 (mh-annotate-msg message folder mh-note-dist
699 "-component" "Resent:"
700 "-text" (format "\"%s %s\"" to cc)))
701 (kill-buffer draft)
702 (message "Redistributing...done"))))
703
704 ;;;###mh-autoload
705 (defun mh-reply (message &optional reply-to includep)
706 "Reply to a MESSAGE.
707
708 When you reply to a message, you are first prompted with \"Reply
709 to whom?\" (unless the optional argument REPLY-TO is provided).
710 You have several choices here.
711
712 Response Reply Goes To
713
714 from The person who sent the message. This is the
715 default, so <RET> is sufficient.
716
717 to Replies to the sender, plus all recipients in the
718 \"To:\" header field.
719
720 all cc Forms a reply to the addresses in the
721 \"Mail-Followup-To:\" header field if one
722 exists; otherwise forms a reply to the sender,
723 plus all recipients.
724
725 Depending on your answer, \"repl\" is given a different argument
726 to form your reply. Specifically, a choice of \"from\" or none at
727 all runs \"repl -nocc all\", and a choice of \"to\" runs \"repl
728 -cc to\". Finally, either \"cc\" or \"all\" runs \"repl -cc all
729 -nocc me\".
730
731 Two windows are then created. One window contains the message to
732 which you are replying in an MH-Show buffer. Your draft, in
733 MH-Letter mode (*note `mh-letter-mode'), is in the other window.
734 If the reply draft was not one that you expected, check the
735 things that affect the behavior of \"repl\" which include the
736 \"repl:\" profile component and the \"replcomps\" and
737 \"replgroupcomps\" files.
738
739 If you supply a prefix argument INCLUDEP, the message you are
740 replying to is inserted in your reply after having first been run
741 through \"mhl\" with the format file \"mhl.reply\".
742
743 Alternatively, you can customize the option `mh-yank-behavior'
744 and choose one of its \"Automatically\" variants to do the same
745 thing. If you do so, the prefix argument has no effect.
746
747 Another way to include the message automatically in your draft is
748 to use \"repl: -filter repl.filter\" in your MH profile.
749
750 If you wish to customize the header or other parts of the reply
751 draft, please see \"repl\" and \"mh-format\".
752
753 See also `mh-reply-show-message-flag',
754 `mh-reply-default-reply-to', and `mh-send'."
755 (interactive (list
756 (mh-get-msg-num t)
757 (let ((minibuffer-help-form
758 "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients"))
759 (or mh-reply-default-reply-to
760 (completing-read "Reply to whom (default from): "
761 '(("from") ("to") ("cc") ("all"))
762 nil
763 t)))
764 current-prefix-arg))
765 (let* ((folder mh-current-folder)
766 (show-buffer mh-show-buffer)
767 (config (current-window-configuration))
768 (group-reply (or (equal reply-to "cc") (equal reply-to "all")))
769 (form-file (cond ((and (mh-variant-p 'nmh 'gnu-mh) group-reply
770 (stringp mh-repl-group-formfile))
771 mh-repl-group-formfile)
772 ((stringp mh-repl-formfile) mh-repl-formfile)
773 (t nil))))
774 (message "Composing a reply...")
775 (mh-exec-cmd "repl" "-build" "-noquery" "-nodraftfolder"
776 (if form-file
777 (list "-form" form-file))
778 mh-current-folder message
779 (cond ((or (equal reply-to "from") (equal reply-to ""))
780 '("-nocc" "all"))
781 ((equal reply-to "to")
782 '("-cc" "to"))
783 (group-reply (if (mh-variant-p 'nmh 'gnu-mh)
784 '("-group" "-nocc" "me")
785 '("-cc" "all" "-nocc" "me"))))
786 (cond ((or (eq mh-yank-behavior 'autosupercite)
787 (eq mh-yank-behavior 'autoattrib))
788 '("-noformat"))
789 (includep '("-filter" "mhl.reply"))
790 (t '())))
791 (let ((draft (mh-read-draft "reply"
792 (expand-file-name "reply" mh-user-path)
793 t)))
794 (delete-other-windows)
795 (save-buffer)
796
797 (let ((to (mh-get-header-field "To:"))
798 (subject (mh-get-header-field "Subject:"))
799 (cc (mh-get-header-field "Cc:")))
800 (goto-char (point-min))
801 (mh-goto-header-end 1)
802 (or includep
803 (not mh-reply-show-message-flag)
804 (mh-in-show-buffer (show-buffer)
805 (mh-display-msg message folder)))
806 (mh-add-msgs-to-seq message 'answered t)
807 (message "Composing a reply...done")
808 (mh-compose-and-send-mail draft "" folder message to subject cc
809 mh-note-repl "Replied:" config))
810 (when (and (or (eq 'autosupercite mh-yank-behavior)
811 (eq 'autoattrib mh-yank-behavior))
812 (eq (mh-show-buffer-message-number) mh-sent-from-msg))
813 (undo-boundary)
814 (mh-yank-cur-msg))
815 (mh-letter-mode-message))))
816
817 ;;;###mh-autoload
818 (defun mh-send (to cc subject)
819 "Compose a message.
820
821 Your letter appears in an Emacs buffer whose mode is
822 MH-Letter (see `mh-letter-mode').
823
824 The arguments TO, CC, and SUBJECT can be used to prefill the
825 draft fields or suppress the prompts if `mh-compose-prompt-flag'
826 is on. They are also passed to the function set in the option
827 `mh-compose-letter-function'.
828
829 See also `mh-insert-x-mailer-flag' and `mh-letter-mode-hook'.
830
831 Outside of an MH-Folder buffer (`mh-folder-mode'), you must call
832 either \\[mh-smail] or \\[mh-smail-other-window] to compose a new
833 message."
834 (interactive (list
835 (mh-interactive-read-address "To: ")
836 (mh-interactive-read-address "Cc: ")
837 (mh-interactive-read-string "Subject: ")))
838 (let ((config (current-window-configuration)))
839 (delete-other-windows)
840 (mh-send-sub to cc subject config)))
841
842 \f
843
844 ;;; Support Routines
845
846 (defun mh-interactive-read-address (prompt)
847 "Read an address.
848 If `mh-compose-prompt-flag' is non-nil, then read an address with
849 PROMPT.
850 Otherwise return the empty string."
851 (if mh-compose-prompt-flag (mh-read-address prompt) ""))
852
853 (defun mh-interactive-read-string (prompt)
854 "Read a string.
855 If `mh-compose-prompt-flag' is non-nil, then read a string with
856 PROMPT.
857 Otherwise return the empty string."
858 (if mh-compose-prompt-flag (read-string prompt) ""))
859
860 ;;;###mh-autoload
861 (defun mh-show-buffer-message-number (&optional buffer)
862 "Message number of displayed message in corresponding show buffer.
863
864 Return nil if show buffer not displayed.
865 If in `mh-letter-mode', don't display the message number being replied
866 to, but rather the message number of the show buffer associated with
867 our originating folder buffer.
868 Optional argument BUFFER can be used to specify the buffer."
869 (save-excursion
870 (if buffer
871 (set-buffer buffer))
872 (cond ((eq major-mode 'mh-show-mode)
873 (let ((number-start (mh-search-from-end ?/ buffer-file-name)))
874 (string-to-number (substring buffer-file-name
875 (1+ number-start)))))
876 ((and (eq major-mode 'mh-folder-mode)
877 mh-show-buffer
878 (get-buffer mh-show-buffer))
879 (mh-show-buffer-message-number mh-show-buffer))
880 ((and (eq major-mode 'mh-letter-mode)
881 mh-sent-from-folder
882 (get-buffer mh-sent-from-folder))
883 (mh-show-buffer-message-number mh-sent-from-folder))
884 (t
885 nil))))
886
887 (defun mh-find-components ()
888 "Return the path to the components file."
889 (let (components)
890 (cond
891 ((file-exists-p
892 (setq components
893 (expand-file-name mh-comp-formfile mh-user-path)))
894 components)
895 ((file-exists-p
896 (setq components
897 (expand-file-name mh-comp-formfile mh-lib)))
898 components)
899 (t
900 (error "Can't find %s in %s or %s"
901 mh-comp-formfile mh-user-path mh-lib)))))
902
903 (defun mh-send-sub (to cc subject config)
904 "Do the real work of composing and sending a letter.
905 Expects the TO, CC, and SUBJECT fields as arguments.
906 CONFIG is the window configuration before sending mail."
907 (let ((folder mh-current-folder)
908 (msg-num (mh-get-msg-num nil)))
909 (message "Composing a message...")
910 (let ((draft (mh-read-draft
911 "message"
912 (mh-find-components)
913 nil)))
914 (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc)
915 (goto-char (point-max))
916 (mh-compose-and-send-mail draft "" folder msg-num
917 to subject cc
918 nil nil config)
919 (mh-letter-mode-message)
920 (mh-letter-adjust-point))))
921
922 (defun mh-read-draft (use initial-contents delete-contents-file)
923 "Read draft file into a draft buffer and make that buffer the current one.
924
925 USE is a message used for prompting about the intended use of the
926 message.
927 INITIAL-CONTENTS is filename that is read into an empty buffer, or nil
928 if buffer should not be modified. Delete the initial-contents file if
929 DELETE-CONTENTS-FILE flag is set.
930 Returns the draft folder's name.
931 If the draft folder facility is enabled in ~/.mh_profile, a new buffer
932 is used each time and saved in the draft folder. The draft file can
933 then be reused."
934 (cond (mh-draft-folder
935 (let ((orig-default-dir default-directory)
936 (draft-file-name (mh-new-draft-name)))
937 (pop-to-buffer (generate-new-buffer
938 (format "draft-%s"
939 (file-name-nondirectory draft-file-name))))
940 (condition-case ()
941 (insert-file-contents draft-file-name t)
942 (file-error))
943 (setq default-directory orig-default-dir)))
944 (t
945 (let ((draft-name (expand-file-name "draft" mh-user-path)))
946 (pop-to-buffer "draft") ; Create if necessary
947 (if (buffer-modified-p)
948 (if (y-or-n-p "Draft has been modified; kill anyway? ")
949 (set-buffer-modified-p nil)
950 (error "Draft preserved")))
951 (setq buffer-file-name draft-name)
952 (clear-visited-file-modtime)
953 (unlock-buffer)
954 (cond ((and (file-exists-p draft-name)
955 (not (equal draft-name initial-contents)))
956 (insert-file-contents draft-name)
957 (delete-file draft-name))))))
958 (cond ((and initial-contents
959 (or (zerop (buffer-size))
960 (if (y-or-n-p
961 (format "A draft exists. Use for %s? " use))
962 (if mh-error-if-no-draft
963 (error "A prior draft exists"))
964 t)))
965 (erase-buffer)
966 (insert-file-contents initial-contents)
967 (if delete-contents-file (delete-file initial-contents))))
968 (auto-save-mode 1)
969 (if mh-draft-folder
970 (save-buffer)) ; Do not reuse draft name
971 (buffer-name))
972
973 (defun mh-new-draft-name ()
974 "Return the pathname of folder for draft messages."
975 (save-excursion
976 (mh-exec-cmd-quiet t "mhpath" mh-draft-folder "new")
977 (buffer-substring (point-min) (1- (point-max)))))
978
979 (defun mh-insert-fields (&rest name-values)
980 "Insert the NAME-VALUES pairs in the current buffer.
981 If the field exists, append the value to it.
982 Do not insert any pairs whose value is the empty string."
983 (let ((case-fold-search t))
984 (while name-values
985 (let ((field-name (car name-values))
986 (value (car (cdr name-values))))
987 (if (not (string-match "^.*:$" field-name))
988 (setq field-name (concat field-name ":")))
989 (cond ((or (null value)
990 (equal value ""))
991 nil)
992 ((mh-position-on-field field-name)
993 (insert " " (or value "")))
994 (t
995 (insert field-name " " value "\n")))
996 (setq name-values (cdr (cdr name-values)))))))
997
998 (defun mh-compose-and-send-mail (draft send-args
999 sent-from-folder sent-from-msg
1000 to subject cc
1001 annotate-char annotate-field
1002 config)
1003 "Edit and compose a draft message in buffer DRAFT and send or save it.
1004 SEND-ARGS is the argument passed to the send command.
1005 SENT-FROM-FOLDER is buffer containing scan listing of current folder,
1006 or nil if none exists.
1007 SENT-FROM-MSG is the message number or sequence name or nil.
1008 The TO, SUBJECT, and CC fields are passed to the
1009 `mh-compose-letter-function'.
1010 If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of
1011 the message. In that case, the ANNOTATE-FIELD is used to build a
1012 string for `mh-annotate-msg'.
1013 CONFIG is the window configuration to restore after sending the
1014 letter."
1015 (pop-to-buffer draft)
1016 (mh-letter-mode)
1017
1018 ;; Insert identity.
1019 (mh-insert-identity mh-identity-default t)
1020 (mh-identity-make-menu)
1021 (mh-identity-add-menu)
1022
1023 ;; Cleanup possibly RFC2047 encoded subject header
1024 (mh-decode-message-subject)
1025
1026 ;; Insert extra fields.
1027 (mh-insert-x-mailer)
1028 (mh-insert-x-face)
1029
1030 (mh-letter-hide-all-skipped-fields)
1031
1032 (setq mh-sent-from-folder sent-from-folder)
1033 (setq mh-sent-from-msg sent-from-msg)
1034 (setq mh-send-args send-args)
1035 (setq mh-annotate-char annotate-char)
1036 (setq mh-annotate-field annotate-field)
1037 (setq mh-previous-window-config config)
1038 (setq mode-line-buffer-identification (list " {%b}"))
1039 (mh-logo-display)
1040 (mh-make-local-hook 'kill-buffer-hook)
1041 (add-hook 'kill-buffer-hook 'mh-tidy-draft-buffer nil t)
1042 (run-hook-with-args 'mh-compose-letter-function to subject cc))
1043
1044 (defun mh-insert-x-mailer ()
1045 "Append an X-Mailer field to the header.
1046 The versions of MH-E, Emacs, and MH are shown."
1047 ;; Lazily initialize mh-x-mailer-string.
1048 (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string))
1049 (setq mh-x-mailer-string
1050 (format "MH-E %s; %s; %sEmacs %s"
1051 mh-version mh-variant-in-use
1052 (if (featurep 'xemacs) "X" "GNU ")
1053 (cond ((not (featurep 'xemacs))
1054 (string-match "[0-9]+\\.[0-9]+\\(\\.[0-9]+\\)?"
1055 emacs-version)
1056 (match-string 0 emacs-version))
1057 ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?"
1058 emacs-version)
1059 (match-string 0 emacs-version))
1060 (t (format "%s.%s" emacs-major-version
1061 emacs-minor-version))))))
1062 ;; Insert X-Mailer, but only if it doesn't already exist.
1063 (save-excursion
1064 (when (and mh-insert-x-mailer-flag
1065 (null (mh-goto-header-field "X-Mailer")))
1066 (mh-insert-fields "X-Mailer:" mh-x-mailer-string))))
1067
1068 (defun mh-insert-x-face ()
1069 "Append X-Face, Face or X-Image-URL field to header.
1070 If the field already exists, this function does nothing."
1071 (when (and (file-exists-p mh-x-face-file)
1072 (file-readable-p mh-x-face-file))
1073 (save-excursion
1074 (unless (or (mh-position-on-field "X-Face")
1075 (mh-position-on-field "Face")
1076 (mh-position-on-field "X-Image-URL"))
1077 (save-excursion
1078 (goto-char (+ (point) (cadr (insert-file-contents mh-x-face-file))))
1079 (if (not (looking-at "^"))
1080 (insert "\n")))
1081 (unless (looking-at "\\(X-Face\\|Face\\|X-Image-URL\\): ")
1082 (insert "X-Face: "))))))
1083
1084 (defun mh-tidy-draft-buffer ()
1085 "Run when a draft buffer is destroyed."
1086 (let ((buffer (get-buffer mh-recipients-buffer)))
1087 (if buffer
1088 (kill-buffer buffer))))
1089
1090 (defun mh-letter-mode-message ()
1091 "Display a help message for users of `mh-letter-mode'.
1092 This should be the last function called when composing the draft."
1093 (message "%s" (substitute-command-keys
1094 (concat "Type \\[mh-send-letter] to send message, "
1095 "\\[mh-help] for help"))))
1096
1097 (defun mh-letter-adjust-point ()
1098 "Move cursor to first header field if are using the no prompt mode."
1099 (unless mh-compose-prompt-flag
1100 (goto-char (point-max))
1101 (mh-letter-next-header-field)))
1102
1103 (defun mh-annotate-msg (msg folder note &rest args)
1104 "Mark MSG in FOLDER with character NOTE and annotate message with ARGS.
1105 MSG can be a message number, a list of message numbers, or a sequence.
1106 The hook `mh-annotate-msg-hook' is run after annotating; see its
1107 documentation for variables it can use."
1108 (apply 'mh-exec-cmd "anno" folder
1109 (if (listp msg) (append msg args) (cons msg args)))
1110 (save-excursion
1111 (cond ((get-buffer folder) ; Buffer may be deleted
1112 (set-buffer folder)
1113 (mh-iterate-on-range nil msg
1114 (mh-notate nil note
1115 (+ mh-cmd-note mh-scan-field-destination-offset))))))
1116 (let ((mh-current-folder folder)
1117 ;; mh-annotate-list is a sequence name or a list of message numbers
1118 (mh-annotate-list (if (numberp msg) (list msg) msg)))
1119 (run-hooks 'mh-annotate-msg-hook)))
1120
1121 (defun mh-insert-header-separator ()
1122 "Insert `mh-mail-header-separator', if absent."
1123 (save-excursion
1124 (goto-char (point-min))
1125 (rfc822-goto-eoh)
1126 (if (looking-at "$")
1127 (insert mh-mail-header-separator))))
1128
1129 ;;;###mh-autoload
1130 (defun mh-insert-auto-fields (&optional non-interactive)
1131 "Insert custom fields if recipient is found in `mh-auto-fields-list'.
1132
1133 Once the header contains one or more recipients, you may run this
1134 command to insert these fields manually. However, if you use this
1135 command, the automatic insertion when the message is sent is
1136 disabled.
1137
1138 In a program, set buffer-local `mh-insert-auto-fields-done-local'
1139 if header fields were added. If NON-INTERACTIVE is non-nil,
1140 perform actions quietly and only if
1141 `mh-insert-auto-fields-done-local' is nil. Return t if fields
1142 added; otherwise return nil."
1143 (interactive)
1144 (when (or (not non-interactive)
1145 (not mh-insert-auto-fields-done-local))
1146 (save-excursion
1147 (when (and (or (mh-goto-header-field "To:")
1148 (mh-goto-header-field "cc:")))
1149 (let ((list mh-auto-fields-list)
1150 (fields-inserted nil))
1151 (while list
1152 (let ((regexp (nth 0 (car list)))
1153 (entries (nth 1 (car list))))
1154 (when (mh-regexp-in-field-p regexp "To:" "cc:")
1155 (setq mh-insert-auto-fields-done-local t)
1156 (setq fields-inserted t)
1157 (if (not non-interactive)
1158 (message "Fields for %s added" regexp))
1159 (let ((entry-list entries))
1160 (while entry-list
1161 (let ((field (caar entry-list))
1162 (value (cdar entry-list)))
1163 (cond
1164 ((equal ":identity" field)
1165 (when
1166 ;;(and (not mh-identity-local)
1167 ;; Bug 1204506. But do we need to be able
1168 ;; to set an identity manually that won't be
1169 ;; overridden by mh-insert-auto-fields?
1170 (assoc value mh-identity-list)
1171 ;;)
1172 (mh-insert-identity value)))
1173 (t
1174 (mh-modify-header-field field value
1175 (equal field "From")))))
1176 (setq entry-list (cdr entry-list))))))
1177 (setq list (cdr list)))
1178 fields-inserted)))))
1179
1180 (defun mh-modify-header-field (field value &optional overwrite-flag)
1181 "To header FIELD add VALUE.
1182 If OVERWRITE-FLAG is non-nil then the old value, if present, is
1183 discarded."
1184 (cond ((and overwrite-flag
1185 (mh-goto-header-field (concat field ":")))
1186 (insert " " value)
1187 (delete-region (point) (mh-line-end-position)))
1188 ((and (not overwrite-flag)
1189 (mh-regexp-in-field-p (concat "\\b" (regexp-quote value) "\\b") field))
1190 ;; Already there, do nothing.
1191 )
1192 ((and (not overwrite-flag)
1193 (mh-goto-header-field (concat field ":")))
1194 (insert " " value ","))
1195 (t
1196 (mh-goto-header-end 0)
1197 (insert field ": " value "\n"))))
1198
1199 (defun mh-regexp-in-field-p (regexp &rest fields)
1200 "Non-nil means REGEXP was found in FIELDS."
1201 (let ((old-syntax-table (syntax-table)))
1202 (unwind-protect
1203 (save-excursion
1204 (let ((search-result nil))
1205 (while fields
1206 (let* ((field (car fields))
1207 (syntax-table
1208 (or mh-regexp-in-field-syntax-table
1209 (let ((case-fold-search t))
1210 (cond
1211 ((string-match field "^To$\\|^[BD]?cc$\\|^From$")
1212 mh-addr-syntax-table)
1213 ((string-match field "^Fcc$")
1214 mh-fcc-syntax-table)
1215 (t
1216 (syntax-table)))
1217 ))))
1218 (if (and (mh-goto-header-field field)
1219 (set-syntax-table syntax-table)
1220 (re-search-forward
1221 regexp (save-excursion (mh-header-field-end)(point)) t))
1222 (setq fields nil
1223 search-result t)
1224 (setq fields (cdr fields)))
1225 (set-syntax-table old-syntax-table)))
1226 search-result))
1227 (set-syntax-table old-syntax-table))))
1228
1229 (defun mh-ascii-buffer-p ()
1230 "Check if current buffer is entirely composed of ASCII.
1231 The function doesn't work for XEmacs since `find-charset-region'
1232 doesn't exist there."
1233 (loop for charset in (mh-funcall-if-exists
1234 find-charset-region (point-min) (point-max))
1235 unless (eq charset 'ascii) return nil
1236 finally return t))
1237
1238 (provide 'mh-comp)
1239
1240 ;; Local Variables:
1241 ;; indent-tabs-mode: nil
1242 ;; sentence-end-double-space: nil
1243 ;; End:
1244
1245 ;;; mh-comp.el ends here