view lisp/mh-e/mh-init.el @ 88336:f6a3624b4de6

*** empty log message ***
author Alex Schroeder <alex@gnu.org>
date Tue, 11 Apr 2006 13:29:07 +0000
parents d7ddb3e565de
children
line wrap: on
line source

;;; mh-init.el --- MH-E initialization

;; Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.

;; Author: Peter S. Galbraith <psg@debian.org>
;; Maintainer: Bill Wohler <wohler@newt.com>
;; Keywords: mail
;; See: mh-e.el

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:

;; Sets up the MH variant (currently nmh, MH, or GNU mailutils).
;;
;; Users may customize `mh-variant' to switch between available variants.
;; Available MH variants are returned by the function `mh-variants'.
;; Developers may check which variant is currently in use with the
;; variable `mh-variant-in-use' or the function `mh-variant-p'.
;;
;; Also contains code that is used at load or initialization time only.

;;; Change Log:

;;; Code:

;;(message "> mh-init")
(eval-when-compile (require 'mh-acros))
(mh-require-cl)
(require 'mh-buffers)
(require 'mh-exec)
;;(message "< mh-init")

(defvar mh-sys-path
  '("/usr/local/nmh/bin"                ; nmh default
    "/usr/local/bin/mh/"
    "/usr/local/mh/"
    "/usr/bin/mh/"                      ; Ultrix 4.2, Linux
    "/usr/new/mh/"                      ; Ultrix < 4.2
    "/usr/contrib/mh/bin/"              ; BSDI
    "/usr/pkg/bin/"                     ; NetBSD
    "/usr/local/bin/"
    "/usr/local/bin/mu-mh/"             ; GNU mailutils - default
    "/usr/bin/mu-mh/")                  ; GNU mailutils - packaged
  "List of directories to search for variants of the MH variant.
The list `exec-path' is searched in addition to this list.
There's no need for users to modify this list. Instead add extra
directories to the customizable variable `mh-path'.")

;; Set for local environment:
;; mh-progs and mh-lib used to be set in paths.el, which tried to
;; figure out at build time which of several possible directories MH
;; was installed into.  But if you installed MH after building Emacs,
;; this would almost certainly be wrong, so now we do it at run time.

(defvar mh-flists-present-flag nil
  "Non-nil means that we have \"flists\".")

(defvar mh-variants nil
  "List describing known MH variants.
Do not access this variable directly as it may not have yet been initialized.
Use the function `mh-variants' instead.")

;;;###mh-autoload
(defun mh-variants ()
  "Return a list of installed variants of MH on the system.
This function looks for MH in `mh-sys-path', `mh-path' and
`exec-path'. The format of the list of variants that is returned
is described by the variable `mh-variants'."
  (if mh-variants
      mh-variants
    (let ((list-unique))
      ;; Make a unique list of directories, keeping the given order.
      ;; We don't want the same MH variant to be listed multiple times.
      (loop for dir in (append mh-path mh-sys-path exec-path) do
            (setq dir (file-chase-links (directory-file-name dir)))
            (add-to-list 'list-unique dir))
      (loop for dir in (nreverse list-unique) do
            (when (and dir (file-directory-p dir) (file-readable-p dir))
              (let ((variant (mh-variant-info dir)))
                (if variant
                    (add-to-list 'mh-variants variant)))))
      mh-variants)))

(defun mh-variant-info (dir)
  "Return MH variant found in DIR, or nil if none present."
  (save-excursion
    (let ((tmp-buffer (get-buffer-create mh-temp-buffer)))
      (set-buffer tmp-buffer)
      (cond
       ((mh-variant-mh-info dir))
       ((mh-variant-nmh-info dir))
       ((mh-variant-mu-mh-info dir))))))

(defun mh-variant-mh-info (dir)
  "Return info for MH variant in DIR assuming a temporary buffer is setup."
  ;; MH does not have the -version option.
  ;; Its version number is included in the output of "-help" as:
  ;;
  ;; version: MH 6.8.4 #2[UCI] (burrito) of Fri Jan 15 20:01:39 EST 1999
  ;; options: [ATHENA] [BIND] [DUMB] [LIBLOCKFILE] [LOCALE] [MAILGROUP] [MHE]
  ;;          [MHRC] [MIME] [MORE='"/usr/bin/sensible-pager"'] [NLINK_HACK]
  ;;          [NORUSERPASS] [OVERHEAD] [POP] [POPSERVICE='"pop-3"'] [RENAME]
  ;;          [RFC1342] [RPATHS] [RPOP] [SENDMTS] [SMTP] [SOCKETS]
  ;;          [SPRINTFTYPE=int] [SVR4] [SYS5] [SYS5DIR] [TERMINFO]
  ;;          [TYPESIG=void] [UNISTD] [UTK] [VSPRINTF]
  (let ((mhparam (expand-file-name "mhparam" dir)))
    (when (mh-file-command-p mhparam)
      (erase-buffer)
      (call-process mhparam nil '(t nil) nil "-help")
      (goto-char (point-min))
      (when (search-forward-regexp "version: MH \\(\\S +\\)" nil t)
        (let ((version (format "MH %s" (match-string 1))))
          (erase-buffer)
          (call-process mhparam nil '(t nil) nil "libdir")
          (goto-char (point-min))
          (when (search-forward-regexp "^.*$" nil t)
            (let ((libdir (match-string 0)))
              `(,version
                (variant        mh)
                (mh-lib-progs   ,libdir)
                (mh-lib         ,libdir)
                (mh-progs       ,dir)
                (flists         nil)))))))))

(defun mh-variant-mu-mh-info (dir)
  "Return info for GNU mailutils variant in DIR.
This assumes that a temporary buffer is setup."
  ;; 'mhparam -version' output:
  ;; mhparam (GNU mailutils 0.3.2)
  (let ((mhparam (expand-file-name "mhparam" dir)))
    (when (mh-file-command-p mhparam)
      (erase-buffer)
      (call-process mhparam nil '(t nil) nil "-version")
      (goto-char (point-min))
      (when (search-forward-regexp "mhparam (\\(GNU [Mm]ailutils \\S +\\))"
                                   nil t)
        (let ((version (match-string 1))
              (mh-progs dir))
          `(,version
            (variant        mu-mh)
            (mh-lib-progs   ,(mh-profile-component "libdir"))
            (mh-lib         ,(mh-profile-component "etcdir"))
            (mh-progs       ,dir)
            (flists         ,(file-exists-p
                              (expand-file-name "flists" dir)))))))))

(defun mh-variant-nmh-info (dir)
  "Return info for nmh variant in DIR assuming a temporary buffer is setup."
  ;; `mhparam -version' outputs:
  ;; mhparam -- nmh-1.1-RC1 [compiled on chaak at Fri Jun 20 11:03:28 PDT 2003]
  (let ((mhparam (expand-file-name "mhparam" dir)))
    (when (mh-file-command-p mhparam)
      (erase-buffer)
      (call-process mhparam nil '(t nil) nil "-version")
      (goto-char (point-min))
      (when (search-forward-regexp "mhparam -- nmh-\\(\\S +\\)" nil t)
        (let ((version (format "nmh %s" (match-string 1)))
              (mh-progs dir))
          `(,version
            (variant        nmh)
            (mh-lib-progs   ,(mh-profile-component "libdir"))
            (mh-lib         ,(mh-profile-component "etcdir"))
            (mh-progs       ,dir)
            (flists         ,(file-exists-p
                              (expand-file-name "flists" dir)))))))))

(defun mh-file-command-p (file)
  "Return t if file FILE is the name of a executable regular file."
  (and (file-regular-p file) (file-executable-p file)))

(defvar mh-variant-in-use nil
  "The MH variant currently in use; a string with variant and version number.
This differs from `mh-variant' when the latter is set to
\"autodetect\".")

;;;###mh-autoload
(defun mh-variant-set (variant)
  "Set the MH variant to VARIANT.
Sets `mh-progs', `mh-lib', `mh-lib-progs' and
`mh-flists-present-flag'.
If the VARIANT is \"autodetect\", then first try nmh, then MH and
finally GNU mailutils."
  (interactive
   (list (completing-read
          "MH variant: "
          (mapcar (lambda (x) (list (car x))) (mh-variants))
          nil t)))
  (let ((valid-list (mapcar (lambda (x) (car x)) (mh-variants))))
    (cond
     ((eq variant 'none))
     ((eq variant 'autodetect)
      (cond
       ((mh-variant-set-variant 'nmh)
        (message "%s installed as MH variant" mh-variant-in-use))
       ((mh-variant-set-variant 'mh)
        (message "%s installed as MH variant" mh-variant-in-use))
       ((mh-variant-set-variant 'mu-mh)
        (message "%s installed as MH variant" mh-variant-in-use))
       (t
        (message "No MH variant found on the system"))))
     ((member variant valid-list)
      (when (not (mh-variant-set-variant variant))
        (message "Warning: %s variant not found. Autodetecting..." variant)
        (mh-variant-set 'autodetect)))
     (t
      (message "Unknown variant; use %s"
               (mapconcat '(lambda (x) (format "%s" (car x)))
                          (mh-variants) " or "))))))

(defun mh-variant-set-variant (variant)
  "Setup the system variables for the MH variant named VARIANT.
If VARIANT is a string, use that key in the alist returned by the
function `mh-variants'.
If VARIANT is a symbol, select the first entry that matches that
variant."
  (cond
   ((stringp variant)                   ;e.g. "nmh 1.1-RC1"
    (when (assoc variant (mh-variants))
      (let* ((alist (cdr (assoc variant (mh-variants))))
             (lib-progs (cadr (assoc 'mh-lib-progs alist)))
             (lib       (cadr (assoc 'mh-lib       alist)))
             (progs     (cadr (assoc 'mh-progs     alist)))
             (flists    (cadr (assoc 'flists       alist))))
        ;;(set-default mh-variant variant)
        (setq mh-x-mailer-string     nil
              mh-flists-present-flag flists
              mh-lib-progs           lib-progs
              mh-lib                 lib
              mh-progs               progs
              mh-variant-in-use      variant))))
   ((symbolp variant)                   ;e.g. 'nmh (pick the first match)
    (loop for variant-list in (mh-variants)
          when (eq variant (cadr (assoc 'variant (cdr variant-list))))
          return (let* ((version   (car variant-list))
                        (alist (cdr variant-list))
                        (lib-progs (cadr (assoc 'mh-lib-progs alist)))
                        (lib       (cadr (assoc 'mh-lib       alist)))
                        (progs     (cadr (assoc 'mh-progs     alist)))
                        (flists    (cadr (assoc 'flists       alist))))
                   ;;(set-default mh-variant flavor)
                   (setq mh-x-mailer-string     nil
                         mh-flists-present-flag flists
                         mh-lib-progs           lib-progs
                         mh-lib                 lib
                         mh-progs               progs
                         mh-variant-in-use      version)
                   t)))))

;;;###mh-autoload
(defun mh-variant-p (&rest variants)
  "Return t if variant is any of VARIANTS.
Currently known variants are 'MH, 'nmh, and 'mu-mh."
  (let ((variant-in-use
         (cadr (assoc 'variant (assoc mh-variant-in-use (mh-variants))))))
    (not (null (member variant-in-use variants)))))



;;; Read MH Profile

(defvar mh-find-path-run nil
  "Non-nil if `mh-find-path' has been run already.
Do not access this variable; `mh-find-path' already uses it to
avoid running more than once.")

(defun mh-find-path ()
  "Set variables from user's MH profile.

This function sets `mh-user-path' from your \"Path:\" MH profile
component (but defaults to \"Mail\" if one isn't present),
`mh-draft-folder' from \"Draft-Folder:\", `mh-unseen-seq' from
\"Unseen-Sequence:\", `mh-previous-seq' from
\"Previous-Sequence:\", and `mh-inbox' from \"Inbox:\" (defaults
to \"+inbox\").

The hook `mh-find-path-hook' is run after these variables have
been set. This hook can be used the change the value of these
variables if you need to run with different values between MH and
MH-E."
  (unless mh-find-path-run
    ;; Sanity checks.
    (if (and (getenv "MH")
             (not (file-readable-p (getenv "MH"))))
        (error "MH environment variable contains unreadable file %s"
               (getenv "MH")))
    (if (null (mh-variants))
        (error "Install MH and run install-mh before running MH-E"))
    (let ((profile "~/.mh_profile"))
      (if (not (file-readable-p profile))
          (error "Run install-mh before running MH-E")))
    ;; Read MH profile.
    (setq mh-user-path (mh-profile-component "Path"))
    (if (not mh-user-path)
        (setq mh-user-path "Mail"))
    (setq mh-user-path
          (file-name-as-directory
           (expand-file-name mh-user-path (expand-file-name "~"))))
    (unless mh-x-image-cache-directory
      (setq mh-x-image-cache-directory
            (expand-file-name ".mhe-x-image-cache" mh-user-path)))
    (setq mh-draft-folder (mh-profile-component "Draft-Folder"))
    (if mh-draft-folder
        (progn
          (if (not (mh-folder-name-p mh-draft-folder))
              (setq mh-draft-folder (format "+%s" mh-draft-folder)))
          (if (not (file-exists-p (mh-expand-file-name mh-draft-folder)))
              (error
               "Draft folder \"%s\" not found; create it and try again"
               (mh-expand-file-name mh-draft-folder)))))
    (setq mh-inbox (mh-profile-component "Inbox"))
    (cond ((not mh-inbox)
           (setq mh-inbox "+inbox"))
          ((not (mh-folder-name-p mh-inbox))
           (setq mh-inbox (format "+%s" mh-inbox))))
    (setq mh-unseen-seq (mh-profile-component "Unseen-Sequence"))
    (if mh-unseen-seq
        (setq mh-unseen-seq (intern mh-unseen-seq))
      (setq mh-unseen-seq 'unseen))     ;old MH default?
    (setq mh-previous-seq (mh-profile-component "Previous-Sequence"))
    (if mh-previous-seq
        (setq mh-previous-seq (intern mh-previous-seq)))
    (run-hooks 'mh-find-path-hook)
    (mh-collect-folder-names)
    (setq mh-find-path-run t)))



;;; MH profile

(defun mh-profile-component (component)
  "Return COMPONENT value from mhparam, or nil if unset."
  (save-excursion
    (mh-exec-cmd-quiet nil "mhparam" "-components" component)
    (mh-profile-component-value component)))

(defun mh-profile-component-value (component)
  "Find and return the value of COMPONENT in the current buffer.
Returns nil if the component is not in the buffer."
  (let ((case-fold-search t))
    (goto-char (point-min))
    (cond ((not (re-search-forward (format "^%s:" component) nil t)) nil)
          ((looking-at "[\t ]*$") nil)
          (t
           (re-search-forward "[\t ]*\\([^\t \n].*\\)$" nil t)
           (let ((start (match-beginning 1)))
             (end-of-line)
             (buffer-substring start (point)))))))



;;; MH-E images

;; Shush compiler.
(eval-when-compile (defvar image-load-path))

(defvar mh-image-load-path-called-flag nil)

;;;###mh-autoload
(defun mh-image-load-path ()
  "Ensure that the MH-E images are accessible by `find-image'.
Images for MH-E are found in ../../etc/images relative to the
files in \"lisp/mh-e\". If `image-load-path' exists (since Emacs
22), then the images directory is added to it if isn't already
there. Otherwise, the images directory is added to the
`load-path' if it isn't already there."
  (unless mh-image-load-path-called-flag
    (let (mh-library-name mh-image-load-path)
      ;; First, find mh-e in the load-path.
      (setq mh-library-name (locate-library "mh-e"))
      (if (not mh-library-name)
        (error "Can not find MH-E in load-path"))
      (setq mh-image-load-path
            (expand-file-name (concat (file-name-directory mh-library-name)
                                      "../../etc/images")))
      (if (not (file-exists-p mh-image-load-path))
          (error "Can not find image directory %s" mh-image-load-path))
      (if (boundp 'image-load-path)
          (add-to-list 'image-load-path mh-image-load-path)
        (add-to-list 'load-path mh-image-load-path)))
    (setq mh-image-load-path-called-flag t)))



;;; Support routines for mh-customize.el

(defvar mh-min-colors-defined-flag (and (not mh-xemacs-flag)
                                        (>= emacs-major-version 22))
  "Non-nil means defface supports min-colors display requirement.")

(defun mh-defface-compat (spec)
  "Convert SPEC for defface if necessary to run on older platforms.
Modifies SPEC in place and returns it. See `defface' for the spec definition.

When `mh-min-colors-defined-flag' is nil, this function finds
display entries with \"min-colors\" requirements and either
removes the \"min-colors\" requirement or strips the display
entirely if the display does not support the number of specified
colors."
  (if mh-min-colors-defined-flag
      spec
    (let ((cells (display-color-cells))
          new-spec)
      ;; Remove entries with min-colors, or delete them if we have fewer colors
      ;; than they specify.
      (loop for entry in (reverse spec) do
            (let ((requirement (if (eq (car entry) t)
                                   nil
                                 (assoc 'min-colors (car entry)))))
              (if requirement
                  (when (>= cells (nth 1 requirement))
                    (setq new-spec (cons (cons (delq requirement (car entry))
                                               (cdr entry))
                                         new-spec)))
                (setq new-spec (cons entry new-spec)))))
      new-spec)))

(provide 'mh-init)

;; Local Variables:
;; indent-tabs-mode: nil
;; sentence-end-double-space: nil
;; End:

;; arch-tag: e8372aeb-d803-42b1-9c95-3c93ad22f63c
;;; mh-init.el ends here