]> code.delx.au - gnu-emacs/blobdiff - lisp/mail/sendmail.el
(mail-alias-file): Add autoload cookie.
[gnu-emacs] / lisp / mail / sendmail.el
index dbb8fc8d5a143f08c9fe1fc0fe00e9440949fc15..0f2861f8868a4afbd68081c15ff32f2298ce4be9 100644 (file)
@@ -1,11 +1,15 @@
-;; Mail sending commands for Emacs.
-;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
+;;; sendmail.el --- mail sending commands for Emacs.
+
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+;; Maintainer: FSF
+;; Keywords: mail
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 1, or (at your option)
+;; the Free Software Foundation; either version 2, or (at your option)
 ;; any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; along with GNU Emacs; see the file COPYING.  If not, write to
 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
+;;; Commentary:
 
-(provide 'sendmail)
+;; This mode provides mail-sending facilities from within Emacs.  It is
+;; documented in the Emacs user's manual.
+
+;;; Code:
 
 ;;;###autoload
-(defconst mail-self-blind nil "\
+(defvar mail-self-blind nil "\
 Non-nil means insert BCC to self in messages to be sent.
 This is done when the message is initialized,
 so you can remove or alter the BCC field to override the default.")
 
 ;;;###autoload
-(defconst mail-interactive nil "\
+(defvar mail-interactive nil "\
 Non-nil means when sending a message wait for and display errors.
 nil means let mailer mail back a message to report errors.")
 
 ;;;###autoload
-(defconst mail-yank-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^status:\\|^remailed\\|^received:\\|^message-id:\\|^summary-line:\\|^to:\\|^subject:\\|^in-reply-to:\\|^return-path:" "\
+(defvar mail-yank-ignored-headers "^via:\\|^mail-from:\\|^origin:\\|^status:\\|^remailed\\|^received:\\|^message-id:\\|^summary-line:\\|^to:\\|^subject:\\|^in-reply-to:\\|^return-path:" "\
 Delete these headers from old message when it's inserted in a reply.")
 
 ;; Useful to set in site-init.el
 ;;;###autoload
-(defconst send-mail-function 'sendmail-send-it "\
+(defvar send-mail-function 'sendmail-send-it "\
 Function to call to send the current buffer as mail.
-The headers are be delimited by a line which is mail-header-separator.")
+The headers are be delimited by a line which is `mail-header-separator'.")
 
 ;;;###autoload
 (defvar mail-header-separator "--text follows this line--" "\
@@ -51,26 +59,61 @@ The headers are be delimited by a line which is mail-header-separator.")
 Do not use an rmail file here!  Instead, use its inbox file.")
 
 ;;;###autoload
-(defvar mail-aliases t "\
-Alias of mail address aliases,
-or t meaning should be initialized from .mailrc.")
-
 (defvar mail-default-reply-to nil
   "*Address to insert as default Reply-to field of outgoing messages.")
 
+;;;###autoload
 (defvar mail-alias-file nil
   "*If non-nil, the name of a file to use instead of `/usr/lib/aliases'.
 This file defines aliases to be expanded by the mailer; this is a different
 feature from that of defining aliases in `.mailrc' to be expanded in Emacs.
 This variable has no effect unless your system uses sendmail as its mailer.")
 
+(defvar mail-aliases t
+  "Alist of mail address aliases,
+or t meaning should be initialized from `~/.mailrc'.
+The alias definitions in `~/.mailrc' have this form:
+    alias ALIAS MEANING")
+
 (defvar mail-yank-prefix nil
   "*Prefix insert on lines of yanked message being replied to.
 nil means use indentation.")
+(defvar mail-indentation-spaces 3
+  "*Number of spaces to insert at the beginning of each cited line.
+Used by `mail-yank-original' via `mail-yank-cite'.")
+(defvar mail-yank-hooks nil
+  "Obsolete hook for modifying a citation just inserted in the mail buffer.
+Each hook function can find the citation between (point) and (mark t).
+And each hook function should leave point and mark around the citation
+text as modified.
+
+This is a normal hook, misnamed for historical reasons.
+It is semi-obsolete and mail agents should no longer use it.")
+
+(defvar mail-citation-hook nil
+  "*Hook for modifying a citation just inserted in the mail buffer.
+Each hook function can find the citation between (point) and (mark t).
+And each hook function should leave point and mark around the citation
+text as modified.
+
+If this hook is entirely empty (nil), a default action is taken
+instead of no action.")
 
 (defvar mail-abbrevs-loaded nil)
 (defvar mail-mode-map nil)
 
+(autoload 'build-mail-aliases "mailalias"
+  "Read mail aliases from `~/.mailrc' and set `mail-aliases'."
+  nil)
+
+(autoload 'expand-mail-aliases "mailalias"
+  "Expand all mail aliases in suitable header fields found between BEG and END.
+Suitable header fields are `To', `Cc' and `Bcc' and their `Resent-' variants.
+Optional second arg EXCLUDE may be a regular expression defining text to be
+removed from alias expansions."
+  nil)
+
+;;;###autoload
 (defvar mail-signature nil
   "*Text inserted at end of mail buffer when a message is initialized.
 If t, it means to insert the contents of the file `~/.signature'.")
@@ -84,6 +127,26 @@ If t, it means to insert the contents of the file `~/.signature'.")
 It is inserted before you edit the message,
 so you can edit or delete these lines.")
 
+;; Note: could use /usr/ucb/mail instead of sendmail;
+;; options -t, and -v if not interactive.
+(defvar mail-mailer-swallows-blank-line
+  (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)" system-configuration)
+          (file-readable-p "/etc/sendmail.cf")
+          (let ((buffer (get-buffer-create " *temp*")))
+            (unwind-protect
+                (save-excursion
+                  (set-buffer buffer)
+                  (insert-file-contents "/etc/sendmail.cf")
+                  (goto-char (point-min))
+                  (let ((case-fold-search nil))
+                    (re-search-forward "^OR\\>" nil t)))
+              (kill-buffer buffer))))
+      '(looking-at "[ \t]\\|[-a-zA-Z]+:"))
+  "Set this non-nil if the system's mailer runs the header and body together.
+\(This problem exists on Sunos 4 when sendmail is run in remote mode.)
+The value should be an expression to test whether the problem will
+actually occur.")
+
 (defvar mail-mode-syntax-table nil
   "Syntax table used while in mail mode.")
 
@@ -92,40 +155,34 @@ so you can edit or delete these lines.")
      (setq mail-mode-syntax-table (copy-syntax-table text-mode-syntax-table))
      (modify-syntax-entry ?% ". " mail-mode-syntax-table)))
 
-(autoload 'build-mail-aliases "mailalias"
-  "Read mail aliases from ~/.mailrc and set mail-aliases."
-  nil)
-
-(autoload 'expand-mail-aliases "mailalias"
-  "Expand all mail aliases in suitable header fields found between BEG and END.
-Suitable header fields are To, CC and BCC."
-  nil)
+(defvar mail-send-hook nil
+  "Normal hook run before sending mail, in Mail mode.")
 
 (defun mail-setup (to subject in-reply-to cc replybuffer actions)
-  (setq mail-send-actions actions)
   (if (eq mail-aliases t)
       (progn
        (setq mail-aliases nil)
        (if (file-exists-p "~/.mailrc")
            (build-mail-aliases))))
+  (setq mail-send-actions actions)
   (setq mail-reply-buffer replybuffer)
   (goto-char (point-min))
   (insert "To: ")
   (save-excursion
     (if to
-       (progn
+       ;; Here removed code to extract names from within <...>
+       ;; on the assumption that mail-strip-quoted-names
+       ;; has been called and has done so.
+       (let ((fill-prefix "\t")
+             (address-start (point)))
          (insert to "\n")
-         ;;; Here removed code to extract names from within <...>
-         ;;; on the assumption that mail-strip-quoted-names
-         ;;; has been called and has done so.
-         (let ((fill-prefix "\t"))
-           (fill-region (point-min) (point-max))))
+         (fill-region-as-paragraph address-start (point-max)))
       (newline))
     (if cc
-       (let ((opos (point))
-             (fill-prefix "\t"))
-         (insert "CC: " cc "\n")
-         (fill-region-as-paragraph opos (point-max))))
+       (let ((fill-prefix "\t")
+             (address-start (progn (insert "CC: ") (point))))
+         (insert cc "\n")
+         (fill-region-as-paragraph address-start (point-max))))
     (if in-reply-to
        (insert "In-reply-to: " in-reply-to "\n"))
     (insert "Subject: " (or subject "") "\n")
@@ -138,16 +195,18 @@ Suitable header fields are To, CC and BCC."
     (if mail-archive-file-name
        (insert "FCC: " mail-archive-file-name "\n"))
     (insert mail-header-separator "\n")
-    ;; Read the .signature file if we haven't already done so
-    ;; (and if the user has not overridden it).
+    ;; Insert the signature.  But remember the beginning of the message.
+    (if to (setq to (point)))
     (cond ((eq mail-signature t)
-          (insert "--\n")
-          (insert-file-contents "~/.signature"))
+          (if (file-exists-p "~/.signature")
+              (progn
+                (insert "\n\n-- \n")
+                (insert-file-contents "~/.signature"))))
          (mail-signature
           (insert mail-signature)))
     (goto-char (point-max))
     (or (bolp) (newline)))
-  (if to (goto-char (point-max)))
+  (if to (goto-char to))
   (or to subject in-reply-to
       (set-buffer-modified-p nil))
   (run-hooks 'mail-setup-hook))
@@ -160,6 +219,7 @@ C-c C-s  mail-send (send the message)    C-c C-c  mail-send-and-exit
 C-c C-f  move to a header field (and create it if there isn't):
         C-c C-f C-t  move to To:       C-c C-f C-s  move to Subj:
         C-c C-f C-b  move to BCC:      C-c C-f C-c  move to CC:
+        C-c C-f C-f  move to FCC:
 C-c C-t  move to message text.
 C-c C-y  mail-yank-original (insert current message, in Rmail).
 C-c C-q  mail-fill-yanked-message (fill what was yanked).
@@ -184,6 +244,8 @@ C-c C-v  mail-sent-via (add a sent-via field for each To or CC)."
                                   "$\\|^[ \t]*[-_][-_][-_]+$\\|"
                                   paragraph-separate))
   (run-hooks 'text-mode-hook 'mail-mode-hook))
+\f
+;;; Set up keymap.
 
 (if mail-mode-map
     nil
@@ -191,6 +253,7 @@ C-c C-v  mail-sent-via (add a sent-via field for each To or CC)."
   (define-key mail-mode-map "\C-c?" 'describe-mode)
   (define-key mail-mode-map "\C-c\C-f\C-t" 'mail-to)
   (define-key mail-mode-map "\C-c\C-f\C-b" 'mail-bcc)
+  (define-key mail-mode-map "\C-c\C-f\C-f" 'mail-fcc)
   (define-key mail-mode-map "\C-c\C-f\C-c" 'mail-cc)
   (define-key mail-mode-map "\C-c\C-f\C-s" 'mail-subject)
   (define-key mail-mode-map "\C-c\C-t" 'mail-text)
@@ -200,16 +263,90 @@ C-c C-v  mail-sent-via (add a sent-via field for each To or CC)."
   (define-key mail-mode-map "\C-c\C-v" 'mail-sent-via)
   (define-key mail-mode-map "\C-c\C-c" 'mail-send-and-exit)
   (define-key mail-mode-map "\C-c\C-s" 'mail-send))
+
+(define-key mail-mode-map [menu-bar mail]
+  (cons "Mail" (make-sparse-keymap "Mail")))
+
+(define-key mail-mode-map [menu-bar mail fill]
+  '("Fill Citation" . mail-fill-yanked-message))
+
+(define-key mail-mode-map [menu-bar mail yank]
+  '("Cite Original" . mail-yank-original))
+
+(define-key mail-mode-map [menu-bar mail signature]
+  '("Insert Signature" . mail-signature))
+
+(define-key mail-mode-map [menu-bar mail cancel]
+  '("Cancel" . mail-dont-send))
+
+(define-key mail-mode-map [menu-bar mail send-stay]
+  '("Send, Keep Editing" . mail-send))
+
+(define-key mail-mode-map [menu-bar mail send]
+  '("Send Message" . mail-send-and-exit))
+
+(define-key mail-mode-map [menu-bar headers]
+  (cons "Headers" (make-sparse-keymap "Headers")))
+
+(define-key mail-mode-map [menu-bar headers sent-via]
+  '("Sent Via" . mail-sent-via))
+
+(define-key mail-mode-map [menu-bar headers text]
+  '("Text" . mail-text))
+
+(define-key mail-mode-map [menu-bar headers bcc]
+  '("Bcc" . mail-bcc))
+
+(define-key mail-mode-map [menu-bar headers fcc]
+  '("Fcc" . mail-fcc))
+
+(define-key mail-mode-map [menu-bar headers cc]
+  '("Cc" . mail-cc))
+
+(define-key mail-mode-map [menu-bar headers subject]
+  '("Subject" . mail-subject))
+
+(define-key mail-mode-map [menu-bar headers to]
+  '("To" . mail-to))
 \f
 (defun mail-send-and-exit (arg)
-  "Send message like mail-send, then, if no errors, exit from mail buffer.
+  "Send message like `mail-send', then, if no errors, exit from mail buffer.
 Prefix arg means don't delete this window."
   (interactive "P")
   (mail-send)
-  (bury-buffer (current-buffer))
-  (if (or (one-window-p) arg)
-      (switch-to-buffer (other-buffer (current-buffer)))
-    (delete-window)))
+  (mail-bury arg))
+
+(defun mail-dont-send (arg)
+  "Don't send the message you have been editing.
+Prefix arg means don't delete this window."
+  (interactive "P")
+  (mail-bury arg))
+
+(defun mail-bury (arg)
+  "Bury this mail buffer."
+  (let ((newbuf (other-buffer (current-buffer))))
+    (bury-buffer (current-buffer))
+    (if (and (fboundp 'frame-parameters)
+            (cdr (assq 'dedicated (frame-parameters)))
+            (not (null (delq (selected-frame) (visible-frame-list)))))
+       (delete-frame (selected-frame))
+      (let (rmail-flag summary-buffer)
+       (and (not arg)
+            (not (one-window-p))
+            (save-excursion
+              (set-buffer (window-buffer (next-window (selected-window) 'not)))
+              (setq rmail-flag (eq major-mode 'rmail-mode))
+              (setq summary-buffer
+                    (and (boundp 'rmail-summary-buffer)
+                         rmail-summary-buffer
+                         (buffer-name rmail-summary-buffer)
+                         (not (get-buffer-window rmail-summary-buffer))
+                         rmail-summary-buffer))))
+       (if rmail-flag
+           ;; If the Rmail buffer has a summary, show that.
+           (if summary-buffer (switch-to-buffer summary-buffer)
+             (delete-window))
+         (switch-to-buffer newbuf))))))
 
 (defun mail-send ()
   "Send the message in the current buffer.
@@ -218,21 +355,27 @@ or error messages, and inform user.
 Otherwise any failure is reported in a message back to
 the user from the mailer."
   (interactive)
-  (if (or (buffer-modified-p)
-          (y-or-n-p "Message already sent; resend? "))
+  (if (if buffer-file-name
+         (y-or-n-p "Send buffer contents as mail message? ")
+       (or (buffer-modified-p)
+           (y-or-n-p "Message already sent; resend? ")))
       (progn
-       (message "Sending...")
        (run-hooks 'mail-send-hook)
+       (message "Sending...")
        (funcall send-mail-function)
        ;; Now perform actions on successful sending.
        (while mail-send-actions
          (condition-case nil
-             (apply (car (car mail-send-actions)) (cdr (car mail-send-actions)))
+             (apply (car (car mail-send-actions))
+                    (cdr (car mail-send-actions)))
            (error))
          (setq mail-send-actions (cdr mail-send-actions)))
-       (set-buffer-modified-p nil)
-       (delete-auto-save-file-if-necessary t)
-       (message "Sending...done"))))
+       (message "Sending...done")
+       ;; If buffer has no file, mark it as unmodified and delete autosave.
+       (if (not buffer-file-name)
+           (progn
+             (set-buffer-modified-p nil)
+             (delete-auto-save-file-if-necessary t))))))
 
 (defun sendmail-send-it ()
   (let ((errbuf (if mail-interactive
@@ -240,6 +383,7 @@ the user from the mailer."
                  0))
        (tembuf (generate-new-buffer " sendmail temp"))
        (case-fold-search nil)
+       resend-to-addresses
        delimline
        (mailbuf (current-buffer)))
     (unwind-protect
@@ -267,26 +411,36 @@ the user from the mailer."
            (replace-match "\n"))
          (let ((case-fold-search t))
            (goto-char (point-min))
-           (if (re-search-forward "^Sender:" delimline t)
-               (error "Sender may not be specified."))
            ;; Find and handle any FCC fields.
            (goto-char (point-min))
            (if (re-search-forward "^FCC:" delimline t)
                (mail-do-fcc delimline))
-           ;; If the From is different than current user, insert Sender.
            (goto-char (point-min))
-           (and (re-search-forward "^From:"  delimline t)
-                (progn
-                  (require 'mail-utils)
-                  (not (string-equal
-                        (mail-strip-quoted-names
-                         (save-restriction
-                           (narrow-to-region (point-min) delimline)
-                           (mail-fetch-field "From")))
-                        (user-login-name))))
-                (progn
-                  (forward-line 1)
-                  (insert "Sender: " (user-login-name) "\n")))
+           (require 'mail-utils)
+           (while (re-search-forward "^Resent-to:" delimline t)
+             (setq resend-to-addresses
+                   (save-restriction
+                     (narrow-to-region (point)
+                                       (save-excursion
+                                         (end-of-line)
+                                         (point)))
+                     (append (mail-parse-comma-list)
+                             resend-to-addresses))))
+;;; Apparently this causes a duplicate Sender.
+;;;        ;; If the From is different than current user, insert Sender.
+;;;        (goto-char (point-min))
+;;;        (and (re-search-forward "^From:"  delimline t)
+;;;             (progn
+;;;               (require 'mail-utils)
+;;;               (not (string-equal
+;;;                     (mail-strip-quoted-names
+;;;                      (save-restriction
+;;;                        (narrow-to-region (point-min) delimline)
+;;;                        (mail-fetch-field "From")))
+;;;                     (user-login-name))))
+;;;             (progn
+;;;               (forward-line 1)
+;;;               (insert "Sender: " (user-login-name) "\n")))
            ;; "S:" is an abbreviation for "Subject:".
            (goto-char (point-min))
            (if (re-search-forward "^S:" delimline t)
@@ -295,6 +449,11 @@ the user from the mailer."
            (goto-char (point-min))
            (if (re-search-forward "^Subject:[ \t]*\n" delimline t)
                (replace-match ""))
+           ;; Insert an extra newline if we need it to work around
+           ;; Sun's bug that swallows newlines.
+           (goto-char (1+ delimline))
+           (if (eval mail-mailer-swallows-blank-line)
+               (newline))
            (if mail-interactive
                (save-excursion
                  (set-buffer errbuf)
@@ -304,8 +463,7 @@ the user from the mailer."
                               (if (boundp 'sendmail-program)
                                   sendmail-program
                                 "/usr/lib/sendmail")
-                              nil errbuf nil
-                              "-oi" "-t")
+                              nil errbuf nil "-oi")
                         ;; Always specify who from,
                         ;; since some systems have broken sendmails.
                         (list "-f" (user-login-name))
@@ -316,7 +474,14 @@ the user from the mailer."
                              (list (concat "-oA" mail-alias-file)))
                         ;; These mean "report errors by mail"
                         ;; and "deliver in background".
-                        (if (null mail-interactive) '("-oem" "-odb"))))
+                        (if (null mail-interactive) '("-oem" "-odb"))
+                        ;; Get the addresses from the message
+                        ;; unless this is a resend.
+                        ;; We must not do that for a resend
+                        ;; because we would find the original addresses.
+                        ;; For a resend, include the specific addresses.
+                        (or resend-to-addresses
+                            '("-t"))))
          (if mail-interactive
              (save-excursion
                (set-buffer errbuf)
@@ -333,6 +498,7 @@ the user from the mailer."
 (defun mail-do-fcc (header-end)
   (let (fcc-list
        (rmailbuf (current-buffer))
+       (time (current-time))
        (tembuf (generate-new-buffer " rmail output"))
        (case-fold-search t))
     (save-excursion
@@ -348,8 +514,15 @@ the user from the mailer."
                       (progn (forward-line 1) (point))))
       (set-buffer tembuf)
       (erase-buffer)
+      ;; This initial newline is written out if the fcc file already exists.
       (insert "\nFrom " (user-login-name) " "
-             (current-time-string) "\n")
+             (current-time-string time) "\n")
+      ;; Insert the time zone before the year.
+      (forward-char -1)
+      (forward-word -1)
+      (require 'mail-utils)
+      (insert (mail-rfc822-time-zone time) " ")
+      (goto-char (point-max))
       (insert-buffer-substring rmailbuf)
       ;; Make sure messages are separated.
       (goto-char (point-max))
@@ -367,7 +540,9 @@ the user from the mailer."
          (if buffer
              ;; File is present in a buffer => append to that buffer.
              (let ((curbuf (current-buffer))
-                   (beg (point-min)) (end (point-max)))
+                   (beg (point-min)) (end (point-max))
+                   (beg2 (save-excursion (goto-char (point-min))
+                                         (forward-line 2) (point))))
                (save-excursion
                  (set-buffer buffer)
                  ;; Keep the end of the accessible portion at the same place
@@ -375,29 +550,38 @@ the user from the mailer."
                  (let ((max (if (/= (1+ (buffer-size)) (point-max))
                                 (point-max))))
                    (unwind-protect
-                       (progn
-                         (narrow-to-region (point-min) (1+ (buffer-size)))
-                         (goto-char (point-max))
-                         (if (eq major-mode 'rmail-mode)
-                             ;; Append as a message to an RMAIL file
-                             (let ((buffer-read-only nil))
-                               ;; This forces RMAIL's message counters to be
-                               ;; recomputed when the next RMAIL operation is
-                               ;; done on the buffer.
-                               ;; See rmail-maybe-set-message-counters.
-                               (setq rmail-total-messages nil)
+                       ;; Code below lifted from rmailout.el
+                       ;; function rmail-output-to-rmail-file:
+                       (let ((buffer-read-only nil)
+                             (msg (and (boundp 'rmail-current-message)
+                                       rmail-current-message)))
+                         ;; If MSG is non-nil, buffer is in RMAIL mode.
+                         (if msg
+                             (progn
+                               (rmail-maybe-set-message-counters)
+                               (widen)
+                               (narrow-to-region (point-max) (point-max))
                                (insert "\C-l\n0, unseen,,\n*** EOOH ***\n"
                                        "From: " (user-login-name) "\n"
-                                       "Date: " (current-time-string) "\n")
-                               (insert-buffer-substring curbuf beg end)
-                               (insert "\n\C-_"))
+                                       "Date: " (mail-rfc822-date) "\n")
+                               (insert-buffer-substring curbuf beg2 end)
+                               (insert "\n\C-_")
+                               (goto-char (point-min))
+                               (widen)
+                               (search-backward "\n\^_")
+                               (narrow-to-region (point) (point-max))
+                               (rmail-count-new-messages t)
+                               (rmail-show-message msg)
+                               (setq max nil))
+                           ;; Output file not in rmail mode
+                           ;; => just insert at the end.
+                           (narrow-to-region (point-min) (1+ (buffer-size)))
+                           (goto-char (point-max))
                            (insert-buffer-substring curbuf beg end)))
                      (if max (narrow-to-region (point-min) max))))))
            ;; Else append to the file directly.
            (write-region
-            ;; Include a blank line before if file already exists.
-            (if (file-exists-p (car fcc-list)) (point-min) (1+ (point-min)))
-            (point-max) (car fcc-list) t)))
+            (1+ (point-min)) (point-max) (car fcc-list) t)))
        (setq fcc-list (cdr fcc-list))))
     (kill-buffer tembuf)))
 
@@ -456,11 +640,19 @@ the user from the mailer."
       (progn (mail-position-on-field "to")
             (insert "\nBCC: "))))
 
+(defun mail-fcc (folder)
+  "Add a new FCC field, with file name completion."
+  (interactive "FFolder carbon copy: ")
+  (expand-abbrev)
+  (or (mail-position-on-field "fcc" t) ;Put new field after exiting FCC.
+      (mail-position-on-field "to"))
+  (insert "\nFCC: " folder))
+
 (defun mail-position-on-field (field &optional soft)
   (let (end
        (case-fold-search t))
     (goto-char (point-min))
-    (search-forward (concat "\n" mail-header-separator "\n"))
+    (re-search-forward (concat "^" (regexp-quote mail-header-separator) "\n"))
     (setq end (match-beginning 0))
     (goto-char (point-min))
     (if (re-search-forward (concat "^" (regexp-quote field) ":") end t)
@@ -471,8 +663,8 @@ the user from the mailer."
          t)
       (or soft
          (progn (goto-char end)
-                (skip-chars-backward "\n")
-                (insert "\n" field ": ")))
+                (insert field ": \n")
+                (skip-chars-backward "\n")))
       nil)))
 
 (defun mail-text ()
@@ -481,15 +673,18 @@ the user from the mailer."
   (goto-char (point-min))
   (search-forward (concat "\n" mail-header-separator "\n")))
 \f
-(defun mail-signature ()
-  "Sign letter with contents of ~/.signature file."
-  (interactive)
+(defun mail-signature (atpoint)
+  "Sign letter with contents of the file `~/.signature'.
+Prefix arg means put contents at point."
+  (interactive "P")
   (save-excursion
-    (goto-char (point-max))
+    (or atpoint
+       (goto-char (point-max)))
     (skip-chars-backward " \t\n")
     (end-of-line)
-    (delete-region (point) (point-max))
-    (insert "\n\n--\n")
+    (or atpoint
+       (delete-region (point) (point-max)))
+    (insert "\n\n-- \n")
     (insert-file-contents (expand-file-name "~/.signature"))))
 
 (defun mail-fill-yanked-message (&optional justifyp)
@@ -504,6 +699,23 @@ Numeric argument means justify as well."
                                justifyp
                                t)))
 
+(defun mail-indent-citation ()
+  "Modify text just inserted from a message to be cited.
+The inserted text should be the region.
+When this function returns, the region is again around the modified text.
+
+Normally, indent each nonblank line `mail-indentation-spaces' spaces.
+However, if `mail-yank-prefix' is non-nil, insert that prefix on each line."
+  (let ((start (point)))
+    (mail-yank-clear-headers start (mark t))
+    (if (null mail-yank-prefix)
+       (indent-rigidly start (mark t) mail-indentation-spaces)
+      (save-excursion
+       (goto-char start)
+       (while (< (point) (mark t))
+         (insert mail-yank-prefix)
+         (forward-line 1))))))
+
 (defun mail-yank-original (arg)
   "Insert the message being replied to, if any (in rmail).
 Puts point before the text and mark after.
@@ -515,20 +727,26 @@ and don't delete any header fields."
   (interactive "P")
   (if mail-reply-buffer
       (let ((start (point)))
-       (delete-windows-on mail-reply-buffer)
+       ;; If the original message is in another window in the same frame,
+       ;; delete that window to save screen space.
+       ;; t means don't alter other frames.
+       (delete-windows-on mail-reply-buffer t)
        (insert-buffer mail-reply-buffer)
        (if (consp arg)
            nil
-         (mail-yank-clear-headers start (mark))
-         (if (null mail-yank-prefix)
-             (indent-rigidly start (mark)
-                             (if arg (prefix-numeric-value arg) 3))
-           (save-excursion
-             (goto-char start)
-             (while (< (point) (mark))
-               (insert mail-yank-prefix)
-               (forward-line 1)))))
-       (exchange-point-and-mark)
+         (goto-char start)
+         (let ((mail-indentation-spaces (if arg (prefix-numeric-value arg)
+                                          mail-indentation-spaces)))
+           (if mail-citation-hook
+               (run-hooks 'mail-citation-hook)
+             (if mail-yank-hooks
+                 (run-hooks 'mail-yank-hooks)
+               (mail-indent-citation)))))
+       ;; This is like exchange-point-and-mark, but doesn't activate the mark.
+       ;; It is cleaner to avoid activation, even though the command
+       ;; loop would deactivate the mark because we inserted text.
+       (goto-char (prog1 (mark t)
+                    (set-marker (mark-marker) (point) (current-buffer))))
        (if (not (eolp)) (insert ?\n)))))
 
 (defun mail-yank-clear-headers (start end)
@@ -550,12 +768,10 @@ and don't delete any header fields."
 
 ;;;###autoload
 (defun mail (&optional noerase to subject in-reply-to cc replybuffer actions)
-  "Edit a message to be sent.  Argument means resume editing (don't erase).
-Search for an existing mail buffer currently not in use and initialize it,
-or make a new one if all existing mail buffers are busy.
-With an argument, search for a busy existing mail buffer and re-select it.
+  "Edit a message to be sent.  Prefix arg means resume editing (don't erase).
+When this function returns, the buffer `*mail*' is selected.
+The value is t if the message was newly initialized; otherwise, nil.
 
-Returns with message buffer selected; value t if message freshly initialized.
 By default, the signature file `~/.signature' is inserted at the end;
 see the variable `mail-signature'.
 
@@ -589,47 +805,67 @@ The seventh argument ACTIONS is a list of actions to take
  when the message is sent, we apply FUNCTION to ARGS.
  This is how Rmail arranges to mark messages `answered'."
   (interactive "P")
-  (let ((index 1)
-       buffer)
-    ;; If requested, look for a mail buffer that is modified and go to it.
-    (if noerase
-       (progn
-         (while (and (setq buffer
-                           (get-buffer (if (= 1 index) "*mail*"
-                                         (format "*mail*<%d>" index))))
-                     (not (buffer-modified-p buffer)))
-           (setq index (1+ index)))
-         (if buffer (switch-to-buffer buffer)
-           ;; If none exists, start a new message.
-           ;; This will never re-use an existing unmodified mail buffer
-           ;; (since index is not 1 anymore).  Perhaps it should.
-           (setq noerase nil))))
-    ;; Unless we found a modified message and are happy, start a new message.
-    (if (not noerase)
-       (progn
-         ;; Look for existing unmodified mail buffer.
-         (while (and (setq buffer
-                           (get-buffer (if (= 1 index) "*mail*"
-                                         (format "*mail*<%d>" index))))
-                     (buffer-modified-p buffer))
-           (setq index (1+ index)))
-         ;; If none, make a new one.
-         (or buffer
-             (setq buffer (generate-new-buffer "*mail*")))
-         ;; Go there and initialize it.
-         (switch-to-buffer buffer)
-         (erase-buffer)
-          (setq default-directory (expand-file-name "~/"))
-          (auto-save-mode auto-save-default)
-          (mail-mode)
-          (mail-setup to subject in-reply-to cc replybuffer actions)
-         (if (and buffer-auto-save-file-name
-                  (file-exists-p buffer-auto-save-file-name))
-             (message "Auto save file for draft message exists; consider M-x mail-recover"))
-          t))))
-
-;;;###autoload
-(define-key ctl-x-map "m" 'mail)
+;;; This is commented out because I found it was confusing in practice.
+;;; It is easy enough to rename *mail* by hand with rename-buffer
+;;; if you want to have multiple mail buffers.
+;;; And then you can control which messages to save. --rms.
+;;;  (let ((index 1)
+;;;    buffer)
+;;;    ;; If requested, look for a mail buffer that is modified and go to it.
+;;;    (if noerase
+;;;    (progn
+;;;      (while (and (setq buffer
+;;;                        (get-buffer (if (= 1 index) "*mail*"
+;;;                                      (format "*mail*<%d>" index))))
+;;;                  (not (buffer-modified-p buffer)))
+;;;        (setq index (1+ index)))
+;;;      (if buffer (switch-to-buffer buffer)
+;;;        ;; If none exists, start a new message.
+;;;        ;; This will never re-use an existing unmodified mail buffer
+;;;        ;; (since index is not 1 anymore).  Perhaps it should.
+;;;        (setq noerase nil))))
+;;;    ;; Unless we found a modified message and are happy, start a new message.
+;;;    (if (not noerase)
+;;;    (progn
+;;;      ;; Look for existing unmodified mail buffer.
+;;;      (while (and (setq buffer
+;;;                        (get-buffer (if (= 1 index) "*mail*"
+;;;                                      (format "*mail*<%d>" index))))
+;;;                  (buffer-modified-p buffer))
+;;;        (setq index (1+ index)))
+;;;      ;; If none, make a new one.
+;;;      (or buffer
+;;;          (setq buffer (generate-new-buffer "*mail*")))
+;;;      ;; Go there and initialize it.
+;;;      (switch-to-buffer buffer)
+;;;      (erase-buffer)
+;;;          (setq default-directory (expand-file-name "~/"))
+;;;          (auto-save-mode auto-save-default)
+;;;          (mail-mode)
+;;;          (mail-setup to subject in-reply-to cc replybuffer actions)
+;;;      (if (and buffer-auto-save-file-name
+;;;               (file-exists-p buffer-auto-save-file-name))
+;;;          (message "Auto save file for draft message exists; consider M-x mail-recover"))
+;;;          t))
+  (switch-to-buffer "*mail*")
+  (if (file-exists-p (expand-file-name "~/"))
+      (setq default-directory (expand-file-name "~/")))
+  (auto-save-mode auto-save-default)
+  (mail-mode)
+  ;; Disconnect the buffer from its visited file
+  ;; (in case the user has actually visited a file *mail*).
+;  (set-visited-file-name nil)
+  (let (initialized)
+    (and (not noerase)
+        (or (not (buffer-modified-p))
+            (y-or-n-p "Unsent message being composed; erase it? "))
+        (progn (erase-buffer)
+               (mail-setup to subject in-reply-to cc replybuffer actions)
+               (setq initialized t)))
+    (if (and buffer-auto-save-file-name
+            (file-exists-p buffer-auto-save-file-name))
+       (message "Auto save file for draft message exists; consider M-x mail-recover"))
+    initialized))
 
 (defun mail-recover ()
   "Reread contents of current buffer from its last auto-save file."
@@ -644,7 +880,7 @@ The seventh argument ACTIONS is a list of actions to take
           (let ((buffer-read-only nil))
             (erase-buffer)
             (insert-file-contents file-name nil)))
-         (t (error "mail-recover cancelled.")))))
+         (t (error "mail-recover cancelled")))))
 
 ;;;###autoload
 (defun mail-other-window (&optional noerase to subject in-reply-to cc replybuffer sendactions)
@@ -655,19 +891,21 @@ The seventh argument ACTIONS is a list of actions to take
   (mail noerase to subject in-reply-to cc replybuffer sendactions))
 
 ;;;###autoload
-(defun mail-other-screen (&optional noerase to subject in-reply-to cc replybuffer sendactions)
-  "Like `mail' command, but display mail buffer in another screen."
+(defun mail-other-frame (&optional noerase to subject in-reply-to cc replybuffer sendactions)
+  "Like `mail' command, but display mail buffer in another frame."
   (interactive "P")
-  (let ((pop-up-screens t))
+  (let ((pop-up-frames t))
     (pop-to-buffer "*mail*"))
   (mail noerase to subject in-reply-to cc replybuffer sendactions))
 
+;;; Do not execute these when sendmail.el is loaded,
+;;; only in loaddefs.el.
+;;;###autoload (define-key ctl-x-map "m" 'mail)
+;;;###autoload (define-key ctl-x-4-map "m" 'mail-other-window)
+;;;###autoload (define-key ctl-x-5-map "m" 'mail-other-frame)
 
-;;;###autoload
-(define-key ctl-x-4-map "m" 'mail-other-window)
-
-;;;###autoload
-(define-key ctl-x-3-map "m" 'mail-other-screen)
+;;; Do not add anything but external entries on this page.
 
+(provide 'sendmail)
 
-;;; Do not add anything but external entries on this page.
+;;; sendmail.el ends here