;; REPORTMAIL: Display time and load in mode line of Emacs.
;; Originally time.el in the emacs distribution.
;; Mods by BCP, DCP, and JWZ to display incoming mail.
;;
;; Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.

;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Installation
; ------------
;
; To use reportmail, add the following to your .emacs file:
;
;    (load-library "reportmail")
;
;    ;; Edit this list as appropriate
;    (setq display-time-my-addresses
;     '("Benjamin.Pierce" "bcp" "Benjamin Pierce" "Benjamin C. Pierce"))
; 
;    ;; By default, mail arrival is reported with a message but no beep  
;    (setq display-time-mail-ring-bell t)
; 
;    (display-time)
; 
; When new mail arrives, a brief blurb about it will be displayed in the
; mode line, and a more verbose message will be printed in the echo area.
; But unlike most echo-area messages, this message will not go away at
; the next keystroke - it doesn't go away until the next extended-command
; is used.  This is cool because that means you won't miss seeing the 
; subject of the newly-arrived mail because you happened to be typing when
; it arrived.
;
; But if you set the variable `display-time-flush-echo-area' to t, then this
; message will be cleared every `display-time-interval' seconds.  This means
; the message will be around for at most 30 seconds or so, which you may
; prefer.
;
; Site Configuration
; ------------------
;
; The variables display-time-incoming-mail-file and 
; display-time-message-separator identify the location and format of 
; your waiting messages.  If you are in the CMU SCS environment, or
; are on a generic BSD unix system, this code should work right away.
; Otherwise, you might need to modify the values of these to make things
; work.
;
; Junk Mail
; ---------
;
; The reportmail package has a notion of "junk mail," which can be used to
; reduce the frequency of irritating interruptions by reporting only the
; arrival of messages that seem to be interesting.  If you're on a lot
; of high-volume mailing lists, this can be quite convenient.  To use
; this facility, add something like the following to your .emacs file:
; 
;   ;; The value of this variable is a list of lists, where the first
;   ;; element in each list is the name of a header field and the
;   ;; remaining elements are various elements of the value of this
;   ;; header field that signal the junkiness of a message.  
;   (setq display-time-junk-mail-checklist
;     '(("From" "bcp" "Benjamin Pierce" "Benjamin.Pierce"
;               "Mail Delivery Subsystem" "network" "daemon@bartok")
;       ("To" "sml-request" "sml-redistribution-request" 
;        "scheme" "TeXhax-Distribution-list")
;       ("Resent-From" "Benjamin.Pierce")
;       ("Sender" "WRITERS" "Haskell" "Electronic Music Digest" "NEW-LIST")))
;   
; By default, the entries in this list are matched exactly as 
; substrings of the given header fields.  If an entry begins with 
; the character ^ it will be matched as a regular expression.  If the 
; variable display-time-match-using-regexps is set, then all entries
; will be matched as regular expressions.
;
; Note that elements of display-time-my-addresses are NOT automatically
; included in display-time-junk-mail-checklist.  If you want mail from
; yourself to be considered junkmail, you must add your addresses to 
; display-time-junk-mail-checklist too.
;
;
; Xbiff Interface
; ---------------
;
; If you normally keep your emacs window iconified, reportmail can 
; maintain an xbiff display as well.  The xbiff window will only be 
; highlighted when non-junk mail is waiting to be read.  For example:
;
;    (if window-system-version
;        (setq display-time-use-xbiff t))
;    (setq display-time-xbiff-arg-list '("-update" "30" "-geometry" "+0+0"))
;
; Other
; -----
;
; There are several other user-customization variables that you may wish
; to modify.  These are documented below.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; HISTORY
;
; 1 may 92	Jamie Zawinski <jwz@lucid.com>
;	Converted to work with Kyle Jones' timer.el package.
;
; 3 may 91	Jamie Zawinski <jwz@lucid.com>
;	Made the display-time-sentinel make a fuss when the process dies.
;
; 26 mar 91	Jamie Zawinski <jwz@lucid.com>
;	Merged with BCP's latest posted version
;
;  5 mar 91	Jamie Zawinski <jwz@lucid.com>
;	Added compatibility with Emacs 18.57.
;
; 25 Jan 91	Benjamin Pierce (bcp@cs.cmu.edu)
;       Added facility for regular-expression matching of junk-mail
;       checklist.  Set inhibit-local-variables to t inside of 
;       display-time-process-new-mail to prevent letterbombs 
;       (suggested by jwz).
;
; 15 feb 91	Jamie Zawinski <jwz@lucid.com>
;	Made the values of display-time-message-separator and 
;	display-time-incoming-mail-file be initialized when this code
;	starts, instead of forcing the user to do it.  This means that
;	this code can safely be dumped with emacs.  Also, it now notices
;	when it's at CMU, and defaults to something reasonable.  Removed
;	display-time-wait-hard, because I learned how to make echo-area
;	messages be persistent (not go away at the first key).  I wish
;	GC messages didn't destroy it, though...
;
; 20 Dec 90	Jamie Zawinski <jwz@lucid.com>
;	Added new variables: display-time-no-file-means-no-mail, 
;	display-time-wait-hard, and display-time-junk-mail-ring-bell.
;	Made display-time-message-separator be compared case-insensitively.
;	Made the junk-mail checklist use a member-search rather than a 
;	prefix-search.
;
; 22 Jul 90     Benjamin Pierce (bcp@cs.cmu.edu)
;       Added support for debugging.
;
; 19 Jul 90     Benjamin Pierce (bcp@cs.cmu.edu)
;       Improved user documentation and eliminated known CMU dependencies.
;
; 13 Jul 90	Mark Leone (mleone@cs.cmu.edu)
;	Added display-time-use-xbiff option.  Various layout changes.
;
; 20 May 90     Benjamin Pierce (bcp@proof)
;       Fixed a bug that occasionally caused fields to be extracted
;       from the wrong buffer.
;
; 14 May 90     Benjamin Pierce (bcp@proof)
;       Added concept of junk mail and ability to display message
;       recipient in addition to sender and subject.  (Major internal
;       reorganization was needed to implement this cleanly.)
;
; 18 Nov 89     Benjamin Pierce (bcp@proof)
;       Fixed to work when display-time is called with 
;       global-mode-string not a list
;
; 15 Jan 89	David Plaut (dcp@k)
;	Added ability to discard load from displayed string
;
;	To use:	(setq display-time-load nil)
;
;	Added facility for reporting incoming mail (modeled after gosmacs
;	reportmail.ml package written by Benjamin Pierce).


(if (string-match "Lucid" emacs-version)
    (require 'timer))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       User Variables                          ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar display-time-announce-mail t
  "*Toggles whether name of mail sender is displayed in mode line.")

(defvar display-time-announce-junk-mail-too nil
  "*When non-NIL, announce incoming junk mail as well as interesting mail")

(defvar display-time-time t
  "*Toggles whether the time is displayed.")

(defvar display-time-load nil
  "*Toggles whether machine load is displayed.")

(defvar display-time-day-and-date nil
  "*Toggles whether day and date are displayed.")

(defvar display-time-mail-ring-bell nil
  "*Toggles whether bell is rung on mail arrival.")

(defvar display-time-junk-mail-ring-bell nil
  "*Toggles whether bell is rung on junk mail arrival.
If display-time-mail-ring-bell is nil, this variable is ignored.")

(defvar display-time-my-addresses nil
  "*Report the addressee of incoming mail in the message announcement, 
unless it appears in this list  (See also display-time-match-using-regexps.)")
;; For example:
;; (setq display-time-my-addresses
;;  '("Benjamin.Pierce" "bcp" "Benjamin Pierce" "Benjamin C. Pierce"))

(defvar display-time-junk-mail-checklist nil
  "*A list of lists of strings.  In each sublist, the first component is the
name of a message field and the rest are values that flag a piece of
junk mail.  If an entry begins with the character ^ it is matched as
a regular expression rather than an exact prefix of the given header 
field.  (See also display-time-match-using-regexps.)  

Note: elements of display-time-my-addresses are NOT automatically
      included in display-time-junk-mail-checklist")
;; For example:
;; (setq display-time-junk-mail-checklist
;;  '(("From" "bcp" "Benjamin Pierce" "Benjamin.Pierce"
;;            "Mail Delivery Subsystem" "network" "daemon@bartok")
;;    ("To" "sml-request" "sml-redistribution-request" "computermusic" 
;;     "scheme" "TeXhax-Distribution-list")
;;    ("Resent-From" "Benjamin.Pierce")
;;    ("Sender" "WRITERS" "Haskell" "Electronic Music Digest" "NEW-LIST")))

(defvar display-time-match-using-regexps nil "*When non-nil, elements of 
display-time-junk-mail-checklist and display-time-my-addresses are matched
as regular expressions instead of literal prefixes of header fields.")

(defvar display-time-max-from-length 35
  "*Truncate sender name to this length in mail announcements")

(defvar display-time-max-to-length 11
  "*Truncate addressee name to this length in mail announcements")

(defvar display-time-interval 30
  "*Seconds between updates of time in the mode line.  Also used
as interval for checking incoming mail.")

(defvar display-time-no-file-means-no-mail t
  "*Set this to T if you are on a system which deletes your mail-spool file 
when there is no new mail.")

(defvar display-time-incoming-mail-file nil
  "*User's incoming mail file.  Default is value of environment variable MAIL,
if set;  otherwise /usr/spool/mail/$USER is used.")

(defvar display-time-message-separator nil)

(defvar display-time-flush-echo-area nil
  "*If true, then display-time's echo-area message will be 
automatically cleared when display-time-interval has expired.")

(defvar display-time-use-xbiff nil
  "*If set, display-time uses xbiff to announce new mail.")

(defvar display-time-xbiff-arg-list nil
  "*List of arguments passed to xbiff.  Useful for setting geometry, etc.")
;;; For example: 
;;; (setq display-time-xbiff-arg-list '("-update" "30" "-geometry" "+0+0"))

(defvar display-time-mail-arrived-file nil
  "New mail announcements saved in this file if xbiff used.  Deleted when 
mail is read.  Xbiff used to monitor existence of this file.")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Internal Variables                      ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar display-time-loadst-process nil
  "The process providing time, load, and mail info.")

(defvar display-time-xbiff-process nil
  "The xbiff process used to announce incoming mail.")

(defvar display-time-string nil
  "Time displayed in mode line")

(defvar display-time-mail-buffer-name "*Mail*"
  "Name of buffer used for announcing mail.")

(defvar display-time-may-need-to-reset t
  "Set to NIL when display-time-reset-mail-processing has not been called 
since the last time we changed from having mail in the queue to an empty
queue.")

(defvar display-time-debugging nil
  "*When non-NIL, reportmail records various status information
as it's working.")

(defvar display-time-debugging-delay nil 
   "*When non-nil and display-time-debugging is set, sit for this 
long after displaying each debugging message in mode line")

(defvar display-time-debugging-buffer "*Reportmail-Debugging*"
  "Status messages are appended here.")
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Macros                                  ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro display-time-del-file (filename)
  (list 'if (list 'file-exists-p filename) (list 'delete-file filename)))

(defmacro display-time-debug (mesg &rest args)
  (list
     'if 'display-time-debugging
         (list 'display-time-debug-mesg
	       (append (list 'format mesg) args))))

(defun display-time-init ()
  ;; If the mail-file isn't set, figure it out.
  (or display-time-incoming-mail-file
      (setq display-time-incoming-mail-file
	    (or (getenv "MAIL")
		(let ((user-name (or (getenv "USER") (user-login-name))))
		  (and user-name
		       (file-directory-p "/usr/spool/mail/")
		       (concat "/usr/spool/mail/" user-name)))
		"")))
  ;; If the message-separator isn't set, set it to "From " unless
  ;; the local hostname ends in ".CMU.EDU", where "^C" is used.
  (or display-time-message-separator
      (setq display-time-message-separator
	    (let ((case-fold-search t))
	      (if (string-match "\\.cmu\\.edu" (system-name))
		  "\^C"
		  "From "))))
  ;; if this isn't set, these are probably right...
  (or display-time-my-addresses
      (setq display-time-my-addresses
	    (list (user-full-name) (user-login-name))))
  ;;
  (or display-time-mail-arrived-file
      (setq display-time-mail-arrived-file
	    (concat (getenv "HOME") "/.mail-arrived")))
  )


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Time Display                            ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun display-time-kill ()
  "Kill all display-time processes.  Done automatically if display-time
is re-invoked."
  (interactive)
  (if display-time-loadst-process (delete-process display-time-loadst-process))
  (if display-time-xbiff-process (delete-process display-time-xbiff-process))
)


(defun display-time ()
  "Displays current time, date, load level, and incoming mail status in 
mode line of each buffer (if corresponding user variables are set)."
  (interactive)
  (display-time-init)
  (let ((process-connection-type nil))  ; UIUCDCS mod
    (save-excursion
      (display-time-kill)
      (if (or (string-equal "" display-time-incoming-mail-file)
	      (and (not display-time-no-file-means-no-mail)
		   (not (file-exists-p display-time-incoming-mail-file))))
	  (progn 
	     (message "Reportmail: mail spool file \"%s\" not found" 
		      display-time-incoming-mail-file)
	     (sit-for 1)
	     (ding nil)))
      (if (not global-mode-string) (setq global-mode-string '("")))
      (if (not (listp global-mode-string))
	  (setq global-mode-string (list global-mode-string "  ")))
      (if (not (memq 'display-time-string global-mode-string))
	  (setq global-mode-string
		(append global-mode-string '(display-time-string))))
      (setq display-time-string "time and load")
      
      (if (featurep 'timer)
	  (let ((old (get-timer "display-time")))
	    (if old (delete-timer old))
	    (start-timer "display-time" 'display-time-timer-function
			 display-time-interval display-time-interval)
	    (display-time-timer-function))
	;; if we don't have timers, then use one of the process mechanisms.
	(setq display-time-loadst-process
	      (if (string-match "18\\.5[0-5]" (emacs-version))
		  (start-process "display-time-loadst" nil
				 "loadst" 
				 "-n" (int-to-string display-time-interval))
		(start-process "display-time-wakeup" nil
			       (concat exec-directory "wakeup")
			       (int-to-string display-time-interval))))
	(process-kill-without-query display-time-loadst-process)
	(set-process-sentinel display-time-loadst-process 
			      'display-time-sentinel)
	(set-process-filter display-time-loadst-process
			    (if (string-match "18\\.5[0-5]" (emacs-version))
				'display-time-filter-18-55
			      'display-time-filter-18-57)))
      
      (if display-time-use-xbiff
	  (progn
	    (display-time-del-file display-time-mail-arrived-file)
	    (setq display-time-xbiff-process
		  (apply 'start-process "display-time-xbiff" nil
			 "xbiff" "-file" display-time-mail-arrived-file
			 display-time-xbiff-arg-list))
	    (process-kill-without-query display-time-xbiff-process)
	    (sit-for 1)			; Need time to see if xbiff fails.
	    (if (/= 0 (process-exit-status display-time-xbiff-process))
		(error "Display time: xbiff failed.  Check xbiff-arg-list"))))))
  (display-time-reset-mail-processing))


(defun display-time-sentinel (proc reason)
  ;; notice if the process has died an untimely death...
  (cond ((memq (process-status proc) '(stop exit closed signal))
	 (if (and (stringp reason) (string-match "\n?\n*\\'" reason))
	     (setq reason (substring reason 0 (match-beginning 0))))
	 (ding)
	 (setq display-time-string (format "%s" reason))
	 (display-time-message "")
	 (message "process %s: %s (%s)" proc reason (process-status proc))))
  ;; Force mode-line updates
  (save-excursion (set-buffer (other-buffer)))
  (set-buffer-modified-p (buffer-modified-p))
  (sit-for 0))

(defun display-time-filter-18-55 (proc string)
  (if display-time-flush-echo-area (display-time-message ""))
  ;; Desired data can't need more than the last 30 chars,
  ;; so save time by flushing the rest.
  ;; This way, if we have many different times all collected at once,
  ;; we can discard all but the last few very fast.
  (if (> (length string) 30) (setq string (substring string -30)))
  ;; Now discard all but the very last one.
  (while (and (> (length string) 4)
	      (string-match "[0-9]+:[0-9][0-9].." string 4))
    (setq string (substring string (match-beginning 0))))
  (if (string-match "[^0-9][0-9]+:" string)
      (setq string (substring string 0 (1+ (match-beginning 0)))))
  ;; If we're announcing mail and mail has come, process any new messages
  (if display-time-announce-mail
      (if (string-match "Mail" string)
	  (display-time-process-new-mail)
	  (display-time-reset-mail-processing)))
  ;; Format the mode line time display
  (let ((time-string (if (string-match "Mail" string)
			 (if display-time-announce-mail 
			     display-time-mail-modeline
			     "Mail "))))
    (if (and display-time-time (string-match "[0-9]+:[0-9][0-9].." string))
	(setq time-string 
	      (concat time-string
		      (substring string (match-beginning 0) (match-end 0))
		      " ")))
    (if display-time-day-and-date
	(setq time-string
	      (concat time-string
		      (substring (current-time-string) 0 11))))
    (if (and display-time-load (string-match "[0-9]+\\.[0-9][0-9]" string))
	(setq time-string
	      (concat time-string
		      (substring string (match-beginning 0) (match-end 0))
		      " ")))
    ;; Install the new time for display.
    (setq display-time-string time-string)
    ;; Force redisplay of all buffers' mode lines to be considered.
    (save-excursion (set-buffer (other-buffer)))
    (set-buffer-modified-p (buffer-modified-p))
    ;; Do redisplay right now, if no input pending.
    (sit-for 0)))

(defun display-time-filter-18-57 (proc string) ; args are ignored
  (if display-time-flush-echo-area (display-time-message ""))
  (let ((mailp (and (file-exists-p display-time-incoming-mail-file)
		    (not (eq 0 (nth 7 (file-attributes
				       display-time-incoming-mail-file)))))))
    (if display-time-announce-mail
	(if mailp
	    (display-time-process-new-mail)
	    (display-time-reset-mail-processing)))
    ;; Format the mode line time display
    (let ((time-string (if mailp
			   (if display-time-announce-mail
			       display-time-mail-modeline
			       "Mail "))))
      (if display-time-time
	  (let* ((time (current-time-string))
		 (hour (read (substring time 11 13)))
		 (pm (>= hour 12)))
	    (if (> hour 12) (setq hour (- hour 12)))
	    (if (= hour 0) (setq hour 12))
	    (setq time-string
		  (concat time-string
			  (format "%d" hour) (substring time 13 16)
			  (if pm "pm " "am ")))))
      (if display-time-day-and-date
	  (setq time-string
		(concat time-string
			(substring (current-time-string) 0 11))))
      (if display-time-load
	  (setq time-string
	      (concat time-string
		      (condition-case ()
			  (if (zerop (car (load-average)))
			      ""
			      (format "%03d" (car (load-average))))
			(error "load-error"))
		      " ")))
      ;; Install the new time for display.
      (setq display-time-string time-string)
      ;; Force redisplay of all buffers' mode lines to be considered.
      (save-excursion (set-buffer (other-buffer)))
      (set-buffer-modified-p (buffer-modified-p))
      ;; Do redisplay right now, if no input pending.
      (sit-for 0))))

(defun display-time-timer-function ()
  (display-time-filter-18-57 nil nil))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Mail processing                         ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar display-time-mail-who-from ""
  "Short-form name of sender of last piece of interesting unread mail")

(defvar display-time-mail-modeline ""
  "Terse mail announcement (displayed in modeline)")

(defvar display-time-previous-mail-buffer-max 1
  "The length of the mail buffer the last time we looked at it")

(defvar display-time-msg-count 0
  "How many interesting messages have arrived")

(defvar display-time-junk-msg-count 0
  "How many junk messages have arrived")


;; A test procedure for trying out new display-time features
;(defun display-time-test ()
;  (interactive)
;  (display-time-reset-mail-processing)
;  (display-time-process-new-mail))

(defun display-time-reset-mail-processing ()
  (if display-time-may-need-to-reset
   (progn
    (setq display-time-may-need-to-reset nil)
    (display-time-debug "Resetting mail processing")
    (let ((mail-buffer (get-buffer display-time-mail-buffer-name)))
      (if mail-buffer (kill-buffer mail-buffer)))
    (if display-time-use-xbiff
	;; This function is only called when no mail is in the spool.
	;; Hence we should delete the mail-arrived file.
	(display-time-del-file display-time-mail-arrived-file))
    (display-time-reset-mail-processing-vars)
    (display-time-message "") ; clear the echo-area.
    )))

(defun display-time-reset-mail-processing-vars ()
  (setq display-time-msg-count 0)
  (setq display-time-junk-msg-count 0)
  (setq display-time-mail-who-from "Junk mail")
  (setq display-time-previous-mail-buffer-max 1))

(defun display-time-process-new-mail ()
  (setq display-time-may-need-to-reset t)
  (let ((mail-buffer (get-buffer display-time-mail-buffer-name))
	(inhibit-local-variables t))
    (if (not (and mail-buffer (verify-visited-file-modtime mail-buffer)))
      (save-window-excursion
       (save-excursion
	(display-time-debug "\n\nProcessing new mail")
	(if mail-buffer (kill-buffer mail-buffer))
	;; Change to pop-to-buffer when we're debugging:
	(set-buffer (find-file-noselect display-time-incoming-mail-file))
	(rename-buffer display-time-mail-buffer-name)
	(display-time-process-mail-buffer))))))

(defun display-time-process-mail-buffer ()
  (let (start)
    (if (< display-time-previous-mail-buffer-max (point-max))
	(let ((case-fold-search nil))
	  (goto-char display-time-previous-mail-buffer-max)
	  (if (not (looking-at
		    (regexp-quote display-time-message-separator)))
	      (display-time-reset-mail-processing-vars)))
      (display-time-reset-mail-processing-vars))
    (goto-char display-time-previous-mail-buffer-max)
    (if (not (eobp))
	(forward-char 1))
    (while (not (eobp))
      (setq start (point))
      (if (not (let ((case-fold-search nil))
		 (search-forward 
		  (concat "\n" display-time-message-separator) nil t)))
	  (goto-char (point-max)))
      (narrow-to-region start (point))
      (display-time-process-this-message)
      (goto-char (point-max))
      (widen))
    (setq display-time-previous-mail-buffer-max (point-max))))

(defun display-time-process-this-message ()
  (display-time-debug "\nProcessing a message")

  (if (display-time-junk-message)
      (display-time-process-junk-message)
      (display-time-process-good-message))
  
  ;; Update mode line contents
  (setq display-time-mail-modeline 
	(concat "[" (display-time-format-msg-count) 
		display-time-mail-who-from
		"] "))
  (display-time-debug "New mode line: %s " display-time-mail-modeline)
  )

(defun display-time-junk-message ()
  "Check to see whether this message is interesting"

  (display-time-debug "Comparing current message to junk mail checklist")

  (let ((checklist display-time-junk-mail-checklist)
	(junk nil))
    (while (and checklist (not junk))
      (if (display-time-member 
	   (display-time-get-field (car (car checklist)))
	   (cdr (car checklist)))
	  (setq junk t)
	  (setq checklist (cdr checklist))))
    junk))

(defun display-time-message (&rest message-args)
  (let ((str (apply 'format message-args))
	(in-echo-area-already (eq (selected-window) (minibuffer-window))))
    (if (not in-echo-area-already)
	;; don't stomp the echo-area-buffer if reading from the minibuffer now.
	(save-excursion
	  (save-window-excursion
	    (select-window (minibuffer-window))
	    (delete-region (point-min) (point-max))
	    (insert str))))
    ;; if we're reading from the echo-area, and all we were going to do is
    ;; clear the thing, like, don't bother, that's annoying.
    (if (and in-echo-area-already (string= "" str))
	nil
      (message "%s" str))
    ))

(defun display-time-process-good-message ()
  (display-time-debug "Formatting message announcement (good message)")

  ;; Update the message counter
  (setq display-time-msg-count (+ display-time-msg-count 1))

  ;; Format components of announcement
  (let* ((subject (display-time-get-field "Subject" ""))
	 (from (display-time-get-field "From" ""))
	 (to (display-time-get-field "To" ""))
         (print-subject (if (string= subject "")
			    ""
			    (concat " (" subject ")")))
	 (print-from (display-time-truncate from display-time-max-from-length))
	 (short-from (display-time-truncate 
		      (display-time-extract-short-addr from) 25))
	 (print-to (if (display-time-member to display-time-my-addresses)
		       ""
		       (display-time-truncate 
			(display-time-extract-short-addr to)
			display-time-max-to-length))))

    ;; Announce message
    (let ((msg (concat 
		   (display-time-format-msg-count)
		   "Mail " 
		   (if (string= print-to "") "" 
		       (concat "to " print-to " "))
		   "from " print-from 
		   print-subject)))
      (if display-time-use-xbiff
	  (save-excursion
	    (let ((tmp-buf (generate-new-buffer "*Tmp*")))
	      (set-buffer tmp-buf)
	      (insert msg)
	      (newline)
	      (append-to-file (point-min) (point-max) 
			      display-time-mail-arrived-file)
	      (kill-buffer tmp-buf))))
      (display-time-debug "Message: %s" msg)
      (display-time-message "%s" msg))

    (if display-time-mail-ring-bell (ding))
    
    ;; Update mode line information
    (setq display-time-mail-who-from short-from)))

(defun display-time-process-junk-message ()
  (display-time-debug "Formatting message announcement (junk message)")

  ;; Update the message counter
  (setq display-time-junk-msg-count (+ display-time-junk-msg-count 1))

  ;; Format components of announcement
  (let* ((subject (display-time-get-field "Subject" ""))
	 (from (display-time-get-field "From" ""))
	 (to (display-time-get-field "To" ""))
         (print-subject (if (string= subject "")
			    ""
			    (concat " (" subject ")")))
	 (print-from (display-time-truncate from display-time-max-from-length))
	 (short-from (display-time-truncate 
		      (display-time-extract-short-addr from) 25))
	 (print-to (if (display-time-member to display-time-my-addresses)
		       ""
		       (display-time-truncate 
			(display-time-extract-short-addr to)
			display-time-max-to-length))))

    ;; Announce message
    (if display-time-announce-junk-mail-too
	  (let ((msg (concat 
		      (display-time-format-msg-count)
		      "Junk Mail " 
		      (if (string= print-to "") "" 
			(concat "to " print-to " "))
		      "from " print-from 
		      print-subject)))
	    (if display-time-use-xbiff
		(save-excursion
		  (let ((tmp-buf (generate-new-buffer "*Tmp*")))
		    (set-buffer tmp-buf)
		    (insert msg)
		    (newline)
		    (append-to-file (point-min) (point-max) 
				    display-time-mail-arrived-file)
		    (kill-buffer tmp-buf))))
	    (display-time-message "%s" msg)
	    (display-time-debug "Message: %s" msg)
	    (if (and display-time-mail-ring-bell
		     display-time-junk-mail-ring-bell)
		(ding))))))

(defun display-time-format-msg-count ()
   (if (> (+ display-time-msg-count display-time-junk-msg-count) 1) 
       (concat 
	(int-to-string display-time-msg-count) 
	(if (> display-time-junk-msg-count 0)
	    (concat "(" (int-to-string display-time-junk-msg-count) ")"))
	": ")
       ""))

(defun display-time-get-field (field &optional default)
  (cond ((not (equal (buffer-name) display-time-mail-buffer-name))
    (beep)
    (message "reportmail bug: processing buffer %s, not %s"
	     (buffer-name)
	     display-time-mail-buffer-name)
    (sit-for 2)))
  (goto-char (point-min))
  (let ((result
	 (if (re-search-forward (concat "^" field ":[ |\C-i]*") nil t)
	     (let ((start (point)))
	       (end-of-line)
	       (while (looking-at "\n[ \t]")
		 (forward-line 1)
		 (end-of-line))
	       (buffer-substring start (point)))
	     (or default "<unknown>"))))
    (display-time-debug "value of %s field is %s" field result)
    result))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Auxilliary Functions                    ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun display-time-member (e l)
  "Is string E matched by an element of list L?  
When an element of L begins with ^, match it as a regexp.  Otherwise,
ignore case and match exactly.  If display-time-match-using-regexps is
non-nil, always match using regexps."
  (let ((done nil)
	(result nil))
    (while (not done)
      (cond 
       ((null l) (setq done t))
       ((or display-time-match-using-regexps (= (elt (car l) 0) ?^))
	(if (string-match (car l) e)
	    (setq result l done t)
	  (setq l (cdr l))))
       ((string-match (regexp-quote (downcase (car l))) (downcase e)) 
	(setq result l done t))
       (t 
	(setq l (cdr l)))))
    result))

(defun display-time-truncate (s max)
  (if (and s (>= (length s) max))
      (concat (substring s 0 max) "\\")
      s))

(defun display-time-extract-short-addr (long-addr)
  (let ((name "\\([A-Za-z0-9-_+\\. ]+\\)"))
    (if (or 
	 ;; David Plaut <dcp@CS.CMU.EDU>	 -> David Plaut
	 (string-match (concat name "[ |	]+<.+>") long-addr)
	
	 ;; anything (David Plaut) anything	 -> David Plaut
	 (string-match ".+(\\(.+\\)).*" long-addr)
	 
	 ;; plaut%address.bitnet@vma.cc.cmu.edu -> plaut
	 (string-match (concat name "%.+@.+") long-addr)

	 ;; random!uucp!addresses!dcp@uu.relay.net -> dcp
	 (string-match (concat ".*!" name "@.+") long-addr)

	 ;; David.Plaut@CS.CMU.EDU		 -> David.Plaut
	 (string-match (concat name "@.+") long-addr)
	 )
	(substring long-addr (match-beginning 1) (match-end 1))
	long-addr)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;                       Debugging Support                       ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; (defvar display-time-debugging-messages t
;;   "When non-NIL, reportmail displays status messages in real time.")

(defun display-time-debug-mesg (mesg)
  ;; (if display-time-debugging-messages
  ;;     (progn 
  ;;	(message "Reportmail: %s" mesg)
  ;;	(sit-for 1)
  ;;	))
  (save-excursion
    (save-window-excursion
      (set-buffer (get-buffer-create display-time-debugging-buffer))
      (goto-char (point-max))
      (insert mesg "\n")))
  (if display-time-debugging-delay
      (progn (message "Reportmail: %s" mesg)
	     (sit-for display-time-debugging-delay))))
