mspools.el 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. ;;; mspools.el --- show mail spools waiting to be read
  2. ;; Copyright (C) 1997, 2001-2012 Free Software Foundation, Inc.
  3. ;; Author: Stephen Eglen <stephen@gnu.org>
  4. ;; Maintainer: Stephen Eglen <stephen@gnu.org>
  5. ;; Created: 22 Jan 1997
  6. ;; Keywords: mail
  7. ;; location: http://www.anc.ed.ac.uk/~stephen/emacs/
  8. ;; This file is part of GNU Emacs.
  9. ;; GNU Emacs is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; GNU Emacs is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;; If you use a mail filter (e.g. procmail, filter) to put mail messages in
  21. ;; folders, this file will let you see which folders have mail waiting
  22. ;; to be read in them. It assumes that new mail for the file `folder'
  23. ;; is written by the filter to a file called `folder.spool'. (If the
  24. ;; file writes directly to `folder' you may lose mail if new mail
  25. ;; arrives whilst you are reading the folder in emacs, hence the use
  26. ;; of a spool file.) For example, the following procmail recipe puts
  27. ;; any mail with `emacs' in the subject line into the spool file
  28. ;; `emacs.spool', ready to go into the folder `emacs'.
  29. ;:0:
  30. ;* ^Subject.*emacs
  31. ;emacs.spool
  32. ;; It also assumes that all of your spool files and mail folders live
  33. ;; in the directory pointed to by `mspools-folder-directory', so you must
  34. ;; set this (see Installation).
  35. ;; When you run `mspools-show', it creates a *spools* buffer containing
  36. ;; all of the spools in the folder directory that are waiting to be
  37. ;; read. On each line is the spool name and its size in bytes. Move
  38. ;; to the line of the folder that you would like to read, and then
  39. ;; press return or space. The mailer (VM or RMAIL) should then read
  40. ;; that folder and get the new mail for you. When you return to the
  41. ;; *spools* buffer, you will either see "*" to indicate that the spool
  42. ;; has been read, or the remaining unread spools, depending on the
  43. ;; value of `mspools-update'.
  44. ;; This file should work with both VM and RMAIL. See the variable
  45. ;; `mspools-using-vm' for details.
  46. ;;; Basic installation.
  47. ;; (autoload 'mspools-show "mspools" "Show outstanding mail spools." t)
  48. ;; (setq mspools-folder-directory "~/MAIL/")
  49. ;;
  50. ;; If you use VM, mspools-folder-directory will default to vm-folder-directory
  51. ;; unless you have already given it a value.
  52. ;; Extras.
  53. ;;
  54. ;; (global-set-key '[S-f1] 'mspools-show) ;Bind mspools-show to Shift F1.
  55. ;; (setq mspools-update t) ;Automatically update buffer.
  56. ;; Interface with the mail filter.
  57. ;; We assume that the mail filter drops new mail into the spool
  58. ;; `folder.spool'. If your spool files are something like folder.xyz
  59. ;; for inbox `folder', then do:
  60. ;; (setq mspools-suffix "xyz")
  61. ;; If you use other conventions for your spool files, this code will
  62. ;; need rewriting.
  63. ;; Warning for VM users
  64. ;; Don't use if you are not sure what you are doing. The value of
  65. ;; vm-spool-files is altered, so you may not be able to read incoming
  66. ;; mail with VM if this is incorrectly set.
  67. ;; Useful settings for VM
  68. ;; vm-auto-get-new-mail should be t (the default).
  69. ;; Acknowledgements
  70. ;; Thanks to jond@mitre.org (Jonathan Doughty) for help with code for
  71. ;; setting up vm-spool-files.
  72. ;;; TODO
  73. ;; What if users have mail spools in more than one directory? Extend
  74. ;; mspools-folder-directory to be a list of directories? Currently,
  75. ;; if mail spools are in other directories, the way to read them is to
  76. ;; put a symbolic link to the spool into the mspools-folder-directory.
  77. ;; I was going to add mouse support so that you could click on a line
  78. ;; to visit the buffer. Tell me if you want it, and I can put the
  79. ;; code in (I don't use the mouse much, so I haven't bothered with it
  80. ;; so far).
  81. ;; Rather than showing size in bytes, could we see the number of msgs
  82. ;; waiting? (Could be more time demanding / system dependent).
  83. ;; Maybe just call a perl script to do all the hard work, and
  84. ;; visualize the results in the buffer.
  85. ;; Shrink wrap the buffer to remove excess white-space?
  86. ;;; Code:
  87. (defvar rmail-inbox-list)
  88. (defvar vm-crash-box)
  89. (defvar vm-folder-directory)
  90. (defvar vm-init-file)
  91. (defvar vm-init-file-loaded)
  92. (defvar vm-primary-inbox)
  93. (defvar vm-spool-files)
  94. ;;; User Variables
  95. (defgroup mspools nil
  96. "Show mail spools waiting to be read."
  97. :group 'mail
  98. :link '(emacs-commentary-link :tag "Commentary" "mspools.el")
  99. )
  100. (defcustom mspools-update nil
  101. "*Non-nil means update *spools* buffer after visiting any folder."
  102. :type 'boolean
  103. :group 'mspools)
  104. (defcustom mspools-suffix "spool"
  105. "*Extension used for spool files (not including full stop)."
  106. :type 'string
  107. :group 'mspools)
  108. (defcustom mspools-using-vm (fboundp 'vm)
  109. "*Non-nil if VM is used as mail reader, otherwise RMAIL is used."
  110. :type 'boolean
  111. :group 'mspools)
  112. (defcustom mspools-folder-directory
  113. (if (boundp 'vm-folder-directory)
  114. vm-folder-directory
  115. "~/MAIL/")
  116. "*Directory where mail folders are kept. Ensure it has a trailing /.
  117. Defaults to `vm-folder-directory' if bound else to ~/MAIL/."
  118. :type 'directory
  119. :group 'mspools)
  120. (defcustom mspools-vm-system-mail (or (getenv "MAIL")
  121. (concat rmail-spool-directory
  122. (user-login-name)))
  123. "*Spool file for main mailbox. Only used by VM.
  124. This needs to be set to your primary mail spool - mspools will not run
  125. without it. By default this will be set to the environment variable
  126. $MAIL. Otherwise it will use `rmail-spool-directory' to guess where
  127. your primary spool is. If this fails, set it to something like
  128. /usr/spool/mail/login-name."
  129. :type 'file
  130. :group 'mspools)
  131. ;;; Internal Variables
  132. (defvar mspools-files nil
  133. "List of entries (SPOOL . SIZE) giving spool name and file size.")
  134. (defvar mspools-files-len nil
  135. "Length of `mspools-files' list.")
  136. (defvar mspools-buffer "*spools*"
  137. "Name of buffer for displaying spool info.")
  138. (defvar mspools-mode-map
  139. (let ((map (make-sparse-keymap)))
  140. (define-key map "\C-c\C-c" 'mspools-visit-spool)
  141. (define-key map "\C-m" 'mspools-visit-spool)
  142. (define-key map " " 'mspools-visit-spool)
  143. (define-key map "?" 'mspools-help)
  144. (define-key map "q" 'mspools-quit)
  145. (define-key map "n" 'next-line)
  146. (define-key map "p" 'previous-line)
  147. (define-key map "g" 'revert-buffer)
  148. map)
  149. "Keymap for the *spools* buffer.")
  150. ;;; Code
  151. ;;; VM Specific code
  152. (if mspools-using-vm
  153. ;; set up vm if not already loaded.
  154. (progn
  155. (require 'vm-vars)
  156. (if (and (not vm-init-file-loaded) (file-readable-p vm-init-file))
  157. (load-file vm-init-file))
  158. (if (not mspools-folder-directory)
  159. (setq mspools-folder-directory vm-folder-directory))
  160. ))
  161. (defun mspools-set-vm-spool-files ()
  162. "Set value of `vm-spool-files'. Only needed for VM."
  163. (if (not (file-readable-p mspools-vm-system-mail))
  164. (error "Need to set mspools-vm-system-mail to the spool for primary inbox"))
  165. (if (null mspools-folder-directory)
  166. (error "Set `mspools-folder-directory' to where the spool files are"))
  167. (setq
  168. vm-spool-files
  169. (append
  170. (list
  171. ;; Main mailbox
  172. (list vm-primary-inbox
  173. mspools-vm-system-mail ; your mailbox
  174. vm-crash-box ;crash for mailbox
  175. ))
  176. ;; Mailing list inboxes
  177. ;; must have VM already loaded to get vm-folder-directory.
  178. (mapcar (lambda (s)
  179. "make the appropriate entry for vm-spool-files"
  180. (list
  181. (concat mspools-folder-directory s)
  182. (concat mspools-folder-directory s "." mspools-suffix)
  183. (concat mspools-folder-directory s ".crash")))
  184. ;; So I create a vm-spool-files entry for each of those mail drops
  185. (mapcar 'file-name-sans-extension
  186. (directory-files mspools-folder-directory nil
  187. (format "^[^.]+\\.%s" mspools-suffix)))
  188. ))
  189. ))
  190. ;;; MSPOOLS-SHOW -- the main function
  191. (defun mspools-show ( &optional noshow)
  192. "Show the list of non-empty spool files in the *spools* buffer.
  193. Buffer is not displayed if SHOW is non-nil."
  194. (interactive)
  195. (if (get-buffer mspools-buffer)
  196. ;; buffer exists
  197. (progn
  198. (set-buffer mspools-buffer)
  199. (setq buffer-read-only nil)
  200. (delete-region (point-min) (point-max)))
  201. ;; else buffer doesn't exist so create it
  202. (get-buffer-create mspools-buffer))
  203. ;; generate the list of spool files
  204. (if mspools-using-vm
  205. (mspools-set-vm-spool-files))
  206. (mspools-get-spool-files)
  207. (if (not noshow) (pop-to-buffer mspools-buffer))
  208. (setq buffer-read-only t)
  209. (mspools-mode)
  210. )
  211. (declare-function rmail-get-new-mail "rmail" (&optional file-name))
  212. ;; External.
  213. (declare-function vm-visit-folder "ext:vm-startup" (folder &optional read-only))
  214. (defun mspools-visit-spool ()
  215. "Visit the folder on the current line of the *spools* buffer."
  216. (interactive)
  217. (let ( spool-name folder-name)
  218. (setq spool-name (mspools-get-spool-name))
  219. (if (null spool-name)
  220. (message "No spool on current line")
  221. (setq folder-name (mspools-get-folder-from-spool spool-name))
  222. ;; put in a little "*" to indicate spool file has been read.
  223. (if (not mspools-update)
  224. (save-excursion
  225. (setq buffer-read-only nil)
  226. (beginning-of-line)
  227. (insert "*")
  228. (delete-char 1)
  229. (setq buffer-read-only t)
  230. ))
  231. (message "folder %s spool %s" folder-name spool-name)
  232. (if (eq (count-lines (point-min) (point-at-eol))
  233. mspools-files-len)
  234. (forward-line (- 1 mspools-files-len)) ;back to top of list
  235. ;; else just on to next line
  236. (forward-line 1))
  237. ;; Choose whether to use VM or RMAIL for reading folder.
  238. (if mspools-using-vm
  239. (vm-visit-folder (concat mspools-folder-directory folder-name))
  240. ;; else using RMAIL
  241. (rmail (concat mspools-folder-directory folder-name))
  242. (setq rmail-inbox-list
  243. (list (concat mspools-folder-directory spool-name)))
  244. (rmail-get-new-mail))
  245. (if mspools-update
  246. ;; generate new list of spools.
  247. (save-excursion
  248. (mspools-show-again 'noshow))))))
  249. (defun mspools-get-folder-from-spool (name)
  250. "Return folder name corresponding to the spool file NAME."
  251. ;; Simply strip of the extension.
  252. (file-name-sans-extension name))
  253. ;; Alternative version if you have more complicated mapping of spool name
  254. ;; to file name.
  255. ;(defun get-folder-from-spool-safe (name)
  256. ; "Return the folder name corresponding to the spool file NAME."
  257. ; (if (string-match "^\\(.*\\)\.spool$" name)
  258. ; (substring name (match-beginning 1) (match-end 1))
  259. ; (error "Could not extract folder name from spool name %s" name)))
  260. ; test
  261. ;(mspools-get-folder-from-spool "happy.spool")
  262. ;(mspools-get-folder-from-spool "happy.sp")
  263. (defun mspools-get-spool-name ()
  264. "Return the name of the spool on the current line."
  265. (let ((line-num (1- (count-lines (point-min) (point-at-eol)))))
  266. (car (nth line-num mspools-files))))
  267. ;;; Spools mode functions
  268. (defun mspools-revert-buffer (ignore noconfirm)
  269. "Re-run mspools-show to revert the *spools* buffer."
  270. (mspools-show 'noshow))
  271. (defun mspools-show-again (&optional noshow)
  272. "Update the *spools* buffer. This is useful if mspools-update is
  273. nil."
  274. (interactive)
  275. (mspools-show noshow))
  276. (defun mspools-help ()
  277. "Show help for `mspools-mode'."
  278. (interactive)
  279. (describe-function 'mspools-mode))
  280. (defun mspools-quit ()
  281. "Quit the *spools* buffer."
  282. (interactive)
  283. (kill-buffer mspools-buffer))
  284. (defun mspools-mode ()
  285. "Major mode for output from mspools-show.
  286. \\<mspools-mode-map>Move point to one of the items in this buffer, then use
  287. \\[mspools-visit-spool] to go to the spool that the current line refers to.
  288. \\[revert-buffer] to regenerate the list of spools.
  289. \\{mspools-mode-map}"
  290. (kill-all-local-variables)
  291. (make-local-variable 'revert-buffer-function)
  292. (setq revert-buffer-function 'mspools-revert-buffer)
  293. (use-local-map mspools-mode-map)
  294. (setq major-mode 'mspools-mode)
  295. (setq mode-name "MSpools")
  296. (run-mode-hooks 'mspools-mode-hook))
  297. (defun mspools-get-spool-files ()
  298. "Find the list of spool files and display them in *spools* buffer."
  299. (let (folders head spool len beg end any)
  300. (if (null mspools-folder-directory)
  301. (error "Set `mspools-folder-directory' to where the spool files are"))
  302. (setq folders (directory-files mspools-folder-directory nil
  303. (format "^[^.]+\\.%s$" mspools-suffix)))
  304. (setq folders (mapcar 'mspools-size-folder folders))
  305. (setq folders (delq nil folders))
  306. (setq mspools-files folders)
  307. (setq mspools-files-len (length mspools-files))
  308. (set-buffer mspools-buffer)
  309. (while folders
  310. (setq any t)
  311. (setq head (car folders))
  312. (setq spool (car head))
  313. (setq len (cdr head))
  314. (setq folders (cdr folders))
  315. (setq beg (point))
  316. (insert (format " %10d %s" len spool))
  317. (setq end (point))
  318. (insert "\n")
  319. ;;(put-text-property beg end 'mouse-face 'highlight)
  320. )
  321. (if any
  322. (delete-char -1)) ;delete last RET
  323. (goto-char (point-min))
  324. ))
  325. (defun mspools-size-folder (spool)
  326. "Return (SPOOL . SIZE ), if SIZE of spool file is non-zero."
  327. ;; 7th file attribute is the size of the file in bytes.
  328. (let ((file (concat mspools-folder-directory spool))
  329. size)
  330. (setq file (or (file-symlink-p file) file))
  331. (setq size (nth 7 (file-attributes file)))
  332. ;; size could be nil if the sym-link points to a non-existent file
  333. ;; so check this first.
  334. (if (and size (> size 0))
  335. (cons spool size)
  336. ;; else SPOOL is empty
  337. nil)))
  338. (provide 'mspools)
  339. ;;; mspools.el ends here