home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / lisp / packages / feedmail.el < prev    next >
Encoding:
Text File  |  1992-06-29  |  19.4 KB  |  495 lines

  1. ;;; From: William.J.Carpenter@hos1cad.att.com (Bill C)
  2. ;;; Subject: feedmail.el, patchlevel 2 [repost]
  3. ;;; Date: 8 Jun 91 22:23:00 GMT
  4. ;;; Organization: AT&T Bell Laboratories
  5. ;;;
  6. ;;; 5-may-92  jwz    Conditionalized calling expand-mail-aliases, since that
  7. ;;;            function doesn't exist in Lucid GNU Emacs or when using
  8. ;;;            mail-abbrevs.el.
  9. ;;; 
  10. ;;; Here's the latest version of feedmail.el, a replacement for parts of
  11. ;;; GNUemacs' sendmail.el (specifically, it's what handles your outgoing
  12. ;;; mail after you type C-c C-c in mail mode).   (Sorry if you're seeing
  13. ;;; this a second time.  Looks like my earlier attempt to post it didn't
  14. ;;; get off the local machine.)
  15. ;;; 
  16. ;;; This version contains the following new things:
  17. ;;; 
  18. ;;;    * fix for handling default-case-fold-search
  19. ;;;    * involve user-full-name in default from line
  20. ;;;    * fix for my improper use of mail-strip-quoted-names when
  21. ;;;      addresses contain a mix of "<>" and "()" styles
  22. ;;;    * new feature allowing optional generation of Message-ID
  23.  
  24. ;;; feedmail.el
  25. ;;; LCD record:
  26. ;;; feedmail|Bill Carpenter|william.j.carpenter@att.com|Outbound mail handling|91-05-24|2|feedmail.el
  27. ;;;
  28. ;;; Written by Bill Carpenter <william.j.carpenter@att.com>
  29. ;;; original,      31 March 1991
  30. ;;; patchlevel 1,   5 April 1991
  31. ;;; patchlevel 2,  24 May   1991
  32. ;;;
  33. ;;; As far as I'm concerned, anyone can do anything they want with
  34. ;;; this specific piece of code.  No warranty or promise of support is
  35. ;;; offered.
  36. ;;;
  37. ;;; This stuff does in elisp the stuff that used to be done
  38. ;;; by the separate program "fakemail" for processing outbound email.
  39. ;;; In other words, it takes over after you hit "C-c C-c" in mail mode.
  40. ;;; By appropriate setting of options, you can still use "fakemail",
  41. ;;; or you can even revert to sendmail (which is not too popular
  42. ;;; locally).  See the variables at the top of the elisp for how to
  43. ;;; achieve these effects:
  44. ;;;
  45. ;;;    --- you can get one last look at the prepped outbound message and
  46. ;;;        be prompted for confirmation
  47. ;;;
  48. ;;;    --- removes BCC: headers after getting address info
  49. ;;;
  50. ;;;    --- does smart filling of TO: and CC: headers
  51. ;;;
  52. ;;;    --- processes FCC: lines and removes them
  53. ;;;
  54. ;;;    --- empty headers are removed
  55. ;;;
  56. ;;;    --- can force FROM: or SENDER: line
  57. ;;;
  58. ;;;    --- can generate a Message-ID line
  59. ;;;
  60. ;;;    --- strips comments from address info (both "()" and "<>" are
  61. ;;;        handled via a call to mail-strip-quoted-names); the
  62. ;;;        comments are stripped in the simplified address list given
  63. ;;;        to a subprocess, not in the headers in the mail itself
  64. ;;;        (they are left unchanged, modulo smart filling)
  65. ;;;
  66. ;;;    --- error info is pumped into a normal buffer instead of the
  67. ;;;        minibuffer
  68. ;;;
  69. ;;;    --- just before the optional prompt for confirmation, lets you
  70. ;;;        run a hook on the prepped message and simplified address
  71. ;;;        list
  72. ;;;
  73. ;;;    --- you can specify something other than /bin/mail for the
  74. ;;;        subprocess
  75. ;;;
  76. ;;; After a few options below, you will find the function
  77. ;;; feedmail-send-it.  Everything after that function is just local
  78. ;;; stuff for this file.  There are two ways you can use the stuff in
  79. ;;; this file:
  80. ;;;
  81. ;;; (1)  Put the contents of this file into sendmail.el and change the
  82. ;;; name of feedmail-send-it to sendmail-send-it, replacing that
  83. ;;; function in sendmail.el.
  84. ;;;
  85. ;;;                              or
  86. ;;;
  87. ;;; (2)  Save this file as feedmail.el somewhere on your elisp
  88. ;;; loadpath; byte-compile it.  Put the following lines somewhere in
  89. ;;; your ~/.emacs stuff:
  90. ;;;
  91. ;;;        (setq send-mail-function 'feedmail-send-it)
  92. ;;;        (autoload 'feedmail-send-it "feedmail")
  93. ;;;
  94.  
  95.  
  96. (defvar feedmail-confirm-outgoing nil
  97.   "*If non-nil, gives a y-or-n confirmation prompt after prepping,
  98. before sending mail.")
  99.  
  100.  
  101. (defvar feedmail-nuke-bcc t
  102.   "*Non-nil means get rid of the BCC: lines from the message header
  103. text before sending the mail.  In any case, the BCC: lines do
  104. participate in the composed address list.  You probably want to keep
  105. them if you're using sendmail (see feedmail-buffer-eating-function).")
  106.  
  107.  
  108. (defvar feedmail-fill-to-cc t
  109.   "*Non-nil means do smart filling (line-wrapping) of TO: and CC: header
  110. lines.  If nil, the lines are left as-is.  The filling is done after
  111. mail address alias expansion.")
  112.  
  113.  
  114. (defvar feedmail-fill-to-cc-fill-column default-fill-column
  115.   "*Fill column used when wrapping mail TO: and CC: lines.")
  116.  
  117.  
  118. (defvar feedmail-nuke-empty-headers t
  119.   "*If non-nil, headers with no contents are removed from the outgoing
  120. email.  A completely empty SUBJECT: header is always removed,
  121. regardless of the setting of this variable.  The only time you would
  122. want them left in would be if you used some headers whose presence
  123. indicated something rather than their contents.")
  124.  
  125. ;;; wjc sez:  I think the use of the SENDER: line is pretty pointless,
  126. ;;; but I left it in to be compatible with sendmail.el and because
  127. ;;; maybe some distant mail system needs it.  Really, though, if you
  128. ;;; want a sender line in your mail, just put one in there and don't
  129. ;;; wait for feedmail to do it for you.
  130.  
  131. (defvar feedmail-sender-line nil
  132.   "*If nil, no SENDER: header is forced.  If non-nil and the email
  133. already has a FROM: header, a SENDER: header is forced with this as
  134. its contents.  You can probably leave this nil, but if you feel like
  135. using it, a good value would be a fully-qualified domain name form of
  136. your address.  For example, william.j.carpenter@att.com.  Don't
  137. include a trailing newline or the keyword SENDER:.  They're
  138. automatically provided.")
  139.  
  140.  
  141. ;; user-full-name suggested by kpc@ptolemy.arc.nasa.gov (=Kimball Collins)
  142. (defvar feedmail-from-line
  143.   (concat (user-login-name) "@" (system-name) " (" (user-full-name) ")")
  144.   "*If non-nil and the email has no FROM: header, one will be forced
  145. with this as its contents. A good value would be a fully-qualified
  146. domain name form of your address.  For example, william.j.carpenter@att.com.
  147. (The default value of this variable is probably not very good, since
  148. it doesn't have a domain part.)  Don't include a trailing newline or
  149. the keyword FROM:.  They're automatically provided.")
  150.  
  151.  
  152. ;;; Here's how I use the GNUS Message-ID generator for mail but not
  153. ;;; for news postings:
  154. ;;;
  155. ;;;   (setq feedmail-message-id-generator 'wjc:gnusish-message-id)
  156. ;;;   (setq gnus-your-domain "hos1cad.ATT.COM")
  157. ;;;   
  158. ;;;   (defun wjc:gnusish-message-id ()
  159. ;;;     (require 'gnuspost)
  160. ;;;     (if (fboundp 'wjc:gnus-inews-message-id)
  161. ;;;         (wjc:gnus-inews-message-id)
  162. ;;;       (gnus-inews-message-id)))
  163. ;;;   
  164. ;;;   (setq news-inews-hook
  165. ;;;         '(lambda () 
  166. ;;;            (defun gnus-inews-date () nil)
  167. ;;;            (fset 'wjc:gnus-inews-message-id (symbol-function 'gnus-inews-message-id))
  168. ;;;            (defun gnus-inews-message-id () nil)
  169. ;;;            ))
  170. ;;;   
  171. (defvar feedmail-message-id-generator nil
  172.   "*If non-nil, should be a function (called with no arguments) which
  173. will generate a unique message ID which will be inserted on a
  174. Message-ID: header.  The message ID should be the return value of the
  175. function.  Don't include trailing newline, leading space, or the
  176. keyword MESSAGE-ID.  They're automatically provided.  Do include
  177. surrounding <> brackets.  For an example of a message ID generating
  178. function, you could look at the GNUS function gnus-inews-message-id.
  179. When called, the current buffer is the prepped outgoing mail buffer
  180. (the function may inspect it, but shouldn't modify it).  If the returned
  181. value doesn't contain any non-whitespace characters, no message ID
  182. header is generated, so you could generate them conditionally,
  183. based on the contents of the mail.")
  184.  
  185.  
  186. (defun feedmail-confirm-addresses-hook-example ()
  187.   "An example of a last chance hook that shows the simple addresses
  188. and gets a confirmation.  Use as (setq feedmail-last-chance-hook
  189. 'feedmail-confirm-addresses-hook-example)."
  190.   (save-window-excursion 
  191.     (display-buffer feedmail-address-buffer)
  192.     (if (not (y-or-n-p "How do you like them apples? "))
  193.         (error "Sending...gave up in last chance hook"))))
  194.  
  195.  
  196. (defvar feedmail-last-chance-hook nil
  197.   "*User's last opportunity to modify the message on its way out.  It
  198. has already had all the header prepping from the standard package.
  199. The next step after running the hook will be to push the buffer into a
  200. subprocess that mails the mail.  The hook might be interested in these
  201. buffers:  (1) feedmail-prepped-text-buffer contains the header and body
  202. of the message, ready to go;  (2) feedmail-address-buffer contains the
  203. space-separated, simplified list of addresses which is to be given to
  204. the subprocess (the hook may change them).  feedmail-error-buffer is
  205. an empty buffer intended to soak up errors for display to the user.
  206. If the hook allows interactive activity, the user should not send more
  207. mail while in the hook since some of the internal buffers will be reused.")
  208.  
  209.  
  210. (defvar feedmail-buffer-eating-function 'feedmail-buffer-to-binmail
  211.   "*Function used to send the prepped buffer to a subprocess.  The
  212. function's three (mandatory) arguments are: (1) the buffer containing
  213. the prepped message; (2) a buffer where errors should be directed; and
  214. (3) a string containing the space-separated list of simplified
  215. addresses.  Two popular choices for this are 'feedmail-buffer-to-binmail
  216. and 'feedmail-buffer-to-sendmail.  If you use the sendmail form, you
  217. probably want to set feedmail-nuke-bcc to nil.  If you use the binmail
  218. form, check the value of feedmail-binmail-template.")
  219.  
  220.  
  221. (defvar feedmail-binmail-template (if mail-interactive "/bin/mail %s" "/bin/rmail %s")
  222.   "*Command template for the subprocess which will get rid of the
  223. mail.  It can result in any command understandable by /bin/sh.  The
  224. single '%s', if present, gets replaced by the space-separated,
  225. simplified list of addressees.  Used in feedmail-buffer-to-binmail to
  226. form the shell command which will receive the contents of the prepped
  227. buffer as stdin.  If you'd like your errors to come back as mail
  228. instead of immediately in a buffer, try /bin/rmail instead of
  229. /bin/mail (this can be accomplished by keeping the default nil setting
  230. of mail-interactive).  You might also like to consult local mail
  231. experts for any other interesting command line possibilities.")
  232.  
  233.  
  234. ;; feedmail-buffer-to-binmail and feedmail-buffer-to-sendmail are the
  235. ;; only things provided for values for the variable
  236. ;; feedmail-buffer-eating-function.  It's pretty easy to write your
  237. ;; own, though.
  238.  
  239. (defun feedmail-buffer-to-binmail (prepped-mail-buffer mail-error-buffer simple-address-list)
  240.   "Function which actually calls /bin/mail as a subprocess and feeds the buffer to it."
  241.   (save-excursion
  242.     (set-buffer prepped-mail-buffer)
  243.     (apply 'call-process-region
  244.            (append (list (point-min) (point-max)
  245.                          "/bin/sh" nil mail-error-buffer nil "-c"
  246.                          (format feedmail-binmail-template simple-address-list ))))
  247.     ) ;; save-excursion
  248.   )
  249.  
  250.  
  251. (defun feedmail-buffer-to-sendmail (prepped-mail-buffer feedmail-error-buffer simple-address-list)
  252.   "Function which actually calls sendmail as a subprocess and feeds the buffer to it."
  253.   (save-excursion
  254.     (set-buffer prepped-mail-buffer)
  255.     (apply 'call-process-region
  256.            (append (list (point-min) (point-max)
  257.                        (if (boundp 'sendmail-program)
  258.                            sendmail-program
  259.                          "/usr/lib/sendmail")
  260.                        nil feedmail-error-buffer nil
  261.                        "-oi" "-t")
  262.                  ;; Don't say "from root" if running under su.
  263.                  (and (equal (user-real-login-name) "root")
  264.                       (list "-f" (user-login-name)))
  265.                  ;; These mean "report errors by mail"
  266.                  ;; and "deliver in background".
  267.                  (if (null mail-interactive) '("-oem" "-odb"))))
  268. ))
  269.  
  270.  
  271. ;; feedmail-send-it is the only "public" function is this file.
  272. ;; All of the others are just little helpers.
  273. (defun feedmail-send-it ()
  274.   (let* ((default-case-fold-search t)
  275.          (feedmail-error-buffer (get-buffer-create " *Outgoing Email Errors*"))
  276.          (feedmail-prepped-text-buffer (get-buffer-create " *Outgoing Email Text*"))
  277.          (feedmail-address-buffer (get-buffer-create " *Outgoing Email Address List*"))
  278.          (feedmail-raw-text-buffer (current-buffer))
  279.          (case-fold-search nil)
  280.          end-of-headers-marker)
  281.  
  282.     (unwind-protect (save-excursion
  283.         (set-buffer feedmail-prepped-text-buffer) (erase-buffer)
  284.  
  285.         ;; jam contents of user-supplied mail buffer into our scratch buffer
  286.         (insert-buffer-substring feedmail-raw-text-buffer)
  287.  
  288.         ;; require one newline at the end.
  289.         (goto-char (point-max))
  290.         (or (= (preceding-char) ?\n) (insert ?\n))
  291.  
  292.         ;; Change header-delimiter to be what mailers expect (empty line).
  293.         (goto-char (point-min))
  294.         (re-search-forward (concat "^" (regexp-quote mail-header-separator) "\n"))
  295.         (replace-match "\n")
  296.         ;; why was this backward-char here?
  297.         ;;(backward-char 1)
  298.         (setq end-of-headers-marker (point-marker))
  299.  
  300.         (if (and (fboundp 'expand-mail-aliases) ; nil = mail-abbrevs.el
  301.              mail-aliases)
  302.             (expand-mail-aliases (point-min) end-of-headers-marker))
  303.  
  304.         ;; make it pretty
  305.         (if feedmail-fill-to-cc (feedmail-fill-to-cc-function end-of-headers-marker))
  306.         ;; ignore any blank lines in the header
  307.         (goto-char (point-min))
  308.         (while (and (re-search-forward "\n\n\n*" end-of-headers-marker t) (< (point) end-of-headers-marker))
  309.           (replace-match "\n"))
  310.       
  311.         (let ((case-fold-search t))
  312.           (feedmail-deduce-address-list feedmail-prepped-text-buffer (point-min) end-of-headers-marker)
  313.           (save-excursion (set-buffer feedmail-address-buffer)
  314.                           (goto-char (point-min))
  315.                           (if (not (re-search-forward "\\S-" (point-max) t))
  316.                               (error "Sending...abandoned, no addressees!")))
  317.  
  318.           ;; Find and handle any BCC fields.
  319.           (if feedmail-nuke-bcc (feedmail-do-bcc end-of-headers-marker))
  320.  
  321.           ;; Find and handle any FCC fields.
  322.           (goto-char (point-min))
  323.           (if (re-search-forward "^FCC:" end-of-headers-marker t)
  324.               (mail-do-fcc end-of-headers-marker))
  325.  
  326.           (goto-char (point-min))
  327.           (if (re-search-forward "^FROM:" end-of-headers-marker t)
  328.               
  329.               ;; If there is a FROM: and no SENDER:, put in a SENDER:
  330.               ;; if requested by user
  331.               (if (and feedmail-sender-line
  332.                        (not (save-excursion (goto-char (point-min))
  333.                            (re-search-forward "^SENDER:" end-of-headers-marker t))))
  334.                   (progn (forward-line 1) (insert "Sender: " feedmail-sender-line "\n")))
  335.  
  336.             ;; no FROM: ... force one?
  337.             (if feedmail-from-line
  338.                 (progn (goto-char (point-min)) (insert "From: " feedmail-from-line "\n")))
  339.             )
  340.  
  341.           ;; don't send out a blank subject line
  342.           (goto-char (point-min))
  343.           (if (re-search-forward "^Subject:[ \t]*\n" end-of-headers-marker t)
  344.               (replace-match ""))
  345.  
  346.           ;; don't send out a blank headers of various sorts
  347.           (goto-char (point-min))
  348.           (and feedmail-nuke-empty-headers  ;; hey, who's an empty-header? 
  349.                (while (re-search-forward "^[A-Za-z0-9-]+:[ \t]*\n" end-of-headers-marker t)
  350.                  (replace-match ""))))
  351.  
  352.         ;; message ID generation
  353.         (if feedmail-message-id-generator
  354.             (progn
  355.               (goto-char (point-min))
  356.               (if (re-search-forward "^MESSAGE-ID:[ \t]*\n" end-of-headers-marker t)
  357.                   (replace-match ""))
  358.               (setq feedmail-msgid-part (funcall feedmail-message-id-generator))
  359.               (goto-char (point-min))
  360.               (and feedmail-msgid-part (string-match "[^ \t]" feedmail-msgid-part)
  361.                   (insert "Message-ID: " feedmail-msgid-part "\n"))))
  362.  
  363.  
  364.         (save-excursion (set-buffer feedmail-error-buffer) (erase-buffer))
  365.  
  366.         (run-hooks 'feedmail-last-chance-hook)
  367.  
  368.         (if (or (not feedmail-confirm-outgoing) (feedmail-one-last-look feedmail-prepped-text-buffer))
  369.             (funcall feedmail-buffer-eating-function feedmail-prepped-text-buffer feedmail-error-buffer
  370.                      (save-excursion (set-buffer feedmail-address-buffer) (buffer-string)))
  371.           (error "Sending...abandoned")
  372.           )
  373.         )  ;; unwind-protect body (save-excursion)
  374.  
  375.       ;; unwind-protect cleanup forms
  376.       (kill-buffer feedmail-prepped-text-buffer)
  377.       (kill-buffer feedmail-address-buffer)
  378.       (set-buffer feedmail-error-buffer)
  379.       (if (zerop (buffer-size))
  380.           (kill-buffer feedmail-error-buffer)
  381.         (progn (display-buffer feedmail-error-buffer)
  382.                (error "Sending...failed")))
  383.       (set-buffer feedmail-raw-text-buffer))
  384.     ) ;; let
  385.   )
  386.  
  387.  
  388. (defun feedmail-do-bcc (header-end)
  389.   "Delete BCC: and their continuation lines from the header area.
  390. There may be multiple BCC: lines, and each may have arbitrarily
  391. many continuation lines."
  392.   (let ((case-fold-search t))
  393.     (save-excursion (goto-char (point-min))
  394.       ;; iterate over all BCC: lines
  395.       (while (re-search-forward "^BCC:" header-end t)
  396.         (delete-region (match-beginning 0) (progn (forward-line 1) (point)))
  397.         ;; get rid of any continuation lines
  398.         (while (and (looking-at "^[ \t].*\n") (< (point) header-end))
  399.           (replace-match ""))
  400.         )
  401.       ) ;; save-excursion
  402.     ) ;; let
  403.   )
  404.  
  405. (defun feedmail-fill-to-cc-function (header-end)
  406.   "Smart filling of TO: and CC: headers.  The filling tries to avoid
  407. splitting lines except at commas.  This avoids, in particular,
  408. splitting within parenthesized comments in addresses."
  409.   (let ((case-fold-search t)
  410.         (fill-prefix "\t")
  411.         (fill-column feedmail-fill-to-cc-fill-column)
  412.         this-line
  413.         this-line-end)
  414.     (save-excursion (goto-char (point-min))
  415.       ;; iterate over all TO:/CC: lines
  416.       (while (re-search-forward "^\\(TO:\\|CC:\\)" header-end t)
  417.         (setq this-line (match-beginning 0))
  418.         (forward-line 1)
  419.         ;; get any continuation lines
  420.         (while (and (looking-at "^[ \t]+") (< (point) header-end))
  421.           (replace-match " ")
  422.           (forward-line 1))
  423.         (setq this-line-end (point-marker))
  424.  
  425.         ;; The general idea is to break only on commas.  Change
  426.         ;; all the blanks to something unprintable; change the
  427.         ;; commas to blanks; fill the region; change it back.
  428.         (subst-char-in-region this-line this-line-end ?   2 t) ;; blank --> C-b
  429.         (subst-char-in-region this-line this-line-end ?, ?  t) ;; comma --> blank
  430.         (fill-region-as-paragraph this-line this-line-end)
  431.  
  432.         (subst-char-in-region this-line this-line-end ?  ?, t) ;; comma <-- blank
  433.         (subst-char-in-region this-line this-line-end  2 ?  t) ;; blank <-- C-b
  434.  
  435.         ;; look out for missing commas before continuation lines
  436.         (save-excursion
  437.           (goto-char this-line)
  438.           (while (re-search-forward "\\([^,]\\)\n\t[ ]*" this-line-end t)
  439.             (replace-match "\\1,\n\t")))
  440.         )
  441.       ) ;; while
  442.     ) ;; save-excursion
  443.   )
  444.  
  445.  
  446. (defun feedmail-deduce-address-list (feedmail-text-buffer header-start header-end)
  447.   "Get address list suitable for command line use on simple /bin/mail."
  448.   (require 'mail-utils)  ;; pick up mail-strip-quoted-names
  449.   (let
  450.       ((case-fold-search t)
  451.        (simple-address-list "")
  452.        this-line
  453.        this-line-end)
  454.     (unwind-protect
  455.         (save-excursion
  456.           (set-buffer feedmail-address-buffer) (erase-buffer)
  457.           (insert-buffer-substring feedmail-text-buffer header-start header-end)
  458.           (goto-char (point-min))
  459.           (while (re-search-forward "^\\(TO:\\|CC:\\|BCC:\\)" header-end t)
  460.             (replace-match "")
  461.             (setq this-line (match-beginning 0))
  462.             (forward-line 1)
  463.             ;; get any continuation lines
  464.             (while (and (looking-at "^[ \t]+") (< (point) header-end))
  465.               (forward-line 1))
  466.             (setq this-line-end (point-marker))
  467.             (setq simple-address-list
  468.                   (concat simple-address-list " "
  469.                           (mail-strip-quoted-names (buffer-substring this-line this-line-end))))
  470.             )
  471.           (erase-buffer)
  472.           (insert-string simple-address-list)
  473.           (subst-char-in-region (point-min) (point-max) 10 ?  t)  ;; newline --> blank
  474.           (subst-char-in-region (point-min) (point-max) ?, ?  t)  ;; comma   --> blank
  475.           (subst-char-in-region (point-min) (point-max)  9 ?  t)  ;; tab     --> blank
  476.  
  477.           (goto-char (point-min))
  478.           ;; tidyness in case hook is not robust when it looks at this
  479.           (while (re-search-forward "[ \t]+" header-end t) (replace-match " "))
  480.  
  481.           )
  482.       )
  483.     )
  484.   )
  485.  
  486.  
  487. (defun feedmail-one-last-look (feedmail-prepped-text-buffer)
  488.   "Offer the user one last chance to give it up."
  489.   (save-excursion (save-window-excursion
  490.     (switch-to-buffer feedmail-prepped-text-buffer)
  491.     (y-or-n-p "Send this email? "))))
  492.  
  493.  
  494. (provide 'feedmail)
  495.