Merge the t-gnus-6_17-quimby branch.
[elisp/gnus.git-] / lisp / message.el
index be0326a..cbd0861 100644 (file)
@@ -1,6 +1,7 @@
 ;;; message.el --- composing mail and news messages
-;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-;;        Free Software Foundation, Inc.
+
+;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+;;   2005, 2006 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
@@ -25,8 +26,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
@@ -45,6 +46,7 @@
 (require 'canlock)
 (require 'mailheader)
 (require 'nnheader)
+(require 'gmm-utils)
 ;; This is apparently necessary even though things are autoloaded.
 ;; Because we dynamically bind mail-abbrev-mode-regexp, we'd better
 ;; require mailabbrev here.
 (put 'user-full-name 'custom-type 'string)
 
 (defgroup message-various nil
-  "Various Message Variables"
+  "Various Message Variables."
   :link '(custom-manual "(message)Various Message Variables")
   :group 'message)
 
 (defgroup message-buffers nil
-  "Message Buffers"
+  "Message Buffers."
   :link '(custom-manual "(message)Message Buffers")
   :group 'message)
 
 (defgroup message-sending nil
-  "Message Sending"
+  "Message Sending."
   :link '(custom-manual "(message)Sending Variables")
   :group 'message)
 
 (defgroup message-interface nil
-  "Message Interface"
+  "Message Interface."
   :link '(custom-manual "(message)Interface")
   :group 'message)
 
 (defgroup message-forwarding nil
-  "Message Forwarding"
+  "Message Forwarding."
   :link '(custom-manual "(message)Forwarding")
   :group 'message-interface)
 
 (defgroup message-insertion nil
-  "Message Insertion"
+  "Message Insertion."
   :link '(custom-manual "(message)Insertion")
   :group 'message)
 
 (defgroup message-headers nil
-  "Message Headers"
+  "Message Headers."
   :link '(custom-manual "(message)Message Headers")
   :group 'message)
 
 (defgroup message-news nil
-  "Composing News Messages"
+  "Composing News Messages."
   :group 'message)
 
 (defgroup message-mail nil
-  "Composing Mail Messages"
+  "Composing Mail Messages."
   :group 'message)
 
 (defgroup message-faces nil
@@ -1021,7 +1023,8 @@ The function `message-supersede' runs this hook."
     (set-keymap-parent map minibuffer-local-map)
     map)
   "Keymap for `message-read-from-minibuffer'."
-  :version "22.1")
+  :version "22.1"
+  :group 'message-various)
 
 ;;;###autoload
 (defcustom message-citation-line-function 'message-insert-citation-line
@@ -1039,33 +1042,23 @@ configuration.  See the variable `gnus-cite-attribution-suffix'."
 (defcustom message-yank-prefix "> "
   "*Prefix inserted on the lines of yanked messages.
 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
-See also `message-yank-cited-prefix'."
+See also `message-yank-cited-prefix' and `message-yank-empty-prefix'."
   :type 'string
   :link '(custom-manual "(message)Insertion Variables")
   :group 'message-insertion)
 
-(defcustom message-yank-add-new-references t
-  "Non-nil means new IDs will be added to \"References\" field when an
-article is yanked by the command `message-yank-original' interactively.
-If it is a symbol `message-id-only', only an ID from \"Message-ID\" field
-is used, otherwise IDs extracted from \"References\", \"In-Reply-To\" and
-\"Message-ID\" fields are used."
-  :type '(radio (const :tag "Do not add anything" nil)
-               (const :tag "From Message-Id, References and In-Reply-To fields" t)
-               (const :tag "From only Message-Id field." message-id-only))
-  :group 'message-insertion)
-
-(defcustom message-list-references-add-position nil
-  "Integer value means position for adding to \"References\" field when
-an article is yanked by the command `message-yank-original' interactively."
-  :type '(radio (const :tag "Add to last" nil)
-               (integer :tag "Position from last ID"))
-  :group 'message-insertion)
-
 (defcustom message-yank-cited-prefix ">"
-  "*Prefix inserted on cited or empty lines of yanked messages.
+  "*Prefix inserted on cited lines of yanked messages.
 Fix `message-cite-prefix-regexp' if it is set to an abnormal value.
-See also `message-yank-prefix'."
+See also `message-yank-prefix' and `message-yank-empty-prefix'."
+  :version "22.1"
+  :type 'string
+  :link '(custom-manual "(message)Insertion Variables")
+  :group 'message-insertion)
+
+(defcustom message-yank-empty-prefix ">"
+  "*Prefix inserted on empty lines of yanked messages.
+See also `message-yank-prefix' and `message-yank-cited-prefix'."
   :version "22.1"
   :type 'string
   :link '(custom-manual "(message)Insertion Variables")
@@ -1083,7 +1076,7 @@ Used by `message-yank-original' via `message-yank-cite'."
   "*Function for citing an original message.
 Predefined functions include `message-cite-original' and
 `message-cite-original-without-signature'.
-Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
+Note that these functions use `mail-citation-hook' if that is non-nil."
   :type '(radio (function-item message-cite-original)
                (function-item message-cite-original-without-signature)
                (function-item mu-cite-original)
@@ -1093,6 +1086,16 @@ Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
   :group 'message-insertion)
 
 ;;;###autoload
+(defcustom message-indent-citation-function 'message-indent-citation
+  "*Function for modifying a citation just inserted in the mail buffer.
+This can also be a list of functions.  Each function can find the
+citation between (point) and (mark t).  And each function should leave
+point and mark around the citation text as modified."
+  :type 'function
+  :link '(custom-manual "(message)Insertion Variables")
+  :group 'message-insertion)
+
+;;;###autoload
 (defcustom message-suspend-font-lock-when-citing nil
   "Non-nil means suspend font-lock'ing while citing an original message.
 Some lazy demand-driven fontification tools (or Emacs itself) have a
@@ -1103,14 +1106,23 @@ even if it is an add-hoc expedient."
   :type 'boolean
   :group 'message-insertion)
 
-;;;###autoload
-(defcustom message-indent-citation-function 'message-indent-citation
-  "*Function for modifying a citation just inserted in the mail buffer.
-This can also be a list of functions.  Each function can find the
-citation between (point) and (mark t).  And each function should leave
-point and mark around the citation text as modified."
-  :type 'function
-  :link '(custom-manual "(message)Insertion Variables")
+(defcustom message-yank-add-new-references t
+  "Non-nil means new IDs will be added to \"References\" field when an
+article is yanked by the command `message-yank-original' interactively.
+If it is a symbol `message-id-only', only an ID from \"Message-ID\" field
+is used, otherwise IDs extracted from \"References\", \"In-Reply-To\" and
+\"Message-ID\" fields are used."
+  :type '(radio
+         (const :tag "Do not add anything" nil)
+         (const :tag "From Message-Id, References and In-Reply-To fields" t)
+         (const :tag "From only Message-Id field." message-id-only))
+  :group 'message-insertion)
+
+(defcustom message-list-references-add-position nil
+  "Integer value means position for adding to \"References\" field when
+an article is yanked by the command `message-yank-original' interactively."
+  :type '(radio (const :tag "Add to last" nil)
+               (integer :tag "Position from last ID"))
   :group 'message-insertion)
 
 ;;;###autoload
@@ -1349,7 +1361,7 @@ starting with `not' and followed by regexps."
     table)
   "Syntax table used while in Message mode.")
 
-(defface message-header-to-face
+(defface message-header-to
   '((((class color)
       (background dark))
      (:foreground "green2" :bold t))
@@ -1360,8 +1372,10 @@ starting with `not' and followed by regexps."
      (:bold t :italic t)))
   "Face used for displaying From headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-to-face 'face-alias 'message-header-to)
 
-(defface message-header-cc-face
+(defface message-header-cc
   '((((class color)
       (background dark))
      (:foreground "green4" :bold t))
@@ -1372,8 +1386,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying Cc headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-cc-face 'face-alias 'message-header-cc)
 
-(defface message-header-subject-face
+(defface message-header-subject
   '((((class color)
       (background dark))
      (:foreground "green3"))
@@ -1384,8 +1400,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying subject headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-subject-face 'face-alias 'message-header-subject)
 
-(defface message-header-newsgroups-face
+(defface message-header-newsgroups
   '((((class color)
       (background dark))
      (:foreground "yellow" :bold t :italic t))
@@ -1396,8 +1414,10 @@ starting with `not' and followed by regexps."
      (:bold t :italic t)))
   "Face used for displaying newsgroups headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-newsgroups-face 'face-alias 'message-header-newsgroups)
 
-(defface message-header-other-face
+(defface message-header-other
   '((((class color)
       (background dark))
      (:foreground "#b00000"))
@@ -1408,8 +1428,10 @@ starting with `not' and followed by regexps."
      (:bold t :italic t)))
   "Face used for displaying newsgroups headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-other-face 'face-alias 'message-header-other)
 
-(defface message-header-name-face
+(defface message-header-name
   '((((class color)
       (background dark))
      (:foreground "DarkGreen"))
@@ -1420,8 +1442,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying header names."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-name-face 'face-alias 'message-header-name)
 
-(defface message-header-xheader-face
+(defface message-header-xheader
   '((((class color)
       (background dark))
      (:foreground "blue"))
@@ -1432,8 +1456,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying X-Header headers."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-header-xheader-face 'face-alias 'message-header-xheader)
 
-(defface message-separator-face
+(defface message-separator
   '((((class color)
       (background dark))
      (:foreground "blue3"))
@@ -1444,8 +1470,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying the separator."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-separator-face 'face-alias 'message-separator)
 
-(defface message-cited-text-face
+(defface message-cited-text
   '((((class color)
       (background dark))
      (:foreground "red"))
@@ -1456,8 +1484,10 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying cited text names."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-cited-text-face 'face-alias 'message-cited-text)
 
-(defface message-mml-face
+(defface message-mml
   '((((class color)
       (background dark))
      (:foreground "ForestGreen"))
@@ -1468,6 +1498,8 @@ starting with `not' and followed by regexps."
      (:bold t)))
   "Face used for displaying MML."
   :group 'message-faces)
+;; backward-compatibility alias
+(put 'message-mml-face 'face-alias 'message-mml)
 
 (defun message-font-lock-make-header-matcher (regexp)
   (let ((form
@@ -1491,44 +1523,44 @@ starting with `not' and followed by regexps."
   (let ((content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)\n?"))
     `((,(message-font-lock-make-header-matcher
         (concat "^\\([Tt]o:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-to-face nil t))
+       (1 'message-header-name)
+       (2 'message-header-to nil t))
       (,(message-font-lock-make-header-matcher
         (concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"
                 "[Mm]ail-[Cc]opies-[Tt]o:\\|"
                 "[Mm]ail-[Rr]eply-[Tt]o:\\|"
                 "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-cc-face nil t))
+       (1 'message-header-name)
+       (2 'message-header-cc nil t))
       (,(message-font-lock-make-header-matcher
         (concat "^\\([Ss]ubject:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-subject-face nil t))
+       (1 'message-header-name)
+       (2 'message-header-subject nil t))
       (,(message-font-lock-make-header-matcher
         (concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-newsgroups-face nil t))
+       (1 'message-header-name)
+       (2 'message-header-newsgroups nil t))
       (,(message-font-lock-make-header-matcher
         (concat "^\\([A-Z][^: \n\t]+:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-other-face nil t))
+       (1 'message-header-name)
+       (2 'message-header-other nil t))
       (,(message-font-lock-make-header-matcher
         (concat "^\\(X-[A-Za-z0-9-]+:\\|In-Reply-To:\\)" content))
-       (1 'message-header-name-face)
-       (2 'message-header-name-face))
+       (1 'message-header-name)
+       (2 'message-header-name))
       ,@(if (and mail-header-separator
                 (not (equal mail-header-separator "")))
            `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
-              1 'message-separator-face))
+              1 'message-separator))
          nil)
       ((lambda (limit)
         (re-search-forward (concat "^\\("
                                    message-cite-prefix-regexp
                                    "\\).*")
                            limit t))
-       (0 'message-cited-text-face))
+       (0 'message-cited-text))
       (,mime-edit-tag-regexp
-       (0 'message-mml-face))))
+       (0 'message-mml))))
   "Additional expressions to highlight in Message mode.")
 
 ;; XEmacs does it like this.  For Emacs, we have to set the
@@ -1536,10 +1568,10 @@ starting with `not' and followed by regexps."
 (put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
 
 (defvar message-face-alist
-  '((bold . bold-region)
+  '((bold . message-bold-region)
     (underline . underline-region)
     (default . (lambda (b e)
-                (unbold-region b e)
+                (message-unbold-region b e)
                 (ununderline-region b e))))
   "Alist of mail and news faces for facemenu.
 The cdr of each entry is a function for applying the face to a region.")
@@ -1603,8 +1635,13 @@ should be sent in several parts.  If it is nil, the size is unlimited."
                 (integer 1000000)))
 
 (defcustom message-alternative-emails nil
-  "A regexp to match the alternative email addresses.
-The first matched address (not primary one) is used in the From field."
+  "*Regexp matching alternative email addresses.
+The first address in the To, Cc or From headers of the original
+article matching this variable is used as the From field of
+outgoing messages.
+
+This variable has precedence over posting styles and anything that runs
+off `message-setup-hook'."
   :group 'message-headers
   :link '(custom-manual "(message)Message Headers")
   :type '(choice (const :tag "Always use primary" nil)
@@ -1658,7 +1695,7 @@ no, only reply back to the author."
   :type 'boolean)
 
 (defcustom message-user-fqdn nil
-  "*Domain part of Messsage-Ids."
+  "*Domain part of Message-Ids."
   :version "22.1"
   :group 'message-headers
   :link '(custom-manual "(message)News Headers")
@@ -1669,8 +1706,13 @@ no, only reply back to the author."
                                   (file-error))
                                 (mm-coding-system-p 'utf-8)
                                 (executable-find idna-program)
-                                'ask)
-  "Whether to encode non-ASCII in domain names into ASCII according to IDNA."
+                                (string= (idna-to-ascii "räksmörgås")
+                                         "xn--rksmrgs-5wao1o")
+                                t)
+  "Whether to encode non-ASCII in domain names into ASCII according to IDNA.
+GNU Libidn, and in particular the elisp package \"idna.el\" and
+the external program \"idn\", must be installed for this
+functionality to work."
   :version "22.1"
   :group 'message-headers
   :link '(custom-manual "(message)IDNA")
@@ -2056,7 +2098,6 @@ Leading \"Re: \" is not stripped by this function.  Use the function
 
 ;;; Suggested by Jonas Steverud  @  www.dtek.chalmers.se/~d4jonas/
 
-;;;###autoload
 (defun message-change-subject (new-subject)
   "Ask for NEW-SUBJECT header, append (was: <Old Subject>)."
   ;; <URL:http://www.landfield.com/usefor/drafts/draft-ietf-usefor-useage--1.02.unpaged>
@@ -2088,32 +2129,31 @@ Leading \"Re: \" is not stripped by this function.  Use the function
                                    " (was: "
                                    old-subject ")\n")))))))))
 
-;;;###autoload
-(defun message-mark-inserted-region (beg end)
+(defun message-mark-inserted-region (beg end &optional verbatim)
   "Mark some region in the current article with enclosing tags.
-See `message-mark-insert-begin' and `message-mark-insert-end'."
-  (interactive "r")
+See `message-mark-insert-begin' and `message-mark-insert-end'.
+If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")."
+  (interactive "r\nP")
   (save-excursion
     ;; add to the end of the region first, otherwise end would be invalid
     (goto-char end)
-    (insert message-mark-insert-end)
+    (insert (if verbatim "#v-\n" message-mark-insert-end))
     (goto-char beg)
-    (insert message-mark-insert-begin)))
+    (insert (if verbatim "#v+\n" message-mark-insert-begin))))
 
-;;;###autoload
-(defun message-mark-insert-file (file)
+(defun message-mark-insert-file (file &optional verbatim)
   "Insert FILE at point, marking it with enclosing tags.
-See `message-mark-insert-begin' and `message-mark-insert-end'."
-  (interactive "fFile to insert: ")
+See `message-mark-insert-begin' and `message-mark-insert-end'.
+If VERBATIM, use slrn style verbatim marks (\"#v+\" and \"#v-\")."
+  (interactive "fFile to insert: \nP")
     ;; reverse insertion to get correct result.
   (let ((p (point)))
-    (insert message-mark-insert-end)
+    (insert (if verbatim "#v-\n" message-mark-insert-end))
     (goto-char p)
     (insert-file-contents file)
     (goto-char p)
-    (insert message-mark-insert-begin)))
+    (insert (if verbatim "#v+\n" message-mark-insert-begin))))
 
-;;;###autoload
 (defun message-add-archive-header ()
   "Insert \"X-No-Archive: Yes\" in the header and a note in the body.
 The note can be customized using `message-archive-note'.  When called with a
@@ -2133,7 +2173,6 @@ body, set  `message-archive-note' to nil."
       (message-add-header message-archive-header)
       (message-sort-headers)))
 
-;;;###autoload
 (defun message-cross-post-followup-to-header (target-group)
   "Mangles FollowUp-To and Newsgroups header to point to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
@@ -2177,7 +2216,6 @@ With prefix-argument just set Follow-Up, don't cross-post."
       (insert (concat "\nFollowup-To: " target-group)))
   (setq message-cross-post-old-target target-group))
 
-;;;###autoload
 (defun message-cross-post-insert-note (target-group cross-post in-old
                                                    old-groups)
   "Insert a in message body note about a set Followup or Crosspost.
@@ -2210,7 +2248,6 @@ been made to before the user asked for a Crosspost."
        (insert (concat message-followup-to-note target-group "\n"))
       (insert (concat message-cross-post-note target-group "\n")))))
 
-;;;###autoload
 (defun message-cross-post-followup-to (target-group)
   "Crossposts message and set Followup-To to TARGET-GROUP.
 With prefix-argument just set Follow-Up, don't cross-post."
@@ -2252,7 +2289,6 @@ With prefix-argument just set Follow-Up, don't cross-post."
 
 ;;; Reduce To: to Cc: or Bcc: header
 
-;;;###autoload
 (defun message-reduce-to-to-cc ()
  "Replace contents of To: header with contents of Cc: or Bcc: header."
  (interactive)
@@ -2444,6 +2480,17 @@ Point is left at the beginning of the narrowed-to region."
     (message-skip-to-next-address)
     (kill-region start (point))))
 
+
+(defun message-info (&optional arg)
+  "Display the Message manual.
+
+Prefixed with one \\[universal-argument], display the Emacs MIME manual.
+Prefixed with two \\[universal-argument]'s, display the PGG manual."
+  (interactive "p")
+  (cond ((eq arg 16) (Info-goto-node "(pgg)Top"))
+       ((eq arg  4) (Info-goto-node "(emacs-mime)Top"))
+       (t           (Info-goto-node "(message)Top"))))
+
 \f
 
 ;;;
@@ -2498,6 +2545,7 @@ Point is left at the beginning of the narrowed-to region."
   (define-key message-mode-map "\C-c\C-fw" 'message-insert-wide-reply)
   (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
   (define-key message-mode-map "\C-c\C-l" 'message-to-list-only)
+  (define-key message-mode-map "\C-c\C-f\C-e" 'message-insert-expires)
 
   (define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
   (define-key message-mode-map "\C-c\M-n"
@@ -2576,7 +2624,11 @@ Point is left at the beginning of the narrowed-to region."
         '(:help "Ask, then arrange to send message at that time"))]
     ["Kill Message" message-kill-buffer
      ,@(if (featurep 'xemacs) '(t)
-        '(:help "Delete this message without sending"))]))
+        '(:help "Delete this message without sending"))]
+    "----"
+    ["Message manual" message-info
+     ,@(if (featurep 'xemacs) '(t)
+        '(:help "Display the Message manual"))]))
 
 (easy-menu-define
   message-mode-field-menu message-mode-map ""
@@ -2609,7 +2661,8 @@ Point is left at the beginning of the narrowed-to region."
     ;; ["Followup-To (with note in body)" message-cross-post-followup-to t]
     ["Crosspost / Followup-To..." message-cross-post-followup-to t]
     ["Distribution" message-goto-distribution t]
-    ["X-No-Archive:" message-add-archive-header t ]
+    ["Expires" message-insert-expires t ]
+    ["X-No-Archive" message-add-archive-header t ]
     "----"
     ;; (typical) mailing-lists stuff
     ["Fetch To" message-insert-to
@@ -2631,6 +2684,8 @@ Point is left at the beginning of the narrowed-to region."
     "----"
     ["Sort Headers" message-sort-headers t]
     ["Encode non-ASCII domain names" message-idna-to-ascii-rhs t]
+    ;; We hide `message-hidden-headers' by narrowing the buffer.
+    ["Show Hidden Headers" widen t]
     ["Goto Body" message-goto-body t]
     ["Goto Signature" message-goto-signature t]))
 
@@ -2717,6 +2772,7 @@ C-c C-f  move to a header field (and create it if there isn't):
         C-c C-f C-f  move to Followup-To
         C-c C-f C-m  move to Mail-Followup-To
         C-c C-f c    move to Mail-Copies-To
+        C-c C-f C-e  move to Expires
         C-c C-f C-i  cycle through Importance values
         C-c C-f s    change subject and append \"(was: <Old Subject>)\"
         C-c C-f x    crossposting with FollowUp-To header and note in body
@@ -2776,7 +2832,7 @@ M-RET    `message-newline-and-reformat' (break the line and reformat)."
     (set (make-local-variable 'font-lock-defaults)
         '(message-font-lock-keywords t))
     (if (boundp 'tool-bar-map)
-       (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
+       (set (make-local-variable 'tool-bar-map) (message-make-tool-bar))))
   (easy-menu-add message-mode-menu message-mode-map)
   (easy-menu-add message-mode-field-menu message-mode-map)
   (gnus-make-local-hook 'after-change-functions)
@@ -2951,6 +3007,11 @@ the address is inserted by default."
   (message-goto-body)
   (forward-line -1))
 
+(defun message-in-body-p ()
+  "Return t if point is in the message body."
+  (let ((body (save-excursion (message-goto-body) (point))))
+    (>= (point) body)))
+
 (defun message-goto-signature ()
   "Move point to the beginning of the message signature.
 If there is no signature in the article, go to the end and
@@ -3016,7 +3077,8 @@ prefix FORCE is given."
     (message-carefully-insert-headers headers)))
 
 (defcustom message-header-synonyms
-  '((To Cc Bcc))
+  '((To Cc Bcc)
+    (Original-To))
   "List of lists of header synonyms.
 E.g., if this list contains a member list with elements `Cc' and `To',
 then `message-carefully-insert-headers' will not insert a `To' header
@@ -3112,7 +3174,7 @@ or in the synonym headers, defined by `message-header-synonyms'."
   "Kill all text up to the signature.
 If a numberic argument or prefix arg is given, leave that number
 of lines before the signature intact."
-  (interactive "p")
+  (interactive "P")
   (save-excursion
     (save-restriction
       (let ((point (point)))
@@ -3124,13 +3186,14 @@ of lines before the signature intact."
            (end-of-line -1)))
        (unless (= point (point))
          (kill-region point (point))
-         (insert "\n"))))))
+         (unless (bolp)
+           (insert "\n")))))))
 
 (defun message-newline-and-reformat (&optional arg not-break)
   "Insert four newlines, and then reformat if inside quoted text.
 Prefix arg means justify as well."
   (interactive (list (if current-prefix-arg 'full)))
-  (let (quoted point beg end leading-space bolp)
+  (let (quoted point beg end leading-space bolp fill-paragraph-function)
     (setq point (point))
     (beginning-of-line)
     (setq beg (point))
@@ -3215,7 +3278,9 @@ Prefix arg means justify as well."
       (if point (goto-char point)))))
 
 (defun message-fill-paragraph (&optional arg)
-  "Like `fill-paragraph'."
+  "Message specific function to fill a paragraph.
+This function is used as the value of `fill-paragraph-function' in
+Message buffers and is not meant to be called directly."
   (interactive (list (if current-prefix-arg 'full)))
   (if (if (boundp 'filladapt-mode) filladapt-mode)
       nil
@@ -3479,9 +3544,12 @@ However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
       (save-excursion
        (goto-char start)
        (while (< (point) (mark t))
-         (if (or (looking-at ">") (looking-at "^$"))
-             (insert message-yank-cited-prefix)
-           (insert message-yank-prefix))
+         (cond ((looking-at ">")
+                (insert message-yank-cited-prefix))
+               ((looking-at "^$")
+                (insert message-yank-empty-prefix))
+               (t
+                (insert message-yank-prefix)))
          (forward-line 1))))
     (goto-char start)))
 
@@ -3531,8 +3599,10 @@ be added to the \"References\" field."
                 (prog1
                     t
                   (delete-windows-on buffer t)
-                  ; The mark will be set at the end of the article.
-                  (insert-buffer buffer))))
+                  ; Set the mark at the end of the yanked message.
+                  (push-mark (save-excursion
+                               (insert-buffer-substring buffer)
+                               (point))))))
       ;; Add new IDs to the References field.
       (when (and message-yank-add-new-references
                 (interactive-p))
@@ -3613,49 +3683,14 @@ be added to the \"References\" field."
          (push (buffer-name buffer) buffers))))
     (nreverse buffers)))
 
-(defun message-cite-original-without-signature ()
-  "Cite function in the standard Message manner."
-  (let ((start (point))
-       (end (mark t))
-       (functions
-        (when message-indent-citation-function
-          (if (listp message-indent-citation-function)
-              message-indent-citation-function
-            (list message-indent-citation-function))))
-       (message-reply-headers (or message-reply-headers
-                                  (make-mail-header))))
-    (mail-header-set-from message-reply-headers
-                         (save-restriction
-                           (narrow-to-region
-                            (point)
-                            (if (search-forward "\n\n" nil t)
-                                (1- (point))
-                              (point-max)))
-                           (or (message-fetch-field "from")
-                               "unknown sender")))
-    ;; Allow undoing.
-    (undo-boundary)
-    (goto-char end)
-    (when (re-search-backward message-signature-separator start t)
-      ;; Also peel off any blank lines before the signature.
-      (forward-line -1)
-      (while (looking-at "^[ \t]*$")
-       (forward-line -1))
-      (forward-line 1)
-      (delete-region (point) end)
-      (unless (search-backward "\n\n" start t)
-       ;; Insert a blank line if it is peeled off.
-       (insert "\n")))
-    (goto-char start)
-    (mapc 'funcall functions)
-    (when message-citation-line-function
-      (unless (bolp)
-       (insert "\n"))
-      (funcall message-citation-line-function))))
+(eval-when-compile (defvar mail-citation-hook))        ; Compiler directive
 
-(eval-when-compile (defvar mail-citation-hook))                ;Compiler directive
-(defun message-cite-original ()
-  "Cite function in the standard Message manner."
+(defun message-cite-original-1 (strip-signature)
+  "Cite an original message.
+If STRIP-SIGNATURE is non-nil, strips off the signature from the
+original message.
+
+This function uses `mail-citation-hook' if that is non-nil."
   (if (and (boundp 'mail-citation-hook)
           mail-citation-hook)
       (run-hooks 'mail-citation-hook)
@@ -3679,6 +3714,20 @@ be added to the \"References\" field."
        (setq x-no-archive (message-fetch-field "x-no-archive")))
       (goto-char start)
       (mapc 'funcall functions)
+      (when strip-signature
+       ;; Allow undoing.
+       (undo-boundary)
+       (goto-char end)
+       (when (re-search-backward message-signature-separator start t)
+         ;; Also peel off any blank lines before the signature.
+         (forward-line -1)
+         (while (looking-at "^[ \t]*$")
+           (forward-line -1))
+         (forward-line 1)
+         (delete-region (point) end)
+         (unless (search-backward "\n\n" start t)
+           ;; Insert a blank line if it is peeled off.
+           (insert "\n"))))
       (when message-citation-line-function
        (unless (bolp)
          (insert "\n"))
@@ -3691,10 +3740,21 @@ be added to the \"References\" field."
        (insert "> [Quoted text removed due to X-No-Archive]\n")
        (forward-line -1)))))
 
+(defun message-cite-original ()
+  "Cite function in the standard Message manner."
+  (message-cite-original-1 nil))
+
+(defun message-cite-original-without-signature ()
+  "Cite function in the standard Message manner.
+This function strips off the signature from the original message."
+  (message-cite-original-1 t))
+
 (defun message-insert-citation-line ()
   "Insert a simple citation line."
   (when message-reply-headers
-    (insert (mail-header-from message-reply-headers) " writes:\n\n")))
+    (insert (mail-header-from message-reply-headers) " writes:")
+    (newline)
+    (newline)))
 
 (defun message-position-on-field (header &rest afters)
   (let ((case-fold-search t))
@@ -4101,8 +4161,8 @@ not have PROP."
 ;;       (when (let ((char (char-after)))
 ;;               (or (< (mm-char-int char) 128)
 ;;                   (and (mm-multibyte-p)
-;;                        ;; Fixme: Wrong for Emacs 22 and for things
-;;                        ;; like undecable utf-8.  Should at least
+;;                        ;; FIXME: Wrong for Emacs 23 (unicode) and for
+;;                        ;; things like undecable utf-8.  Should at least
 ;;                        ;; use find-coding-systems-region.
 ;;                        (memq (char-charset char)
 ;;                              '(eight-bit-control eight-bit-graphic
@@ -4779,7 +4839,7 @@ Otherwise, generate and save a value for `canlock-password' first."
                   (zerop
                    (length
                     (setq to (completing-read
-                              "Followups to (default: no Followup-To header) "
+                              "Followups to (default no Followup-To header): "
                               (mapcar #'list
                                       (cons "poster"
                                             (message-tokenize-header
@@ -5237,6 +5297,22 @@ If NOW, use that time instead."
     (concat "Re: " (message-strip-subject-re subject)))
    (t subject)))
 
+(defun message-insert-expires (days)
+  "Insert the Expires header.  Expiry in DAYS days."
+  (interactive "NExpire article in how many days? ")
+  (save-excursion
+    (message-position-on-field "Expires" "X-Draft-From")
+    (insert (message-make-expires-date days))))
+
+(defun message-make-expires-date (days)
+  "Make date string for the Expires header.  Expiry in DAYS days.
+
+In posting styles use `(\"Expires\" (make-expires-date 30))'."
+  (let* ((cur (decode-time (current-time)))
+        (nday (+ days (nth 3 cur))))
+    (setf (nth 3 cur) nday)
+    (message-make-date (apply 'encode-time cur))))
+
 (defun message-make-message-id ()
   "Make a unique Message-ID."
   (concat "<" (message-unique-id)
@@ -5273,7 +5349,9 @@ If NOW, use that time instead."
           (* 25 25)))
   (let ((tm (current-time)))
     (concat
-     (if (memq system-type '(ms-dos emx vax-vms))
+     (if (or (memq system-type '(ms-dos emx vax-vms))
+            ;; message-number-base36 doesn't handle bigints.
+            (floatp (user-uid)))
         (let ((user (downcase (user-login-name))))
           (while (string-match "[^a-z0-9_]" user)
             (aset user (match-beginning 0) ?_))
@@ -5593,13 +5671,17 @@ subscribed address (and not the additional To and Cc header contents)."
   (let ((field (message-fetch-field header))
        rhs ace  address)
     (when field
-      (dolist (address (mail-header-parse-addresses field))
-       (setq address (car address)
-             rhs (downcase (or (cadr (split-string address "@")) ""))
-             ace (downcase (idna-to-ascii rhs)))
+      (dolist (rhs
+              (mm-delete-duplicates
+               (mapcar (lambda (rhs) (or (cadr (split-string rhs "@")) ""))
+                       (mapcar 'downcase
+                               (mapcar
+                                'car (mail-header-parse-addresses field))))))
+       (setq ace (downcase (idna-to-ascii rhs)))
        (when (and (not (equal rhs ace))
                   (or (not (eq message-use-idna 'ask))
-                      (y-or-n-p (format "Replace %s with %s? " rhs ace))))
+                      (y-or-n-p (format "Replace %s with %s in %s:? "
+                                        rhs ace header))))
          (goto-char (point-min))
          (while (re-search-forward (concat "^" header ":") nil t)
            (message-narrow-to-field)
@@ -5619,6 +5701,8 @@ See `message-idna-encode'."
        (message-idna-to-ascii-rhs-1 "From")
        (message-idna-to-ascii-rhs-1 "To")
        (message-idna-to-ascii-rhs-1 "Reply-To")
+       (message-idna-to-ascii-rhs-1 "Mail-Reply-To")
+       (message-idna-to-ascii-rhs-1 "Mail-Followup-To")
        (message-idna-to-ascii-rhs-1 "Cc")))))
 
 (defun message-generate-headers (headers)
@@ -5710,7 +5794,8 @@ Headers already prepared in the buffer are not modified."
                  ;; The element is a symbol.  We insert the value
                  ;; of this symbol, if any.
                  (symbol-value header))
-                ((not (message-check-element header))
+                ((not (message-check-element
+                       (intern (downcase (symbol-name header)))))
                  ;; We couldn't generate a value for this header,
                  ;; so we just ask the user.
                  (read-from-minibuffer
@@ -6201,11 +6286,6 @@ are not included."
     (when message-default-mail-headers
       (insert message-default-mail-headers)
       (or (bolp) (insert ?\n)))
-    (save-restriction
-      (message-narrow-to-headers)
-      (if (and replybuffer
-              message-alternative-emails)
-         (message-use-alternative-email-as-from)))
     (when message-generate-headers-first
       (message-generate-headers
        (message-headers-to-generate
@@ -6224,6 +6304,12 @@ are not included."
     ;; Generate hashcash headers for recipients already known
     (mail-add-payment-async))
   (run-hooks 'message-setup-hook)
+  ;; Do this last to give it precedence over posting styles, etc.
+  (when (message-mail-p)
+    (save-restriction
+      (message-narrow-to-headers)
+      (if message-alternative-emails
+         (message-use-alternative-email-as-from))))
   (message-position-point)
   (undo-boundary))
 
@@ -6708,9 +6794,9 @@ that further discussion should take place only in "
 
 (defun message-is-yours-p ()
   "Non-nil means current article is yours.
-If you have added 'cancel-messages to 'message-shoot-gnksa-feet', all articles
+If you have added 'cancel-messages to `message-shoot-gnksa-feet', all articles
 are yours except those that have Cancel-Lock header not belonging to you.
-Instead of shooting GNKSA feet, you should modify 'message-alternative-emails'
+Instead of shooting GNKSA feet, you should modify `message-alternative-emails'
 regexp to match all of yours addresses."
   ;; Canlock-logic as suggested by Per Abrahamsen
   ;; <abraham@dina.kvl.dk>
@@ -6898,7 +6984,9 @@ news, Source is the list of newsgroups is was posted to."
                (gnus-group-decoded-name group)
              (or (and (setq from (message-fetch-field "from"))
                       (car (std11-extract-address-components
-                            (nnheader-decode-from from))))
+                            (nnheader-decode-from from)))
+                      (cadr (std11-extract-address-components
+                             (nnheader-decode-from from))))
                  "(nowhere)")))
          "] " subject))
 
@@ -7128,6 +7216,7 @@ Optional NEWS will use news to forward instead of mail."
        (set-buffer (get-buffer-create " *message resend*"))
        (erase-buffer))
       (let ((message-this-is-mail t)
+           message-generate-hashcash
            message-setup-hook)
        (message-setup `((To . ,address))))
       ;; Insert our usual headers.
@@ -7165,7 +7254,8 @@ Optional NEWS will use news to forward instead of mail."
       ;; Send it.
       (let ((message-encoding-buffer (current-buffer))
            (message-edit-buffer (current-buffer))
-           message-required-mail-headers)
+           message-required-mail-headers
+           message-generate-hashcash)
        (message-send-mail))
       (kill-buffer (current-buffer)))
     (message "Resending message to %s...done" address)))
@@ -7291,7 +7381,7 @@ you."
 ;; This code should be moved to underline.el (from which it is stolen).
 
 ;;;###autoload
-(defun bold-region (start end)
+(defun message-bold-region (start end)
   "Bold all nonblank characters in the region.
 Works by overstriking characters.
 Called from program, takes two arguments START and END
@@ -7307,7 +7397,7 @@ which specify the range to operate on."
        (forward-char 1)))))
 
 ;;;###autoload
-(defun unbold-region (start end)
+(defun message-unbold-region (start end)
   "Remove all boldness (overstruck characters) in the region.
 Called from program, takes two arguments START and END
 which specify the range to operate on."
@@ -7337,55 +7427,123 @@ which specify the range to operate on."
 
 ;; Support for toolbar
 (eval-when-compile
-  (defvar tool-bar-map)
   (defvar tool-bar-mode))
 
-(defun message-tool-bar-local-item-from-menu (command icon in-map &optional from-map &rest props)
-  ;; We need to make tool bar entries in local keymaps with
-  ;; `tool-bar-local-item-from-menu' in Emacs > 21.3
-  (if (fboundp 'tool-bar-local-item-from-menu)
-      ;; This is for Emacs 21.3
-      (tool-bar-local-item-from-menu command icon in-map from-map props)
-    (tool-bar-add-item-from-menu command icon from-map props)))
-
-(defun message-tool-bar-map ()
-  (or message-tool-bar-map
-      (setq message-tool-bar-map
-           (and
-            (condition-case nil (require 'tool-bar) (error nil))
-            (fboundp 'tool-bar-add-item-from-menu)
+;; Note: The :set function in the `message-tool-bar*' variables will only
+;; affect _new_ message buffers.  We might add a function that walks thru all
+;; message-mode buffers and force the update.
+(defun message-tool-bar-update (&optional symbol value)
+  "Update message mode toolbar.
+Setter function for custom variables."
+  (setq-default message-tool-bar-map nil)
+  (when symbol
+    ;; When used as ":set" function:
+    (set-default symbol value)))
+
+(defcustom message-tool-bar (if (eq gmm-tool-bar-style 'gnome)
+                               'message-tool-bar-gnome
+                             'message-tool-bar-retro)
+  "Specifies the message mode tool bar.
+
+It can be either a list or a symbol refering to a list.  See
+`gmm-tool-bar-from-list' for the format of the list.  The
+default key map is `message-mode-map'.
+
+Pre-defined symbols include `message-tool-bar-gnome' and
+`message-tool-bar-retro'."
+  :type '(repeat gmm-tool-bar-list-item)
+  :type '(choice (const :tag "GNOME style" message-tool-bar-gnome)
+                (const :tag "Retro look"  message-tool-bar-retro)
+                (repeat :tag "User defined list" gmm-tool-bar-item)
+                (symbol))
+  :version "23.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'message-tool-bar-update
+  :group 'message)
+
+(defcustom message-tool-bar-gnome
+  '((ispell-message "spell" nil
+                   :visible (or (not (boundp 'flyspell-mode))
+                                (not flyspell-mode)))
+    (flyspell-buffer "spell" t
+                    :visible (and (boundp 'flyspell-mode)
+                                  flyspell-mode)
+                    :help "Flyspell whole buffer")
+    (gmm-ignore "separator")
+    (message-send-and-exit "mail/send")
+    (message-dont-send "mail/save-draft")
+    (message-kill-buffer "close") ;; stock_cancel
+    (mime-edit-insert-file "attach" mime-edit-mode-map)
+    (mime-edit-preview-message "mail/preview" mime-edit-mode-map)
+    ;;(mml-secure-message-sign-encrypt "lock" mml-mode-map :visible nil)
+    (message-insert-importance-high "important" nil :visible nil)
+    (message-insert-importance-low "unimportant" nil :visible nil)
+    (message-insert-disposition-notification-to "receipt" nil :visible nil)
+    (gmm-customize-mode "preferences" t :help "Edit mode preferences")
+    (message-info "help" t :help "Message manual"))
+  "List of items for the message tool bar (GNOME style).
+
+See `gmm-tool-bar-from-list' for details on the format of the list."
+  :type '(repeat gmm-tool-bar-item)
+  :version "23.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'message-tool-bar-update
+  :group 'message)
+
+(defcustom message-tool-bar-retro
+  '(;; Old Emacs 21 icon for consistency.
+    (message-send-and-exit "gnus/mail_send")
+    (message-kill-buffer "close")
+    (message-dont-send "cancel")
+    (mime-edit-insert-file "attach" mime-edit-mode-map)
+    (ispell-message "spell")
+    (mime-edit-preview-message "preview" mime-edit-mode-map)
+    (message-insert-importance-high "gnus/important")
+    (message-insert-importance-low "gnus/unimportant")
+    (message-insert-disposition-notification-to "gnus/receipt"))
+  "List of items for the message tool bar (retro style).
+
+See `gmm-tool-bar-from-list' for details on the format of the list."
+  :type '(repeat gmm-tool-bar-item)
+  :version "23.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'message-tool-bar-update
+  :group 'message)
+
+(defcustom message-tool-bar-zap-list
+  '(new-file open-file dired kill-buffer write-file
+            print-buffer customize help)
+  "List of icon items from the global tool bar.
+These items are not displayed on the message mode tool bar.
+
+See `gmm-tool-bar-from-list' for the format of the list."
+  :type 'gmm-tool-bar-zap-list
+  :version "23.0" ;; No Gnus
+  :initialize 'custom-initialize-default
+  :set 'message-tool-bar-update
+  :group 'message)
+
+(defvar image-load-path)
+
+(defun message-make-tool-bar (&optional force)
+  "Make a message mode tool bar from `message-tool-bar-list'.
+When FORCE, rebuild the tool bar."
+  (when (and (not (featurep 'xemacs))
+            (boundp 'tool-bar-mode)
             tool-bar-mode
-            (let ((tool-bar-map (copy-keymap tool-bar-map))
-                  (load-path (mm-image-load-path)))
-              ;; Zap some items which aren't so relevant and take
-              ;; up space.
-              (dolist (key '(print-buffer kill-buffer save-buffer
-                                          write-file dired open-file))
-                (define-key tool-bar-map (vector key) nil))
-              (message-tool-bar-local-item-from-menu
-               'message-send-and-exit "mail_send" tool-bar-map message-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'message-kill-buffer "close" tool-bar-map message-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'message-dont-send "cancel" tool-bar-map message-mode-map)
-;;            (message-tool-bar-local-item-from-menu
-;;             'mime-edit-insert-file "attach"
-;;             tool-bar-map mime-edit-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'ispell-message "spell" tool-bar-map message-mode-map)
-;;            (message-tool-bar-local-item-from-menu
-;;             'mime-edit-preview-message "preview"
-;;             tool-bar-map mime-edit-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'message-insert-importance-high "important"
-               tool-bar-map message-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'message-insert-importance-low "unimportant"
-               tool-bar-map message-mode-map)
-              (message-tool-bar-local-item-from-menu
-               'message-insert-disposition-notification-to "receipt"
-               tool-bar-map message-mode-map)
-              tool-bar-map)))))
+            (or (not message-tool-bar-map) force))
+    (setq message-tool-bar-map
+         (let* ((load-path
+                 (gmm-image-load-path-for-library "message"
+                                                  "mail/save-draft.xpm"
+                                                  nil t))
+                (image-load-path (cons (car load-path)
+                                       (when (boundp 'image-load-path)
+                                         image-load-path))))
+           (gmm-tool-bar-from-list message-tool-bar
+                                   message-tool-bar-zap-list
+                                   'message-mode-map))))
+  message-tool-bar-map)
 
 ;;; Group name completion.
 
@@ -7439,7 +7597,8 @@ If nil, the function bound in `text-mode-map' or `global-map' is executed."
   :version "22.1"
   :group 'message
   :link '(custom-manual "(message)Various Commands")
-  :type 'function)
+  :type '(choice (const nil)
+                function))
 
 (defun message-tab ()
   "Complete names according to `message-completion-alist'.
@@ -7456,6 +7615,17 @@ those headers."
                 (lookup-key global-map "\t")
                 'indent-relative))))
 
+(eval-and-compile
+  (condition-case nil
+      (with-temp-buffer
+       (let ((standard-output (current-buffer)))
+         (eval '(display-completion-list nil "")))
+       (defalias 'message-display-completion-list 'display-completion-list))
+    (error ;; Don't use `wrong-number-of-arguments' here because of XEmacs.
+     (defun message-display-completion-list (completions &optional ignore)
+       "Display the list of completions, COMPLETIONS, using `standard-output'."
+       (display-completion-list completions)))))
+
 (defun message-expand-group ()
   "Expand the group name under point."
   (let* ((b (save-excursion
@@ -7494,7 +7664,9 @@ those headers."
          (let ((buffer-read-only nil))
            (erase-buffer)
            (let ((standard-output (current-buffer)))
-             (display-completion-list (sort completions 'string<)))
+             (message-display-completion-list (sort completions 'string<)
+                                              string))
+           (setq buffer-read-only nil)
            (goto-char (point-min))
            (delete-region (point) (progn (forward-line 3) (point))))))))))
 
@@ -7671,6 +7843,9 @@ regexp VARSTR."
       (read-string prompt initial-contents))))
 
 (defun message-use-alternative-email-as-from ()
+  "Set From field of the outgoing message to the first matching
+address in `message-alternative-emails', looking at To, Cc and
+From headers in the original article."
   (require 'mail-utils)
   (let* ((fields '("To" "Cc" "From"))
         (emails
@@ -7685,6 +7860,7 @@ regexp VARSTR."
                emails nil))
       (pop emails))
     (unless (or (not email) (equal email user-mail-address))
+      (message-remove-header "From")
       (goto-char (point-max))
       (insert "From: " (let ((user-mail-address email)) (message-make-from))
              "\n"))))