home *** CD-ROM | disk | FTP | other *** search
- ;;; Summary gathering and formatting routines for VM
- ;;; Copyright (C) 1989 Kyle E. Jones
- ;;;
- ;;; This program is free software; you can redistribute it and/or modify
- ;;; it under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 1, or (at your option)
- ;;; any later version.
- ;;;
- ;;; This program is distributed in the hope that it will be useful,
- ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with this program; if not, write to the Free Software
- ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- (require 'vm)
-
- (defun vm-summary-mode ()
- "Major mode for VM folder summaries.
- This major mode use the same keymap as vm-mode. See the vm-mode documentation
- for a list of available commands."
- (setq mode-name "VM Summary"
- major-mode 'vm-summary-mode
- mode-line-buffer-identification '("VM " vm-version ": %b")
- buffer-read-only t
- overlay-arrow-string "->"
- overlay-arrow-position nil
- truncate-lines t)
- (use-local-map vm-mode-map)
- (save-excursion
- (set-buffer vm-mail-buffer)
- (vm-set-summary-pointer (car vm-message-pointer))))
-
- (put 'vm-summary-mode 'mode-class 'special)
-
- (defun vm-summarize (&optional dont-redo)
- "Summarize the contents of the folder in a summary buffer.
- The format is as described by the variable vm-summary-format. Generally
- one line per message is most pleasing to the eye but this is not
- mandatory."
- (interactive "p")
- (if vm-mail-buffer
- (set-buffer vm-mail-buffer))
- (vm-error-if-folder-empty)
- (if (or (null vm-summary-buffer) (not dont-redo))
- (let ((b (current-buffer))
- (inhibit-quit t))
- (setq vm-summary-buffer
- (get-buffer-create (format "%s Summary" (buffer-name))))
- (save-excursion
- (set-buffer vm-summary-buffer)
- (abbrev-mode 0)
- (auto-fill-mode 0)
- (setq vm-mail-buffer b))
- (vm-do-summary)
- (save-excursion
- (set-buffer vm-summary-buffer)
- (vm-summary-mode))))
- (if vm-mutable-windows
- (let ((pop-up-windows (and pop-up-windows (eq vm-mutable-windows t))))
- (display-buffer vm-summary-buffer))
- (switch-to-buffer vm-summary-buffer))
- (if (eq vm-mutable-windows t)
- (vm-proportion-windows))
- (if vm-mail-buffer
- (set-buffer vm-mail-buffer))
- (vm-set-summary-pointer (car vm-message-pointer)))
-
- (defun vm-do-summary ()
- (let ((mp vm-message-list)
- (n 0)
- ;; Just for laughs, make the update interval variable.
- (modulus (+ (% (vm-abs (random)) 7) 10))
- summary)
- (message "Generating summary...")
- (save-excursion
- (set-buffer vm-summary-buffer)
- (let ((buffer-read-only nil))
- (erase-buffer)
- (while mp
- (set-buffer vm-mail-buffer)
- (setq summary (vm-sprintf 'vm-summary-format (car mp)))
- (set-buffer vm-summary-buffer)
- (vm-set-su-start-of (car mp) (point-marker))
- ;; the leading spaces are to make room for the overlay-arrow-string
- (insert " " summary)
- (vm-set-su-end-of (car mp) (point-marker))
- (setq mp (cdr mp) n (1+ n))
- (if (zerop (% n modulus))
- (message "Generating summary... %d" n)))))
- (message "Generating summary... done")))
-
- (defun vm-update-message-summary (mp)
- (if vm-summary-buffer
- (let ((summary (vm-sprintf 'vm-summary-format (car mp))))
- (save-excursion
- (set-buffer vm-summary-buffer)
- (let ((inhibit-quit t) buffer-read-only)
- (goto-char (vm-su-start-of (car mp)))
- ;; We insert a char here and delete it later to avoid
- ;; markers clumping at the beginning of the summary,
- (insert "*")
- (delete-region (point) (vm-su-end-of (car mp)))
- (insert-before-markers " " summary)
- (goto-char (vm-su-start-of (car mp)))
- (delete-char 1))))))
-
- (defun vm-set-summary-pointer (m)
- (setq overlay-arrow-position (vm-su-start-of m))
- (cond (vm-summary-buffer
- (let ((w (get-buffer-window vm-summary-buffer)))
- (save-excursion
- (set-buffer vm-summary-buffer)
- (goto-char overlay-arrow-position)
- (and w (set-window-point w overlay-arrow-position)))))))
-
- (defun vm-follow-summary-cursor ()
- (and vm-follow-summary-cursor (eq major-mode 'vm-summary-mode)
- (let ((point (point))
- message-pointer message-list)
- (save-excursion
- (set-buffer vm-mail-buffer)
- (setq message-pointer vm-message-pointer
- message-list vm-message-list))
- (if (or (null message-pointer)
- (and (>= point (vm-su-start-of (car message-pointer)))
- (< point (vm-su-end-of (car message-pointer)))))
- ()
- (if (< point (vm-su-start-of (car message-pointer)))
- (setq mp message-list)
- (setq mp (cdr message-pointer) message-pointer nil))
- (while (and (not (eq mp message-pointer))
- (>= point (vm-su-end-of (car mp))))
- (setq mp (cdr mp)))
- (if (not (eq mp message-pointer))
- (save-excursion
- (set-buffer vm-mail-buffer)
- (setq vm-last-message-pointer vm-message-pointer
- vm-message-pointer mp)
- (vm-set-summary-pointer (car vm-message-pointer))
- (vm-preview-current-message)
- ;; return non-nil so the caller will know the
- ;; a new message was selected.
- t ))))))
-
- (defun vm-sprintf (format-variable message)
- (if (not (eq (get format-variable 'vm-compiled-format)
- (symbol-value format-variable)))
- (vm-compile-format format-variable))
- ;; The local variable name `vm-su-message' is mandatory here for
- ;; the format s-expression to work.
- (let ((vm-su-message message))
- (eval (get format-variable 'vm-format-sexp))))
-
- (defun vm-compile-format (format-variable)
- (let ((format (symbol-value format-variable))
- sexp sexp-fmt conv-spec last-match-end case-fold-search)
- (store-match-data nil)
- (while (string-match
- "%\\(-\\)?\\([0-9]\\)*\\(\\.\\([0-9]+\\)\\)?\\([acdfFhilmnswyz%]\\)"
- format (match-end 0))
- (setq conv-spec (aref format (match-beginning 5)))
- (if (memq conv-spec '(?a ?c ?d ?f ?F ?h ?i ?l ?m ?n ?s ?w ?y ?z))
- (progn
- (cond ((= conv-spec ?a)
- (setq sexp (cons (list 'vm-su-attribute-indicators
- 'vm-su-message) sexp)))
- ((= conv-spec ?c)
- (setq sexp (cons (list 'vm-su-byte-count
- 'vm-su-message) sexp)))
- ((= conv-spec ?d)
- (setq sexp (cons (list 'vm-su-monthday
- 'vm-su-message) sexp)))
- ((= conv-spec ?f)
- (setq sexp (cons (list 'vm-su-from
- 'vm-su-message) sexp)))
- ((= conv-spec ?F)
- (setq sexp (cons (list 'vm-su-full-name
- 'vm-su-message) sexp)))
- ((= conv-spec ?h)
- (setq sexp (cons (list 'vm-su-hour
- 'vm-su-message) sexp)))
- ((= conv-spec ?i)
- (setq sexp (cons (list 'vm-su-message-id
- 'vm-su-message) sexp)))
- ((= conv-spec ?l)
- (setq sexp (cons (list 'vm-su-line-count
- 'vm-su-message) sexp)))
- ((= conv-spec ?m)
- (setq sexp (cons (list 'vm-su-month
- 'vm-su-message) sexp)))
- ((= conv-spec ?n)
- (setq sexp (cons (list 'vm-su-message-number
- 'vm-su-message) sexp)))
- ((= conv-spec ?s)
- (setq sexp (cons (list 'vm-su-subject
- 'vm-su-message) sexp)))
- ((= conv-spec ?w)
- (setq sexp (cons (list 'vm-su-weekday
- 'vm-su-message) sexp)))
- ((= conv-spec ?y)
- (setq sexp (cons (list 'vm-su-year
- 'vm-su-message) sexp)))
- ((= conv-spec ?z)
- (setq sexp (cons (list 'vm-su-zone
- 'vm-su-message) sexp))))
- (cond ((match-beginning 1)
- (setcar sexp
- (list 'vm-left-justify-string (car sexp)
- (string-to-int (substring format
- (match-beginning 2)
- (match-end 2))))))
- ((match-beginning 2)
- (setcar sexp
- (list 'vm-right-justify-string (car sexp)
- (string-to-int (substring format
- (match-beginning 2)
- (match-end 2)))))))
- (cond ((match-beginning 3)
- (setcar sexp
- (list 'vm-truncate-string (car sexp)
- (string-to-int (substring format
- (match-beginning 4)
- (match-end 4)))))))
- (setq sexp-fmt
- (cons "%s"
- (cons (substring format
- (or last-match-end 0)
- (match-beginning 0))
- sexp-fmt))))
- (setq sexp-fmt
- (cons "%%"
- (cons (substring format
- (or last-match-end 0)
- (match-beginning 0))
- sexp-fmt))))
- (setq last-match-end (match-end 0)))
- (setq sexp-fmt
- (cons (substring format
- (or last-match-end 0)
- (length format))
- sexp-fmt)
- sexp-fmt (apply 'concat (nreverse sexp-fmt))
- sexp (cons 'format (cons sexp-fmt (nreverse sexp))))
- (put format-variable 'vm-format-sexp sexp)
- (put format-variable 'vm-compiled-format format)))
-
- (defun vm-get-header-contents (message header-name)
- (let (contents regexp)
- (setq regexp (format vm-header-regexp-format header-name))
- (save-excursion
- (set-buffer (marker-buffer (vm-start-of message)))
- (save-restriction
- (widen)
- (goto-char (vm-start-of message))
- (while (re-search-forward regexp (vm-text-of message) t)
- (if contents
- (setq contents
- (concat
- contents ",\n\t"
- (buffer-substring (match-beginning 1) (match-end 1))))
- (setq contents
- (buffer-substring (match-beginning 1) (match-end 1)))))
- contents))))
-
- (defun vm-left-justify-string (string width)
- (if (>= (length string) width)
- string
- (concat string (make-string (- width (length string)) ?\ ))))
-
- (defun vm-right-justify-string (string width)
- (if (>= (length string) width)
- string
- (concat (make-string (- width (length string)) ?\ ) string)))
-
- (defun vm-truncate-string (string width)
- (if (<= (length string) width)
- string
- (substring string 0 width)))
-
- (defun vm-su-attribute-indicators (m)
- (concat
- (cond ((vm-deleted-flag m) "D")
- ((vm-new-flag m) "N")
- ((vm-unread-flag m) "U")
- (t " "))
- (cond ((vm-filed-flag m) "F")
- (t " "))
- (cond ((vm-replied-flag m) "R")
- (t " "))))
-
- (defun vm-su-byte-count (m)
- (or (vm-byte-count-of m)
- (vm-set-byte-count-of m (int-to-string
- (- (vm-text-end-of m) (vm-text-of m))))))
-
- (defun vm-su-weekday (m)
- (or (vm-weekday-of m)
- (progn (vm-su-do-date m) (vm-weekday-of m))))
-
- (defun vm-su-monthday (m)
- (or (vm-monthday-of m)
- (progn (vm-su-do-date m) (vm-monthday-of m))))
-
- (defun vm-su-month (m)
- (or (vm-month-of m)
- (progn (vm-su-do-date m) (vm-month-of m))))
-
- (defun vm-su-year (m)
- (or (vm-year-of m)
- (progn (vm-su-do-date m) (vm-year-of m))))
-
- (defun vm-su-hour (m)
- (or (vm-hour-of m)
- (progn (vm-su-do-date m) (vm-hour-of m))))
-
- (defun vm-su-zone (m)
- (or (vm-zone-of m)
- (progn (vm-su-do-date m) (vm-zone-of m))))
-
- ;; Some yogurt-headed delivery agents don't even provide a Date: header.
- (defun vm-grok-From_-date (message)
- ;; If this is MMDF, forget it.
- (if (eq vm-folder-type 'mmdf)
- nil
- (save-excursion
- (set-buffer (marker-buffer (vm-start-of message)))
- (save-restriction
- (widen)
- (goto-char (vm-start-of message))
- (if (looking-at "From [^ \t\n]+[ \t]+\\([^ \t\n].*\\)")
- (buffer-substring (match-beginning 1) (match-end 1)))))))
-
- (defun vm-su-do-date (m)
- (let (date)
- (setq date (or (vm-get-header-contents m "Date") (vm-grok-From_-date m)))
- (cond
- ((null date)
- (vm-set-weekday-of m "")
- (vm-set-monthday-of m "")
- (vm-set-month-of m "")
- (vm-set-year-of m "")
- (vm-set-hour-of m "")
- (vm-set-zone-of m ""))
- ((string-match
- ;; The date format recognized here is the one specified in RFC 822.
- ;; Some slop is allowed e.g. dashes between the monthday, month and year
- ;; because such malformed headers headers have been observed.
- "\\(\\([a-z][a-z][a-z]\\),\\)?[ \t\n]*\\([0-9][0-9]?\\)[ \t\n---]*\\([a-z][a-z][a-z]\\)[ \t\n---]*[0-9]*\\([0-9][0-9]\\)[ \t\n]*\\([0-9:]+\\)[ \t\n]*\\([a-z][a-z]?[a-z]?\\|[---+][0-9][0-9][0-9][0-9]\\)"
- date)
- (if (match-beginning 2)
- (vm-set-weekday-of m (substring date (match-beginning 2)
- (match-end 2)))
- (vm-set-weekday-of m ""))
- (vm-set-monthday-of m (substring date (match-beginning 3) (match-end 3)))
- (vm-set-month-of m (substring date (match-beginning 4) (match-end 4)))
- (vm-set-year-of m (substring date (match-beginning 5) (match-end 5)))
- (vm-set-hour-of m (substring date (match-beginning 6) (match-end 6)))
- (vm-set-zone-of m (substring date (match-beginning 7) (match-end 7))))
- ((string-match
- ;; UNIX ctime(3) format with slop allowed in the whitespace and we allow for
- ;; the possibility of a timezone at the end.
- "\\([a-z][a-z][a-z]\\)[ \t\n]*\\([a-z][a-z][a-z]\\)[ \t\n]*\\([0-9][0-9]?\\)[ \t\n]*\\([0-9:]+\\)[ \t\n]*[0-9][0-9]\\([0-9][0-9]\\)[ \t\n]*\\([a-z][a-z]?[a-z]?\\|[---+][0-9][0-9][0-9][0-9]\\)?"
- date)
- (vm-set-weekday-of m (substring date (match-beginning 1) (match-end 1)))
- (vm-set-month-of m (substring date (match-beginning 2) (match-end 2)))
- (vm-set-monthday-of m (substring date (match-beginning 3) (match-end 3)))
- (vm-set-hour-of m (substring date (match-beginning 4) (match-end 4)))
- (vm-set-year-of m (substring date (match-beginning 5) (match-end 5)))
- (if (match-beginning 6)
- (vm-set-zone-of m (substring date (match-beginning 6)
- (match-end 6)))))
- (t
- (vm-set-weekday-of m "")
- (vm-set-monthday-of m "")
- (vm-set-month-of m "")
- (vm-set-year-of m "")
- (vm-set-hour-of m "")
- (vm-set-zone-of m "")))))
-
- (defun vm-su-full-name (m)
- (or (vm-full-name-of m)
- (progn (vm-su-do-author m) (vm-full-name-of m))))
-
- (defun vm-su-from (m)
- (or (vm-from-of m)
- (progn (vm-su-do-author m) (vm-from-of m))))
-
- ;; Some yogurt-headed delivery agents don't even provide a From: header.
- (defun vm-grok-From_-author (message)
- ;; If this is MMDF, forget it.
- (if (eq vm-folder-type 'mmdf)
- nil
- (save-excursion
- (set-buffer (marker-buffer (vm-start-of message)))
- (save-restriction
- (widen)
- (goto-char (vm-start-of message))
- (if (looking-at "From \\([^ \t\n]+\\)")
- (buffer-substring (match-beginning 1) (match-end 1)))))))
-
- (defun vm-su-do-author (m)
- (let (full-name from)
- (setq full-name (vm-get-header-contents m "Full-Name"))
- (setq from (or (vm-get-header-contents m "From") (vm-grok-From_-author m)))
- (cond ((null from)
- (setq from "???")
- (if (null full-name)
- (setq full-name "???")))
- ((string-match "^\\(\\([^<]+[^ \t\n]\\)[ \t\n]+\\)?<\\([^>]+\\)>"
- from)
- (if (and (match-beginning 2) (null full-name))
- (setq full-name
- (substring from (match-beginning 2) (match-end 2))))
- (setq from (substring from (match-beginning 3) (match-end 3))))
- ((string-match "[\000-\177]*(\\([^)]+\\))[\000-\177]*" from)
- (if (null full-name)
- (setq full-name (substring from (match-beginning 1)
- (match-end 1))))
- (setq from
- (concat
- (substring from (match-beginning 0) (1- (match-beginning 1)))
- (substring from (1+ (match-end 1)) (match-end 0))))))
- ;; ewe ewe see pee...
- (if (and vm-gargle-uucp (string-match
- "\\([^!@:.]+\\)\\(\\.[^!@:]+\\)?!\\([^!@: \t\n]+\\)\\(@\\([^!@:. \t\n]+\\)\\(.[^ \t\n]+\\)?\\)?[ \t\n]*$"
- from))
- (setq from
- (concat
- (substring from (match-beginning 3) (match-end 3)) "@"
- (if (and (match-beginning 5) (match-beginning 2)
- (not (match-beginning 6)))
- (concat (substring from (match-beginning 5) (match-end 5))
- ".")
- "")
- (substring from (match-beginning 1)
- (or (match-end 2) (match-end 1)))
- (if (match-end 2) "" ".UUCP"))))
- (if (or (null full-name) (string-match "^[ \t\n]*$" full-name))
- (setq full-name from))
- (vm-set-full-name-of m full-name)
- (vm-set-from-of m from)))
-
- (defun vm-su-message-id (m)
- (or (vm-message-id-of m)
- (vm-set-message-id-of m
- (or (vm-get-header-contents m "Message-Id")
- ""))))
-
- (defun vm-su-line-count (m)
- (or (vm-line-count-of m)
- (vm-set-line-count-of
- m
- (save-restriction
- (widen)
- (int-to-string
- (count-lines (vm-text-of m) (vm-text-end-of m)))))))
-
- (defun vm-su-message-number (m)
- (vm-number-of m))
-
- (defun vm-su-subject (m)
- (or (vm-subject-of m)
- (vm-set-subject-of m
- (or (vm-get-header-contents m "Subject") ""))))
-