|
|
1.1 ! root 1: ;;; mh-e.el (Version: 3.8 for GNU Emacs Version 18 and MH.5 and MH.6) ! 2: ! 3: (defvar mh-e-RCS-id) ! 4: (setq mh-e-RCS-id "$Header: mh-e.el,v 3.5 92/01/21 11:21:59 gildea Exp $") ! 5: (setq mh-e-time-stamp "92/01/21 10:59:18 gildea") ! 6: (provide 'mh-e) ! 7: ! 8: ;;; Copyright (c) 1985,1986,1987,1988,1990,1992 Free Software Foundation ! 9: ;;; Maintainer: Stephen Gildea <[email protected]> ! 10: ;;; Please send suggestions and corrections to the above address. ! 11: ;;; ! 12: ;;; This file contains mh-e, a GNU Emacs front end to the MH mail system. ! 13: ! 14: ;; This file is part of GNU Emacs. ! 15: ! 16: ;; GNU Emacs is free software; you can redistribute it and/or modify ! 17: ;; it under the terms of the GNU General Public License as published by ! 18: ;; the Free Software Foundation; either version 1, or (at your option) ! 19: ;; any later version. ! 20: ! 21: ;; GNU Emacs is distributed in the hope that it will be useful, ! 22: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ! 23: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 24: ;; GNU General Public License for more details. ! 25: ! 26: ;; You should have received a copy of the GNU General Public License ! 27: ;; along with GNU Emacs; see the file COPYING. If not, write to ! 28: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 29: ! 30: ! 31: ;;; Original version for Gosling emacs by Brian Reid, Stanford, 1982. ! 32: ;;; Modified by James Larus, BBN, July 1984 and UCB, 1984 & 1985. ! 33: ;;; Rewritten for GNU Emacs, James Larus 1985. [email protected] ! 34: ;;; Modified by Stephen Gildea, BBN, 1988, and MIT, 1990. [email protected] ! 35: ! 36: ! 37: ;;; NB. MH must have been compiled with the MHE compiler flag or several ! 38: ;;; features necessary mh-e will be missing from MH commands, specifically ! 39: ;;; the -build switch to repl and forw. ! 40: ! 41: ;;; HOW TO USE: ! 42: ;;; M-x mh-rmail to read mail. Type C-h m there for a list of commands. ! 43: ;;; C-u M-x mh-rmail to visit any folder. ! 44: ;;; M-x mh-smail to send mail. From within the mail reader, "m" works, too. ! 45: ;;; Your .emacs might benefit from these bindings: ! 46: ;;; (global-set-key "\C-xm" 'mh-smail) ! 47: ;;; (global-set-key "\C-x4m" 'mh-smail-other-window) ! 48: ;;; (global-set-key "\C-xr" 'mh-rmail) ;clobbers copy-rectangle-to-register ! 49: ! 50: ! 51: ! 52: ;;; Constants: ! 53: ! 54: ;;; Set for local environment: ! 55: ;;;* These are now in paths.el. ! 56: ;;;(defvar mh-progs "/usr/new/mh/" "Directory containing MH commands.") ! 57: ;;;(defvar mh-lib "/usr/new/lib/mh/" "Directory of MH library.") ! 58: ! 59: (defvar mh-redist-full-contents nil ! 60: "Non-nil if the `dist' command needs whole letter for redistribution. ! 61: This is the case when `send' is compiled with the BERK option.") ! 62: ! 63: ! 64: ;;; Hooks: ! 65: ! 66: (defvar mh-folder-mode-hook nil ! 67: "Invoked in `mh-folder mode' on a new folder.") ! 68: ! 69: (defvar mh-letter-mode-hook nil ! 70: "Invoked in `mh-letter-mode' on a new letter.") ! 71: ! 72: (defvar mh-compose-letter-function nil ! 73: "Invoked in `mh-compose-and-send-mail' on a draft letter. ! 74: It is passed three arguments: TO recipients, SUBJECT, and CC recipients.") ! 75: ! 76: (defvar mh-before-send-letter-hook nil ! 77: "Invoked at the beginning of the \\[mh-send-letter] command.") ! 78: ! 79: (defvar mh-inc-folder-hook nil ! 80: "Invoked after incorporating mail into a folder with \\[mh-inc-folder].") ! 81: ! 82: (defvar mh-before-quit-hook nil ! 83: "Invoked by \\[mh-quit] before quitting mh-e. See also mh-quit-hook") ! 84: ! 85: (defvar mh-quit-hook nil ! 86: "Invoked after quitting mh-e by \\[mh-quit]. See also mh-before-quit-hook") ! 87: ! 88: ! 89: (defvar mh-ins-string nil ! 90: "Temporarily set by `mh-insert-prefix' prior to running `mh-yank-hooks'.") ! 91: ! 92: (defvar mh-yank-hooks ! 93: '(lambda () ! 94: (save-excursion ! 95: (goto-char (point)) ! 96: (or (bolp) (forward-line 1)) ! 97: (while (< (point) (mark)) ! 98: (insert mh-ins-string) ! 99: (forward-line 1)))) ! 100: "Hook to run citation function. ! 101: Expects POINT and MARK to be set to the region to cite.") ! 102: ! 103: ! 104: ;;; Personal preferences: ! 105: ! 106: (defvar mh-clean-message-header nil ! 107: "*Non-nil means clean headers of messages that are displayed or inserted. ! 108: The variables `mh-visible-headers' and `mh-invisible-headers' control what ! 109: is removed.") ! 110: ! 111: (defvar mh-visible-headers nil ! 112: "*If non-nil, contains a regexp specifying the headers to keep when cleaning. ! 113: Only used if `mh-clean-message-header' is non-nil. Setting this variable ! 114: overrides `mh-invisible-headers'.") ! 115: ! 116: (defvar mhl-formfile nil ! 117: "*Name of format file to be used by mhl to show messages. ! 118: A value of T means use the default format file. ! 119: Nil means don't use mhl to format messages.") ! 120: ! 121: (defvar mh-lpr-command-format "lpr -p -J '%s'" ! 122: "*Format for Unix command that prints a message. ! 123: The string should be a Unix command line, with the string '%s' where ! 124: the job's name (folder and message number) should appear. The message text ! 125: is piped to this command.") ! 126: ! 127: (defvar mh-print-background nil ! 128: "*Print messages in the background if non-nil. ! 129: WARNING: do not delete the messages until printing is finished; ! 130: otherwise, your output may be truncated.") ! 131: ! 132: (defvar mh-summary-height 4 ! 133: "*Number of lines in summary window (including the mode line).") ! 134: ! 135: (defvar mh-recenter-summary-p nil ! 136: "*Recenter summary window when the show window is toggled off if non-nil.") ! 137: ! 138: (defvar mh-ins-buf-prefix "> " ! 139: "*String to put before each non-blank line of a yanked or inserted message. ! 140: Used when the message is inserted in an outgoing letter.") ! 141: ! 142: (defvar mh-do-not-confirm nil ! 143: "*Non-nil means do not prompt for confirmation before some commands. ! 144: Only affects certain innocuous commands.") ! 145: ! 146: (defvar mh-bury-show-buffer t ! 147: "*Non-nil means that the displayed show buffer for a folder is buried.") ! 148: ! 149: (defvar mh-delete-yanked-msg-window nil ! 150: "*Controls window display when a message is yanked by \\[mh-yank-cur-msg]. ! 151: If non-nil, yanking the current message into a draft letter deletes any ! 152: windows displaying the message.") ! 153: ! 154: (defvar mh-yank-from-start-of-msg t ! 155: "*Controls which part of a message is yanked by \\[mh-yank-cur-msg]. ! 156: If non-nil, include the entire message. If the symbol `body', then yank the ! 157: message minus the header. If nil, yank only the portion of the message ! 158: following the point. If the show buffer has a region, this variable is ! 159: ignored.") ! 160: ! 161: (defvar mh-reply-default-reply-to nil ! 162: "*Sets the person or persons to whom a reply will be sent. ! 163: If nil, prompt for recipient. If non-nil, then \\[mh-reply] will use this ! 164: value and it should be one of \"from\", \"to\", or \"cc\".") ! 165: ! 166: (defvar mh-recursive-folders nil ! 167: "*If non-nil, then commands which operate on folders do so recursively.") ! 168: ! 169: (defvar mh-unshar-default-directory "" ! 170: "*Default for directory name prompted for by mh-unshar-msg.") ! 171: ! 172: ! 173: ;;; Parameterize mh-e to work with different scan formats. The defaults work ! 174: ;;; with the standard MH scan listings. ! 175: ! 176: (defvar mh-cmd-note 4 ! 177: "Offset to insert notation.") ! 178: ! 179: (defvar mh-note-repl "-" ! 180: "String whose first character is used to notate replied to messages.") ! 181: ! 182: (defvar mh-note-forw "F" ! 183: "String whose first character is used to notate forwarded messages.") ! 184: ! 185: (defvar mh-note-dist "R" ! 186: "String whose first character is used to notate redistributed messages.") ! 187: ! 188: (defvar mh-good-msg-regexp "^....[^D^]" ! 189: "Regexp specifiying the scan lines that are 'good' messages.") ! 190: ! 191: (defvar mh-deleted-msg-regexp "^....D" ! 192: "Regexp matching scan lines of deleted messages.") ! 193: ! 194: (defvar mh-refiled-msg-regexp "^....\\^" ! 195: "Regexp matching scan lines of refiled messages.") ! 196: ! 197: (defvar mh-valid-scan-line "^ *[0-9]" ! 198: "Regexp matching scan lines for messages (not error messages).") ! 199: ! 200: (defvar mh-msg-number-regexp "^ *\\([0-9]+\\)" ! 201: "Regexp to find the number of a message in a scan line. ! 202: The message's number must be surrounded with \\( \\)") ! 203: ! 204: (defvar mh-msg-search-regexp "^[^0-9]*%d[^0-9]" ! 205: "Format string containing a regexp matching the scan listing for a message. ! 206: The desired message's number will be an argument to format.") ! 207: ! 208: (defvar mh-flagged-scan-msg-regexp "^....\\D\\|^....\\^\\|^....\\+\\|^.....%" ! 209: "Regexp matching flagged scan lines. ! 210: Matches lines marked as deleted, refiled, in a sequence, or the cur message.") ! 211: ! 212: (defvar mh-cur-scan-msg-regexp "^....\\+" ! 213: "Regexp matching scan line for the cur message.") ! 214: ! 215: (defvar mh-show-buffer-mode-line-buffer-id "{%%b} %s/%d" ! 216: "Format string to produce `mode-line-buffer-id' for show buffers. ! 217: First argument is folder name. Second is message number.") ! 218: ! 219: (defvar mh-partial-folder-mode-line-annotation "select" ! 220: "Annotation when displaying part of a folder. ! 221: The string is displayed after the folder's name. NIL for no annotation.") ! 222: ! 223: ! 224: ;;; Real constants: ! 225: ! 226: (defvar mh-invisible-headers ! 227: "^Received: \\|^Message-Id: \\|^Remailed-\\|^Via: \\|^Mail-from: \\|^Return-Path: \\|^In-Reply-To: \\|^Resent-" ! 228: "Regexp matching lines in a message header that are not to be shown. ! 229: If `mh-visible-headers' is non-nil, it is used instead to specify what ! 230: to keep.") ! 231: ! 232: (defvar mh-rejected-letter-start ! 233: (concat "^ ----- Unsent message follows -----$" ;from mail system ! 234: "\\|^------- Unsent Draft$" ;from MH itself ! 235: "\\|^ --- The unsent message follows ---$") ;from AIX mail system ! 236: "Regexp specifying the beginning of the wrapper around a returned letter. ! 237: This wrapper is generated by the mail system when rejecting a letter.") ! 238: ! 239: (defvar mh-to-field-choices '((?t . "To:") (?s . "Subject:") (?c . "Cc:") ! 240: (?b . "Bcc:") (?f . "Fcc:")) ! 241: "A-list of (character . field name) strings for mh-to-field.") ! 242: ! 243: ! 244: ;;; Global variables: ! 245: ! 246: (defvar mh-user-path "" ! 247: "User's mail folder.") ! 248: ! 249: (defvar mh-last-destination nil ! 250: "Destination of last refile or write command.") ! 251: ! 252: (defvar mh-folder-mode-map (make-keymap) ! 253: "Keymap for MH folders.") ! 254: ! 255: (defvar mh-letter-mode-map (copy-keymap text-mode-map) ! 256: "Keymap for composing mail.") ! 257: ! 258: (defvar mh-pick-mode-map (make-sparse-keymap) ! 259: "Keymap for searching folder.") ! 260: ! 261: (defvar mh-letter-mode-syntax-table nil ! 262: "Syntax table used while in mh-e letter mode.") ! 263: ! 264: (if mh-letter-mode-syntax-table ! 265: () ! 266: (setq mh-letter-mode-syntax-table ! 267: (make-syntax-table text-mode-syntax-table)) ! 268: (set-syntax-table mh-letter-mode-syntax-table) ! 269: (modify-syntax-entry ?% "." mh-letter-mode-syntax-table)) ! 270: ! 271: (defvar mh-folder-list nil ! 272: "List of folder names for completion.") ! 273: ! 274: (defvar mh-draft-folder nil ! 275: "Name of folder containing draft messages. ! 276: NIL means do not use draft folder.") ! 277: ! 278: (defvar mh-unseen-seq nil ! 279: "Name of the unseen sequence.") ! 280: ! 281: (defvar mh-previous-window-config nil ! 282: "Window configuration before mh-e command.") ! 283: ! 284: (defvar mh-previous-seq nil ! 285: "Name of the sequence to which a message was last added.") ! 286: ! 287: ! 288: ;;; Macros and generic functions: ! 289: ! 290: (defmacro mh-push (v l) ! 291: (list 'setq l (list 'cons v l))) ! 292: ! 293: ! 294: (defmacro mh-when (pred &rest body) ! 295: (list 'cond (cons pred body))) ! 296: ! 297: ! 298: (defmacro with-mh-folder-updating (save-modification-flag-p &rest body) ! 299: ;; Format is (with-mh-folder-updating (SAVE-MODIFICATION-FLAG-P) &body BODY). ! 300: ;; Execute BODY, which can modify the folder buffer without having to ! 301: ;; worry about file locking or the read-only flag, and return its result. ! 302: ;; If SAVE-MODIFICATION-FLAG-P is non-nil, the buffer's modification ! 303: ;; flag is unchanged, otherwise it is cleared. ! 304: (setq save-modification-flag-p (car save-modification-flag-p)) ; CL style ! 305: (` (let ((folder-updating-mod-flag (buffer-modified-p))) ! 306: (prog1 ! 307: (let ((buffer-read-only nil) ! 308: (buffer-file-name nil)) ; don't let the buffer get locked ! 309: (,@ body)) ! 310: (, (if save-modification-flag-p ! 311: '(mh-set-folder-modified-p folder-updating-mod-flag) ! 312: '(mh-set-folder-modified-p nil))))))) ! 313: ! 314: ! 315: (defun mh-mapc (func list) ! 316: (while list ! 317: (funcall func (car list)) ! 318: (setq list (cdr list)))) ! 319: ! 320: ! 321: ! 322: ;;; Entry points: ! 323: ! 324: ;;;###autoload ! 325: (defun mh-rmail (&optional arg) ! 326: "Inc(orporate) new mail (no arg) or scan a MH mail box (arg given). ! 327: This front end uses the MH mail system, which uses different conventions ! 328: from the usual mail system." ! 329: (interactive "P") ! 330: (mh-find-path) ! 331: (if arg ! 332: (call-interactively 'mh-visit-folder) ! 333: (mh-inc-folder))) ! 334: ! 335: ! 336: ;;;###autoload ! 337: (defun mh-smail () ! 338: "Compose and send mail with the MH mail system." ! 339: (interactive) ! 340: (mh-find-path) ! 341: (call-interactively 'mh-send)) ! 342: ! 343: ! 344: (defun mh-smail-other-window () ! 345: "Compose and send mail in other window with the MH mail system." ! 346: (interactive) ! 347: (mh-find-path) ! 348: (call-interactively 'mh-send-other-window)) ! 349: ! 350: ! 351: ! 352: ;;; User executable mh-e commands: ! 353: ! 354: (defun mh-burst-digest () ! 355: "Burst apart the current message, which should be a digest. ! 356: The message is replaced by its table of contents and the letters from the ! 357: digest are inserted into the folder after that message." ! 358: (interactive) ! 359: (let ((digest (mh-get-msg-num t))) ! 360: (mh-process-or-undo-commands mh-current-folder) ! 361: (mh-set-folder-modified-p t) ; lock folder while bursting ! 362: (message "Bursting digest...") ! 363: (mh-exec-cmd "burst" mh-current-folder digest "-inplace") ! 364: (mh-scan-folder mh-current-folder (format "%d-last" mh-first-msg-num)) ! 365: (message "Bursting digest...done"))) ! 366: ! 367: ! 368: (defun mh-copy-msg (prefix-provided msg-or-seq dest) ! 369: "Copy specified MESSAGE(s) to another FOLDER without deleting them. ! 370: Default is the displayed message. If optional prefix argument is ! 371: provided, then prompt for the message sequence." ! 372: (interactive (list current-prefix-arg ! 373: (if current-prefix-arg ! 374: (mh-read-seq-default "Copy" t) ! 375: (mh-get-msg-num t)) ! 376: (mh-prompt-for-folder "Copy to" "" t))) ! 377: (mh-exec-cmd "refile" msg-or-seq "-link" "-src" mh-current-folder dest) ! 378: (if prefix-provided ! 379: (mh-notate-seq msg-or-seq ?C mh-cmd-note) ! 380: (mh-notate msg-or-seq ?C mh-cmd-note))) ! 381: ! 382: ! 383: (defun mh-delete-msg (msg-or-seq) ! 384: "Mark the specified MESSAGE(s) for subsequent deletion and move to the next. ! 385: Default is the displayed message. If optional prefix argument is ! 386: given then prompt for the message sequence." ! 387: (interactive (list (if current-prefix-arg ! 388: (mh-read-seq-default "Delete" t) ! 389: (mh-get-msg-num t)))) ! 390: (if (numberp msg-or-seq) ! 391: (mh-delete-a-msg msg-or-seq) ! 392: (mh-map-to-seq-msgs 'mh-delete-a-msg msg-or-seq)) ! 393: (mh-next-msg)) ! 394: ! 395: ! 396: (defun mh-delete-msg-no-motion (msg-or-seq) ! 397: "Mark the specified MESSAGE(s) for subsequent deletion. ! 398: Default is the displayed message. If optional prefix argument is ! 399: provided, then prompt for the message sequence." ! 400: (interactive (list (if current-prefix-arg ! 401: (mh-read-seq-default "Delete" t) ! 402: (mh-get-msg-num t)))) ! 403: (if (numberp msg-or-seq) ! 404: (mh-delete-a-msg msg-or-seq) ! 405: (mh-map-to-seq-msgs 'mh-delete-a-msg msg-or-seq))) ! 406: ! 407: ! 408: (defun mh-delete-msg-from-seq (prefix-provided msg-or-seq &optional from-seq) ! 409: "Delete MESSAGE (default: displayed message) from SEQUENCE. ! 410: If optional prefix argument provided, then delete all messages ! 411: from a sequence." ! 412: (interactive (let ((argp current-prefix-arg)) ! 413: (list argp ! 414: (if argp ! 415: (mh-read-seq-default "Delete" t) ! 416: (mh-get-msg-num t)) ! 417: (if (not argp) ! 418: (mh-read-seq-default "Delete from" t))))) ! 419: (if prefix-provided ! 420: (mh-remove-seq msg-or-seq) ! 421: (mh-remove-msg-from-seq msg-or-seq from-seq))) ! 422: ! 423: ! 424: (defun mh-edit-again (msg) ! 425: "Clean-up a draft or a message previously sent and make it resendable." ! 426: (interactive (list (mh-get-msg-num t))) ! 427: (let* ((from-folder mh-current-folder) ! 428: (config (current-window-configuration)) ! 429: (draft ! 430: (cond ((and mh-draft-folder (equal from-folder mh-draft-folder)) ! 431: (pop-to-buffer (find-file-noselect (mh-msg-filename msg)) t) ! 432: (rename-buffer (format "draft-%d" msg)) ! 433: (buffer-name)) ! 434: (t ! 435: (mh-read-draft "clean-up" (mh-msg-filename msg) nil))))) ! 436: (mh-clean-msg-header (point-min) ! 437: "^Date:\\|^Received:\\|^Message-Id:\\|^From:\\|^Delivery-Date:" ! 438: nil) ! 439: (goto-char (point-min)) ! 440: (set-buffer-modified-p nil) ! 441: (mh-compose-and-send-mail draft "" from-folder nil nil nil nil nil nil ! 442: config))) ! 443: ! 444: ! 445: (defun mh-execute-commands () ! 446: "Process outstanding delete and refile requests." ! 447: (interactive) ! 448: (if mh-narrowed-to-seq (mh-widen)) ! 449: (mh-process-commands mh-current-folder) ! 450: (mh-set-scan-mode) ! 451: (mh-goto-cur-msg) ; after mh-set-scan-mode for efficiency ! 452: (mh-make-folder-mode-line) ! 453: t) ; return t for write-file-hooks ! 454: ! 455: ! 456: (defun mh-extract-rejected-mail (msg) ! 457: "Extract a letter returned by the mail system and make it resendable. ! 458: Default is the displayed message." ! 459: (interactive (list (mh-get-msg-num t))) ! 460: (let ((from-folder mh-current-folder) ! 461: (config (current-window-configuration)) ! 462: (draft (mh-read-draft "extraction" (mh-msg-filename msg) nil))) ! 463: (goto-char (point-min)) ! 464: (cond ((re-search-forward mh-rejected-letter-start nil t) ! 465: (forward-char 1) ! 466: (delete-region (point-min) (point)) ! 467: (mh-clean-msg-header (point-min) ! 468: "^Date:\\|^Received:\\|^Message-Id:\\|^From:\\|^Sender:\\|^Return-Path:" ! 469: nil)) ! 470: (t ! 471: (message "Does not appear to be a rejected letter."))) ! 472: (goto-char (point-min)) ! 473: (set-buffer-modified-p nil) ! 474: (mh-compose-and-send-mail draft "" from-folder msg (mh-get-field "To") ! 475: (mh-get-field "From") (mh-get-field "cc") ! 476: nil nil config))) ! 477: ! 478: ! 479: (defun mh-first-msg () ! 480: "Move to the first message." ! 481: (interactive) ! 482: (goto-char (point-min))) ! 483: ! 484: ! 485: (defun mh-forward (prefix-provided msg-or-seq to cc) ! 486: "Forward MESSAGE(s) (default: displayed message). ! 487: If optional prefix argument provided, then prompt for the message sequence." ! 488: (interactive (list current-prefix-arg ! 489: (if current-prefix-arg ! 490: (mh-read-seq-default "Forward" t) ! 491: (mh-get-msg-num t)) ! 492: (read-string "To: ") ! 493: (read-string "Cc: "))) ! 494: (let* ((folder mh-current-folder) ! 495: (config (current-window-configuration)) ! 496: ;; forw always leaves file in "draft" since it doesn't have -draft ! 497: (draft-name (expand-file-name "draft" mh-user-path)) ! 498: (draft (cond ((or (not (file-exists-p draft-name)) ! 499: (y-or-n-p "The file 'draft' exists. Discard it? ")) ! 500: (mh-exec-cmd "forw" ! 501: "-build" mh-current-folder msg-or-seq) ! 502: (prog1 ! 503: (mh-read-draft "" draft-name t) ! 504: (mh-insert-fields "To:" to "Cc:" cc) ! 505: (set-buffer-modified-p nil))) ! 506: (t ! 507: (mh-read-draft "" draft-name nil))))) ! 508: (goto-char (point-min)) ! 509: (re-search-forward "^------- Forwarded Message") ! 510: (forward-line -1) ! 511: (narrow-to-region (point) (point-max)) ! 512: (let* ((subject (save-excursion (mh-get-field "From:"))) ! 513: (trim (string-match "<" subject)) ! 514: (forw-subject (save-excursion (mh-get-field "Subject:")))) ! 515: (if trim ! 516: (setq subject (substring subject 0 (1- trim)))) ! 517: (widen) ! 518: (save-excursion ! 519: (mh-insert-fields "Subject:" (format "[%s: %s]" subject forw-subject))) ! 520: (delete-other-windows) ! 521: (if prefix-provided ! 522: (mh-add-msgs-to-seq (mh-seq-to-msgs msg-or-seq) 'forwarded t) ! 523: (mh-add-msgs-to-seq msg-or-seq 'forwarded t)) ! 524: (mh-compose-and-send-mail draft "" folder msg-or-seq ! 525: to subject cc ! 526: mh-note-forw "Forwarded:" ! 527: config)))) ! 528: ! 529: ! 530: (defun mh-goto-msg (number &optional no-error-if-no-message dont-show) ! 531: "Position the cursor at message NUMBER. ! 532: Non-nil second argument means do not signal an error if message does not exist. ! 533: Non-nil third argument means not to show the message. ! 534: Return non-nil if cursor is at message." ! 535: (interactive "NGoto message: ") ! 536: (let ((cur-msg (mh-get-msg-num nil)) ! 537: (starting-place (point)) ! 538: (msg-pattern (mh-msg-search-pat number))) ! 539: (cond ((cond ((and cur-msg (= cur-msg number)) t) ! 540: ((and cur-msg ! 541: (< cur-msg number) ! 542: (re-search-forward msg-pattern nil t)) t) ! 543: ((and cur-msg ! 544: (> cur-msg number) ! 545: (re-search-backward msg-pattern nil t)) t) ! 546: (t ; Do thorough search of buffer ! 547: (goto-char (point-max)) ! 548: (re-search-backward msg-pattern nil t))) ! 549: (beginning-of-line) ! 550: (if (not dont-show) (mh-maybe-show number)) ! 551: t) ! 552: (t ! 553: (goto-char starting-place) ! 554: (if (not no-error-if-no-message) ! 555: (error "No message %d" number)) ! 556: nil)))) ! 557: ! 558: ! 559: (defun mh-inc-folder (&optional maildrop-name) ! 560: "Inc(orporate) new mail into +inbox. ! 561: Optional prefix argument specifies an alternate maildrop from the default. ! 562: If this is given, incorporate mail into the current folder, rather ! 563: than +inbox. Run `mh-inc-folder-hook' after incorporating new mail." ! 564: (interactive (list (if current-prefix-arg ! 565: (expand-file-name ! 566: (read-file-name "inc mail from file: " ! 567: mh-user-path))))) ! 568: (let ((config (current-window-configuration))) ! 569: (if (not maildrop-name) ! 570: (cond ((not (get-buffer "+inbox")) ! 571: (mh-make-folder "+inbox") ! 572: (setq mh-previous-window-config config)) ! 573: ((not (eq (current-buffer) (get-buffer "+inbox"))) ! 574: (switch-to-buffer "+inbox") ! 575: (setq mh-previous-window-config config))))) ! 576: (mh-get-new-mail maildrop-name) ! 577: (run-hooks 'mh-inc-folder-hook)) ! 578: ! 579: ! 580: (defun mh-kill-folder () ! 581: "Remove the current folder." ! 582: (interactive) ! 583: (if (or mh-do-not-confirm ! 584: (yes-or-no-p (format "Remove folder %s? " mh-current-folder))) ! 585: (let ((folder mh-current-folder)) ! 586: (mh-set-folder-modified-p t) ; lock folder to kill it ! 587: (mh-exec-cmd-daemon "rmf" folder) ! 588: (mh-remove-folder-from-folder-list folder) ! 589: (message "Folder %s removed" folder) ! 590: (mh-set-folder-modified-p nil) ; so kill-buffer doesn't complain ! 591: (if (get-buffer mh-show-buffer) ! 592: (kill-buffer mh-show-buffer)) ! 593: (kill-buffer folder)) ! 594: (message "Folder not removed"))) ! 595: ! 596: ! 597: (defun mh-last-msg () ! 598: "Move to the last message." ! 599: (interactive) ! 600: (goto-char (point-max)) ! 601: (while (and (not (bobp)) (looking-at "^$")) ! 602: (forward-line -1))) ! 603: ! 604: ! 605: (defun mh-list-folders () ! 606: "List mail folders." ! 607: (interactive) ! 608: (with-output-to-temp-buffer " *mh-temp*" ! 609: (save-excursion ! 610: (switch-to-buffer " *mh-temp*") ! 611: (erase-buffer) ! 612: (message "Listing folders...") ! 613: (mh-exec-cmd-output "folders" t (if mh-recursive-folders ! 614: "-recurse" ! 615: "-norecurse")) ! 616: (goto-char (point-min)) ! 617: (message "Listing folders...done")))) ! 618: ! 619: ! 620: (defun mh-msg-is-in-seq (msg) ! 621: "Display the sequences that contain MESSAGE (default: displayed message)." ! 622: (interactive (list (mh-get-msg-num t))) ! 623: (message "Message %d is in sequences: %s" ! 624: msg ! 625: (mapconcat 'concat ! 626: (mh-list-to-string (mh-seq-containing-msg msg)) ! 627: " "))) ! 628: ! 629: ! 630: (defun mh-narrow-to-seq (seq) ! 631: "Restrict display of this folder to just messages in a sequence. ! 632: Reads which sequence. Use \\[mh-widen] to undo this command." ! 633: (interactive (list (mh-read-seq "Narrow to" t))) ! 634: (let ((eob (point-max))) ! 635: (with-mh-folder-updating (t) ! 636: (cond ((mh-seq-to-msgs seq) ! 637: (mh-copy-seq-to-point seq eob) ! 638: (narrow-to-region eob (point-max)) ! 639: (mh-make-folder-mode-line (symbol-name seq)) ! 640: (mh-recenter nil) ! 641: (setq mh-narrowed-to-seq seq)) ! 642: (t ! 643: (error "No messages in sequence `%s'" (symbol-name seq))))))) ! 644: ! 645: ! 646: (defun mh-next-undeleted-msg (&optional arg) ! 647: "Move to next undeleted message in window." ! 648: (interactive "P") ! 649: (forward-line (prefix-numeric-value arg)) ! 650: (setq mh-next-direction 'forward) ! 651: (cond ((re-search-forward mh-good-msg-regexp nil 0 arg) ! 652: (beginning-of-line) ! 653: (mh-maybe-show)) ! 654: (t ! 655: (forward-line -1) ! 656: (if (get-buffer mh-show-buffer) ! 657: (delete-windows-on mh-show-buffer))))) ! 658: ! 659: ! 660: (defun mh-pack-folder (range) ! 661: "Renumber the messages of a folder to be 1..n. ! 662: First, offer to execute any outstanding commands for the current folder. ! 663: If optional prefix argument provided, prompt for the range of messages ! 664: to display after packing. Otherwise, show the entire folder." ! 665: (interactive (list (if current-prefix-arg ! 666: (mh-read-msg-range ! 667: "Range to scan after packing [all]? ") ! 668: "all"))) ! 669: (mh-pack-folder-1 range) ! 670: (mh-goto-cur-msg) ! 671: (message "Packing folder...done")) ! 672: ! 673: ! 674: (defun mh-pipe-msg (prefix-provided command) ! 675: "Pipe the current message through the given shell COMMAND. ! 676: If optional prefix argument is provided, send the entire message. ! 677: Otherwise just send the message's body." ! 678: (interactive ! 679: (list current-prefix-arg (read-string "Shell command on message: "))) ! 680: (save-excursion ! 681: (mh-display-msg (mh-get-msg-num t) mh-current-folder) ;update show buffer ! 682: (goto-char (point-min)) ! 683: (if (not prefix-provided) (search-forward "\n\n")) ! 684: (shell-command-on-region (point) (point-max) command nil))) ! 685: ! 686: ! 687: (defun mh-refile-msg (prefix-provided msg-or-seq dest) ! 688: "Refile MESSAGE(s) (default: displayed message) in FOLDER. ! 689: If optional prefix argument provided, then prompt for message sequence." ! 690: (interactive ! 691: (list current-prefix-arg ! 692: (if current-prefix-arg ! 693: (mh-read-seq-default "Refile" t) ! 694: (mh-get-msg-num t)) ! 695: (intern ! 696: (mh-prompt-for-folder "Destination" ! 697: (if (eq 'refile (car mh-last-destination)) ! 698: (symbol-name (cdr mh-last-destination)) ! 699: "") ! 700: t)))) ! 701: (setq mh-last-destination (cons 'refile dest)) ! 702: (if prefix-provided ! 703: (mh-map-to-seq-msgs 'mh-refile-a-msg msg-or-seq dest) ! 704: (mh-refile-a-msg msg-or-seq dest)) ! 705: (mh-next-msg)) ! 706: ! 707: ! 708: (defun mh-refile-or-write-again (msg) ! 709: "Re-execute the last refile or write command on the given MESSAGE. ! 710: Default is the displayed message. Use the same folder or file as the ! 711: previous refile or write command." ! 712: (interactive (list (mh-get-msg-num t))) ! 713: (if (null mh-last-destination) ! 714: (error "No previous refile or write")) ! 715: (cond ((eq (car mh-last-destination) 'refile) ! 716: (mh-refile-a-msg msg (cdr mh-last-destination)) ! 717: (message "Destination folder: %s" (cdr mh-last-destination))) ! 718: (t ! 719: (mh-write-msg-to-file msg (cdr mh-last-destination)) ! 720: (message "Destination: %s" (cdr mh-last-destination)))) ! 721: (mh-next-msg)) ! 722: ! 723: ! 724: (defun mh-reply (prefix-provided msg) ! 725: "Reply to a MESSAGE (default: displayed message). ! 726: If optional prefix argument provided, then include the message in the reply ! 727: using filter mhl.reply in your MH directory." ! 728: (interactive (list current-prefix-arg (mh-get-msg-num t))) ! 729: (let ((minibuffer-help-form ! 730: "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients")) ! 731: (let ((reply-to (or mh-reply-default-reply-to ! 732: (completing-read "Reply to whom: " ! 733: '(("from") ("to") ("cc") ("all")) ! 734: nil ! 735: t))) ! 736: (folder mh-current-folder) ! 737: (show-buffer mh-show-buffer) ! 738: (config (current-window-configuration))) ! 739: (message "Composing a reply...") ! 740: (cond ((or (equal reply-to "from") (equal reply-to "")) ! 741: (apply 'mh-exec-cmd ! 742: "repl" "-build" "-noquery" ! 743: "-nodraftfolder" mh-current-folder ! 744: msg ! 745: "-nocc" "all" ! 746: (if prefix-provided ! 747: (list "-filter" "mhl.reply")))) ! 748: ((equal reply-to "to") ! 749: (apply 'mh-exec-cmd ! 750: "repl" "-build" "-noquery" ! 751: "-nodraftfolder" mh-current-folder ! 752: msg ! 753: "-cc" "to" ! 754: (if prefix-provided ! 755: (list "-filter" "mhl.reply")))) ! 756: ((or (equal reply-to "cc") (equal reply-to "all")) ! 757: (apply 'mh-exec-cmd ! 758: "repl" "-build" "-noquery" ! 759: "-nodraftfolder" mh-current-folder ! 760: msg ! 761: "-cc" "all" "-nocc" "me" ! 762: (if prefix-provided ! 763: (list "-filter" "mhl.reply"))))) ! 764: ! 765: (let ((draft (mh-read-draft "reply" ! 766: (expand-file-name "reply" mh-user-path) ! 767: t))) ! 768: (delete-other-windows) ! 769: (set-buffer-modified-p nil) ! 770: ! 771: (let ((to (mh-get-field "To:")) ! 772: (subject (mh-get-field "Subject:")) ! 773: (cc (mh-get-field "Cc:"))) ! 774: (goto-char (point-min)) ! 775: (mh-goto-header-end 1) ! 776: (if (not prefix-provided) ! 777: (mh-display-msg msg folder)) ! 778: (mh-add-msgs-to-seq msg 'answered t) ! 779: (message "Composing a reply...done") ! 780: (mh-compose-and-send-mail draft "" folder msg to subject cc ! 781: mh-note-repl "Replied:" config)))))) ! 782: ! 783: ! 784: (defun mh-quit () ! 785: "Quit mh-e. ! 786: Start by running mh-before-quit-hook. Restore the previous window ! 787: configuration, if one exists. Finish by running mh-quit-hook." ! 788: (interactive) ! 789: (run-hooks 'mh-before-quit-hook) ! 790: (if mh-previous-window-config ! 791: (set-window-configuration mh-previous-window-config)) ! 792: (run-hooks 'mh-quit-hook)) ! 793: ! 794: ! 795: (defun mh-page-digest () ! 796: "Advance displayed message to next digested message." ! 797: (interactive) ! 798: (save-excursion ! 799: (mh-show-message-in-other-window) ! 800: ;; Go to top of screen (in case user moved point). ! 801: (move-to-window-line 0) ! 802: (let ((case-fold-search nil)) ! 803: ;; Search for blank line and then for From: ! 804: (mh-when (not (and (search-forward "\n\n" nil t) ! 805: (search-forward "From:" nil t))) ! 806: (other-window -1) ! 807: (error "No more messages"))) ! 808: ;; Go back to previous blank line, then forward to the first non-blank. ! 809: (search-backward "\n\n" nil t) ! 810: (forward-line 2) ! 811: (mh-recenter 0) ! 812: (other-window -1))) ! 813: ! 814: ! 815: (defun mh-page-digest-backwards () ! 816: "Back up displayed message to previous digested message." ! 817: (interactive) ! 818: (save-excursion ! 819: (mh-show-message-in-other-window) ! 820: ;; Go to top of screen (in case user moved point). ! 821: (move-to-window-line 0) ! 822: (let ((case-fold-search nil)) ! 823: (beginning-of-line) ! 824: (mh-when (not (and (search-backward "\n\n" nil t) ! 825: (search-backward "From:" nil t))) ! 826: (other-window -1) ! 827: (error "No more messages"))) ! 828: ;; Go back to previous blank line, then forward to the first non-blank. ! 829: (search-backward "\n\n" nil t) ! 830: (forward-line 2) ! 831: (mh-recenter 0) ! 832: (other-window -1))) ! 833: ! 834: ! 835: (defun mh-page-msg (&optional arg) ! 836: "Page the displayed message forwards. ! 837: Scrolls ARG lines or a full screen if no argument is supplied." ! 838: (interactive "P") ! 839: (scroll-other-window arg)) ! 840: ! 841: ! 842: (defun mh-previous-page (&optional arg) ! 843: "Page the displayed message backwards. ! 844: Scrolls ARG lines or a full screen if no argument is supplied." ! 845: (interactive "P") ! 846: (save-excursion ! 847: (mh-show-message-in-other-window) ! 848: (unwind-protect ! 849: (scroll-down arg) ! 850: (other-window -1)))) ! 851: ! 852: ! 853: (defun mh-previous-undeleted-msg (&optional arg) ! 854: "Move to previous undeleted message in window." ! 855: (interactive "p") ! 856: (setq mh-next-direction 'backward) ! 857: (beginning-of-line) ! 858: (cond ((re-search-backward mh-good-msg-regexp nil 0 arg) ! 859: (mh-maybe-show)) ! 860: (t ! 861: (if (get-buffer mh-show-buffer) ! 862: (delete-windows-on mh-show-buffer))))) ! 863: ! 864: ! 865: (defun mh-print-msg (prefix-provided msg-or-seq) ! 866: "Print MESSAGE(s) (default: displayed message) on a line printer. ! 867: If optional prefix argument provided, then prompt for the message sequence." ! 868: (interactive (list current-prefix-arg ! 869: (if current-prefix-arg ! 870: (reverse (mh-seq-to-msgs ! 871: (mh-read-seq-default "Print" t))) ! 872: (mh-get-msg-num t)))) ! 873: (if prefix-provided ! 874: (message "Printing sequence...") ! 875: (message "Printing message...")) ! 876: (let ((print-command ! 877: (if prefix-provided ! 878: (format "(scan -clear %s ; %s -nobell -clear %s %s) | %s" ! 879: (mapconcat (function (lambda (msg) msg)) msg-or-seq " ") ! 880: (expand-file-name "mhl" mh-lib) ! 881: (if (stringp mhl-formfile) ! 882: (format "-form %s" mhl-formfile) ! 883: "") ! 884: (mh-msg-filenames msg-or-seq) ! 885: (format mh-lpr-command-format ! 886: (if prefix-provided ! 887: (format "Sequence from %s" mh-current-folder) ! 888: (format "%s/%d" mh-current-folder ! 889: msg-or-seq)))) ! 890: (format "%s -nobell -clear %s %s | %s" ! 891: (expand-file-name "mhl" mh-lib) ! 892: (mh-msg-filename msg-or-seq) ! 893: (if (stringp mhl-formfile) ! 894: (format "-form %s" mhl-formfile) ! 895: "") ! 896: (format mh-lpr-command-format ! 897: (if prefix-provided ! 898: (format "Sequence from %s" mh-current-folder) ! 899: (format "%s/%d" mh-current-folder ! 900: msg-or-seq))))))) ! 901: (if mh-print-background ! 902: (mh-exec-cmd-daemon shell-file-name "-c" print-command) ! 903: (call-process shell-file-name nil nil nil "-c" print-command)) ! 904: (if prefix-provided ! 905: (mh-notate-seq msg-or-seq ?P mh-cmd-note) ! 906: (mh-notate msg-or-seq ?P mh-cmd-note)) ! 907: (mh-add-msgs-to-seq msg-or-seq 'printed t) ! 908: (if prefix-provided ! 909: (message "Printing sequence...done") ! 910: (message "Printing message...done")))) ! 911: ! 912: ! 913: (defun mh-put-msg-in-seq (prefix-provided from to) ! 914: "Add MESSAGE(s) (default: displayed message) to SEQUENCE. ! 915: If optional prefix argument provided, then prompt for the message sequence." ! 916: (interactive (list current-prefix-arg ! 917: (if current-prefix-arg ! 918: (mh-seq-to-msgs ! 919: (mh-read-seq-default "Add messages from" t)) ! 920: (mh-get-msg-num t)) ! 921: (mh-read-seq-default "Add to" nil))) ! 922: (setq mh-previous-seq to) ! 923: (mh-add-msgs-to-seq from to)) ! 924: ! 925: ! 926: (defun mh-rescan-folder (&optional range) ! 927: "Rescan a folder after optionally processing the outstanding commands. ! 928: If optional prefix argument is provided, prompt for the range of ! 929: messages to display. Otherwise show the entire folder." ! 930: (interactive (list (if current-prefix-arg ! 931: (mh-read-msg-range "Range to scan [all]? ") ! 932: nil))) ! 933: (setq mh-next-direction 'forward) ! 934: (mh-scan-folder mh-current-folder (or range "all"))) ! 935: ! 936: ! 937: (defun mh-redistribute (to cc msg) ! 938: "Redistribute a letter. ! 939: Depending on how your copy of MH was compiled, you may need to change the ! 940: setting of the variable mh-redist-full-contents. See its documentation." ! 941: (interactive (list (read-string "Redist-To: ") ! 942: (read-string "Redist-Cc: ") ! 943: (mh-get-msg-num t))) ! 944: (save-window-excursion ! 945: (let ((folder mh-current-folder) ! 946: (draft (mh-read-draft "redistribution" ! 947: (if mh-redist-full-contents ! 948: (mh-msg-filename msg) ! 949: nil) ! 950: nil))) ! 951: (mh-goto-header-end 0) ! 952: (insert "Resent-To: " to "\n") ! 953: (if (not (equal cc "")) (insert "Resent-cc: " cc "\n")) ! 954: (mh-clean-msg-header (point-min) ! 955: "^Message-Id:\\|^Received:\\|^Return-Path:\\|^Sender:\\|^Date:\\|^From:" ! 956: nil) ! 957: (save-buffer) ! 958: (message "Redistributing...") ! 959: (if mh-redist-full-contents ! 960: (call-process "/bin/sh" nil 0 nil "-c" ! 961: (format "mhdist=1 mhaltmsg=%s %s -push %s" ! 962: (buffer-file-name) ! 963: (expand-file-name "send" mh-progs) ! 964: (buffer-file-name))) ! 965: (call-process "/bin/sh" nil 0 nil "-c" ! 966: (format "mhdist=1 mhaltmsg=%s mhannotate=1 %s -push %s" ! 967: (mh-msg-filename msg folder) ! 968: (expand-file-name "send" mh-progs) ! 969: (buffer-file-name)))) ! 970: (mh-annotate-msg msg folder mh-note-dist ! 971: "-component" "Resent:" ! 972: "-text" (format "\"%s %s\"" to cc)) ! 973: (kill-buffer draft) ! 974: (message "Redistributing...done")))) ! 975: ! 976: ! 977: (defun mh-write-msg-to-file (msg file) ! 978: "Append MESSAGE to the end of a FILE." ! 979: (interactive ! 980: (list (mh-get-msg-num t) ! 981: (let ((default-dir (if (eq 'write (car mh-last-destination)) ! 982: (file-name-directory (cdr mh-last-destination)) ! 983: default-directory))) ! 984: (read-file-name "Save message in file: " default-dir ! 985: (expand-file-name "mail.out" default-dir))))) ! 986: (let ((file-name (mh-msg-filename msg)) ! 987: (output-file (mh-expand-file-name file))) ! 988: (setq mh-last-destination (cons 'write file)) ! 989: (save-excursion ! 990: (set-buffer (get-buffer-create " *mh-temp*")) ! 991: (erase-buffer) ! 992: (insert-file-contents file-name) ! 993: (append-to-file (point-min) (point-max) output-file)))) ! 994: ! 995: ! 996: (defun mh-search-folder (folder) ! 997: "Search FOLDER for messages matching a pattern." ! 998: (interactive (list (mh-prompt-for-folder "Search" ! 999: mh-current-folder ! 1000: t))) ! 1001: (switch-to-buffer-other-window "pick-pattern") ! 1002: (if (or (zerop (buffer-size)) ! 1003: (not (y-or-n-p "Reuse pattern? "))) ! 1004: (mh-make-pick-template) ! 1005: (message "")) ! 1006: (setq mh-searching-folder folder)) ! 1007: ! 1008: ! 1009: (defun mh-send (to cc subject) ! 1010: "Compose and send a letter. ! 1011: The letter is composed in mh-letter-mode; see its documentation for more ! 1012: details. If `mh-compose-letter-function' is defined, it is called on the ! 1013: draft and passed three arguments: to, subject, and cc." ! 1014: (interactive "sTo: \nsCc: \nsSubject: ") ! 1015: (let ((config (current-window-configuration))) ! 1016: (delete-other-windows) ! 1017: (mh-send-sub to cc subject config))) ! 1018: ! 1019: ! 1020: (defun mh-send-other-window (to cc subject) ! 1021: "Compose and send a letter in another window.." ! 1022: (interactive "sTo: \nsCc: \nsSubject: ") ! 1023: (let ((pop-up-windows t)) ! 1024: (mh-send-sub to cc subject (current-window-configuration)))) ! 1025: ! 1026: ! 1027: (defun mh-send-sub (to cc subject config) ! 1028: "Do the real work of composing and sending a letter. ! 1029: Expects the TO, CC, and SUBJECT fields as arguments. ! 1030: CONFIG is the window configuration before sending mail." ! 1031: (let ((folder mh-current-folder) ! 1032: (msg-num (mh-get-msg-num nil))) ! 1033: (message "Composing a message...") ! 1034: (let ((draft (mh-read-draft ! 1035: "message" ! 1036: (if (file-exists-p ! 1037: (expand-file-name "components" mh-user-path)) ! 1038: (expand-file-name "components" mh-user-path) ! 1039: (if (file-exists-p ! 1040: (expand-file-name "components" mh-lib)) ! 1041: (expand-file-name "components" mh-lib) ! 1042: (error "Can't find components file"))) ! 1043: nil))) ! 1044: (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc) ! 1045: (set-buffer-modified-p nil) ! 1046: (goto-char (point-max)) ! 1047: (message "Composing a message...done") ! 1048: (mh-compose-and-send-mail draft "" folder msg-num ! 1049: to subject cc ! 1050: nil nil config)))) ! 1051: ! 1052: ! 1053: (defun mh-show (&optional msg) ! 1054: "Show MESSAGE (default: displayed message). ! 1055: Forces a two-window display with the folder window on top (size ! 1056: mh-summary-height) and the show buffer below it." ! 1057: (interactive) ! 1058: (if (not msg) ! 1059: (setq msg (mh-get-msg-num t))) ! 1060: (setq mh-showing t) ! 1061: (mh-set-mode-name "mh-e show") ! 1062: (if (not (eql (next-window (minibuffer-window)) (selected-window))) ! 1063: (delete-other-windows)) ; force ourself to the top window ! 1064: (let ((folder mh-current-folder)) ! 1065: (mh-show-message-in-other-window) ! 1066: (mh-display-msg msg folder)) ! 1067: (other-window -1) ! 1068: (if (not (= (1+ (window-height)) (screen-height))) ;not horizontally split ! 1069: (shrink-window (- (window-height) mh-summary-height))) ! 1070: (mh-recenter nil) ! 1071: (if (not (memq msg mh-seen-list)) (mh-push msg mh-seen-list))) ! 1072: ! 1073: ! 1074: (defun mh-sort-folder () ! 1075: "Sort the messages in the current folder by date." ! 1076: (interactive) ! 1077: (mh-process-or-undo-commands mh-current-folder) ! 1078: (setq mh-next-direction 'forward) ! 1079: (mh-set-folder-modified-p t) ; lock folder while sorting ! 1080: (message "Sorting folder...") ! 1081: (mh-exec-cmd "sortm" mh-current-folder) ! 1082: (message "Sorting folder...done") ! 1083: (mh-scan-folder mh-current-folder "all")) ! 1084: ! 1085: ! 1086: (defun mh-toggle-showing () ! 1087: "Toggle the scanning mode/showing mode of displaying messages." ! 1088: (interactive) ! 1089: (if mh-showing ! 1090: (mh-set-scan-mode) ! 1091: (mh-show))) ! 1092: ! 1093: ! 1094: (defun mh-undo (prefix-provided msg-or-seq) ! 1095: "Undo the deletion or refile of the specified MESSAGE(s). ! 1096: Default is the displayed message. If optional prefix argument is ! 1097: provided, then prompt for the message sequence." ! 1098: (interactive (list current-prefix-arg ! 1099: (if current-prefix-arg ! 1100: (mh-read-seq-default "Undo" t) ! 1101: (mh-get-msg-num t)))) ! 1102: (cond (prefix-provided ! 1103: (mh-mapc (function mh-undo-msg) (mh-seq-to-msgs msg-or-seq))) ! 1104: (t ! 1105: (let ((original-position (point))) ! 1106: (beginning-of-line) ! 1107: (while (not (or (looking-at mh-deleted-msg-regexp) ! 1108: (looking-at mh-refiled-msg-regexp) ! 1109: (and (eq mh-next-direction 'forward) (bobp)) ! 1110: (and (eq mh-next-direction 'backward) ! 1111: (save-excursion (forward-line) (eobp))))) ! 1112: (forward-line (if (eq mh-next-direction 'forward) -1 1))) ! 1113: (if (or (looking-at mh-deleted-msg-regexp) ! 1114: (looking-at mh-refiled-msg-regexp)) ! 1115: (progn ! 1116: (mh-undo-msg (mh-get-msg-num t)) ! 1117: (mh-maybe-show)) ! 1118: (goto-char original-position) ! 1119: (error "Nothing to undo"))))) ! 1120: ;; update the mh-refile-list so mh-outstanding-commands-p will work ! 1121: (mh-mapc (function ! 1122: (lambda (elt) ! 1123: (if (not (mh-seq-to-msgs elt)) ! 1124: (setq mh-refile-list (delq elt mh-refile-list))))) ! 1125: mh-refile-list) ! 1126: (if (not (mh-outstanding-commands-p)) ! 1127: (mh-set-folder-modified-p nil))) ! 1128: ! 1129: ! 1130: (defun mh-undo-msg (msg) ! 1131: ;; Undo the deletion or refile of one MESSAGE. ! 1132: (cond ((memq msg mh-delete-list) ! 1133: (setq mh-delete-list (delq msg mh-delete-list)) ! 1134: (mh-remove-msg-from-seq msg 'deleted t)) ! 1135: (t ! 1136: (mh-mapc (function (lambda (dest) ! 1137: (mh-remove-msg-from-seq msg dest t))) ! 1138: mh-refile-list))) ! 1139: (mh-notate msg ? mh-cmd-note)) ! 1140: ! 1141: ! 1142: (defun mh-undo-folder (&rest ignore) ! 1143: "Undo all commands in current folder." ! 1144: (interactive) ! 1145: (cond ((or mh-do-not-confirm ! 1146: (yes-or-no-p "Undo all commands in folder? ")) ! 1147: (setq mh-delete-list nil ! 1148: mh-refile-list nil ! 1149: mh-seq-list nil ! 1150: mh-next-direction 'forward) ! 1151: (with-mh-folder-updating (nil) ! 1152: (mh-unmark-all-headers t))) ! 1153: (t ! 1154: (message "Commands not undone.") ! 1155: (sit-for 2)))) ! 1156: ! 1157: ! 1158: (defun mh-unshar-msg (dir) ! 1159: "Unpack the shar file contained in the current message into directory DIR." ! 1160: (interactive (list (read-file-name "Unshar message in directory: " ! 1161: mh-unshar-default-directory ! 1162: mh-unshar-default-directory nil))) ! 1163: (mh-display-msg (mh-get-msg-num t) mh-current-folder) ;update show buffer ! 1164: (mh-unshar-buffer dir)) ! 1165: ! 1166: (defun mh-unshar-buffer (dir) ! 1167: ;; Unpack the shar file contained in the current buffer into directory DIR. ! 1168: (goto-char (point-min)) ! 1169: (if (or (re-search-forward "^#![ \t]*/bin/sh" nil t) ! 1170: (and (re-search-forward "^[^a-z0-9\"]*cut here\b" nil t) ! 1171: (forward-line 1)) ! 1172: (re-search-forward "^#" nil t) ! 1173: (re-search-forward "^: " nil t)) ! 1174: (let ((default-directory (expand-file-name dir)) ! 1175: (start (progn (beginning-of-line) (point))) ! 1176: (log-buffer (get-buffer-create "*Unshar Output*"))) ! 1177: (save-excursion ! 1178: (set-buffer log-buffer) ! 1179: (setq default-directory (expand-file-name dir)) ! 1180: (erase-buffer) ! 1181: (if (file-directory-p default-directory) ! 1182: (insert "cd " dir "\n") ! 1183: (insert "mkdir " dir "\n") ! 1184: (call-process "mkdir" nil log-buffer t default-directory))) ! 1185: (set-window-start (display-buffer log-buffer) 0) ;so can watch progress ! 1186: (call-process-region start (point-max) "sh" nil log-buffer t)) ! 1187: (error "Cannot find start of shar."))) ! 1188: ! 1189: ! 1190: (defun mh-visit-folder (folder &optional range) ! 1191: "Visit FOLDER and display RANGE of messages. ! 1192: Assumes mh-e has already been initialized." ! 1193: (interactive (list (mh-prompt-for-folder "Visit" "+inbox" t) ! 1194: (mh-read-msg-range "Range [all]? "))) ! 1195: (let ((config (current-window-configuration))) ! 1196: (mh-scan-folder folder (or range "all")) ! 1197: (setq mh-previous-window-config config)) ! 1198: nil) ! 1199: ! 1200: ! 1201: (defun mh-widen () ! 1202: "Remove restrictions from the current folder, thereby showing all messages." ! 1203: (interactive) ! 1204: (if mh-narrowed-to-seq ! 1205: (with-mh-folder-updating (t) ! 1206: (delete-region (point-min) (point-max)) ! 1207: (widen) ! 1208: (mh-make-folder-mode-line))) ! 1209: (setq mh-narrowed-to-seq nil)) ! 1210: ! 1211: ! 1212: ! 1213: ;;; Support routines. ! 1214: ! 1215: (defun mh-delete-a-msg (msg) ! 1216: ;; Delete the MESSAGE. ! 1217: (save-excursion ! 1218: (mh-goto-msg msg nil t) ! 1219: (if (looking-at mh-refiled-msg-regexp) ! 1220: (error "Message %d is refiled. Undo refile before deleting." msg)) ! 1221: (if (looking-at mh-deleted-msg-regexp) ! 1222: nil ! 1223: (mh-set-folder-modified-p t) ! 1224: (mh-push msg mh-delete-list) ! 1225: (mh-add-msgs-to-seq msg 'deleted t) ! 1226: (mh-notate msg ?D mh-cmd-note)))) ! 1227: ! 1228: ! 1229: (defun mh-refile-a-msg (msg destination) ! 1230: ;; Refile MESSAGE in FOLDER. FOLDER is a symbol, not a string. ! 1231: (save-excursion ! 1232: (mh-goto-msg msg nil t) ! 1233: (cond ((looking-at mh-deleted-msg-regexp) ! 1234: (error "Message %d is deleted. Undo delete before moving." msg)) ! 1235: ((looking-at mh-refiled-msg-regexp) ! 1236: (if (y-or-n-p ! 1237: (format "Message %d already refiled. Copy to %s as well? " ! 1238: msg destination)) ! 1239: (mh-exec-cmd "refile" (mh-get-msg-num t) "-link" ! 1240: "-src" mh-current-folder ! 1241: (symbol-name destination)) ! 1242: (message "Message not copied."))) ! 1243: (t ! 1244: (mh-set-folder-modified-p t) ! 1245: (if (not (memq destination mh-refile-list)) ! 1246: (mh-push destination mh-refile-list)) ! 1247: (if (not (memq msg (mh-seq-to-msgs destination))) ! 1248: (mh-add-msgs-to-seq msg destination t)) ! 1249: (mh-notate msg ?^ mh-cmd-note))))) ! 1250: ! 1251: ! 1252: (defun mh-display-msg (msg-num folder) ! 1253: ;; Display message NUMBER of FOLDER. ! 1254: ;; Sets the current buffer to the show buffer. ! 1255: (set-buffer folder) ! 1256: ;; Bind variables in folder buffer in case they are local ! 1257: (let ((formfile mhl-formfile) ! 1258: (clean-message-header mh-clean-message-header) ! 1259: (invisible-headers mh-invisible-headers) ! 1260: (visible-headers mh-visible-headers) ! 1261: (msg-filename (mh-msg-filename msg-num)) ! 1262: (show-buffer mh-show-buffer) ! 1263: (folder mh-current-folder)) ! 1264: (if (not (file-exists-p msg-filename)) ! 1265: (error "Message %d does not exist" msg-num)) ! 1266: (switch-to-buffer show-buffer) ! 1267: (if mh-bury-show-buffer (bury-buffer (current-buffer))) ! 1268: (mh-when (not (equal msg-filename buffer-file-name)) ! 1269: ;; Buffer does not yet contain message. ! 1270: (clear-visited-file-modtime) ! 1271: (unlock-buffer) ! 1272: (setq buffer-file-name nil) ; no locking during setup ! 1273: (erase-buffer) ! 1274: (if formfile ! 1275: (if (stringp formfile) ! 1276: (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" ! 1277: "-form" formfile msg-filename) ! 1278: (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" ! 1279: msg-filename)) ! 1280: (insert-file-contents msg-filename)) ! 1281: (goto-char (point-min)) ! 1282: (cond (clean-message-header ! 1283: (mh-clean-msg-header (point-min) ! 1284: invisible-headers ! 1285: visible-headers) ! 1286: (goto-char (point-min))) ! 1287: (t ! 1288: (let ((case-fold-search t)) ! 1289: (re-search-forward ! 1290: "^To:\\|^From:\\|^Subject:\\|^Date:" nil t) ! 1291: (beginning-of-line) ! 1292: (mh-recenter 0)))) ! 1293: (set-buffer-modified-p nil) ! 1294: (setq buffer-file-name msg-filename) ! 1295: (set-mark nil) ! 1296: (setq mode-line-buffer-identification ! 1297: (list (format mh-show-buffer-mode-line-buffer-id ! 1298: folder msg-num)))))) ! 1299: ! 1300: ! 1301: (defun mh-invalidate-show-buffer () ! 1302: ;; Invalidate the show buffer so we must update it to use it. ! 1303: (if (get-buffer mh-show-buffer) ! 1304: (save-excursion ! 1305: (set-buffer mh-show-buffer) ! 1306: (setq buffer-file-name nil)))) ! 1307: ! 1308: ! 1309: (defun mh-show-message-in-other-window () ! 1310: (switch-to-buffer-other-window mh-show-buffer) ! 1311: (if mh-bury-show-buffer (bury-buffer (current-buffer)))) ! 1312: ! 1313: ! 1314: (defun mh-clean-msg-header (start invisible-headers visible-headers) ! 1315: ;; Flush extraneous lines in a message header, from the given POINT to the ! 1316: ;; end of the message header. If VISIBLE-HEADERS is non-nil, it contains a ! 1317: ;; regular expression specifying the lines to display, otherwise ! 1318: ;; INVISIBLE-HEADERS contains a regular expression specifying lines to ! 1319: ;; delete from the header. ! 1320: (let ((case-fold-search t)) ! 1321: (save-restriction ! 1322: (goto-char start) ! 1323: (if (search-forward "\n\n" nil t) ! 1324: (backward-char 1)) ! 1325: (narrow-to-region start (point)) ! 1326: (goto-char (point-min)) ! 1327: (if visible-headers ! 1328: (while (< (point) (point-max)) ! 1329: (beginning-of-line) ! 1330: (cond ((looking-at visible-headers) ! 1331: (forward-line 1) ! 1332: (while (looking-at "^[ \t]+") (forward-line 1))) ! 1333: (t ! 1334: (mh-delete-line 1) ! 1335: (while (looking-at "^[ \t]+") ! 1336: (beginning-of-line) ! 1337: (mh-delete-line 1))))) ! 1338: (while (re-search-forward invisible-headers nil t) ! 1339: (beginning-of-line) ! 1340: (mh-delete-line 1) ! 1341: (while (looking-at "^[ \t]+") ! 1342: (beginning-of-line) ! 1343: (mh-delete-line 1)))) ! 1344: (unlock-buffer)))) ! 1345: ! 1346: ! 1347: (defun mh-delete-line (lines) ! 1348: ;; Delete version of kill-line. ! 1349: (delete-region (point) (save-excursion (forward-line lines) (point)))) ! 1350: ! 1351: ! 1352: (defun mh-read-draft (use initial-contents delete-contents-file) ! 1353: ;; Read draft file into a draft buffer and make that buffer the current one. ! 1354: ;; USE is a message used for prompting about the intended use of the message. ! 1355: ;; INITIAL-CONTENTS is filename that is read into an empty buffer, or NIL ! 1356: ;; if buffer should not be modified. Delete the initial-contents file if ! 1357: ;; DELETE-CONTENTS-FILE flag is set. ! 1358: ;; Returns the draft folder's name. ! 1359: ;; If the draft folder facility is enabled in ~/.mh_profile, a new buffer is ! 1360: ;; used each time and saved in the draft folder. The draft file can then be ! 1361: ;; reused. ! 1362: (cond (mh-draft-folder ! 1363: (let ((orig-default-dir default-directory)) ! 1364: (pop-to-buffer (find-file-noselect (mh-new-draft-name)) t) ! 1365: (rename-buffer (format "draft-%s" (buffer-name))) ! 1366: (setq default-directory orig-default-dir))) ! 1367: (t ! 1368: (let ((draft-name (expand-file-name "draft" mh-user-path))) ! 1369: (pop-to-buffer "draft") ; Create if necessary ! 1370: (if (buffer-modified-p) ! 1371: (if (y-or-n-p "Draft has been modified; kill anyway? ") ! 1372: (set-buffer-modified-p nil) ! 1373: (error "Draft preserved"))) ! 1374: (setq buffer-file-name draft-name) ! 1375: (clear-visited-file-modtime) ! 1376: (unlock-buffer) ! 1377: (mh-when (and (file-exists-p draft-name) ! 1378: (not (equal draft-name initial-contents))) ! 1379: (insert-file-contents draft-name) ! 1380: (delete-file draft-name))))) ! 1381: (mh-when (and initial-contents ! 1382: (or (zerop (buffer-size)) ! 1383: (not (y-or-n-p ! 1384: (format "A draft exists. Use for %s? " use))))) ! 1385: (erase-buffer) ! 1386: (insert-file-contents initial-contents) ! 1387: (if delete-contents-file (delete-file initial-contents))) ! 1388: (auto-save-mode 1) ! 1389: (if mh-draft-folder ! 1390: (save-buffer)) ; Do not reuse draft name ! 1391: (buffer-name)) ! 1392: ! 1393: ! 1394: (defun mh-new-draft-name () ! 1395: ;; Returns the pathname of folder for draft messages. ! 1396: (save-excursion ! 1397: (set-buffer (get-buffer-create " *mh-temp*")) ! 1398: (erase-buffer) ! 1399: (mh-exec-cmd-output "mhpath" nil mh-draft-folder "new") ! 1400: (buffer-substring (point) (1- (mark))))) ! 1401: ! 1402: ! 1403: (defun mh-next-msg () ! 1404: ;; Move backward or forward to the next undeleted message in the buffer. ! 1405: (if (eq mh-next-direction 'forward) ! 1406: (mh-next-undeleted-msg 1) ! 1407: (mh-previous-undeleted-msg 1))) ! 1408: ! 1409: ! 1410: (defun mh-set-scan-mode () ! 1411: ;; Display the scan listing buffer, but do not show a message. ! 1412: (if (get-buffer mh-show-buffer) ! 1413: (delete-windows-on mh-show-buffer)) ! 1414: (mh-set-mode-name "mh-e scan") ! 1415: (setq mh-showing nil) ! 1416: (if mh-recenter-summary-p ! 1417: (mh-recenter nil))) ! 1418: ! 1419: ! 1420: (defun mh-maybe-show (&optional msg) ! 1421: ;; If in showing mode, then display the message pointed to by the cursor. ! 1422: (if mh-showing (mh-show msg))) ! 1423: ! 1424: ! 1425: (defun mh-set-mode-name (mode-name-string) ! 1426: ;; Set the mode-name and ensure that the mode line is updated. ! 1427: (setq mode-name mode-name-string) ! 1428: ;; Force redisplay of all buffers' mode lines to be considered. ! 1429: (save-excursion (set-buffer (other-buffer))) ! 1430: (set-buffer-modified-p (buffer-modified-p))) ! 1431: ! 1432: ! 1433: ! 1434: ;;; The folder data abstraction. ! 1435: ! 1436: (defvar mh-current-folder nil "Name of current folder, a string.") ! 1437: (defvar mh-show-buffer nil "Buffer that displays mesage for this folder.") ! 1438: (defvar mh-folder-filename nil "Full path of directory for this folder.") ! 1439: (defvar mh-showing nil "If non-nil, show the message in a separate window.") ! 1440: (defvar mh-next-seq-num nil "Index of free sequence id.") ! 1441: (defvar mh-delete-list nil "List of msg numbers to delete.") ! 1442: (defvar mh-refile-list nil "List of folder names in mh-seq-list.") ! 1443: (defvar mh-seq-list nil "Alist of (seq . msgs) numbers.") ! 1444: (defvar mh-seen-list nil "List of displayed messages.") ! 1445: (defvar mh-next-direction 'forward "Direction to move to next message.") ! 1446: (defvar mh-narrowed-to-seq nil "Sequence display is narrowed to.") ! 1447: (defvar mh-first-msg-num nil "Number of first msg in buffer.") ! 1448: (defvar mh-last-msg-num nil "Number of last msg in buffer.") ! 1449: ! 1450: ! 1451: (defun mh-make-folder (name) ! 1452: ;; Create and initialize a new mail folder called NAME and make it the ! 1453: ;; current folder. ! 1454: (switch-to-buffer name) ! 1455: (setq buffer-read-only nil) ! 1456: (erase-buffer) ! 1457: (setq buffer-read-only t) ! 1458: (mh-folder-mode) ! 1459: (mh-set-folder-modified-p nil) ! 1460: (setq buffer-file-name mh-folder-filename) ! 1461: (mh-set-mode-name "mh-e scan")) ! 1462: ! 1463: ! 1464: ;;; Don't use this mode when creating buffers if default-major-mode is nil. ! 1465: (put 'mh-folder-mode 'mode-class 'special) ! 1466: ! 1467: (defun mh-folder-mode () ! 1468: "Major mode for \"editing\" an MH folder scan listing. ! 1469: Messages can be marked for refiling and deletion. However, both actions ! 1470: are deferred until you request execution with \\[mh-execute-commands]. ! 1471: \\{mh-folder-mode-map} ! 1472: A prefix argument (\\[universal-argument]) to delete, refile, list, or undo ! 1473: applies the action to a message sequence. ! 1474: ! 1475: Variables controlling mh-e operation are (defaults in parentheses): ! 1476: ! 1477: mh-bury-show-buffer (t) ! 1478: Non-nil means that the buffer used to display message is buried. ! 1479: It will never be offered as the default other buffer. ! 1480: ! 1481: mh-clean-message-header (nil) ! 1482: Non-nil means remove header lines matching the regular expression ! 1483: specified in mh-invisible-headers from messages. ! 1484: ! 1485: mh-visible-headers (nil) ! 1486: If non-nil, it contains a regexp specifying the headers that are shown in ! 1487: a message if mh-clean-message-header is non-nil. Setting this variable ! 1488: overrides mh-invisible-headers. ! 1489: ! 1490: mh-do-not-confirm (nil) ! 1491: Non-nil means do not prompt for confirmation before executing some ! 1492: non-recoverable commands such as mh-kill-folder and mh-undo-folder. ! 1493: ! 1494: mhl-formfile (nil) ! 1495: Name of format file to be used by mhl to show messages. ! 1496: A value of T means use the default format file. ! 1497: Nil means don't use mhl to format messages. ! 1498: ! 1499: mh-lpr-command-format (\"lpr -p -J '%s'\") ! 1500: Format for command used to print a message on a system printer. ! 1501: ! 1502: mh-recenter-summary-p (nil) ! 1503: If non-nil, then the scan listing is recentered when the window displaying ! 1504: a messages is toggled off. ! 1505: ! 1506: mh-summary-height (4) ! 1507: Number of lines in the summary window including the mode line. ! 1508: ! 1509: mh-ins-buf-prefix (\"> \") ! 1510: String to insert before each non-blank line of a message as it is ! 1511: inserted in a draft letter. ! 1512: ! 1513: The value of mh-folder-mode-hook is called when a new folder is set up." ! 1514: ! 1515: (kill-all-local-variables) ! 1516: (use-local-map mh-folder-mode-map) ! 1517: (setq major-mode 'mh-folder-mode) ! 1518: (mh-set-mode-name "mh-e folder") ! 1519: (make-local-vars ! 1520: 'mh-current-folder (buffer-name) ; Name of folder, a string ! 1521: 'mh-show-buffer (format "show-%s" (buffer-name)) ; Buffer that displays msgs ! 1522: 'mh-folder-filename ; e.g. "/usr/foobar/Mail/inbox/" ! 1523: (file-name-as-directory (mh-expand-file-name (buffer-name))) ! 1524: 'mh-showing nil ; Show message also? ! 1525: 'mh-next-seq-num 0 ; Index of free sequence id ! 1526: 'mh-delete-list nil ; List of msgs nums to delete ! 1527: 'mh-refile-list nil ; List of folder names in mh-seq-list ! 1528: 'mh-seq-list nil ; Alist of (seq . msgs) nums ! 1529: 'mh-seen-list nil ; List of displayed messages ! 1530: 'mh-next-direction 'forward ; Direction to move to next message ! 1531: 'mh-narrowed-to-seq nil ; Sequence display is narrowed to ! 1532: 'mh-first-msg-num nil ; Number of first msg in buffer ! 1533: 'mh-last-msg-num nil ; Number of last msg in buffer ! 1534: 'mh-previous-window-config nil) ; Previous window configuration ! 1535: (setq truncate-lines t) ! 1536: (auto-save-mode -1) ! 1537: (setq buffer-offer-save t) ! 1538: (make-local-variable 'write-file-hooks) ! 1539: (setq write-file-hooks '(mh-execute-commands)) ! 1540: (make-local-variable 'revert-buffer-function) ! 1541: (setq revert-buffer-function 'mh-undo-folder) ! 1542: (run-hooks 'mh-folder-mode-hook)) ! 1543: ! 1544: ! 1545: (defun make-local-vars (&rest pairs) ! 1546: ;; Take VARIABLE-VALUE pairs and makes local variables initialized to the ! 1547: ;; value. ! 1548: (while pairs ! 1549: (make-variable-buffer-local (car pairs)) ! 1550: (set (car pairs) (car (cdr pairs))) ! 1551: (setq pairs (cdr (cdr pairs))))) ! 1552: ! 1553: ! 1554: (defun mh-scan-folder (folder range) ! 1555: ;; Scan the FOLDER over the RANGE. Return in the folder's buffer. ! 1556: (cond ((null (get-buffer folder)) ! 1557: (mh-make-folder folder)) ! 1558: (t ! 1559: (mh-process-or-undo-commands folder) ! 1560: (switch-to-buffer folder))) ! 1561: (mh-regenerate-headers range) ! 1562: (mh-when (zerop (buffer-size)) ! 1563: (if (equal range "all") ! 1564: (message "Folder %s is empty" folder) ! 1565: (message "No messages in %s, range %s" folder range)) ! 1566: (sit-for 5)) ! 1567: (mh-goto-cur-msg)) ! 1568: ! 1569: ! 1570: (defun mh-regenerate-headers (range) ! 1571: ;; Replace buffer with scan of its contents over range RANGE. ! 1572: (let ((folder mh-current-folder)) ! 1573: (message "Scanning %s..." folder) ! 1574: (with-mh-folder-updating (nil) ! 1575: (erase-buffer) ! 1576: (mh-exec-cmd-output "scan" nil ! 1577: "-noclear" "-noheader" ! 1578: "-width" (window-width) ! 1579: folder range) ! 1580: (goto-char (point-min)) ! 1581: (cond ((looking-at "scan: no messages in") ! 1582: (keep-lines mh-valid-scan-line)) ; Flush random scan lines ! 1583: ((looking-at "scan: ")) ; Keep error messages ! 1584: (t ! 1585: (keep-lines mh-valid-scan-line))) ; Flush random scan lines ! 1586: (mh-delete-seq-locally 'cur) ; To pick up new one ! 1587: (setq mh-seq-list (mh-read-folder-sequences folder nil)) ! 1588: (mh-notate-user-sequences) ! 1589: (mh-make-folder-mode-line (if (equal range "all") ! 1590: nil ! 1591: mh-partial-folder-mode-line-annotation))) ! 1592: (message "Scanning %s...done" folder))) ! 1593: ! 1594: ! 1595: (defun mh-get-new-mail (maildrop-name) ! 1596: ;; Read new mail from a maildrop into the current buffer. ! 1597: ;; Return T if there was new mail, NIL otherwise. Return in the current ! 1598: ;; buffer. ! 1599: (let ((point-before-inc (point)) ! 1600: (folder mh-current-folder) ! 1601: (return-value t)) ! 1602: (with-mh-folder-updating (t) ! 1603: (message (if maildrop-name ! 1604: (format "inc %s -file %s..." folder maildrop-name) ! 1605: (format "inc %s..." folder))) ! 1606: (mh-unmark-all-headers nil) ! 1607: (setq mh-next-direction 'forward) ! 1608: (goto-char (point-max)) ! 1609: (let ((start-of-inc (point))) ! 1610: (if maildrop-name ! 1611: (mh-exec-cmd-output "inc" nil folder ! 1612: "-file" (expand-file-name maildrop-name) ! 1613: "-width" (window-width) ! 1614: "-truncate") ! 1615: (mh-exec-cmd-output "inc" nil ! 1616: "-width" (window-width))) ! 1617: (message ! 1618: (if maildrop-name ! 1619: (format "inc %s -file %s...done" folder maildrop-name) ! 1620: (format "inc %s...done" folder))) ! 1621: (goto-char start-of-inc) ! 1622: (cond ((looking-at "inc: no mail") ! 1623: (keep-lines mh-valid-scan-line) ; Flush random scan lines ! 1624: (goto-char point-before-inc) ! 1625: (message "No new mail%s%s" (if maildrop-name " in " "") ! 1626: (if maildrop-name maildrop-name ""))) ! 1627: ((re-search-forward "^inc:" nil t) ; Error messages ! 1628: (error "inc error")) ! 1629: (t ! 1630: (mh-delete-seq-locally 'cur) ; To pick up new one ! 1631: (setq mh-seq-list (mh-read-folder-sequences folder t)) ! 1632: (mh-notate-user-sequences) ! 1633: (keep-lines mh-valid-scan-line) ! 1634: (mh-make-folder-mode-line) ! 1635: (mh-goto-cur-msg) ! 1636: (setq return-value t)))) ! 1637: return-value))) ! 1638: ! 1639: ! 1640: (defun mh-make-folder-mode-line (&optional annotation) ! 1641: ;; Set the fields of the mode line for a folder buffer. ! 1642: ;; The optional ANNOTATION string is displayed after the folder's name. ! 1643: (save-excursion ! 1644: (mh-first-msg) ! 1645: (setq mh-first-msg-num (mh-get-msg-num nil)) ! 1646: (mh-last-msg) ! 1647: (setq mh-last-msg-num (mh-get-msg-num nil)) ! 1648: (let ((lines (count-lines (point-min) (point-max)))) ! 1649: (setq mode-line-buffer-identification ! 1650: (list (format "{%%b%s} %d msg%s" ! 1651: (if annotation (format "/%s" annotation) "") ! 1652: lines ! 1653: (if (zerop lines) ! 1654: "s" ! 1655: (if (> lines 1) ! 1656: (format "s (%d-%d)" mh-first-msg-num ! 1657: mh-last-msg-num) ! 1658: (format " (%d)" mh-first-msg-num))))))))) ! 1659: ! 1660: ! 1661: (defun mh-unmark-all-headers (remove-all-flags) ! 1662: ;; Remove all '+' flags from the headers, and if called with a non-nil ! 1663: ;; argument, remove all 'D', '^' and '%' flags too. ! 1664: ;; Optimized for speed (i.e., no regular expressions). ! 1665: (save-excursion ! 1666: (let ((case-fold-search nil) ! 1667: (last-line (- (point-max) mh-cmd-note)) ! 1668: char) ! 1669: (mh-first-msg) ! 1670: (while (<= (point) last-line) ! 1671: (forward-char mh-cmd-note) ! 1672: (setq char (following-char)) ! 1673: (if (or (and remove-all-flags ! 1674: (or (eql char ?D) ! 1675: (eql char ?^) ! 1676: (eql char ?%))) ! 1677: (eql char ?+)) ! 1678: (progn ! 1679: (delete-char 1) ! 1680: (insert " "))) ! 1681: (forward-line))))) ! 1682: ! 1683: ! 1684: (defun mh-goto-cur-msg () ! 1685: ;; Position the cursor at the current message. ! 1686: (let ((cur-msg (car (mh-seq-to-msgs 'cur)))) ! 1687: (cond ((and cur-msg ! 1688: (mh-goto-msg cur-msg t nil)) ! 1689: (mh-notate nil ?+ mh-cmd-note) ! 1690: (mh-recenter 0) ! 1691: (mh-maybe-show cur-msg)) ! 1692: (t ! 1693: (mh-last-msg) ! 1694: (message "No current message"))))) ! 1695: ! 1696: ! 1697: (defun mh-pack-folder-1 (range) ! 1698: ;; Close and pack the current folder. ! 1699: (mh-process-or-undo-commands mh-current-folder) ! 1700: (message "Packing folder...") ! 1701: (mh-set-folder-modified-p t) ; lock folder while packing ! 1702: (save-excursion ! 1703: (mh-exec-cmd-quiet " *mh-temp*" "folder" mh-current-folder "-pack")) ! 1704: (mh-regenerate-headers range)) ! 1705: ! 1706: ! 1707: (defun mh-process-or-undo-commands (folder) ! 1708: ;; If FOLDER has outstanding commands, then either process or discard them. ! 1709: (set-buffer folder) ! 1710: (if (mh-outstanding-commands-p) ! 1711: (if (or mh-do-not-confirm ! 1712: (y-or-n-p ! 1713: "Process outstanding deletes and refiles (or lose them)? ")) ! 1714: (mh-process-commands folder) ! 1715: (mh-undo-folder)) ! 1716: (mh-invalidate-show-buffer))) ! 1717: ! 1718: ! 1719: (defun mh-process-commands (folder) ! 1720: ;; Process outstanding commands for the folder FOLDER. ! 1721: (message "Processing deletes and refiles for %s..." folder) ! 1722: (set-buffer folder) ! 1723: (with-mh-folder-updating (nil) ! 1724: ;; Update the unseen sequence if it exists ! 1725: (if (and mh-seen-list (mh-seq-to-msgs mh-unseen-seq)) ! 1726: (mh-undefine-sequence mh-unseen-seq mh-seen-list)) ! 1727: ! 1728: ;; Then refile messages ! 1729: (mh-mapc ! 1730: (function ! 1731: (lambda (dest) ! 1732: (let ((msgs (mh-seq-to-msgs dest))) ! 1733: (mh-when msgs ! 1734: (apply 'mh-exec-cmd "refile" ! 1735: "-src" folder (symbol-name dest) msgs) ! 1736: (mh-delete-scan-msgs msgs))))) ! 1737: mh-refile-list) ! 1738: ! 1739: ;; Now delete messages ! 1740: (mh-when mh-delete-list ! 1741: (apply 'mh-exec-cmd "rmm" folder mh-delete-list) ! 1742: (mh-delete-scan-msgs mh-delete-list)) ! 1743: ! 1744: ;; Don't need to remove sequences since delete and refile do so. ! 1745: ! 1746: ;; Mark cur message ! 1747: (if (> (buffer-size) 0) ! 1748: (mh-define-sequence 'cur (list (or (mh-get-msg-num nil) "last")))) ! 1749: ! 1750: (mh-invalidate-show-buffer) ! 1751: ! 1752: (setq mh-delete-list nil ! 1753: mh-refile-list nil ! 1754: mh-seq-list (mh-read-folder-sequences mh-current-folder nil) ! 1755: mh-seen-list nil) ! 1756: (mh-unmark-all-headers t) ! 1757: (mh-notate-user-sequences) ! 1758: (message "Processing deletes and refiles for %s...done" folder))) ! 1759: ! 1760: ! 1761: (defun mh-delete-scan-msgs (msgs) ! 1762: ;; Delete the scan listing lines for each of the msgs in the LIST. ! 1763: ;; Optimized for speed (i.e., no regular expressions). ! 1764: (setq msgs (sort msgs (function <))) ;okay to clobber msgs ! 1765: (save-excursion ! 1766: (mh-first-msg) ! 1767: (while (and msgs (< (point) (point-max))) ! 1768: (cond ((equal (mh-get-msg-num nil) (car msgs)) ! 1769: (delete-region (point) (save-excursion (forward-line) (point))) ! 1770: (setq msgs (cdr msgs))) ! 1771: (t ! 1772: (forward-line)))))) ! 1773: ! 1774: ! 1775: (defun mh-set-folder-modified-p (flag) ! 1776: "Mark current folder as modified or unmodified according to FLAG." ! 1777: (set-buffer-modified-p flag)) ! 1778: ! 1779: ! 1780: (defun mh-outstanding-commands-p () ! 1781: ;; Returns non-nil if there are outstanding deletes or refiles. ! 1782: (or mh-delete-list mh-refile-list)) ! 1783: ! 1784: ! 1785: ! 1786: ;;; Mode for composing and sending a draft message. ! 1787: ! 1788: (defvar mh-sent-from-folder nil ! 1789: "Folder of msg associated with this letter.") ! 1790: ! 1791: (defvar mh-sent-from-msg nil ! 1792: "Number of msg associated with this letter.") ! 1793: ! 1794: (defvar mh-send-args nil ! 1795: "Extra arguments to pass to \"send\" command.") ! 1796: ! 1797: (defvar mh-annotate-char nil ! 1798: "Character to use to annotate mh-sent-from-msg.") ! 1799: ! 1800: (defvar mh-annotate-field nil ! 1801: "Field name for message annotation.") ! 1802: ! 1803: (defun mh-letter-mode () ! 1804: "Mode for composing letters in mh-e. ! 1805: When you have finished composing, type \\[mh-send-letter] to send the letter. ! 1806: ! 1807: Variables controlling this mode (defaults in parentheses): ! 1808: ! 1809: mh-delete-yanked-msg-window (nil) ! 1810: If non-nil, \\[mh-yank-cur-msg] will delete any windows displaying ! 1811: the yanked message. ! 1812: ! 1813: mh-yank-from-start-of-msg (t) ! 1814: If non-nil, \\[mh-yank-cur-msg] will include the entire message. ! 1815: If `body', just yank the body (no header). ! 1816: If nil, only the portion of the message following the point will be yanked. ! 1817: If there is a region, this variable is ignored. ! 1818: ! 1819: Upon invoking mh-letter-mode, text-mode-hook and mh-letter-mode-hook are ! 1820: invoked with no args, if those values are non-nil. ! 1821: ! 1822: \\{mh-letter-mode-map}" ! 1823: (interactive) ! 1824: (kill-all-local-variables) ! 1825: (make-local-variable 'paragraph-start) ! 1826: (setq paragraph-start (concat "^[ \t]*[-_][-_][-_]+$\\|" paragraph-start)) ! 1827: (make-local-variable 'paragraph-separate) ! 1828: (setq paragraph-separate ! 1829: (concat "^[ \t]*[-_][-_][-_]+$\\|" paragraph-separate)) ! 1830: (make-local-variable 'mh-send-args) ! 1831: (make-local-variable 'mh-annotate-char) ! 1832: (make-local-variable 'mh-annotate-field) ! 1833: (make-local-variable 'mh-previous-window-config) ! 1834: (make-local-variable 'mh-sent-from-folder) ! 1835: (make-local-variable 'mh-sent-from-msg) ! 1836: (use-local-map mh-letter-mode-map) ! 1837: (setq major-mode 'mh-letter-mode) ! 1838: (mh-set-mode-name "mh-e letter") ! 1839: (set-syntax-table mh-letter-mode-syntax-table) ! 1840: (run-hooks 'text-mode-hook 'mh-letter-mode-hook) ! 1841: (mh-when auto-fill-hook ! 1842: (make-local-variable 'auto-fill-hook) ! 1843: (setq auto-fill-hook 'mh-auto-fill-for-letter))) ! 1844: ! 1845: ! 1846: (defun mh-auto-fill-for-letter () ! 1847: ;; Auto-fill in letters treats the header specially by inserting a tab ! 1848: ;; before continuation line. ! 1849: (do-auto-fill) ! 1850: (if (mh-in-header-p) ! 1851: (save-excursion ! 1852: (beginning-of-line nil) ! 1853: (insert-char ?\t 1)))) ! 1854: ! 1855: ! 1856: (defun mh-in-header-p () ! 1857: ;; Return non-nil if the point is in the header of a draft message. ! 1858: (save-excursion ! 1859: (let ((cur-point (point))) ! 1860: (goto-char (dot-min)) ! 1861: (re-search-forward "^--------" nil t) ! 1862: (< cur-point (point))))) ! 1863: ! 1864: ! 1865: (defun mh-to-field () ! 1866: "Move point to the end of a specified header field. ! 1867: The field is indicated by the previous keystroke. Create the field if ! 1868: it does not exist. Set the mark to point before moving." ! 1869: (interactive) ! 1870: (expand-abbrev) ! 1871: (let ((target (cdr (assoc (logior last-input-char ?`) mh-to-field-choices))) ! 1872: (case-fold-search t)) ! 1873: (cond ((mh-position-on-field target t) ! 1874: (let ((eol (point))) ! 1875: (skip-chars-backward " \t") ! 1876: (delete-region (point) eol)) ! 1877: (if (and (not (eq (logior last-input-char ?`) ?s)) ! 1878: (save-excursion ! 1879: (backward-char 1) ! 1880: (not (looking-at "[:,]")))) ! 1881: (insert ", ") ! 1882: (insert " "))) ! 1883: (t ! 1884: (goto-char (dot-min)) ! 1885: (re-search-forward "^To:") ! 1886: (forward-line 1) ! 1887: (while (looking-at "^[ \t]") (forward-line 1)) ! 1888: (insert (format "%s \n" target)) ! 1889: (backward-char 1))))) ! 1890: ! 1891: ! 1892: (defun mh-to-fcc () ! 1893: "Insert an Fcc: field in the current message. ! 1894: Prompt for the field name with a completion list of the current folders." ! 1895: (interactive) ! 1896: (let ((last-input-char ?\C-f) ! 1897: (folder (mh-prompt-for-folder "Fcc" "" t))) ! 1898: (expand-abbrev) ! 1899: (save-excursion ! 1900: (mh-to-field) ! 1901: (insert (substring folder 1 nil))))) ! 1902: ! 1903: ! 1904: (defun mh-insert-signature () ! 1905: "Insert the file ~/.signature at the current point." ! 1906: (interactive) ! 1907: (insert-file-contents "~/.signature") ! 1908: (set-buffer-modified-p (buffer-modified-p))) ; force mode line update ! 1909: ! 1910: ! 1911: (defun mh-check-whom () ! 1912: "Verify recipients of the current letter." ! 1913: (interactive) ! 1914: (let ((file-name (buffer-file-name))) ! 1915: (set-buffer-modified-p t) ; Force writing of contents ! 1916: (save-buffer) ! 1917: (message "Checking recipients...") ! 1918: (switch-to-buffer-other-window "*Mail Recipients*") ! 1919: (bury-buffer (current-buffer)) ! 1920: (erase-buffer) ! 1921: (mh-exec-cmd-output "whom" t file-name) ! 1922: (other-window -1) ! 1923: (message "Checking recipients...done"))) ! 1924: ! 1925: ! 1926: ! 1927: ;;; Routines to make a search pattern and search for a message. ! 1928: ! 1929: (defvar mh-searching-folder nil "Folder this pick is searching.") ! 1930: ! 1931: ! 1932: (defun mh-make-pick-template () ! 1933: ;; Initialize the current buffer with a template for a pick pattern. ! 1934: (erase-buffer) ! 1935: (kill-all-local-variables) ! 1936: (make-local-variable 'mh-searching-folder) ! 1937: (insert "From: \n" ! 1938: "To: \n" ! 1939: "Cc: \n" ! 1940: "Date: \n" ! 1941: "Subject: \n" ! 1942: "---------\n") ! 1943: (mh-letter-mode) ! 1944: (use-local-map mh-pick-mode-map) ! 1945: (goto-char (point-min)) ! 1946: (end-of-line)) ! 1947: ! 1948: ! 1949: (defun mh-do-pick-search () ! 1950: "Find messages that match the qualifications in the current pattern buffer. ! 1951: Messages are searched for in the folder named in mh-searching-folder. ! 1952: Put messages found in a sequence named `search'." ! 1953: (interactive) ! 1954: (let ((pattern-buffer (buffer-name)) ! 1955: (searching-buffer mh-searching-folder) ! 1956: (range) ! 1957: (pattern nil) ! 1958: (new-buffer nil)) ! 1959: (save-excursion ! 1960: (cond ((get-buffer searching-buffer) ! 1961: (set-buffer searching-buffer) ! 1962: (setq range (format "%d-%d" mh-first-msg-num mh-last-msg-num))) ! 1963: (t ! 1964: (mh-make-folder searching-buffer) ! 1965: (setq range "all") ! 1966: (setq new-buffer t)))) ! 1967: (message "Searching...") ! 1968: (goto-char (point-min)) ! 1969: (while (setq pattern (mh-next-pick-field pattern-buffer)) ! 1970: (setq msgs (mh-seq-from-command searching-buffer ! 1971: 'search ! 1972: (nconc (cons "pick" pattern) ! 1973: (list searching-buffer ! 1974: range ! 1975: "-sequence" "search" ! 1976: "-list")))) ! 1977: (setq range "search")) ! 1978: (message "Searching...done") ! 1979: (if new-buffer ! 1980: (mh-scan-folder searching-buffer msgs) ! 1981: (switch-to-buffer searching-buffer)) ! 1982: (delete-other-windows) ! 1983: (mh-notate-seq 'search ?% (1+ mh-cmd-note)))) ! 1984: ! 1985: ! 1986: (defun mh-next-pick-field (buffer) ! 1987: ;; Return the next piece of a pick argument that can be extracted from the ! 1988: ;; BUFFER. Returns nil if no pieces remain. ! 1989: (set-buffer buffer) ! 1990: (let ((case-fold-search t)) ! 1991: (cond ((eobp) ! 1992: nil) ! 1993: ((re-search-forward "^\\([a-z].*\\):[ \t]*\\([a-z0-9].*\\)$" nil t) ! 1994: (let* ((component ! 1995: (format "--%s" ! 1996: (downcase (buffer-substring (match-beginning 1) ! 1997: (match-end 1))))) ! 1998: (pat (buffer-substring (match-beginning 2) (match-end 2)))) ! 1999: (forward-line 1) ! 2000: (list component pat))) ! 2001: ((re-search-forward "^-*$" nil t) ! 2002: (forward-char 1) ! 2003: (let ((body (buffer-substring (point) (point-max)))) ! 2004: (if (and (> (length body) 0) (not (equal body "\n"))) ! 2005: (list "-search" body) ! 2006: nil))) ! 2007: (t ! 2008: nil)))) ! 2009: ! 2010: ! 2011: ! 2012: ;;; Routines to compose and send a letter. ! 2013: ! 2014: (defun mh-compose-and-send-mail (draft send-args ! 2015: sent-from-folder sent-from-msg ! 2016: to subject cc ! 2017: annotate-char annotate-field ! 2018: config) ! 2019: ;; Edit and compose a draft message in buffer DRAFT and send or save it. ! 2020: ;; SENT-FROM-FOLDER is buffer containing scan listing of current folder, or ! 2021: ;; nil if none exists. ! 2022: ;; SENT-FROM-MSG is the message number or sequence name or nil. ! 2023: ;; SEND-ARGS is an optional argument passed to the send command. ! 2024: ;; The TO, SUBJECT, and CC fields are passed to the ! 2025: ;; mh-compose-letter-function. ! 2026: ;; If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of the ! 2027: ;; message. In that case, the ANNOTATE-FIELD is used to build a string ! 2028: ;; for mh-annotate-msg. ! 2029: ;; CONFIG is the window configuration to restore after sending the letter. ! 2030: (pop-to-buffer draft) ! 2031: (mh-letter-mode) ! 2032: (setq mh-sent-from-folder sent-from-folder) ! 2033: (setq mh-sent-from-msg sent-from-msg) ! 2034: (setq mh-send-args send-args) ! 2035: (setq mh-annotate-char annotate-char) ! 2036: (setq mh-annotate-field annotate-field) ! 2037: (setq mh-previous-window-config config) ! 2038: (setq mode-line-buffer-identification (list "{%b}")) ! 2039: (if (and (boundp 'mh-compose-letter-function) ! 2040: (symbol-value 'mh-compose-letter-function)) ! 2041: ;; run-hooks will not pass arguments. ! 2042: (let ((value (symbol-value 'mh-compose-letter-function))) ! 2043: (if (and (listp value) (not (eq (car value) 'lambda))) ! 2044: (while value ! 2045: (funcall (car value) to subject cc) ! 2046: (setq value (cdr value))) ! 2047: (funcall mh-compose-letter-function to subject cc))))) ! 2048: ! 2049: ! 2050: (defun mh-send-letter (&optional arg) ! 2051: "Send the draft letter in the current buffer. ! 2052: If optional prefix argument is provided, monitor delivery. ! 2053: Run mh-before-send-letter-hook before doing anything." ! 2054: (interactive "P") ! 2055: (run-hooks 'mh-before-send-letter-hook) ! 2056: (set-buffer-modified-p t) ; Make sure buffer is written ! 2057: (save-buffer) ! 2058: (message "Sending...") ! 2059: (let ((draft-buffer (current-buffer)) ! 2060: (file-name (buffer-file-name)) ! 2061: (config mh-previous-window-config)) ! 2062: (cond (arg ! 2063: (pop-to-buffer "MH mail delivery") ! 2064: (erase-buffer) ! 2065: (if mh-send-args ! 2066: (mh-exec-cmd-output "send" t "-watch" "-nopush" ! 2067: "-nodraftfolder" mh-send-args file-name) ! 2068: (mh-exec-cmd-output "send" t "-watch" "-nopush" ! 2069: "-nodraftfolder" file-name)) ! 2070: (goto-char (point-max)) ; show the interesting part ! 2071: (recenter -1) ! 2072: (set-buffer draft-buffer)) ; for annotation below ! 2073: (mh-send-args ! 2074: (mh-exec-cmd-daemon "send" "-nodraftfolder" "-noverbose" ! 2075: mh-send-args file-name)) ! 2076: (t ! 2077: (mh-exec-cmd-daemon "send" "-nodraftfolder" "-noverbose" ! 2078: file-name))) ! 2079: ! 2080: (if mh-annotate-char ! 2081: (mh-annotate-msg mh-sent-from-msg ! 2082: mh-sent-from-folder ! 2083: mh-annotate-char ! 2084: "-component" mh-annotate-field ! 2085: "-text" (format "\"%s %s\"" ! 2086: (mh-get-field "To:") ! 2087: (mh-get-field "Cc:")))) ! 2088: ! 2089: (mh-when (or (not arg) ! 2090: (y-or-n-p "Kill draft buffer? ")) ! 2091: (kill-buffer draft-buffer) ! 2092: (if config ! 2093: (set-window-configuration config))) ! 2094: (message "Sending...done"))) ! 2095: ! 2096: ! 2097: (defun mh-insert-letter (prefix-provided folder msg) ! 2098: "Insert a message from any folder into the current letter. ! 2099: Removes the message's headers using mh-invisible-headers. ! 2100: Prefixes each non-blank line with mh-ins-buf-prefix (default \">> \"). ! 2101: If optional prefix argument provided, do not indent and do not delete ! 2102: headers. Leaves the mark before the letter and point after it." ! 2103: (interactive ! 2104: (list current-prefix-arg ! 2105: (mh-prompt-for-folder "Message from" mh-sent-from-folder nil) ! 2106: (read-input (format "Message number%s: " ! 2107: (if mh-sent-from-msg ! 2108: (format " [%d]" mh-sent-from-msg) ! 2109: ""))))) ! 2110: (save-restriction ! 2111: (narrow-to-region (point) (point)) ! 2112: (let ((start (point-min))) ! 2113: (if (equal msg "") (setq msg (int-to-string mh-sent-from-msg))) ! 2114: (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" ! 2115: (expand-file-name msg ! 2116: (mh-expand-file-name folder))) ! 2117: (mh-when (not prefix-provided) ! 2118: (mh-clean-msg-header start mh-invisible-headers mh-visible-headers) ! 2119: (set-mark start) ; since mh-clean-msg-header moves it ! 2120: (mh-insert-prefix-string mh-ins-buf-prefix))))) ! 2121: ! 2122: ! 2123: (defun mh-yank-cur-msg () ! 2124: "Insert the current message into the draft buffer. ! 2125: Prefix each non-blank line in the message with the string in ! 2126: `mh-ins-buf-prefix'. If a region is set in the message's buffer, then ! 2127: only the region will be inserted. Otherwise, the entire message will ! 2128: be inserted if `mh-yank-from-start-of-msg' is non-nil. If this variable ! 2129: is nil, the portion of the message following the point will be yanked. ! 2130: If `mh-delete-yanked-msg-window' is non-nil, any window displaying the ! 2131: yanked message will be deleted." ! 2132: (interactive) ! 2133: (if (and mh-sent-from-folder mh-sent-from-msg) ! 2134: (let ((to-point (point)) ! 2135: (to-buffer (current-buffer))) ! 2136: (set-buffer mh-sent-from-folder) ! 2137: (if mh-delete-yanked-msg-window ! 2138: (delete-windows-on mh-show-buffer)) ! 2139: (set-buffer mh-show-buffer) ; Find displayed message ! 2140: (let ((mh-ins-str (cond ((mark) ! 2141: (buffer-substring (region-beginning) ! 2142: (region-end))) ! 2143: ((eq 'body mh-yank-from-start-of-msg) ! 2144: (buffer-substring ! 2145: (save-excursion ! 2146: (goto-char (point-min)) ! 2147: (mh-goto-header-end 1) ! 2148: (point)) ! 2149: (point-max))) ! 2150: (mh-yank-from-start-of-msg ! 2151: (buffer-substring (point-min) (point-max))) ! 2152: (t ! 2153: (buffer-substring (point) (point-max)))))) ! 2154: (set-buffer to-buffer) ! 2155: (narrow-to-region to-point to-point) ! 2156: (push-mark) ! 2157: (insert mh-ins-str) ! 2158: (mh-insert-prefix-string mh-ins-buf-prefix) ! 2159: (insert "\n") ! 2160: (widen))) ! 2161: (error "There is no current message"))) ! 2162: ! 2163: ! 2164: (defun mh-insert-prefix-string (mh-ins-string) ! 2165: ;; Run MH-YANK-HOOK to insert a prefix string before each line in the buffer. ! 2166: ;; Generality for supercite users. ! 2167: (save-excursion ! 2168: (set-mark (point-max)) ! 2169: (goto-char (point-min)) ! 2170: (run-hooks 'mh-yank-hooks))) ! 2171: ! 2172: ! 2173: (defun mh-fully-kill-draft () ! 2174: "Kill the draft message file and the draft message buffer. ! 2175: Use \\[kill-buffer] if you don't want to delete the draft message file." ! 2176: (interactive) ! 2177: (if (y-or-n-p "Kill draft message? ") ! 2178: (let ((config mh-previous-window-config)) ! 2179: (if (file-exists-p (buffer-file-name)) ! 2180: (delete-file (buffer-file-name))) ! 2181: (set-buffer-modified-p nil) ! 2182: (kill-buffer (buffer-name)) ! 2183: (message "") ! 2184: (if config ! 2185: (set-window-configuration config))) ! 2186: (error "Message not killed"))) ! 2187: ! 2188: ! 2189: (defun mh-recenter (arg) ! 2190: ;; Like recenter but with two improvements: nil arg means recenter, ! 2191: ;; and only does anything if the current buffer is in the selected ! 2192: ;; window. (Commands like save-some-buffers can make this false.) ! 2193: (if (eql (get-buffer-window (current-buffer)) ! 2194: (selected-window)) ! 2195: (recenter (if arg arg '(t))))) ! 2196: ! 2197: ! 2198: ! 2199: ;;; Commands to manipulate sequences. Sequences are stored in an alist ! 2200: ;;; of the form: ! 2201: ;;; ((seq-name msgs ...) (seq-name msgs ...) ...) ! 2202: ! 2203: (defun mh-make-seq (name msgs) (cons name msgs)) ! 2204: ! 2205: (defmacro mh-seq-name (pair) (list 'car pair)) ! 2206: ! 2207: (defmacro mh-seq-msgs (pair) (list 'cdr pair)) ! 2208: ! 2209: (defun mh-find-seq (name) (assoc name mh-seq-list)) ! 2210: ! 2211: ! 2212: (defun mh-seq-to-msgs (seq) ! 2213: "Return a list of the messages in SEQUENCE." ! 2214: (mh-seq-msgs (mh-find-seq seq))) ! 2215: ! 2216: ! 2217: (defun mh-seq-containing-msg (msg) ! 2218: ;; Return a list of the sequences containing MESSAGE. ! 2219: (let ((l mh-seq-list) ! 2220: (seqs ())) ! 2221: (while l ! 2222: (if (memq msg (mh-seq-msgs (car l))) ! 2223: (mh-push (mh-seq-name (car l)) seqs)) ! 2224: (setq l (cdr l))) ! 2225: seqs)) ! 2226: ! 2227: ! 2228: (defun mh-msg-to-seq (msg) ! 2229: ;; Given a MESSAGE number, return the first sequence in which it occurs. ! 2230: (car (mh-seq-containing-msg msg))) ! 2231: ! 2232: ! 2233: (defun mh-read-seq-default (prompt not-empty) ! 2234: ;; Read and return sequence name with default narrowed or previous sequence. ! 2235: (mh-read-seq prompt not-empty (or mh-narrowed-to-seq mh-previous-seq))) ! 2236: ! 2237: ! 2238: (defun mh-read-seq (prompt not-empty &optional default) ! 2239: ;; Read and return a sequence name. Prompt with PROMPT, raise an error ! 2240: ;; if the sequence is empty and the NOT-EMPTY flag is non-nil, and supply ! 2241: ;; an optional DEFAULT sequence. ! 2242: ;; A reply of '%' defaults to the first sequence containing the current ! 2243: ;; message. ! 2244: (let* ((input (completing-read (format "%s %s %s" prompt "sequence:" ! 2245: (if default ! 2246: (format "[%s] " default) ! 2247: "")) ! 2248: (mh-seq-names mh-seq-list))) ! 2249: (seq (cond ((equal input "%") (mh-msg-to-seq (mh-get-msg-num t))) ! 2250: ((equal input "") default) ! 2251: (t (intern input)))) ! 2252: (msgs (mh-seq-to-msgs seq))) ! 2253: (if (and (null msgs) not-empty) ! 2254: (error (format "No messages in sequence `%s'" seq))) ! 2255: seq)) ! 2256: ! 2257: ! 2258: (defun mh-read-folder-sequences (folder define-sequences) ! 2259: ;; Read and return the predefined sequences for a FOLDER. If ! 2260: ;; DEFINE-SEQUENCES is non-nil, then define mh-e's sequences before ! 2261: ;; reading MH's sequences. ! 2262: (let ((seqs ())) ! 2263: (mh-when define-sequences ! 2264: (mh-define-sequences mh-seq-list) ! 2265: (mh-mapc (function (lambda (seq) ; Save the internal sequences ! 2266: (if (mh-folder-name-p (mh-seq-name seq)) ! 2267: (mh-push seq seqs)))) ! 2268: mh-seq-list)) ! 2269: (save-excursion ! 2270: (mh-exec-cmd-quiet " *mh-temp*" "mark" folder "-list") ! 2271: (goto-char (point-min)) ! 2272: ;; look for name in line of form "cur: 4" or "myseq (private): 23" ! 2273: (while (re-search-forward "^[^: ]+" nil t) ! 2274: (mh-push (mh-make-seq (intern (buffer-substring (match-beginning 0) ! 2275: (match-end 0))) ! 2276: (mh-read-msg-list)) ! 2277: seqs)) ! 2278: (delete-region (point-min) (point))) ; avoid race with mh-process-daemon ! 2279: seqs)) ! 2280: ! 2281: ! 2282: (defun mh-seq-names (seq-list) ! 2283: ;; Return an alist containing the names of the SEQUENCES. ! 2284: (mapcar (function (lambda (entry) (list (symbol-name (mh-seq-name entry))))) ! 2285: seq-list)) ! 2286: ! 2287: ! 2288: (defun mh-seq-from-command (folder seq seq-command) ! 2289: ;; In FOLDER, make a sequence named SEQ by executing COMMAND. ! 2290: ;; COMMAND is a list. The first element is a program name ! 2291: ;; and the subsequent elements are its arguments, all strings. ! 2292: (let ((msg) ! 2293: (msgs ()) ! 2294: (case-fold-search t)) ! 2295: (save-excursion ! 2296: (save-window-excursion ! 2297: (apply 'mh-exec-cmd-quiet " *mh-temp*" seq-command) ! 2298: (goto-char (point-min)) ! 2299: (while (setq msg (car (mh-read-msg-list))) ! 2300: (mh-push msg msgs) ! 2301: (forward-line 1))) ! 2302: (set-buffer folder) ! 2303: (setq msgs (nreverse msgs)) ; Put in ascending order ! 2304: (mh-push (mh-make-seq seq msgs) mh-seq-list) ! 2305: msgs))) ! 2306: ! 2307: ! 2308: (defun mh-read-msg-list () ! 2309: ;; Return a list of message numbers from the current point to the end of ! 2310: ;; the line. ! 2311: (let ((msgs ()) ! 2312: (end-of-line (save-excursion (end-of-line) (point))) ! 2313: num) ! 2314: (while (re-search-forward "[0-9]+" end-of-line t) ! 2315: (setq num (string-to-int (buffer-substring (match-beginning 0) ! 2316: (match-end 0)))) ! 2317: (cond ((looking-at "-") ; Message range ! 2318: (forward-char 1) ! 2319: (re-search-forward "[0-9]+" end-of-line t) ! 2320: (let ((num2 (string-to-int (buffer-substring (match-beginning 0) ! 2321: (match-end 0))))) ! 2322: (if (< num2 num) ! 2323: (error "Bad message range: %d-%d" num num2)) ! 2324: (while (<= num num2) ! 2325: (mh-push num msgs) ! 2326: (setq num (1+ num))))) ! 2327: ((not (zerop num)) (mh-push num msgs)))) ! 2328: msgs)) ! 2329: ! 2330: ! 2331: (defun mh-remove-seq (seq) ! 2332: ;; Delete the SEQUENCE. ! 2333: (mh-map-to-seq-msgs 'mh-notate-if-in-one-seq seq ? (1+ mh-cmd-note) seq) ! 2334: (mh-undefine-sequence seq (list "all")) ! 2335: (mh-delete-seq-locally seq)) ! 2336: ! 2337: ! 2338: (defun mh-delete-seq-locally (seq) ! 2339: ;; Remove mh-e's record of SEQUENCE. ! 2340: (let ((entry (mh-find-seq seq))) ! 2341: (setq mh-seq-list (delq entry mh-seq-list)))) ! 2342: ! 2343: ! 2344: (defun mh-remove-msg-from-seq (msg seq &optional internal-flag) ! 2345: ;; Remove MESSAGE from the SEQUENCE. If optional FLAG is non-nil, do not ! 2346: ;; inform MH of the change. ! 2347: (let ((entry (mh-find-seq seq))) ! 2348: (mh-when entry ! 2349: (mh-notate-if-in-one-seq msg ? (1+ mh-cmd-note) (mh-seq-name entry)) ! 2350: (if (not internal-flag) ! 2351: (mh-undefine-sequence seq (list msg))) ! 2352: (setcdr entry (delq msg (mh-seq-msgs entry)))))) ! 2353: ! 2354: ! 2355: (defun mh-add-msgs-to-seq (msgs seq &optional internal-flag) ! 2356: ;; Add MESSAGE(s) to the SEQUENCE. If optional FLAG is non-nil, do not mark ! 2357: ;; the message in the scan listing or inform MH of the addition. ! 2358: (let ((entry (mh-find-seq seq))) ! 2359: (if (and msgs (atom msgs)) (setq msgs (list msgs))) ! 2360: (if (null entry) ! 2361: (mh-push (mh-make-seq seq msgs) mh-seq-list) ! 2362: (if msgs (setcdr entry (append msgs (cdr entry))))) ! 2363: (mh-when (not internal-flag) ! 2364: (mh-add-to-sequence seq msgs) ! 2365: (mh-notate-seq seq ?% (1+ mh-cmd-note))))) ! 2366: ! 2367: ! 2368: (defun mh-rename-seq (seq new-name) ! 2369: "Rename a SEQUENCE to have a new NAME." ! 2370: (interactive "SOld sequence name: \nSNew name: ") ! 2371: (let ((old-seq (mh-find-seq seq))) ! 2372: (if old-seq ! 2373: (rplaca old-seq new-name) ! 2374: (error "Sequence %s does not exists" seq)) ! 2375: (mh-undefine-sequence seq (mh-seq-msgs old-seq)) ! 2376: (mh-define-sequence new-name (mh-seq-msgs old-seq)))) ! 2377: ! 2378: ! 2379: (defun mh-notate-user-sequences () ! 2380: ;; Mark the scan listing of all messages in user-defined sequences. ! 2381: (let ((seqs mh-seq-list) ! 2382: name) ! 2383: (while seqs ! 2384: (setq name (mh-seq-name (car seqs))) ! 2385: (if (not (mh-internal-seq name)) ! 2386: (mh-notate-seq name ?% (1+ mh-cmd-note))) ! 2387: (setq seqs (cdr seqs))))) ! 2388: ! 2389: ! 2390: (defun mh-internal-seq (name) ! 2391: ;; Return non-NIL if NAME is the name of an internal mh-e sequence. ! 2392: (or (memq name '(answered cur deleted forwarded printed)) ! 2393: (eq name mh-unseen-seq) ! 2394: (mh-folder-name-p name))) ! 2395: ! 2396: ! 2397: (defun mh-folder-name-p (name) ! 2398: ;; Return non-NIL if NAME is possibly the name of a folder. ! 2399: ;; A name (a string or symbol) can be a folder name if it begins with "+". ! 2400: (if (symbolp name) ! 2401: (eql (aref (symbol-name name) 0) ?+) ! 2402: (eql (aref name 0) ?+))) ! 2403: ! 2404: ! 2405: (defun mh-notate-seq (seq notation offset) ! 2406: ;; Mark the scan listing of all messages in the SEQUENCE with the CHARACTER ! 2407: ;; at the given OFFSET from the beginning of the listing line. ! 2408: (mh-map-to-seq-msgs 'mh-notate seq notation offset)) ! 2409: ! 2410: ! 2411: (defun mh-notate-if-in-one-seq (msg notation offset seq) ! 2412: ;; If the MESSAGE is in only the SEQUENCE, then mark the scan listing of the ! 2413: ;; message with the CHARACTER at the given OFFSET from the beginning of the ! 2414: ;; listing line. ! 2415: (let ((in-seqs (mh-seq-containing-msg msg))) ! 2416: (if (and (eq seq (car in-seqs)) (null (cdr in-seqs))) ! 2417: (mh-notate msg notation offset)))) ! 2418: ! 2419: ! 2420: (defun mh-map-to-seq-msgs (func seq &rest args) ! 2421: ;; Invoke the FUNCTION at each message in the SEQUENCE, passing the ! 2422: ;; remaining ARGS as arguments. ! 2423: (save-excursion ! 2424: (let ((msgs (mh-seq-to-msgs seq))) ! 2425: (while msgs ! 2426: (if (mh-goto-msg (car msgs) t t) ! 2427: (apply func (car msgs) args)) ! 2428: (setq msgs (cdr msgs)))))) ! 2429: ! 2430: ! 2431: (defun mh-map-over-seqs (func seq-list) ! 2432: ;; Apply the FUNCTION to each element in the list of SEQUENCES, ! 2433: ;; passing the sequence name and the list of messages as arguments. ! 2434: (while seq-list ! 2435: (funcall func (mh-seq-name (car seq-list)) (mh-seq-msgs (car seq-list))) ! 2436: (setq seq-list (cdr seq-list)))) ! 2437: ! 2438: ! 2439: (defun mh-define-sequences (seq-list) ! 2440: ;; Define the sequences in SEQ-LIST. ! 2441: (mh-map-over-seqs 'mh-define-sequence seq-list)) ! 2442: ! 2443: ! 2444: (defun mh-add-to-sequence (seq msgs) ! 2445: ;; Add to a SEQUENCE each message the list of MSGS. ! 2446: (if (not (mh-folder-name-p seq)) ! 2447: (if msgs ! 2448: (apply 'mh-exec-cmd "mark" mh-current-folder ! 2449: "-sequence" (symbol-name seq) ! 2450: "-add" msgs)))) ! 2451: ! 2452: ! 2453: (defun mh-define-sequence (seq msgs) ! 2454: ;; Define the SEQUENCE to contain the list of MSGS. Do not mark ! 2455: ;; pseudo-sequences or empty sequences. ! 2456: (if (and msgs ! 2457: (not (mh-folder-name-p seq))) ! 2458: (save-excursion ! 2459: (apply 'mh-exec-cmd "mark" mh-current-folder ! 2460: "-sequence" (symbol-name seq) ! 2461: "-add" "-zero" (mh-list-to-string msgs))))) ! 2462: ! 2463: ! 2464: (defun mh-undefine-sequence (seq msgs) ! 2465: ;; Remove from the SEQUENCE the list of MSGS. ! 2466: (apply 'mh-exec-cmd "mark" mh-current-folder ! 2467: "-sequence" (symbol-name seq) ! 2468: "-delete" msgs)) ! 2469: ! 2470: ! 2471: (defun mh-copy-seq-to-point (seq location) ! 2472: ;; Copy the scan listing of the messages in SEQUENCE to after the point ! 2473: ;; LOCATION in the current buffer. ! 2474: (mh-map-to-seq-msgs 'mh-copy-line-to-point seq location)) ! 2475: ! 2476: ! 2477: (defun mh-copy-line-to-point (msg location) ! 2478: ;; Copy the current line to the LOCATION in the current buffer. ! 2479: (beginning-of-line) ! 2480: (let ((beginning-of-line (point))) ! 2481: (forward-line 1) ! 2482: (copy-region-as-kill beginning-of-line (point)) ! 2483: (goto-char location) ! 2484: (yank) ! 2485: (goto-char beginning-of-line))) ! 2486: ! 2487: ! 2488: ! 2489: ;;; Issue commands to MH. ! 2490: ! 2491: (defun mh-exec-cmd (command &rest args) ! 2492: ;; Execute MH command COMMAND with ARGS. ! 2493: ;; Any output is assumed to be an error and is shown to the user. ! 2494: (save-excursion ! 2495: (set-buffer " *mh-temp*") ! 2496: (erase-buffer) ! 2497: (apply 'call-process ! 2498: (expand-file-name command mh-progs) nil t nil ! 2499: (mh-list-to-string args)) ! 2500: (if (> (buffer-size) 0) ! 2501: (save-window-excursion ! 2502: (switch-to-buffer-other-window " *mh-temp*") ! 2503: (sit-for 5))))) ! 2504: ! 2505: ! 2506: (defun mh-exec-cmd-quiet (buffer command &rest args) ! 2507: ;; In BUFFER, execute MH command COMMAND with ARGS. ! 2508: ;; ARGS is a list of strings. Return in BUFFER, if one exists. ! 2509: (mh-when (stringp buffer) ! 2510: (set-buffer buffer) ! 2511: (erase-buffer)) ! 2512: (apply 'call-process ! 2513: (expand-file-name command mh-progs) nil buffer nil ! 2514: args)) ! 2515: ! 2516: ! 2517: (defun mh-exec-cmd-output (command display &rest args) ! 2518: ;; Execute MH command COMMAND with DISPLAY flag and ARGS putting the output ! 2519: ;; into buffer after point. Set mark after inserted text. ! 2520: (push-mark (point) t) ! 2521: (apply 'call-process ! 2522: (expand-file-name command mh-progs) nil t display ! 2523: (mh-list-to-string args)) ! 2524: (exchange-point-and-mark)) ! 2525: ! 2526: ! 2527: (defun mh-exec-cmd-daemon (command &rest args) ! 2528: ;; Execute MH command COMMAND with ARGS. Any output from command is ! 2529: ;; displayed in an asynchronous pop-up window. ! 2530: (save-excursion ! 2531: (set-buffer (get-buffer-create " *mh-temp*")) ! 2532: (erase-buffer)) ! 2533: (let* ((process-connection-type nil) ! 2534: (process (apply 'start-process ! 2535: command nil ! 2536: (expand-file-name command mh-progs) ! 2537: (mh-list-to-string args)))) ! 2538: (set-process-filter process 'mh-process-daemon))) ! 2539: ! 2540: ! 2541: (defun mh-process-daemon (process output) ! 2542: ;; Process daemon that puts output into a temporary buffer. ! 2543: (set-buffer (get-buffer-create " *mh-temp*")) ! 2544: (insert-before-markers output) ! 2545: (display-buffer " *mh-temp*")) ! 2546: ! 2547: ! 2548: (defun mh-exec-lib-cmd-output (command &rest args) ! 2549: ;; Execute MH library command COMMAND with ARGS. ! 2550: ;; Put the output into buffer after point. Set mark after inserted text. ! 2551: (push-mark (point) t) ! 2552: (apply 'call-process ! 2553: (expand-file-name command mh-lib) nil t nil ! 2554: (mh-list-to-string args)) ! 2555: (exchange-point-and-mark)) ! 2556: ! 2557: ! 2558: (defun mh-list-to-string (l) ! 2559: ;; Flattens the list L and makes every element of the new list into a string. ! 2560: (let ((new-list nil)) ! 2561: (while l ! 2562: (cond ((null (car l))) ! 2563: ((symbolp (car l)) (mh-push (symbol-name (car l)) new-list)) ! 2564: ((numberp (car l)) (mh-push (int-to-string (car l)) new-list)) ! 2565: ((equal (car l) "")) ! 2566: ((stringp (car l)) (mh-push (car l) new-list)) ! 2567: ((listp (car l)) ! 2568: (setq new-list (nconc (nreverse (mh-list-to-string (car l))) ! 2569: new-list))) ! 2570: (t (error "Bad element in mh-list-to-string: %s" (car l)))) ! 2571: (setq l (cdr l))) ! 2572: (nreverse new-list))) ! 2573: ! 2574: ! 2575: ! 2576: ;;; Commands to annotate a message. ! 2577: ! 2578: (defun mh-annotate-msg (msg buffer note &rest args) ! 2579: ;; Mark the MESSAGE in BUFFER listing with the character NOTE and annotate ! 2580: ;; the saved message with ARGS. ! 2581: (apply 'mh-exec-cmd "anno" buffer msg args) ! 2582: (save-excursion ! 2583: (cond ((get-buffer buffer) ; Buffer may be deleted ! 2584: (set-buffer buffer) ! 2585: (if (symbolp msg) ! 2586: (mh-notate-seq msg note (1+ mh-cmd-note)) ! 2587: (mh-notate msg note (1+ mh-cmd-note))))))) ! 2588: ! 2589: ! 2590: (defun mh-notate (msg notation offset) ! 2591: ;; Marks MESSAGE with the character NOTATION at position OFFSET. ! 2592: ;; Null MESSAGE means the message that the cursor points to. ! 2593: (save-excursion ! 2594: (if (or (null msg) ! 2595: (mh-goto-msg msg t t)) ! 2596: (with-mh-folder-updating (t) ! 2597: (beginning-of-line) ! 2598: (forward-char offset) ! 2599: (delete-char 1) ! 2600: (insert notation))))) ! 2601: ! 2602: ! 2603: ! 2604: ;;; User prompting commands. ! 2605: ! 2606: (defun mh-prompt-for-folder (prompt default can-create) ! 2607: ;; Prompt for a folder name with PROMPT. Returns the folder's name as a ! 2608: ;; string. DEFAULT is used if the folder exists and the user types return. ! 2609: ;; If the CAN-CREATE flag is t, then a non-existent folder is made. ! 2610: (let* ((prompt (format "%s folder%s" prompt ! 2611: (if (equal "" default) ! 2612: "? " ! 2613: (format " [%s]? " default)))) ! 2614: name) ! 2615: (if (null mh-folder-list) ! 2616: (mh-set-folder-list)) ! 2617: (while (and (setq name (completing-read prompt mh-folder-list ! 2618: nil nil "+")) ! 2619: (equal name "") ! 2620: (equal default ""))) ! 2621: (cond ((or (equal name "") (equal name "+")) ! 2622: (setq name default)) ! 2623: ((not (mh-folder-name-p name)) ! 2624: (setq name (format "+%s" name)))) ! 2625: (let ((new-file-p (not (file-exists-p (mh-expand-file-name name))))) ! 2626: (cond ((and new-file-p ! 2627: (y-or-n-p ! 2628: (format "Folder %s does not exist. Create it? " name))) ! 2629: (message "Creating %s" name) ! 2630: (call-process "mkdir" nil nil nil (mh-expand-file-name name)) ! 2631: (message "Creating %s...done" name) ! 2632: (mh-push (list name) mh-folder-list)) ! 2633: (new-file-p ! 2634: (error "Folder %s is not created" name)) ! 2635: (t ! 2636: (mh-when (null (assoc name mh-folder-list)) ! 2637: (mh-push (list name) mh-folder-list))))) ! 2638: name)) ! 2639: ! 2640: ! 2641: (defun mh-set-folder-list () ! 2642: "Sets mh-folder-list correctly. ! 2643: A useful function for the command line or for when you need to sync by hand." ! 2644: (setq mh-folder-list (mh-make-folder-list))) ! 2645: ! 2646: ! 2647: (defun mh-make-folder-list () ! 2648: "Return a list of the user's folders. ! 2649: Result is in a form suitable for completing read." ! 2650: (interactive) ! 2651: (message "Collecting folder names...") ! 2652: (save-window-excursion ! 2653: (mh-exec-cmd-quiet " *mh-temp*" "folders" "-fast" ! 2654: (if mh-recursive-folders ! 2655: "-recurse" ! 2656: "-norecurse")) ! 2657: (goto-char (point-min)) ! 2658: (let ((list nil) ! 2659: start) ! 2660: (while (not (eobp)) ! 2661: (setq start (point)) ! 2662: (forward-line 1) ! 2663: (mh-push (list (format "+%s" (buffer-substring start (1- (point))))) ! 2664: list)) ! 2665: (message "Collecting folder names...done") ! 2666: list))) ! 2667: ! 2668: ! 2669: (defun mh-remove-folder-from-folder-list (folder) ! 2670: ;; Remove FOLDER from the list of folders. ! 2671: (setq mh-folder-list ! 2672: (delq (assoc folder mh-folder-list) mh-folder-list))) ! 2673: ! 2674: ! 2675: (defun mh-read-msg-range (prompt) ! 2676: ;; Read a list of blank-separated items. ! 2677: (let* ((buf (read-string prompt)) ! 2678: (buf-size (length buf)) ! 2679: (start 0) ! 2680: (input ())) ! 2681: (while (< start buf-size) ! 2682: (let ((next (read-from-string buf start buf-size))) ! 2683: (mh-push (car next) input) ! 2684: (setq start (cdr next)))) ! 2685: (nreverse input))) ! 2686: ! 2687: ! 2688: ! 2689: ;;; Misc. functions. ! 2690: ! 2691: (defun mh-get-msg-num (error-if-no-message) ! 2692: ;; Return the message number of the displayed message. If the argument ! 2693: ;; ERROR-IF-NO-MESSAGE is non-nil, then complain if the cursor is not ! 2694: ;; pointing to a message. ! 2695: (save-excursion ! 2696: (beginning-of-line) ! 2697: (cond ((looking-at mh-msg-number-regexp) ! 2698: (string-to-int (buffer-substring (match-beginning 1) ! 2699: (match-end 1)))) ! 2700: (error-if-no-message ! 2701: (error "Cursor not pointing to message")) ! 2702: (t nil)))) ! 2703: ! 2704: ! 2705: (defun mh-msg-search-pat (n) ! 2706: ;; Return a search pattern for message N in the scan listing. ! 2707: (format mh-msg-search-regexp n)) ! 2708: ! 2709: ! 2710: (defun mh-msg-filename (msg &optional folder) ! 2711: ;; Return the file name of MESSAGE in FOLDER (default current folder). ! 2712: (expand-file-name (int-to-string msg) ! 2713: (if folder ! 2714: (mh-expand-file-name folder) ! 2715: mh-folder-filename))) ! 2716: ! 2717: ! 2718: (defun mh-msg-filenames (msgs &optional folder) ! 2719: ;; Return a list of file names for MSGS in FOLDER (default current folder). ! 2720: (mapconcat (function (lambda (msg) (mh-msg-filename msg folder))) msgs " ")) ! 2721: ! 2722: ! 2723: (defun mh-expand-file-name (filename &optional default) ! 2724: "Just like `expand-file-name', but also handles MH folder names. ! 2725: Assumes that any filename that starts with '+' is a folder name." ! 2726: (if (mh-folder-name-p filename) ! 2727: (expand-file-name (substring filename 1) mh-user-path) ! 2728: (expand-file-name filename default))) ! 2729: ! 2730: ! 2731: (defun mh-find-path () ! 2732: ;; Set mh-user-path, mh-draft-folder, and mh-unseen-seq from profile file. ! 2733: (save-excursion ! 2734: ;; Be sure profile is fully expanded before switching buffers ! 2735: (let ((profile (expand-file-name (or (getenv "MH") "~/.mh_profile")))) ! 2736: (if (not (file-exists-p profile)) ! 2737: (error "Cannot find MH profile %s" profile)) ! 2738: (set-buffer (get-buffer-create " *mh-temp*")) ! 2739: (erase-buffer) ! 2740: (insert-file-contents profile) ! 2741: (setq mh-draft-folder (mh-get-field "Draft-Folder:")) ! 2742: (cond ((equal mh-draft-folder "") ! 2743: (setq mh-draft-folder nil)) ! 2744: ((not (mh-folder-name-p mh-draft-folder)) ! 2745: (setq mh-draft-folder (format "+%s" mh-draft-folder)))) ! 2746: (setq mh-user-path (mh-get-field "Path:")) ! 2747: (if (equal mh-user-path "") ! 2748: (setq mh-user-path "Mail")) ! 2749: (setq mh-user-path ! 2750: (file-name-as-directory ! 2751: (expand-file-name mh-user-path (expand-file-name "~")))) ! 2752: (if (and mh-draft-folder ! 2753: (not (file-exists-p (mh-expand-file-name mh-draft-folder)))) ! 2754: (error "Draft folder %s does not exist. Create it and try again." ! 2755: mh-draft-folder)) ! 2756: (setq mh-unseen-seq (mh-get-field "Unseen-Sequence:")) ! 2757: (if (equal mh-unseen-seq "") ! 2758: (setq mh-unseen-seq 'unseen) ! 2759: (setq mh-unseen-seq (intern mh-unseen-seq)))))) ! 2760: ! 2761: ! 2762: (defun mh-get-field (field) ! 2763: ;; Find and return the value of field FIELD in the current buffer. ! 2764: ;; Returns the empty string if the field is not in the message. ! 2765: (let ((case-fold-search t)) ! 2766: (goto-char (point-min)) ! 2767: (cond ((not (re-search-forward (format "^%s" field) nil t)) "") ! 2768: ((looking-at "[\t ]*$") "") ! 2769: (t ! 2770: (re-search-forward "[\t ]*\\([^\t \n].*\\)$" nil t) ! 2771: (let ((start (match-beginning 1))) ! 2772: (forward-line 1) ! 2773: (while (looking-at "[ \t]") ! 2774: (forward-line 1)) ! 2775: (buffer-substring start (1- (point)))))))) ! 2776: ! 2777: ! 2778: (defun mh-insert-fields (&rest name-values) ! 2779: ;; Insert the NAME-VALUE pairs in the current buffer. ! 2780: ;; Do not insert any pairs whose value is the empty string. ! 2781: (let ((case-fold-search t)) ! 2782: (while name-values ! 2783: (let ((field-name (car name-values)) ! 2784: (value (car (cdr name-values)))) ! 2785: (mh-when (not (equal value "")) ! 2786: (goto-char (point-min)) ! 2787: (cond ((not (re-search-forward (format "^%s" field-name) nil t)) ! 2788: (mh-goto-header-end 0) ! 2789: (insert field-name " " value "\n")) ! 2790: (t ! 2791: (end-of-line) ! 2792: (insert " " value)))) ! 2793: (setq name-values (cdr (cdr name-values))))))) ! 2794: ! 2795: ! 2796: (defun mh-position-on-field (field set-mark) ! 2797: ;; Set point to the end of the line beginning with FIELD. ! 2798: ;; Set the mark to the old value of point, if SET-MARK is non-nil. ! 2799: ;; Returns non-nil iff the field was found. ! 2800: (let ((case-fold-search t)) ! 2801: (if set-mark (push-mark)) ! 2802: (goto-char (point-min)) ! 2803: (mh-goto-header-end 0) ! 2804: (if (re-search-backward (format "^%s" field) nil t) ! 2805: (progn (end-of-line) t) ! 2806: nil))) ! 2807: ! 2808: ! 2809: (defun mh-goto-header-end (arg) ! 2810: ;; Find the end of the message header in the current buffer and position ! 2811: ;; the cursor at the ARG'th newline after the header. ! 2812: (if (re-search-forward "^$\\|^-+$" nil nil) ! 2813: (forward-line arg))) ! 2814: ! 2815: ! 2816: ! 2817: ;;; Build the folder-mode keymap: ! 2818: ! 2819: (suppress-keymap mh-folder-mode-map) ! 2820: (define-key mh-folder-mode-map "q" 'mh-quit) ! 2821: (define-key mh-folder-mode-map "b" 'mh-quit) ! 2822: (define-key mh-folder-mode-map "?" 'mh-msg-is-in-seq) ! 2823: (define-key mh-folder-mode-map "%" 'mh-put-msg-in-seq) ! 2824: (define-key mh-folder-mode-map "|" 'mh-pipe-msg) ! 2825: (define-key mh-folder-mode-map "\ea" 'mh-edit-again) ! 2826: (define-key mh-folder-mode-map "\e%" 'mh-delete-msg-from-seq) ! 2827: (define-key mh-folder-mode-map "\C-xn" 'mh-narrow-to-seq) ! 2828: (define-key mh-folder-mode-map "\C-xw" 'mh-widen) ! 2829: (define-key mh-folder-mode-map "\eb" 'mh-burst-digest) ! 2830: (define-key mh-folder-mode-map "\eu" 'mh-undo-folder) ! 2831: (define-key mh-folder-mode-map "\e " 'mh-page-digest) ! 2832: (define-key mh-folder-mode-map "\e\177" 'mh-page-digest-backwards) ! 2833: (define-key mh-folder-mode-map "\ee" 'mh-extract-rejected-mail) ! 2834: (define-key mh-folder-mode-map "\ef" 'mh-visit-folder) ! 2835: (define-key mh-folder-mode-map "\ek" 'mh-kill-folder) ! 2836: (define-key mh-folder-mode-map "\el" 'mh-list-folders) ! 2837: (define-key mh-folder-mode-map "\eo" 'mh-write-msg-to-file) ! 2838: (define-key mh-folder-mode-map "\ep" 'mh-pack-folder) ! 2839: (define-key mh-folder-mode-map "\es" 'mh-search-folder) ! 2840: (define-key mh-folder-mode-map "\er" 'mh-rescan-folder) ! 2841: (define-key mh-folder-mode-map "l" 'mh-print-msg) ! 2842: (define-key mh-folder-mode-map "t" 'mh-toggle-showing) ! 2843: (define-key mh-folder-mode-map "c" 'mh-copy-msg) ! 2844: (define-key mh-folder-mode-map ">" 'mh-write-msg-to-file) ! 2845: (define-key mh-folder-mode-map "i" 'mh-inc-folder) ! 2846: (define-key mh-folder-mode-map "x" 'mh-execute-commands) ! 2847: (define-key mh-folder-mode-map "e" 'mh-execute-commands) ! 2848: (define-key mh-folder-mode-map "r" 'mh-redistribute) ! 2849: (define-key mh-folder-mode-map "f" 'mh-forward) ! 2850: (define-key mh-folder-mode-map "s" 'mh-send) ! 2851: (define-key mh-folder-mode-map "m" 'mh-send) ! 2852: (define-key mh-folder-mode-map "a" 'mh-reply) ! 2853: (define-key mh-folder-mode-map "j" 'mh-goto-msg) ! 2854: (define-key mh-folder-mode-map "<" 'mh-first-msg) ! 2855: (define-key mh-folder-mode-map "g" 'mh-goto-msg) ! 2856: (define-key mh-folder-mode-map "\177" 'mh-previous-page) ! 2857: (define-key mh-folder-mode-map " " 'mh-page-msg) ! 2858: (define-key mh-folder-mode-map "." 'mh-show) ! 2859: (define-key mh-folder-mode-map "u" 'mh-undo) ! 2860: (define-key mh-folder-mode-map "!" 'mh-refile-or-write-again) ! 2861: (define-key mh-folder-mode-map "^" 'mh-refile-msg) ! 2862: (define-key mh-folder-mode-map "d" 'mh-delete-msg) ! 2863: (define-key mh-folder-mode-map "\C-d" 'mh-delete-msg-no-motion) ! 2864: (define-key mh-folder-mode-map "p" 'mh-previous-undeleted-msg) ! 2865: (define-key mh-folder-mode-map "n" 'mh-next-undeleted-msg) ! 2866: (define-key mh-folder-mode-map "o" 'mh-refile-msg) ! 2867: ! 2868: ! 2869: ;;; Build the letter-mode keymap: ! 2870: ! 2871: (define-key mh-letter-mode-map "\C-c\C-f\C-b" 'mh-to-field) ! 2872: (define-key mh-letter-mode-map "\C-c\C-f\C-c" 'mh-to-field) ! 2873: (define-key mh-letter-mode-map "\C-c\C-f\C-f" 'mh-to-fcc) ! 2874: (define-key mh-letter-mode-map "\C-c\C-f\C-s" 'mh-to-field) ! 2875: (define-key mh-letter-mode-map "\C-c\C-f\C-t" 'mh-to-field) ! 2876: (define-key mh-letter-mode-map "\C-c\C-fb" 'mh-to-field) ! 2877: (define-key mh-letter-mode-map "\C-c\C-fc" 'mh-to-field) ! 2878: (define-key mh-letter-mode-map "\C-c\C-ff" 'mh-to-fcc) ! 2879: (define-key mh-letter-mode-map "\C-c\C-fs" 'mh-to-field) ! 2880: (define-key mh-letter-mode-map "\C-c\C-ft" 'mh-to-field) ! 2881: (define-key mh-letter-mode-map "\C-c\C-q" 'mh-fully-kill-draft) ! 2882: (define-key mh-letter-mode-map "\C-c\C-w" 'mh-check-whom) ! 2883: (define-key mh-letter-mode-map "\C-c\C-i" 'mh-insert-letter) ! 2884: (define-key mh-letter-mode-map "\C-c\C-y" 'mh-yank-cur-msg) ! 2885: (define-key mh-letter-mode-map "\C-c\C-s" 'mh-insert-signature) ! 2886: (define-key mh-letter-mode-map "\C-c\C-c" 'mh-send-letter) ! 2887: ! 2888: ! 2889: ;;; Build the pick-mode keymap: ! 2890: ! 2891: (define-key mh-pick-mode-map "\C-c\C-c" 'mh-do-pick-search) ! 2892: (define-key mh-pick-mode-map "\C-c\C-f\C-b" 'mh-to-field) ! 2893: (define-key mh-pick-mode-map "\C-c\C-f\C-c" 'mh-to-field) ! 2894: (define-key mh-pick-mode-map "\C-c\C-f\C-f" 'mh-to-field) ! 2895: (define-key mh-pick-mode-map "\C-c\C-f\C-s" 'mh-to-field) ! 2896: (define-key mh-pick-mode-map "\C-c\C-f\C-t" 'mh-to-field) ! 2897: (define-key mh-pick-mode-map "\C-c\C-fb" 'mh-to-field) ! 2898: (define-key mh-pick-mode-map "\C-c\C-fc" 'mh-to-field) ! 2899: (define-key mh-pick-mode-map "\C-c\C-ff" 'mh-to-field) ! 2900: (define-key mh-pick-mode-map "\C-c\C-fs" 'mh-to-field) ! 2901: (define-key mh-pick-mode-map "\C-c\C-ft" 'mh-to-field) ! 2902: (define-key mh-pick-mode-map "\C-c\C-w" 'mh-check-whom) ! 2903: ! 2904: ! 2905: ! 2906: ;;; For Gnu Emacs. ! 2907: ;;; Local Variables: *** ! 2908: ;;; eval: (put 'mh-when 'lisp-indent-hook 1) *** ! 2909: ;;; eval: (put 'with-mh-folder-updating 'lisp-indent-hook 1) *** ! 2910: ;;; End: ***
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.