|
|
1.1 ! root 1: ;;; mh-e.el (Version: 3.3c for GNU Emacs Version 17 and MH.5 and MH.6) ! 2: ! 3: ;;; Copyright (C) James Larus ([email protected], ucbvax!larus), 1985 ! 4: ;;; Please send suggestions and corrections to the above address. ! 5: ;;; ! 6: ;;; This file contains mh-e, a GNU Emacs front end to the MH mail system. ! 7: ! 8: ! 9: ;; GNU Emacs is distributed in the hope that it will be useful, ! 10: ;; but without any warranty. No author or distributor ! 11: ;; accepts responsibility to anyone for the consequences of using it ! 12: ;; or for whether it serves any particular purpose or works at all, ! 13: ;; unless he says so in writing. ! 14: ! 15: ;; Everyone is granted permission to copy, modify and redistribute ! 16: ;; GNU Emacs, but only under the conditions described in the ! 17: ;; document "GNU Emacs copying permission notice". An exact copy ! 18: ;; of the document is supposed to have been given to you along with ! 19: ;; GNU Emacs so that you can know how you may redistribute it all. ! 20: ;; It should be in a file named COPYING. Among other things, the ! 21: ;; copyright notice and this notice must be preserved on all copies. ! 22: ! 23: ! 24: ;;; Original version for Gosling emacs by Brian Reid, Stanford, 1982. ! 25: ;;; Modified by James Larus, BBN, July 1984 and UCB, 1984 & 1985. ! 26: ;;; Rewritten for GNU Emacs, James Larus 1985. ! 27: ! 28: ! 29: ;;; NB MH must have been compiled with the MHE compiler flag or several ! 30: ;;; features necessary to this program will be missing. ! 31: ! 32: ! 33: ! 34: ;;; Constants: ! 35: ! 36: ;;; Set for local environment: ! 37: ;;;* These are now in paths.el. ! 38: ;;;(defvar mh-progs "/usr/new/mh/" "Directory containing MH commands") ! 39: ;;;(defvar mh-lib "/usr/new/lib/mh/" "Directory of MH library") ! 40: ! 41: ! 42: ;;; Mode hooks: ! 43: ! 44: (defvar mh-folder-mode-hook nil ! 45: "*Invoked in mh-folder-mode on a new folder.") ! 46: (defvar mh-letter-mode-hook nil ! 47: "*Invoked in mh-letter-mode on a new letter.") ! 48: (defvar mh-compose-letter-hook nil ! 49: "*Invoked in mh-compose-and-send-mail on an outgoing letter. It is passed ! 50: three arguments: TO recipients, SUBJECT, and CC recipients.") ! 51: ! 52: ! 53: ;;; Personal preferences: ! 54: ! 55: (defvar mh-auto-fill-letters t ! 56: "*Non-nil means invoke auto-fill-mode in draft messages.") ! 57: (defvar mh-clean-message-header nil ! 58: "*Non-nil means remove invisible header lines in messages.") ! 59: (defvar mh-use-mhl nil ! 60: "*Non-nil means use mhl to format messages.") ! 61: (defvar mh-lpr-command-format "lpr -p -J '%s'" ! 62: "*Format for Unix command line to print a message. The format should be ! 63: a unix command line, with the string \"%s\" where the folder and message ! 64: number should appear.") ! 65: (defvar mh-summary-height 4 ! 66: "*Number of lines in summary window.") ! 67: (defvar mh-ins-buf-prefix ">> " ! 68: "*String to put before each non-blank line of the the current message ! 69: as it is inserted in an outgoing letter.") ! 70: ! 71: ! 72: ;;; Real constants: ! 73: ! 74: (defvar mh-cmd-note 4 "Offset to insert notation") ! 75: (defvar mh-invisible-headers ! 76: "^Received: \\|^Message-Id: \\|^Remailed-\\|^Via: \\|^Mail-from: \\|\^Return-Path: \\|^In-Reply-To: \\|^Resent-" ! 77: "Regexp specifying headers that are not to be shown.") ! 78: (defvar mh-rejected-letter-start "^ ----- Unsent message follows -----$" ! 79: "Regexp specifying the beginning of the wrapper around a letter returned ! 80: by the mail system.") ! 81: (defvar mh-good-msg-regexp "^....[^D^]" ! 82: "Regexp specifiying the scan lines that are 'good' messages") ! 83: ! 84: ;;; Global variables: ! 85: ! 86: (defvar mh-user-path "" "User's mail folder.") ! 87: (defvar mh-last-destination nil "Destination of last "move" command.") ! 88: (defvar mh-folder-mode-map (make-sparse-keymap) "Keymap for MH folders.") ! 89: (defvar mh-letter-mode-map (make-sparse-keymap) "Keymap for composing mail.") ! 90: (defvar mh-pick-mode-map (make-sparse-keymap) "Keymap for searching folder.") ! 91: (defvar mh-folder-list nil "List of folder names for completion.") ! 92: ! 93: ;;; Macros and generic functions: ! 94: ! 95: (defmacro push (v l) ! 96: (list 'setq l (list 'cons v l))) ! 97: ! 98: (defmacro caar (l) ! 99: (list 'car (list 'car l))) ! 100: ! 101: (defmacro cadr (l) ! 102: (list 'car (list 'cdr l))) ! 103: ! 104: (defmacro cdar (l) ! 105: (list 'cdr (list 'car l))) ! 106: ! 107: (defmacro cddr (l) ! 108: (list 'cdr (list 'cdr l))) ! 109: ! 110: (defmacro when (pred &rest body) ! 111: (list 'cond (cons pred body))) ! 112: ! 113: (defun mapc (func list) ! 114: (while list ! 115: (funcall func (car list)) ! 116: (setq list (cdr list)))) ! 117: ! 118: ! 119: ! 120: ;;; Entry points: ! 121: ! 122: (defun mh-rmail (&optional arg) ! 123: "Inc(orporate) new mail if optional ARG omitted, or scan a MH mail box ! 124: if arg is present. This front end uses the MH mail system, which uses ! 125: different conventions from the usual mail system." ! 126: (interactive "P") ! 127: (mh-find-path) ! 128: (if (null mh-folder-list) ! 129: (setq mh-folder-list (mh-make-folder-list))) ! 130: (cond (arg ! 131: (let ((folder (mh-prompt-for-folder "mh" "+inbox" t)) ! 132: (range (read-string "Range [all]? "))) ! 133: (mh-scan-folder folder (if (equal range "") "all" range)) ! 134: (delete-other-windows))) ! 135: (t ! 136: (mh-inc-folder)))) ! 137: ! 138: ! 139: (defun mh-smail () ! 140: "Send mail using the MH mail system." ! 141: (interactive) ! 142: (mh-find-path) ! 143: (call-interactively 'mh-send)) ! 144: ! 145: ! 146: ! 147: ;;; User executable mh-e commands: ! 148: ! 149: ! 150: (defun mh-answer (&optional arg) ! 151: "Answer a letter. If given a non-nil ARGUMENT, then include the current ! 152: message in the reply." ! 153: (interactive "P") ! 154: (let ((msg-filename (mh-msg-filename)) ! 155: (msg (mh-get-msg-num t)) ! 156: (minibuffer-help-form ! 157: "from => Sender\n to => Sender and primary recipients\n cc => Sender and all recipients") ! 158: (folder mh-current-folder) ! 159: (show-buffer mh-show-buffer)) ! 160: (let ((reply-to (completing-read ! 161: "Reply to whom: " '(("from") ("to") ("cc")) nil t))) ! 162: (message "Composing a reply...") ! 163: (cond ((or (equal reply-to "from") (equal reply-to "")) ! 164: (apply 'mh-exec-cmd ! 165: (nconc ! 166: (list "repl" "-build" mh-current-folder msg "-nocc" "all") ! 167: (if arg (list "-filter" "mhl.reply"))))) ! 168: ((equal reply-to "to") ! 169: (apply 'mh-exec-cmd ! 170: (nconc ! 171: (list "repl" "-build" mh-current-folder msg "-cc" "to") ! 172: (if arg (list "-filter" "mhl.reply"))))) ! 173: ((equal reply-to "cc") ! 174: (apply 'mh-exec-cmd ! 175: (nconc ! 176: (list "repl" "-build" mh-current-folder msg "-cc" "all") ! 177: (if arg (list "-filter" "mhl.reply")))))) ! 178: ! 179: (mh-read-draft) ! 180: (delete-other-windows) ! 181: (when (or (zerop (buffer-size)) ! 182: (not (y-or-n-p "The file 'draft' exists. Use for reply? "))) ! 183: (erase-buffer) ! 184: (insert-file-contents (format "%sreply" mh-user-path)) ! 185: (delete-file (format "%sreply" mh-user-path))) ! 186: (set-buffer-modified-p nil) ! 187: ! 188: (let ((to (mh-get-field "To:")) ! 189: (subject (mh-get-field "Subject:")) ! 190: (cc (mh-get-field "Cc:"))) ! 191: (goto-char (point-min)) ! 192: (re-search-forward "^$" (point-max) nil) ! 193: (if (not arg) ! 194: (mh-display-msg msg msg-filename show-buffer)) ! 195: (mh-add-msg-to-seq msg "answered" t) ! 196: (message "Composing a reply...done") ! 197: (mh-compose-and-send-mail "" folder to subject cc "-" "Replied:"))))) ! 198: ! 199: ! 200: (defun my-apply-command-to-seq (command) ! 201: "Applies the next command to all messages in a sequence to be prompted for." ! 202: (interactive "k") ! 203: (funcall (key-binding command) (mh-read-seq "Apply to" mh-narrowed-to-seq))) ! 204: ! 205: ! 206: (defun mh-copy-msg (&optional seq) ! 207: "Copy specified message(s) to another folder without deleting them. ! 208: Optional argument is a SEQUENCE name to copy." ! 209: (interactive "P") ! 210: (let ((msgs (if seq seq (mh-get-msg-num t)))) ! 211: (mh-exec-cmd "refile" msgs "-link" "-src" mh-current-folder ! 212: (mh-prompt-for-folder "Copy to" "" t)) ! 213: (if seq ! 214: (mh-notate-seq msgs ?C mh-cmd-note) ! 215: (mh-notate ?C mh-cmd-note)))) ! 216: ! 217: ! 218: (defun mh-delete-msg (&optional seq) ! 219: "Marks the specified message(s) for later deletion. Optional argument is a ! 220: SEQUENCE name to delete." ! 221: (interactive "P") ! 222: (if seq ! 223: (mh-map-to-seq-msgs 'mh-delete-a-msg seq) ! 224: (mh-delete-a-msg)) ! 225: (mh-next-msg)) ! 226: ! 227: ! 228: (defun mh-delete-msg-from-seq (&optional arg) ! 229: "Deletes a message from a sequence or, if optional ARG is non-nil, deletes ! 230: the sequence." ! 231: (interactive "P") ! 232: (if arg ! 233: (mh-remove-seq (mh-read-seq "Delete")) ! 234: (mh-remove-msg-from-seq (mh-get-msg-num t) (mh-read-seq "Delete from"))) ! 235: (mh-next-msg)) ! 236: ! 237: ! 238: (defun mh-execute-commands () ! 239: "Process outstanding delete and move commands." ! 240: (interactive) ! 241: (if mh-narrowed-to-seq (mh-widen)) ! 242: (save-excursion ! 243: (mh-process-commands mh-current-folder)) ! 244: (delete-other-windows) ! 245: (setq mh-summarize t) ! 246: (setq mode-name "Mh-Summary") ! 247: (setq mode-line-format (mh-make-mode-line))) ! 248: ! 249: ! 250: (defun mh-extract-rejected-mail () ! 251: "Extract a letter returned by the mail system and make it resendable." ! 252: (interactive "") ! 253: (let ((msg-filename (format "%s%d" mh-folder-filename (mh-get-msg-num t)))) ! 254: (mh-read-draft) ! 255: (when (or (zerop (buffer-size)) ! 256: (not (y-or-n-p "The file 'draft' exists. Use it? "))) ! 257: (erase-buffer) ! 258: (insert-file-contents msg-filename)) ! 259: (goto-char (point-min)) ! 260: (re-search-forward mh-rejected-letter-start) ! 261: (forward-char 1) ! 262: (kill-region (point-min) (point)) ! 263: (let ((mh-invisible-headers "^Date:\\|^Received:\\|^Message-Id:\\|^From:")) ! 264: (mh-clean-msg-header (point-min))) ! 265: (goto-char (point-min)) ! 266: (switch-to-buffer mh-current-folder) ! 267: (mh-compose-and-send-mail "" mh-current-folder (mh-get-field "To") ! 268: (mh-get-field "From") (mh-get-field "cc")))) ! 269: ! 270: ! 271: (defun mh-forward (&optional seq) ! 272: "Forward a letter. Optional argument is a SEQUENCE of messages to forward." ! 273: (interactive "P") ! 274: (let ((to (read-string "To: ")) ! 275: (cc (read-string "Cc: ")) ! 276: (msg-filename (mh-msg-filename)) ! 277: (msg (if seq seq (mh-get-msg-num t))) ! 278: (folder mh-current-folder)) ! 279: (cond ((or (not (file-exists-p (format "%sdraft" mh-user-path))) ! 280: (y-or-n-p "The file 'draft' exists. Discard it? ")) ! 281: (mh-exec-cmd "forw" "-build" mh-current-folder msg) ! 282: (mh-read-draft) ! 283: (mh-insert-fields "To:" to "Cc:" cc) ! 284: (set-buffer-modified-p nil)) ! 285: (t ! 286: (mh-read-draft))) ! 287: (goto-char (point-min)) ! 288: (re-search-forward "^------- Forwarded Message") ! 289: (previous-line 1) ! 290: (narrow-to-region (point) (point-max)) ! 291: (let* ((subject (save-excursion (mh-get-field "From:"))) ! 292: (trim (string-match "<" subject)) ! 293: (forw-subject (save-excursion (mh-get-field "Subject:")))) ! 294: (if trim ! 295: (setq subject (substring subject 0 (1- trim)))) ! 296: (widen) ! 297: (save-excursion ! 298: (mh-insert-fields "Subject:" (format "[%s: %s]" subject forw-subject))) ! 299: (delete-other-windows) ! 300: (if seq ! 301: (mh-add-msg-list-to-seq (mh-seq-to-msgs seq) "forwarded" t) ! 302: (mh-add-msg-to-seq msg "forwarded" t)) ! 303: (mh-compose-and-send-mail "" folder to subject cc "F" "Forwarded:")))) ! 304: ! 305: ! 306: (defun mh-goto-msg (number &optional no-error-if-no-message) ! 307: "Position the cursor at message NUMBER. Do not signal an error if optional ! 308: ARG is t. Return non-nil if cursor is at message." ! 309: (interactive "nMessage number? ") ! 310: (let ((starting-place (point))) ! 311: (goto-char (point-min)) ! 312: (cond ((not (re-search-forward (mh-msg-search-pat number) nil t)) ! 313: (goto-char starting-place) ! 314: (if (not no-error-if-no-message) (error "No message %d " number)) ! 315: nil) ! 316: (t ! 317: (beginning-of-line) ! 318: (mh-maybe-show) ! 319: t)))) ! 320: ! 321: ! 322: (defun mh-inc-folder () ! 323: "inc(orporate) new mail into inbox." ! 324: (interactive) ! 325: (pop-to-buffer "+inbox") ! 326: (if (or (not (boundp 'mh-current-folder)) (null mh-current-folder)) ! 327: (mh-make-folder "+inbox")) ! 328: (if (mh-get-new-mail) ! 329: (mh-show))) ! 330: ! 331: ! 332: (defun mh-kill-folder () ! 333: "Removes the current folder." ! 334: (interactive) ! 335: (if (yes-or-no-p (format "Remove folder %s? " mh-current-folder)) ! 336: (let ((folder mh-current-folder)) ! 337: (switch-to-buffer-other-window " *mh-temp*") ! 338: (mh-exec-cmd "rmf" "-nointeractive" folder) ! 339: (mh-remove-folder-from-folder-list folder) ! 340: (message "Folder removed") ! 341: (kill-buffer folder)) ! 342: (message "Folder not removed"))) ! 343: ! 344: ! 345: (defun mh-list-folders () ! 346: "List mail folders." ! 347: (interactive) ! 348: (message "listing folders...") ! 349: (switch-to-buffer-other-window " *mh-temp*") ! 350: (erase-buffer) ! 351: (mh-exec-cmd-output "folders") ! 352: (goto-char (point-min)) ! 353: (message "listing folders...done")) ! 354: ! 355: ! 356: (defun mh-msg-is-in-seq () ! 357: "Displays the sequences that the current messages is in." ! 358: (interactive) ! 359: (let ((msg (mh-get-msg-num t)) ! 360: (l mh-seq-list) ! 361: (seqs "")) ! 362: (while l ! 363: (if (memq msg (cdar l)) ! 364: (setq seqs (format "%s %s" (symbol-name (caar l)) seqs))) ! 365: (setq l (cdr l))) ! 366: (message "Message %d is in sequences: %s" msg seqs))) ! 367: ! 368: ! 369: (defun mh-move-msg (&optional seq) ! 370: "Move specified message(s) to another folder. Optional argument is a ! 371: SEQUENCE of messages to refile." ! 372: (interactive "P") ! 373: (setq mh-last-destination ! 374: (cons 'move (intern (mh-prompt-for-folder "Destination" "" t)))) ! 375: (if seq ! 376: (mh-map-to-seq-msgs 'mh-move-a-msg seq (cdr mh-last-destination)) ! 377: (mh-move-a-msg (cdr mh-last-destination))) ! 378: (mh-next-msg)) ! 379: ! 380: ! 381: (defun mh-move-or-write-again () ! 382: "Move or write the current message to same folder or file as the last move ! 383: or write." ! 384: (interactive) ! 385: (if (null mh-last-destination) ! 386: (error "No previous move")) ! 387: (cond ((eq (car mh-last-destination) 'move) ! 388: (mh-move-a-msg (cdr mh-last-destination)) ! 389: (message "Destination folder: %s" (cdr mh-last-destination))) ! 390: (t ! 391: (mh-write-msg-to-file (cdr mh-last-destination)) ! 392: (message "Destination: %s" (cdr mh-last-destination)))) ! 393: (sit-for 3) ! 394: (mh-next-msg)) ! 395: ! 396: ! 397: (defun mh-narrow-to-seq () ! 398: "Restrict the display of the current folder to the messages in the sequence ! 399: to be prompted for. Use \\[mh-widen] to undo this command." ! 400: (interactive "") ! 401: (let ((seq (mh-read-seq "Narrow to")) ! 402: (eob (point-max)) ! 403: (buffer-read-only nil)) ! 404: (cond ((mh-seq-to-msgs seq) ! 405: (mh-copy-seq-to-point seq eob) ! 406: (narrow-to-region eob (point-max)) ! 407: (setq mode-line-format (mh-make-mode-line (symbol-name seq))) ! 408: (recenter) ! 409: (setq mh-narrowed-to-seq seq)) ! 410: (t ! 411: (error "No messages in sequence `%s'" (symbol-name seq)))))) ! 412: ! 413: ! 414: (defun mh-next-line (&optional arg) ! 415: "Move to next undeleted message in window and display body if summary ! 416: flag set." ! 417: (interactive "p") ! 418: (forward-line (if arg arg 1)) ! 419: (setq mh-next-direction 'forward) ! 420: (cond ((re-search-forward mh-good-msg-regexp nil 0 arg) ! 421: (beginning-of-line) ! 422: (mh-maybe-show)) ! 423: (t ! 424: (forward-line -1) ! 425: (sit-for 2) ! 426: (message "No more messages")))) ! 427: ! 428: ! 429: (defun mh-renumber-folder () ! 430: "Renumber messages in folder to be 1..N." ! 431: (interactive) ! 432: (message "packing buffer...") ! 433: (mh-pack-folder) ! 434: (mh-unmark-all-headers nil) ! 435: (mh-goto-cur-msg) ! 436: (message "packing buffer...done")) ! 437: ! 438: ! 439: (defun mh-page-digest () ! 440: "Advance displayed message to next digested message." ! 441: (interactive) ! 442: (save-excursion ! 443: (switch-to-buffer-other-window mh-show-buffer) ! 444: (move-to-window-line nil) ! 445: (let ((case-fold-search nil)) ! 446: (when (not (search-forward "\nFrom:" nil t)) ! 447: (other-window -1) ! 448: (error "No more messages"))) ! 449: (recenter 0) ! 450: (other-window -1))) ! 451: ! 452: ! 453: (defun mh-page-msg (&optional arg) ! 454: (interactive "P") ! 455: (scroll-other-window arg)) ! 456: ! 457: ! 458: (defun mh-previous-line (&optional arg) ! 459: "Move to previous message in window and display body if summary flag set." ! 460: (interactive "p") ! 461: (setq mh-next-direction 'backward) ! 462: (if (not (re-search-backward mh-good-msg-regexp nil 0 arg)) ! 463: (message "Beginning of messages") ! 464: (mh-maybe-show))) ! 465: ! 466: ! 467: (defun mh-previous-page () ! 468: "Page the displayed message backwards." ! 469: (interactive) ! 470: (save-excursion ! 471: (switch-to-buffer-other-window mh-show-buffer) ! 472: (unwind-protect ! 473: (scroll-down nil) ! 474: (other-window -1)))) ! 475: ! 476: ! 477: (defun mh-print-msg (&optional seq) ! 478: "Print specified message(s) on a line printer. Optional argument is a ! 479: SEQUENCE of messages to print." ! 480: (interactive "P") ! 481: (let ((msgs (if seq ! 482: (reverse (mh-seq-to-msgs seq)) ! 483: (list (mh-get-msg-num t))))) ! 484: (message "printing message...") ! 485: (call-process shell-file-name nil 0 nil "-c" ! 486: (if seq ! 487: (format "(scan -clear %s ; %smhl -nobell -clear %s) | %s" ! 488: (mapconcat (function (lambda (msg) msg)) msgs " ") ! 489: mh-lib ! 490: (mh-msg-filenames msgs mh-folder-filename) ! 491: (format mh-lpr-command-format ! 492: (if seq ! 493: "Mail" ! 494: (format "%s/%d" mh-current-folder ! 495: (mh-get-msg-num t))))) ! 496: (format "%smhl -nobell -clear %s | %s" ! 497: mh-lib ! 498: (mh-msg-filenames msgs mh-folder-filename) ! 499: (format mh-lpr-command-format ! 500: (if seq ! 501: "Mail" ! 502: (format "%s/%d" mh-current-folder ! 503: (mh-get-msg-num t))))))) ! 504: (if seq ! 505: (mh-notate-seq msgs ?P mh-cmd-note) ! 506: (mh-notate ?P mh-cmd-note)) ! 507: (mh-add-msg-list-to-seq msgs 'printed t) ! 508: (message "printing message...done"))) ! 509: ! 510: ! 511: (defun mh-put-msg-in-seq (&optional arg) ! 512: "Add a message to a sequence or, if optional ARG is non-nil, add the ! 513: messages from a sequence to another sequence." ! 514: (interactive "P") ! 515: (if arg ! 516: (mh-add-msg-list-to-seq (mh-seq-to-msgs ! 517: (mh-read-seq "Add messages from")) ! 518: (mh-read-seq "to")) ! 519: (mh-add-msg-to-seq (mh-get-msg-num t) (mh-read-seq "Add to"))) ! 520: (mh-next-msg)) ! 521: ! 522: ! 523: (defun mh-rescan-folder (&optional arg) ! 524: "Rescan a folder after optionally processing the outstanding commands. If ! 525: the optional argument is non-nil, then prompt for the range of messages to ! 526: display, otherwise assume the whole buffer." ! 527: (interactive "P") ! 528: (if (and (or mh-delete-list mh-move-list) ! 529: (y-or-n-p "Process commands? ")) ! 530: (mh-process-commands mh-current-folder)) ! 531: (setq mh-next-direction 'forward) ! 532: (mh-scan-folder mh-current-folder ! 533: (if arg (read-string "Range [all]? ") "all"))) ! 534: ! 535: ! 536: (defun mh-redistribute (to cc) ! 537: "Redistribute a letter." ! 538: (interactive "sRedist-To: \nsRedist-Cc: ") ! 539: (let ((msg-filename (mh-msg-filename)) ! 540: (msg (mh-get-msg-num t)) ! 541: (folder mh-current-folder)) ! 542: (save-window-excursion ! 543: (mh-read-draft) ! 544: (when (or (zerop (buffer-size)) ! 545: (not (y-or-n-p ! 546: "The file 'draft' exists. Redistribute old version? "))) ! 547: (erase-buffer) ! 548: (insert-file-contents msg-filename)) ! 549: (re-search-forward "^$\\|^---") ! 550: (insert "Resent-To: " to "\n") ! 551: (if (not (equal cc "")) ! 552: (insert "Resent-cc: " cc "\n")) ! 553: (let ((mh-invisible-headers "^Message-Id:\\|^Received:\\|Return-Path:")) ! 554: (mh-clean-msg-header (point-min))) ! 555: (save-buffer) ! 556: (message "Redistributing...") ! 557: (call-process "/bin/sh" nil 0 nil "-c" ! 558: (format "mhdist=1 mhaltmsg=%s %s/send -push %s/draft" ! 559: msg-filename mh-progs mh-user-path)) ! 560: (mh-annotate-msg msg folder "R" ! 561: "-component" "Resent:" ! 562: "-text" (format "\"%s %s\"" to cc)) ! 563: (message "Redistributing...done")))) ! 564: ! 565: ! 566: (defun mh-write-msg-to-file (file) ! 567: "Append the current message to the end of a file." ! 568: (interactive "FSave message in file: ") ! 569: (setq mh-last-destination (cons 'write file)) ! 570: (call-process shell-file-name nil 0 nil "-c" ! 571: (format "cat %s >> %s " (mh-msg-filename) file))) ! 572: ! 573: ! 574: (defun mh-search-folder () ! 575: "Search the current folder for messages matching a pattern." ! 576: (interactive) ! 577: (let ((folder mh-current-folder)) ! 578: (switch-to-buffer-other-window "pick-pattern") ! 579: (if (or (zerop (buffer-size)) ! 580: (not (y-or-n-p "Reuse pattern? "))) ! 581: (mh-make-pick-template) ! 582: (message "")) ! 583: (setq mh-searching-folder folder))) ! 584: ! 585: ! 586: (defun mh-send (to cc subject) ! 587: "Compose and send a letter." ! 588: (interactive "sTo: \nsCc: \nsSubject: ") ! 589: (let ((folder (if (boundp 'mh-current-folder) mh-current-folder))) ! 590: (message "Composing a message...") ! 591: (mh-read-draft) ! 592: (delete-other-windows) ! 593: (when (or (zerop (buffer-size)) ! 594: (not (y-or-n-p "The file 'draft' exists. Use it? "))) ! 595: (erase-buffer) ! 596: (if (file-exists-p (format "%scomponents" mh-user-path)) ! 597: (insert-file-contents (format "%scomponents" mh-user-path)) ! 598: (if (file-exists-p (format "%scomponents" mh-lib)) ! 599: (insert-file-contents (format "%scomponents" mh-lib)) ! 600: (error "Can't find components"))) ! 601: (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc) ! 602: (set-buffer-modified-p nil) ! 603: (goto-char (point-max)) ! 604: (message "Composing a message...done")) ! 605: (mh-compose-and-send-mail "" folder to subject cc))) ! 606: ! 607: ! 608: (defun mh-show () ! 609: "Show message indicated by cursor in scan buffer." ! 610: (interactive) ! 611: (setq mh-summarize nil) ! 612: (setq mode-name "Mh-Show") ! 613: (let ((msg-num (mh-get-msg-num t)) ! 614: (folder mh-current-folder)) ! 615: (mh-display-msg msg-num (mh-msg-filename) mh-show-buffer) ! 616: ! 617: ;; These contortions are to force the summary line to be the top window. ! 618: (switch-to-buffer-other-window folder) ! 619: (delete-other-windows) ! 620: (switch-to-buffer-other-window mh-show-buffer) ! 621: (switch-to-buffer-other-window folder) ! 622: (shrink-window (- (window-height) mh-summary-height)) ! 623: (recenter 1) ! 624: (push msg-num mh-seen-list))) ! 625: ! 626: ! 627: (defun mh-sort-folder () ! 628: "Sort the messages in the current folder by date." ! 629: (interactive "") ! 630: (mh-process-commands mh-current-folder) ! 631: (setq mh-next-direction 'forward) ! 632: (message "sorting folder...") ! 633: (mh-exec-cmd "sortm" mh-current-folder) ! 634: (message "sorting folder...done") ! 635: (mh-scan-folder mh-current-folder "all")) ! 636: ! 637: ! 638: (defun mh-toggle-summarize () ! 639: "Turn the summary mode of displaying messages on or off." ! 640: (interactive) ! 641: (setq mh-summarize (not mh-summarize)) ! 642: (cond (mh-summarize ! 643: (delete-other-windows) ! 644: (setq mode-name "Mh-Summarize") ! 645: (recenter (/ (window-height) 2))) ! 646: (t ! 647: (setq mode-name "Mh-Show") ! 648: (mh-show)))) ! 649: ! 650: ! 651: (defun mh-undo (&optional seq) ! 652: "Undo the deletion or move of the specified message(s). Optional argument ! 653: is a name of a sequence of messages to apply undo to." ! 654: (interactive "P") ! 655: (cond ((looking-at "^....D") ! 656: (cond (seq ! 657: (mapc (function (lambda (msg) ! 658: (setq mh-delete-list ! 659: (delq msg mh-delete-list)) ! 660: (mh-remove-msg-from-seq msg 'deleted))) ! 661: (mh-seq-to-msgs seq)) ! 662: (mh-notate-seq seq ? mh-cmd-note)) ! 663: (t ! 664: (let ((msg (mh-get-msg-num t))) ! 665: (setq mh-delete-list (delq msg mh-delete-list)) ! 666: (mh-remove-msg-from-seq msg 'deleted) ! 667: (mh-notate ? mh-cmd-note))))) ! 668: ! 669: ((looking-at "^....\\^") ! 670: (cond (seq ! 671: (mapc (function (lambda (msg) ! 672: (mapc (function ! 673: (lambda (dest) ! 674: (mh-remove-msg-from-seq msg dest))) ! 675: mh-move-list))) ! 676: (mh-seq-to-msgs seq)) ! 677: (mh-notate-seq seq ? mh-cmd-note)) ! 678: (t ! 679: (let ((msg (mh-get-msg-num t))) ! 680: (mapc (function (lambda (dest) ! 681: (mh-remove-msg-from-seq msg dest))) ! 682: mh-move-list) ! 683: (mh-notate ? mh-cmd-note))))) ! 684: ! 685: (t nil))) ! 686: ! 687: ! 688: (defun mh-undo-folder () ! 689: "Undo all commands in current folder." ! 690: (interactive "") ! 691: (cond ((yes-or-no-p "Undo all commands in folder? ") ! 692: (setq mh-delete-list nil ! 693: mh-move-list nil ! 694: mh-seq-list nil ! 695: mh-next-direction 'forward) ! 696: (mh-unmark-all-headers t)) ! 697: (t ! 698: (message "Commands not undone.")))) ! 699: ! 700: ! 701: (defun mh-visit-folder (&optional arg) ! 702: "Visit a new folder. If optional argument is non-nil, then prompt for the ! 703: range of messages, otherwise scan the whole buffer." ! 704: (interactive "p") ! 705: (let ((folder (mh-prompt-for-folder "Visit" "" t)) ! 706: (range (if arg (read-string "Range [all]? ") "all"))) ! 707: (mh-scan-folder folder (if (equal range "") "all" range)) ! 708: (delete-other-windows))) ! 709: ! 710: ! 711: (defun mh-widen () ! 712: "Remove restrictions from the current folder, thereby showing all messages." ! 713: (interactive "") ! 714: (let ((buffer-read-only nil)) ! 715: (delete-region (point-min) (point-max)) ! 716: (widen) ! 717: (setq mode-line-format (mh-make-mode-line))) ! 718: (setq mh-narrowed-to-seq nil)) ! 719: ! 720: ! 721: ! 722: ;;; Support routines. ! 723: ! 724: (defun mh-delete-a-msg () ! 725: "Delete the message pointed to by the cursor." ! 726: (let ((msg (mh-get-msg-num t))) ! 727: (if (looking-at "....\\^") ! 728: (error "Message %d already moved. Undo move before deleting." msg)) ! 729: (push msg mh-delete-list) ! 730: (mh-add-msg-to-seq msg 'deleted t) ! 731: (mh-notate ?D mh-cmd-note))) ! 732: ! 733: ! 734: (defun mh-move-a-msg (destination) ! 735: "Move the message pointed to by the cursor." ! 736: (if (looking-at "....D") ! 737: (error "Message %d is already deleted. Undo delete before moving." ! 738: (mh-get-msg-num nil)) ! 739: (let ((msg (mh-get-msg-num t))) ! 740: (if (not (memq destination mh-move-list)) ! 741: (push destination mh-move-list)) ! 742: (mh-add-msg-to-seq msg destination t) ! 743: (mh-notate ?^ mh-cmd-note)))) ! 744: ! 745: ! 746: (defun mh-display-msg (msg-num msg-filename show-buffer) ! 747: "Displays the message NUMBER and PATHNAME in BUFFER." ! 748: (if (not (file-exists-p msg-filename)) ! 749: (error "Message %d does not exist." msg-num)) ! 750: (switch-to-buffer show-buffer) ! 751: (buffer-flush-undo (current-buffer)) ! 752: (when (not (equal msg-filename buffer-file-name)) ! 753: ;; Buffer does not yet contain message. ! 754: (setq buffer-file-name msg-filename) ! 755: (erase-buffer) ! 756: (unlock-buffer) ! 757: (if mh-use-mhl ! 758: (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" msg-filename) ! 759: (insert-file-contents msg-filename)) ! 760: (goto-char (point-min)) ! 761: (cond (mh-clean-message-header ! 762: (mh-clean-msg-header (point-min)) ! 763: (goto-char (point-min))) ! 764: (t ! 765: (let ((case-fold-search t)) ! 766: (re-search-forward "^To:\\|^From:\\|^Subject:\\|^Date:" nil t) ! 767: (beginning-of-line) ! 768: (recenter 0)))) ! 769: (set-buffer-modified-p nil) ! 770: (setq mode-line-format ! 771: (concat "{%b} %[%p of " folder "/" msg-num "%] %M")))) ! 772: ! 773: ! 774: (defun mh-clean-msg-header (start) ! 775: "Flush extraneous lines in a message header. The variable ! 776: mh-invisible-headers contains a regular expression specifying these lines." ! 777: (save-restriction ! 778: (goto-char start) ! 779: (search-forward "\n\n" nil t) ! 780: (narrow-to-region start (point)) ! 781: (goto-char (point-min)) ! 782: (while (re-search-forward mh-invisible-headers nil t) ! 783: (beginning-of-line) ! 784: (kill-line 1) ! 785: (while (looking-at "^[ \t]+") ! 786: (beginning-of-line) ! 787: (kill-line 1))) ! 788: (unlock-buffer))) ! 789: ! 790: ! 791: (defun mh-read-draft () ! 792: "Read draft file into draft buffer. Avoids errors even if disk file has been ! 793: modified underneath the buffer. Causes an error if the folder is modified and ! 794: the user does not want to change it." ! 795: (switch-to-buffer "draft") ! 796: (if (buffer-modified-p) ! 797: (if (y-or-n-p "Draft is modified; kill anyways? ") ! 798: (set-buffer-modified-p nil) ! 799: (error "Draft is not killed."))) ! 800: (kill-buffer "draft") ! 801: (switch-to-buffer-other-window ! 802: (find-file-noselect (format "%sdraft" mh-user-path)))) ! 803: ! 804: ! 805: (defun mh-next-msg () ! 806: "Move backward or forward to the next message in the buffer." ! 807: (if (eq mh-next-direction 'forward) ! 808: (mh-next-line 1) ! 809: (mh-previous-line 1))) ! 810: ! 811: ! 812: (defun mh-maybe-show () ! 813: "If the scan listing is not summarized, show the message pointed to ! 814: by the cursor." ! 815: (if (not mh-summarize) (mh-show))) ! 816: ! 817: ! 818: ! 819: ;;; The folder data abstraction. ! 820: ! 821: (defun mh-make-folder (name) ! 822: "Create and initialize a new mail folder called NAME and make it the ! 823: current folder." ! 824: (switch-to-buffer name) ! 825: (buffer-flush-undo (current-buffer)) ! 826: (kill-all-local-variables) ! 827: (setq buffer-read-only nil) ! 828: (erase-buffer) ! 829: (make-local-vars ! 830: 'mh-current-folder name ;Name of folder ! 831: 'mh-show-buffer (format "show-%s" name) ; Buffer that displays messages ! 832: 'mh-folder-filename ; e.g. /usr/foobar/Mail/inbox/ ! 833: (format "%s%s/" mh-user-path (substring name 1)) ! 834: 'mh-summarize t ; Show scan list only? ! 835: 'mh-next-seq-num 0 ; Index of free sequence id ! 836: 'mh-delete-list nil ; List of msgs nums to delete ! 837: 'mh-move-list nil ; List of folder names in mh-seq-list ! 838: 'mh-seq-list nil ; Alist of seq . msgs nums ! 839: 'mh-seen-list nil ; List of displayed messages ! 840: 'mh-next-direction 'forward ; Direction to move to next message ! 841: 'mh-narrowed-to-seq nil) ; Sequence display is narrowed to ! 842: (mh-folder-mode) ! 843: (setq buffer-read-only t) ! 844: (setq mode-name "Mh-Summarize")) ! 845: ! 846: ! 847: (defun make-local-vars (&rest pairs) ! 848: "Takes VARIABLE-VALUE pairs and makes local variables initialized to the ! 849: value." ! 850: (while pairs ! 851: (make-local-variable (car pairs)) ! 852: (set (car pairs) (cadr pairs)) ! 853: (setq pairs (cddr pairs)))) ! 854: ! 855: ! 856: (defun mh-folder-mode () ! 857: "Major mode for \"editing\" an MH folder scan listing. ! 858: Messages can be marked for refiling and deletion. However, both actions ! 859: are defered until you request execution with \\[mh-execute-commands]. ! 860: \\{mh-folder-mode-map} ! 861: A prefix argument (\\[universal-argument]) to delete, move, list, or undo applies the action to a message sequence. ! 862: ! 863: Variables controlling mh-e operation are (defaults in parentheses): ! 864: ! 865: mh-auto-fill-letters (t) ! 866: Non-nil means invoke auto-fill-mode in draft messages. ! 867: ! 868: mh-clean-message-header (nil) ! 869: Non-nil means remove header lines matching the regular expression ! 870: specified in mh-invisible-headers from messages. ! 871: ! 872: mh-use-mhl (nil) ! 873: Non-nil means use mhl to format displayed messages. ! 874: ! 875: mh-lpr-command-format (\"lpr -p -J '%s'\") ! 876: Format for command used to print a message on a system printer. ! 877: ! 878: mh-summary-height (4) ! 879: Number of lines in the summary window. ! 880: ! 881: mh-ins-buf-prefix (\">> \") ! 882: String to insert before each non-blank line of a message as it is ! 883: inserted in a letter being composed." ! 884: ! 885: (use-local-map mh-folder-mode-map) ! 886: (setq major-mode 'mh-folder-mode) ! 887: (setq mode-name "mh-folder") ! 888: (if (and (boundp 'mh-folder-mode-hook) mh-folder-mode-hook) ! 889: (funcall mh-folder-mode-hook))) ! 890: ! 891: ! 892: (defun mh-scan-folder (folder range) ! 893: "Scan the folder FOLDER over the range RANGE. Return in the folder." ! 894: (cond ((null (get-buffer folder)) ! 895: (mh-make-folder folder)) ! 896: (t ! 897: (if (or mh-delete-list mh-move-list mh-seq-list) ! 898: (if (y-or-n-p "Process commands?") ! 899: (mh-process-commands folder) ! 900: (mh-undo-folder))) ! 901: (switch-to-buffer-other-window folder))) ! 902: (mh-regenerate-headers range) ! 903: (when (= (count-lines (point-min) (point-max)) 0) ! 904: (if (equal range "all") ! 905: (message "Folder %s is empty" folder) ! 906: (message "No messages in %s, range %s" folder range)) ! 907: (sit-for 5)) ! 908: (setq mode-line-format (mh-make-mode-line)) ! 909: (mh-unmark-all-headers nil) ! 910: (mh-goto-cur-msg)) ! 911: ! 912: ! 913: (defun mh-regenerate-headers (range) ! 914: "Replace buffer with scan of its contents over range RANGE." ! 915: (let ((buffer-read-only nil)) ! 916: (message (format "scanning %s..." (buffer-name))) ! 917: (delete-other-windows) ! 918: (erase-buffer) ! 919: (mh-exec-cmd-output "scan" "-noclear" "-noheader" (buffer-name) range) ! 920: (goto-char (point-min)) ! 921: (cond ((looking-at "scan: no messages in") ! 922: (keep-lines "^[ ]*[0-9]")) ; Flush random scan lines ! 923: ((looking-at "scan: ")) ; Keep error messages ! 924: (t ! 925: (keep-lines "^[ ]*[0-9]"))) ; Flush random scan lines ! 926: (message (format "scanning %s...done" (buffer-name))))) ! 927: ! 928: ! 929: (defun mh-get-new-mail () ! 930: "Read new mail into the current buffer. Return t if there was new mail, ! 931: nil otherwise. Leave cursor in current buffer." ! 932: (let ((buffer-read-only nil) ! 933: (point-before-inc (point))) ! 934: (message (format "inc %s..." (buffer-name))) ! 935: (mh-unmark-all-headers nil) ! 936: (setq mh-next-direction 'forward) ! 937: (flush-lines "^inc:\\|^scan:") ; Kill old error messages ! 938: (goto-char (point-max)) ! 939: (let ((start-of-inc (point))) ! 940: (mh-exec-cmd-output "inc") ! 941: (goto-char start-of-inc) ! 942: (message (format "inc %s...done" (buffer-name))) ! 943: (cond ((looking-at "inc: no mail") ! 944: (keep-lines "^[ ]*[0-9]") ; Flush random scan lines ! 945: (setq mode-line-format (mh-make-mode-line)) ! 946: (goto-char point-before-inc) ! 947: (message "No new mail.") ! 948: nil) ! 949: ((looking-at "inc:") ; Error messages ! 950: (setq mode-line-format (mh-make-mode-line)) ! 951: (goto-char point-before-inc) ! 952: (message "inc error") ! 953: nil) ! 954: (t ! 955: (keep-lines "^[ ]*[0-9]") ! 956: (setq mode-line-format (mh-make-mode-line)) ! 957: (mh-goto-cur-msg) ! 958: t))))) ! 959: ! 960: ! 961: (defun mh-make-mode-line (&optional annotation) ! 962: "Returns a string for mode-line-format. The optional ANNOTATION string is ! 963: displayed after the folder's name." ! 964: (save-excursion ! 965: (goto-char (point-min)) ! 966: (let ((lines (count-lines (point-min) (point-max)))) ! 967: (let* ((first (mh-get-msg-num nil)) ! 968: (case-fold-search nil) ! 969: (current (and (re-search-forward "....\\+" nil t) ! 970: (mh-get-msg-num nil)))) ! 971: (goto-char (point-max)) ! 972: (previous-line 1) ! 973: (let ((last (mh-get-msg-num nil))) ! 974: (format "{%%b%s} [%d messages%s%s] (%%p%%%% - %%m) %%M" ! 975: (if annotation (format "/%s" annotation) "") ! 976: lines ! 977: (if (> lines 0) ! 978: (format " (%d - %d)" first last) ! 979: "") ! 980: (if current ! 981: (format " cur = %d" current) ! 982: ""))))))) ! 983: ! 984: ! 985: (defun mh-unmark-all-headers (remove-all-flags) ! 986: "This function removes all + flags from the headers, and if called ! 987: with a non-nil argument, removes all D and ^ flags too." ! 988: (let ((buffer-read-only nil) ! 989: (case-fold-search nil)) ! 990: (goto-char (point-min)) ! 991: (while (if remove-all-flags ! 992: (re-search-forward "^....\\D\\|^....\\^\\|^....\\+\\|.....%" ! 993: nil t) ! 994: (re-search-forward "^....\\+" nil t)) ! 995: (delete-backward-char 1) ! 996: (insert " ")))) ! 997: ! 998: ! 999: (defun mh-goto-cur-msg () ! 1000: "Position the cursor at the current message." ! 1001: (let ((curmsg (mh-get-cur-msg mh-current-folder))) ! 1002: (cond ((or (zerop curmsg) (not (mh-goto-msg curmsg t))) ! 1003: (goto-char (point-max)) ! 1004: (forward-line -1) ! 1005: (mh-maybe-show) ! 1006: (message "No current message")) ! 1007: (t ! 1008: (mh-notate ?+ 4) ! 1009: (recenter 0))))) ! 1010: ! 1011: ! 1012: (defun mh-pack-folder () ! 1013: "Closes and packs the current folder." ! 1014: (let ((buffer-read-only nil)) ! 1015: (message "closing folder...") ! 1016: (mh-process-commands mh-current-folder) ! 1017: (message "packing folder...") ! 1018: (mh-exec-cmd-quiet 0 "folder" mh-current-folder "-pack") ! 1019: (mh-regenerate-headers "all") ! 1020: (message "packing done")) ! 1021: (setq mode-line-format (mh-make-mode-line))) ! 1022: ! 1023: ! 1024: (defun mh-process-commands (buffer) ! 1025: "Process outstanding commands for the buffer BUFFER." ! 1026: (message "Processing deletes and moves...") ! 1027: (switch-to-buffer buffer) ! 1028: (let ((buffer-read-only nil)) ! 1029: ;; Sequences must be first ! 1030: (mh-process-seq-commands mh-seq-list) ! 1031: ;; Update the unseen sequence ! 1032: (if mh-seen-list ! 1033: (let ((unseen-seq (mh-get-profile-field "Unseen-Sequence:"))) ! 1034: (if (null unseen-seq) ; For MH.5 ! 1035: (setq unseen-seq "unseen")) ! 1036: (apply 'mh-exec-cmd-quiet ! 1037: (nconc (list 0 "mark" mh-current-folder) ! 1038: mh-seen-list ! 1039: (list "-sequence" unseen-seq "-delete"))))) ! 1040: ! 1041: ;; Then refile messages ! 1042: (mapc (function ! 1043: (lambda (dest) ! 1044: (let ((msgs (mh-seq-to-msgs dest))) ! 1045: (when msgs ! 1046: (mh-delete-scan-msgs msgs) ! 1047: (apply 'mh-exec-cmd ! 1048: (nconc (cons "refile" msgs) ! 1049: (list "-src" buffer (symbol-name dest)))))))) ! 1050: mh-move-list) ! 1051: ! 1052: ;; Now delete messages ! 1053: (when mh-delete-list ! 1054: (apply 'mh-exec-cmd ! 1055: (nconc (list "rmm" (format "%s" buffer)) mh-delete-list)) ! 1056: (mh-delete-scan-msgs mh-delete-list)) ! 1057: ! 1058: ;; Mark as cur message. ! 1059: (cond ((mh-get-msg-num nil) ! 1060: (mh-exec-cmd "mark" mh-current-folder (mh-get-msg-num nil) ! 1061: "-seq" "cur" "-add" "-zero")) ! 1062: ((> (buffer-size) 0) ; Some messages left in folder. ! 1063: (mh-exec-cmd "mark" mh-current-folder ! 1064: "-seq" "cur" "-delete" "all"))) ! 1065: ! 1066: (save-excursion ! 1067: (switch-to-buffer mh-show-buffer) ! 1068: (setq buffer-file-name nil)) ; Invalidate buffer file cache ! 1069: ! 1070: (setq mh-delete-list nil ! 1071: mh-move-list nil ! 1072: mh-seq-list nil ! 1073: mh-seen-list nil)) ! 1074: (message "Processing deletes and moves...done")) ! 1075: ! 1076: ! 1077: (defun mh-delete-scan-msgs (msgs) ! 1078: "Delete the scan listing lines for each of the msgs in the LIST." ! 1079: (save-excursion ! 1080: (goto-char (point-min)) ! 1081: (while msgs ! 1082: (flush-lines (mh-msg-search-pat (car msgs))) ! 1083: (setq msgs (cdr msgs))))) ! 1084: ! 1085: ! 1086: ! 1087: ;;; A mode for composing and sending a message. ! 1088: ! 1089: (defun mh-letter-mode () ! 1090: "Mode for composing letters in mh-e. ! 1091: \\{mh-letter-mode-map}" ! 1092: (text-mode) ! 1093: (if mh-auto-fill-letters ! 1094: (auto-fill-mode 1)) ! 1095: (make-local-variable 'paragraph-start) ! 1096: (setq paragraph-start (concat "^[ \t]*[-_][-_][-_]+$\\|" paragraph-start)) ! 1097: (make-local-variable 'paragraph-separate) ! 1098: (setq paragraph-separate ! 1099: (concat "^[ \t]*[-_][-_][-_]+$\\|" paragraph-separate)) ! 1100: (use-local-map mh-letter-mode-map) ! 1101: (setq major-mode 'mh-letter-mode) ! 1102: (setq mode-name "mh-letter") ! 1103: (if (and (boundp 'mh-letter-mode-hook) mh-letter-mode-hook) ! 1104: (funcall mh-letter-mode-hook))) ! 1105: ! 1106: ! 1107: (defun mh-to-to () ! 1108: "Move point to end of To: field." ! 1109: (interactive) ! 1110: (expand-abbrev) ! 1111: (mh-position-on-field "To:" t)) ! 1112: ! 1113: ! 1114: (defun mh-to-subject () ! 1115: "Move point to end of Subject: field. Creates the field if necessary" ! 1116: (interactive) ! 1117: (expand-abbrev) ! 1118: (when (not (mh-position-on-field "Subject:" t)) ! 1119: (mh-position-on-field "To:" nil) ! 1120: (insert-string "\nSubject: "))) ! 1121: ! 1122: ! 1123: (defun mh-to-cc () ! 1124: "Move point to end of Cc: field. Creates the field if necessary" ! 1125: (interactive) ! 1126: (expand-abbrev) ! 1127: (when (not (mh-position-on-field "Cc:" t)) ! 1128: (mh-position-on-field "To:" nil) ! 1129: (insert-string "\nCc: "))) ! 1130: ! 1131: ! 1132: (defun mh-to-bcc () ! 1133: "Move point to end of Bcc: field. Creates the field if necessary" ! 1134: (interactive) ! 1135: (expand-abbrev) ! 1136: (when (not (mh-position-on-field "Bcc:" t)) ! 1137: (mh-position-on-field "To:" nil) ! 1138: (insert-string "\nBcc: "))) ! 1139: ! 1140: ! 1141: (defun mh-to-fcc () ! 1142: "Move point to end of Fcc: field. Creates the field if necessary" ! 1143: (interactive) ! 1144: (expand-abbrev) ! 1145: (when (not (mh-position-on-field "Fcc:" t)) ! 1146: (mh-position-on-field "To:" nil) ! 1147: (insert-string "\nFcc: "))) ! 1148: ! 1149: ! 1150: (defun mh-check-whom () ! 1151: "List recipients of the current message." ! 1152: (interactive) ! 1153: (let ((file-name (buffer-file-name))) ! 1154: (set-buffer-modified-p t) ; Force writing of contents ! 1155: (save-buffer) ! 1156: (message "Checking recipients...") ! 1157: (switch-to-buffer-other-window "*Mail Recipients*") ! 1158: (bury-buffer (current-buffer)) ! 1159: (erase-buffer) ! 1160: (mh-exec-cmd-output "whom" file-name) ! 1161: (other-window -1) ! 1162: (message "Checking recipients...done"))) ! 1163: ! 1164: ! 1165: ! 1166: ;;; Routines to make a search pattern and search for a message. ! 1167: ! 1168: (defun mh-make-pick-template () ! 1169: "Initialize a buffer with a template for a pick pattern." ! 1170: (erase-buffer) ! 1171: (kill-all-local-variables) ! 1172: (make-local-variable 'mh-searching-folder) ! 1173: (insert "From: \n" ! 1174: "To: \n" ! 1175: "Cc: \n" ! 1176: "Date: \n" ! 1177: "Subject: \n" ! 1178: "---------\n") ! 1179: (mh-letter-mode) ! 1180: (use-local-map mh-pick-mode-map) ! 1181: (setq mode-line-format "{%b}\tPick Pattern\t(^C^C to do search)") ! 1182: (goto-char (point-min)) ! 1183: (end-of-line)) ! 1184: ! 1185: ! 1186: (defun mh-do-pick-search () ! 1187: "Search the current folder for the messages matching the qualification ! 1188: in the current buffer and make them into a sequence called `search'." ! 1189: (interactive) ! 1190: (let* ((pattern-buffer (buffer-name)) ! 1191: (searching-buffer mh-searching-folder) ! 1192: (range "all") ! 1193: (pattern nil)) ! 1194: (message "Searching...") ! 1195: (goto-char (point-min)) ! 1196: (while (setq pattern (mh-next-pick-field pattern-buffer)) ! 1197: (setq msgs ! 1198: (mh-seq-from-command searching-buffer ! 1199: 'search ! 1200: (nconc (cons "pick" pattern) ! 1201: (list searching-buffer ! 1202: range ! 1203: "-sequence" "search" ! 1204: "-list")))) ! 1205: (setq range "search")) ! 1206: (message "Searching...done") ! 1207: (switch-to-buffer searching-buffer) ! 1208: (mh-notate-seq 'search ?% (+ mh-cmd-note 1)))) ! 1209: ! 1210: ! 1211: (defun mh-next-pick-field (buffer) ! 1212: "Return the next piece of a pick argument that can be extracted from the ! 1213: BUFFER. Returns nil if no pieces remain." ! 1214: (switch-to-buffer buffer) ! 1215: (let ((case-fold-search t)) ! 1216: (cond ((eobp) ! 1217: nil) ! 1218: ((re-search-forward "^\\([a-z].*\\):[ \t]*\\([a-z0-9].*\\)$" nil t) ! 1219: (let* ((component ! 1220: (format "-%s" ! 1221: (downcase (buffer-substring (match-beginning 1) ! 1222: (match-end 1))))) ! 1223: (pat (buffer-substring (match-beginning 2) (match-end 2)))) ! 1224: (forward-line 1) ! 1225: (list component pat))) ! 1226: ((re-search-forward "^-*$" nil t) ! 1227: (forward-char 1) ! 1228: (let ((body (buffer-substring (point) (point-max)))) ! 1229: (if (and (> (length body) 0) (not (equal body "\n"))) ! 1230: (list "-search" body) ! 1231: nil))) ! 1232: (t ! 1233: nil)))) ! 1234: ! 1235: ! 1236: ! 1237: ;;; Routines compose and send a letter. ! 1238: ! 1239: (defun mh-compose-and-send-mail (send-args sent-from-folder to subject cc ! 1240: &optional annotate-char ! 1241: annotate-field search-prefix) ! 1242: "Edit and compose a draft message and send or save it. ! 1243: SENT-FROM-FOLDER is buffer containing summary of current folder, if any. ! 1244: SEND-ARGS is an optional argument passed to the send command. ! 1245: The TO, SUBJECT, and CC fields are passed to the mh-compose-letter-hook. ! 1246: If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of the ! 1247: current message. In that case, the ANNOTATE-FIELD is used to build a string ! 1248: for mh-annotate-msg." ! 1249: (let ((sent-from-msg)) ! 1250: (save-window-excursion ! 1251: (when sent-from-folder ! 1252: (switch-to-buffer sent-from-folder) ! 1253: (setq sent-from-msg (mh-get-msg-num nil)))) ! 1254: (pop-to-buffer "draft") ! 1255: (mh-letter-mode) ! 1256: (make-local-vars ! 1257: 'mh-send-args send-args ! 1258: 'mh-sent-from-folder sent-from-folder ! 1259: 'mh-sent-from-msg sent-from-msg ! 1260: 'mh-annotate-field annotate-field ! 1261: 'mh-annotate-char annotate-char ! 1262: 'mh-annotate-search-prefix (if search-prefix search-prefix "")) ! 1263: (setq mode-line-format "{%b} %[Mail/draft%] (%p - %m) (^C^C to send) %M") ! 1264: (if (and (boundp 'mh-compose-letter-hook) mh-compose-letter-hook) ! 1265: (funcall mh-compose-letter-hook to subject cc)))) ! 1266: ! 1267: ! 1268: (defun mh-send-letter (&optional arg) ! 1269: "Send the letter in the current buffer. If given an ARGUMENT, the delivery ! 1270: process is monitored and displayed." ! 1271: (interactive "P") ! 1272: (save-buffer) ! 1273: (message "Sending...") ! 1274: (if arg ! 1275: (let ((from-buffer (buffer-name)) ! 1276: (file-name (buffer-file-name))) ! 1277: (pop-to-buffer " *mh-temp*") ! 1278: (erase-buffer) ! 1279: (if mh-send-args ! 1280: (mh-exec-cmd-output "send" "-watch" "-unique" mh-send-args ! 1281: file-name) ! 1282: (mh-exec-cmd-output "send" "-watch" "-unique" file-name)) ! 1283: (pop-to-buffer from-buffer)) ! 1284: (if mh-send-args ! 1285: (mh-exec-cmd-quiet 0 "send" "-push" "-unique" mh-send-args ! 1286: (buffer-file-name)) ! 1287: (mh-exec-cmd-quiet 0 "send" "-push" "-unique" (buffer-file-name)))) ! 1288: (if mh-annotate-char ! 1289: (mh-annotate-msg mh-sent-from-msg ! 1290: mh-sent-from-folder ! 1291: mh-annotate-char ! 1292: "-component" mh-annotate-field ! 1293: "-text" (format "\"%s %s\"" ! 1294: (mh-get-field ! 1295: (format "%s%s" ! 1296: mh-annotate-search-prefix ! 1297: "To:")) ! 1298: (mh-get-field ! 1299: (format "%s%s" ! 1300: mh-annotate-search-prefix ! 1301: "Cc:"))))) ! 1302: (message "Sending...done") ! 1303: (kill-buffer (buffer-name))) ! 1304: ! 1305: ! 1306: (defun mh-insert-letter (&optional arg) ! 1307: "Insert a message in the current letter, asking for folder and number. ! 1308: Removes headers using mh-invisible-headers. ! 1309: Prefixes each non-blank line with mh-ins-buf-prefix (default \">> \"). ! 1310: Just \\[universal-argument] means do not indent and do not delete any ! 1311: header fields. Leaves point before the text and mark after it." ! 1312: (interactive "p") ! 1313: (let ((folder (mh-prompt-for-folder "Message from" mh-sent-from-folder nil)) ! 1314: (message (read-input (format "Message number%s: " ! 1315: (if mh-sent-from-msg ! 1316: (format " [%d]" mh-sent-from-msg) ! 1317: "")))) ! 1318: (start (point))) ! 1319: (if (equal message "") (setq message (format "%d" mh-sent-from-msg))) ! 1320: (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" ! 1321: (format "%s%s/%s" mh-user-path ! 1322: (substring folder 1) message)) ! 1323: (when (not (equal arg 4)) ! 1324: (mh-clean-msg-header start) ! 1325: (narrow-to-region start (mark)) ! 1326: (mh-insert-prefix-string mh-ins-buf-prefix) ! 1327: (widen)) ! 1328: (exchange-point-and-mark))) ! 1329: ! 1330: ! 1331: (defun mh-insert-cur-msg () ! 1332: "Inserts the currently displayed message into the current draft buffer. ! 1333: Prefixes each non-blank line with the string mh-ins-buf-prefix. ! 1334: If there is a region set in the message's buffer,only the region will ! 1335: be inserted. Otherwise, the region from (point) to the end will be grabbed." ! 1336: (interactive) ! 1337: (let ((to-point (point)) ! 1338: (to-buffer (current-buffer))) ! 1339: (set-buffer mh-sent-from-folder) ! 1340: (set-buffer mh-show-buffer) ; Find displayed message ! 1341: (let ((mh-ins-str (if (mark) ! 1342: (buffer-substring (point) (mark)) ! 1343: (buffer-substring (point) (point-max))))) ! 1344: (set-buffer to-buffer) ! 1345: (narrow-to-region to-point to-point) ! 1346: (insert-string mh-ins-str) ! 1347: (mh-insert-prefix-string mh-ins-buf-prefix) ! 1348: (widen)))) ! 1349: ! 1350: ! 1351: (defun mh-insert-prefix-string (ins-string) ! 1352: "Preface each line in the current buffer with STRING." ! 1353: (goto-char (point-min)) ! 1354: (replace-regexp "^." (concat ins-string "\\&") nil) ! 1355: (goto-char (point-min))) ! 1356: ! 1357: ! 1358: (defun mh-fully-kill-draft () ! 1359: "Kill the draft message file and the draft message buffer. Use kill-buffer ! 1360: if you don't want to delete the draft message file." ! 1361: (interactive "") ! 1362: (if (file-exists-p (buffer-file-name)) ! 1363: (delete-file (buffer-file-name))) ! 1364: (kill-buffer (buffer-name))) ! 1365: ! 1366: ! 1367: ! 1368: ;;; Commands to manipulate sequences. Sequences are stored in an alist ! 1369: ;;; of the form: ! 1370: ;;; ((seq-name msgs ...) (seq-name msgs ...) ...) ! 1371: ! 1372: ! 1373: (defmacro mh-seq-name (pair) ! 1374: (list 'car pair)) ! 1375: ! 1376: (defmacro mh-seq-msgs (pair) ! 1377: (list 'cdr pair)) ! 1378: ! 1379: ! 1380: (defun mh-seq-to-msgs (seq) ! 1381: "Returns the messages in sequence SEQ." ! 1382: (mh-seq-msgs (assoc seq mh-seq-list))) ! 1383: ! 1384: ! 1385: (defun mh-msg-to-seq (msg) ! 1386: "Given a MESSAGE number, return the first sequence in which it occurs." ! 1387: (let ((l mh-seq-list)) ! 1388: (while (and l (not (memq msg (cdar l)))) ! 1389: (setq l (cdr l))) ! 1390: (caar l))) ! 1391: ! 1392: ! 1393: (defun mh-read-seq (prompt &optional seq) ! 1394: "Read and return a sequence name from the minibuffer, prompting with ! 1395: the string PROMPT and supplying the optional DEFAULT. ! 1396: % defaults to the sequences that the current message is in. ! 1397: Makes sure that the sequence is known to MH commands." ! 1398: (let ((input (completing-read ! 1399: (format "%s %s %s" prompt "sequence:" ! 1400: (if seq (format "[%s] " (symbol-name seq)) "")) ! 1401: (mh-seq-names mh-seq-list)))) ! 1402: (let ((seq (cond ((equal input "%") (mh-msg-to-seq (mh-get-msg-num t))) ! 1403: ((equal input "") seq) ! 1404: (t (intern input))))) ! 1405: (mh-process-seq seq (mh-seq-to-msgs seq)) ! 1406: seq))) ! 1407: ! 1408: ! 1409: (defun mh-seq-names (seq-list) ! 1410: "Returns an alist of the names of the SEQUENCES." ! 1411: (mapcar (function (lambda (entry) (cons (symbol-name (car entry)) nil))) ! 1412: seq-list)) ! 1413: ! 1414: ! 1415: (defun mh-seq-from-command (folder seq command) ! 1416: "In FOLDER, make a sequence named SEQ by executing COMMAND." ! 1417: (let ((msgs ()) ! 1418: (case-fold-search t)) ! 1419: (save-excursion ! 1420: (save-window-excursion ! 1421: (apply 'mh-exec-cmd-quiet (cons " *mh-temp*" command)) ! 1422: (goto-char (point-min)) ! 1423: (while (re-search-forward "\\([0-9]+\\)" nil t) ! 1424: (let ((num (string-to-int (buffer-substring (match-beginning 1) ! 1425: (match-end 1))))) ! 1426: (if (not (zerop num)) ! 1427: (push num msgs))))) ! 1428: (switch-to-buffer folder) ! 1429: (push (cons seq msgs) mh-seq-list) ! 1430: msgs))) ! 1431: ! 1432: ! 1433: (defun mh-remove-seq (seq) ! 1434: "Delete the sequence SEQ." ! 1435: (let ((entry (assoc seq mh-seq-list))) ! 1436: (setq mh-seq-list (delq (car entry) mh-seq-list)) ! 1437: (mh-notate-seq (mh-seq-msgs (car entry)) ? (+ mh-cmd-note 1)))) ! 1438: ! 1439: ! 1440: (defun mh-remove-msg-from-seq (msg-num seq &optional do-not-mark) ! 1441: "Remove a message MSG-NUM from the sequence SEQ. If optional FLAG is ! 1442: non-nil, do not mark the message as being part of a sequence." ! 1443: (let ((seq (assoc seq mh-seq-list))) ! 1444: (if seq ! 1445: (setcdr seq (delq msg-num (mh-seq-msgs seq))))) ! 1446: (if (not do-not-mark) (mh-notate ? (+ mh-cmd-note 1)))) ! 1447: ! 1448: ! 1449: (defun mh-add-msg-to-seq (msg-num seq &optional do-not-mark) ! 1450: "Add the message MSG-NUM to the SEQUENCE. If optional FLAG is non-nil, ! 1451: do not mark the message as being part of a sequence." ! 1452: (let ((seq-list (assoc seq mh-seq-list))) ! 1453: (if (not do-not-mark) (mh-notate ?% (+ mh-cmd-note 1))) ! 1454: (if (null seq-list) ! 1455: (push (cons seq (list msg-num)) mh-seq-list) ! 1456: (setcdr seq-list (cons msg-num (cdr seq-list)))))) ! 1457: ! 1458: ! 1459: (defun mh-add-msg-list-to-seq (msgs seq &optional do-not-mark) ! 1460: "Add the messages in LIST to the SEQUENCE. If optional FLAG is non-nil, ! 1461: do not mark the messages as being part of a sequence." ! 1462: (mapc (function (lambda (msg) (mh-add-msg-to-seq msg seq do-not-mark))) ! 1463: msgs)) ! 1464: ! 1465: ! 1466: (defun mh-rename-seq (seq new-name) ! 1467: "Rename a SEQUENCE to have a new NAME." ! 1468: (interactive "SOld sequence name: \nSNew name: ") ! 1469: (let ((old-seq (assoc seq mh-seq-list))) ! 1470: (if old-seq ! 1471: (rplaca old-seq new-name) ! 1472: (error "Sequence %s does not exists" (symbol-name seq))))) ! 1473: ! 1474: ! 1475: (defun mh-notate-seq (seq notation offset) ! 1476: "Mark all messages in the sequence SEQ with the NOTATION at character ! 1477: OFFSET." ! 1478: (mh-map-to-seq-msgs 'mh-notate seq notation offset)) ! 1479: ! 1480: ! 1481: (defun mh-map-to-seq-msgs (func seq &rest args) ! 1482: "Invoke the function FUNC at each message in the sequence SEQ, passing ! 1483: the remaining ARGS as arguments." ! 1484: (let ((msgs (mh-seq-to-msgs seq))) ! 1485: (while msgs ! 1486: (mh-goto-msg (car msgs)) ! 1487: (apply func args) ! 1488: (setq msgs (cdr msgs))))) ! 1489: ! 1490: ! 1491: (defun mh-map-over-seqs (func seq-list) ! 1492: "Apply the function FUNC to each element in the sequence LIST, ! 1493: passing the sequence name and a list of messages as arguments." ! 1494: (while seq-list ! 1495: (funcall func (caar seq-list) (cdar seq-list)) ! 1496: (setq seq-list (cdr seq-list)))) ! 1497: ! 1498: ! 1499: (defun mh-process-seq-commands (seq-list) ! 1500: "Process outstanding sequence commands for the sequences in SEQ-LIST." ! 1501: (mh-map-over-seqs 'mh-process-seq seq-list)) ! 1502: ! 1503: ! 1504: (defun mh-process-seq (seq msgs) ! 1505: "Mark sequence SEQ to contain MSGS." ! 1506: ;; Do not mark pseudo-sequences. ! 1507: (if (not (equal (substring (symbol-name seq) 0 1) "+")) ! 1508: (apply 'mh-exec-cmd ! 1509: (nconc (list "mark" "-zero" "-seq" (format "%s" seq) "-add") ! 1510: msgs)))) ! 1511: ! 1512: ! 1513: (defun mh-copy-seq-to-point (seq location) ! 1514: "Copy the messages in SEQUENCE to after the LOCATION in the current buffer." ! 1515: (mh-map-to-seq-msgs 'mh-copy-line-to-point seq location)) ! 1516: ! 1517: ! 1518: (defun mh-copy-line-to-point (location) ! 1519: "Copy the current line to the LOCATION in the current buffer." ! 1520: (beginning-of-line) ! 1521: (let ((beginning-of-line (point))) ! 1522: (forward-line 1) ! 1523: (copy-region-as-kill beginning-of-line (point)) ! 1524: (goto-char location) ! 1525: (yank) ! 1526: (goto-char beginning-of-line))) ! 1527: ! 1528: ! 1529: ! 1530: ;;; Issue commands to mh. ! 1531: ! 1532: (defun mh-exec-cmd (command &rest args) ! 1533: "Execute MH command COMMAND with ARGS. Any output is shown to the user." ! 1534: (save-excursion ! 1535: (switch-to-buffer-other-window " *mh-temp*") ! 1536: (erase-buffer) ! 1537: (apply 'call-process ! 1538: (nconc (list (format "%s%s" mh-progs command) nil t nil) ! 1539: (mh-list-to-string args))) ! 1540: (if (> (buffer-size) 0) ! 1541: (sit-for 5)))) ! 1542: ! 1543: ! 1544: (defun mh-exec-cmd-quiet (buffer command &rest args) ! 1545: "In BUFFER, execute MH command COMMAND with ARGS. Return in buffer, if ! 1546: one exists." ! 1547: (when (stringp buffer) ! 1548: (switch-to-buffer buffer) ! 1549: (erase-buffer)) ! 1550: (apply 'call-process ! 1551: (nconc (list (format "%s%s" mh-progs command) nil buffer nil) ! 1552: (mh-list-to-string args)))) ! 1553: ! 1554: ! 1555: (defun mh-exec-cmd-output (command &rest args) ! 1556: "Execute MH command COMMAND with ARGS putting the output into buffer after ! 1557: point. Set mark after inserted text." ! 1558: (set-mark (point)) ! 1559: (apply 'call-process ! 1560: (nconc (list (format "%s%s" mh-progs command) nil t nil) ! 1561: (mh-list-to-string args))) ! 1562: (exchange-point-and-mark)) ! 1563: ! 1564: ! 1565: ! 1566: (defun mh-exec-lib-cmd-output (command &rest args) ! 1567: "Execute MH library command COMMAND with ARGS. Put the output into ! 1568: buffer after point. Set mark after inserted text." ! 1569: (set-mark (point)) ! 1570: (apply 'call-process ! 1571: (nconc (list (format "%s%s" mh-lib command) nil t nil) ! 1572: (mh-list-to-string args))) ! 1573: (exchange-point-and-mark)) ! 1574: ! 1575: ! 1576: (defun mh-list-to-string (l) ! 1577: "Flattens the list L and makes every element a string." ! 1578: (let ((new-list nil)) ! 1579: (while l ! 1580: (cond ((symbolp (car l)) (push (format "%s" (car l)) new-list)) ! 1581: ((numberp (car l)) (push (format "%d" (car l)) new-list)) ! 1582: ((equal (car l) "")) ! 1583: ((stringp (car l)) (push (car l) new-list)) ! 1584: ((null (car l))) ! 1585: ((listp (car l)) (setq new-list ! 1586: (nconc (mh-list-to-string (car l)) ! 1587: new-list))) ! 1588: (t (error "Bad argument %s" (car l)))) ! 1589: (setq l (cdr l))) ! 1590: (nreverse new-list))) ! 1591: ! 1592: ! 1593: ! 1594: ;;; Commands to annotate a message. ! 1595: ! 1596: (defun mh-annotate-msg (msg buffer note &rest args) ! 1597: "Mark the MESSAGE in BUFFER listing with the character NOTE and annotate ! 1598: the saved message with ARGS." ! 1599: ;; Wait for annotation to finish, to avoid race condition with reading msg. ! 1600: (apply 'mh-exec-cmd (cons "anno" (cons buffer (cons msg args)))) ! 1601: (save-excursion ! 1602: (switch-to-buffer buffer) ! 1603: (if (mh-goto-msg msg t) ! 1604: (mh-notate note (+ mh-cmd-note 1))))) ! 1605: ! 1606: ! 1607: (defun mh-notate (notation offset) ! 1608: "Marks the current message with the character NOTATION at position OFFSET." ! 1609: (let ((buffer-read-only nil)) ! 1610: (beginning-of-line) ! 1611: (goto-char (+ (point) offset)) ! 1612: (delete-char 1) ! 1613: (insert notation) ! 1614: (beginning-of-line))) ! 1615: ! 1616: ! 1617: ! 1618: ;;; User prompting commands. ! 1619: ! 1620: (defun mh-prompt-for-folder (prompt default can-create) ! 1621: "Prompt for a folder name with PROMPT. Returns the folder's name. ! 1622: DEFAULT is used if the folder exists and the user types CR. ! 1623: If the CAN-CREATE flag is t,then a non-existant folder is made." ! 1624: (let* ((prompt (format "%s folder%s" prompt ! 1625: (if (equal "" default) ! 1626: "? " ! 1627: (format " [%s]? " default)))) ! 1628: name) ! 1629: (while (and (setq name (completing-read prompt mh-folder-list ! 1630: nil (not can-create) "+")) ! 1631: (equal name "") ! 1632: (equal default ""))) ! 1633: (cond ((or (equal name "") (equal name "+")) ! 1634: (setq name default)) ! 1635: ((not (equal (substring name 0 1) "+")) ! 1636: (setq name (format "+%s" name)))) ! 1637: (let ((new-file-p ! 1638: (not ! 1639: (file-exists-p (format "%s%s" mh-user-path (substring name 1)))))) ! 1640: (cond ((and new-file-p ! 1641: (y-or-n-p ! 1642: (format "Folder %s does not exist. Create it? " name))) ! 1643: (message "Creating %s" name) ! 1644: (call-process "mkdir" nil nil nil ! 1645: (format "%s%s" mh-user-path (substring name 1))) ! 1646: (message "Creating %s...done" name) ! 1647: (push (list name) mh-folder-list) ! 1648: (push (list (substring name 1 nil)) mh-folder-list)) ! 1649: (new-file-p ! 1650: (error "")) ! 1651: (t ! 1652: (when (null (assoc name mh-folder-list)) ! 1653: (push (list name) mh-folder-list) ! 1654: (push (list (substring name 1 nil)) mh-folder-list))))) ! 1655: name)) ! 1656: ! 1657: ! 1658: (defun mh-make-folder-list () ! 1659: "Returns a list of the user's folders in a form suitable for completing ! 1660: read." ! 1661: (interactive) ! 1662: (save-window-excursion ! 1663: (mh-exec-cmd-quiet " *mh-temp*" "folders" "-fast" "-norecurse") ! 1664: (goto-char (point-min)) ! 1665: (let ((list nil)) ! 1666: (while (not (eobp)) ! 1667: (let ((start (point))) ! 1668: (search-forward "\n" nil t) ! 1669: (let ((folder (buffer-substring start (- (point) 1)))) ! 1670: (push (list (format "+%s" folder)) list)))) ! 1671: list))) ! 1672: ! 1673: ! 1674: (defun mh-remove-folder-from-folder-list (folder) ! 1675: "Remove FOLDER from the list of folders." ! 1676: (setq mh-folder-list ! 1677: (delq (assoc (substring folder 1 nil) mh-folder-list) ! 1678: mh-folder-list))) ! 1679: ! 1680: ! 1681: ! 1682: ;;; Misc. functions. ! 1683: ! 1684: (defun mh-get-msg-num (error-if-no-message) ! 1685: "Returns the message number of the current message. If the argument ! 1686: ERROR-IF-NO-MESSAGE is non-nil, then complain if the cursor is not ! 1687: pointing to a message." ! 1688: (save-excursion ! 1689: (beginning-of-line) ! 1690: (cond ((looking-at "^[ ]*\\([0-9]+\\)") ! 1691: (string-to-int (buffer-substring (match-beginning 1) ! 1692: (match-end 1)))) ! 1693: (error-if-no-message ! 1694: (error "Cursor not pointing to message")) ! 1695: (t nil)))) ! 1696: ! 1697: ! 1698: (defun mh-msg-search-pat (n) ! 1699: "Returns a search pattern for message N in the scan listing." ! 1700: (cond ((< n 10) (format "^[^0-9][^0-9][^0-9]%d" n)) ! 1701: ((< n 100) (format "^[^0-9][^0-9]%d" n)) ! 1702: ((< n 1000) (format "^[^0-9]%d" n)) ! 1703: (t (format "^%d" n)))) ! 1704: ! 1705: ! 1706: (defun mh-msg-filename () ! 1707: "Returns a string containing the pathname for the file containing the ! 1708: current message." ! 1709: (format "%s%d" mh-folder-filename (mh-get-msg-num t))) ! 1710: ! 1711: ! 1712: (defun mh-msg-filenames (msgs folder) ! 1713: "Returns a string of filenames for MSGS in FOLDER." ! 1714: (mapconcat (function (lambda (msg) (concat folder msg))) msgs " ")) ! 1715: ! 1716: ! 1717: (defun mh-find-path () ! 1718: "Set mh-user-path to the user's Mail directory from ~/.mh_profile." ! 1719: (if (equal (setq mh-user-path (mh-get-profile-field "Path:")) "") ! 1720: (setq mh-user-path "Mail/") ! 1721: (setq mh-user-path (format "%s/" mh-user-path))) ! 1722: (if (not (equal (substring mh-user-path 0 1) "/")) ! 1723: (setq mh-user-path (format "%s/%s" (getenv "HOME") mh-user-path)))) ! 1724: ! 1725: ! 1726: (defun mh-get-profile-field (field) ! 1727: "Return FIELD from the user's .mh_profile file." ! 1728: (save-window-excursion ! 1729: (if (not (file-exists-p "~/.mh_profile")) ! 1730: (error "Cannot find .mh_profile file.")) ! 1731: (switch-to-buffer " *mh_temp*") ! 1732: (erase-buffer) ! 1733: (insert-file-contents "~/.mh_profile") ! 1734: (mh-get-field field))) ! 1735: ! 1736: ! 1737: (defun mh-get-cur-msg (folder) ! 1738: "Returns the number of the 'cur' message in FOLDER." ! 1739: (save-excursion ! 1740: (switch-to-buffer " *mh_temp*") ! 1741: (erase-buffer) ! 1742: (mh-exec-cmd-output "pick" folder "cur") ! 1743: (string-to-int (buffer-substring (point-min) (point-max))))) ! 1744: ! 1745: ! 1746: (defun mh-get-field (field) ! 1747: "Find and return the value of field FIELD in the current buffer. ! 1748: Returns the empty string if the field is not in the message." ! 1749: (let ((case-fold-search t)) ! 1750: (goto-char (point-min)) ! 1751: (cond ((not (search-forward field nil t)) "") ! 1752: ((looking-at "[\t ]*$") "") ! 1753: (t ! 1754: (re-search-forward "[\t ]*\\([a-zA-z0-9/\.].*\\)$" nil t) ! 1755: (let ((field (buffer-substring (match-beginning 1) ! 1756: (match-end 1))) ! 1757: (end-of-match (point))) ! 1758: (forward-line) ! 1759: (while (looking-at "[ \t]") (forward-line 1)) ! 1760: (backward-char 1) ! 1761: (format "%s%s" field (buffer-substring end-of-match (point)))))))) ! 1762: ! 1763: ! 1764: (defun mh-insert-fields (&rest name-values) ! 1765: "Insert the NAME-VALUE pairs in the current buffer." ! 1766: (let ((case-fold-search t)) ! 1767: (while name-values ! 1768: (let ((field-name (car name-values)) ! 1769: (value (cadr name-values))) ! 1770: (goto-char (point-min)) ! 1771: (cond ((not (re-search-forward (format "^%s" field-name) nil t)) ! 1772: (re-search-forward "^---\\|^$") ! 1773: (beginning-of-line) ! 1774: (insert field-name " " value "\n")) ! 1775: (t ! 1776: (end-of-line) ! 1777: (insert " " value))) ! 1778: (setq name-values (cddr name-values)))))) ! 1779: ! 1780: ! 1781: (defun mh-position-on-field (field set-mark) ! 1782: "Set point to the end of the line beginning with FIELD. Sets the mark ! 1783: to the point, if SET-MARK is non-nil." ! 1784: (if set-mark (set-mark (point))) ! 1785: (goto-char (point-min)) ! 1786: (if (re-search-forward (format "^%s" field) nil t) ! 1787: (progn (end-of-line) t) ! 1788: nil)) ! 1789: ! 1790: ! 1791: ! 1792: ;;; Build the folder-mode keymap: ! 1793: ! 1794: (define-key mh-folder-mode-map "?" 'mh-msg-is-in-seq) ! 1795: (define-key mh-folder-mode-map "%" 'mh-put-msg-in-seq) ! 1796: (define-key mh-folder-mode-map "\e%" 'mh-delete-msg-from-seq) ! 1797: (define-key mh-folder-mode-map "\^xn" 'mh-narrow-to-seq) ! 1798: (define-key mh-folder-mode-map "\^xw" 'mh-widen) ! 1799: ! 1800: (define-key mh-folder-mode-map "\^c" 'my-apply-command-to-seq) ! 1801: (define-key mh-folder-mode-map "\eu" 'mh-undo-folder) ! 1802: (define-key mh-folder-mode-map "\e " 'mh-page-digest) ! 1803: (define-key mh-folder-mode-map "\ee" 'mh-extract-rejected-mail) ! 1804: (define-key mh-folder-mode-map "\ef" 'mh-visit-folder) ! 1805: (define-key mh-folder-mode-map "\ek" 'mh-kill-folder) ! 1806: (define-key mh-folder-mode-map "\el" 'mh-list-folders) ! 1807: (define-key mh-folder-mode-map "\ep" 'mh-renumber-folder) ! 1808: (define-key mh-folder-mode-map "\es" 'mh-search-folder) ! 1809: (define-key mh-folder-mode-map "\er" 'mh-rescan-folder) ! 1810: (define-key mh-folder-mode-map "l" 'mh-print-msg) ! 1811: (define-key mh-folder-mode-map "t" 'mh-toggle-summarize) ! 1812: (define-key mh-folder-mode-map "c" 'mh-copy-msg) ! 1813: (define-key mh-folder-mode-map ">" 'mh-write-msg-to-file) ! 1814: (define-key mh-folder-mode-map "i" 'mh-inc-folder) ! 1815: (define-key mh-folder-mode-map "x" 'mh-execute-commands) ! 1816: (define-key mh-folder-mode-map "e" 'mh-execute-commands) ! 1817: (define-key mh-folder-mode-map "r" 'mh-redistribute) ! 1818: (define-key mh-folder-mode-map "f" 'mh-forward) ! 1819: (define-key mh-folder-mode-map "s" 'mh-send) ! 1820: (define-key mh-folder-mode-map "a" 'mh-answer) ! 1821: (define-key mh-folder-mode-map "g" 'mh-goto-msg) ! 1822: (define-key mh-folder-mode-map "\177" 'mh-previous-page) ! 1823: (define-key mh-folder-mode-map " " 'mh-page-msg) ! 1824: (define-key mh-folder-mode-map "." 'mh-show) ! 1825: (define-key mh-folder-mode-map "u" 'mh-undo) ! 1826: (define-key mh-folder-mode-map "!" 'mh-move-or-write-again) ! 1827: (define-key mh-folder-mode-map "^" 'mh-move-msg) ! 1828: (define-key mh-folder-mode-map "d" 'mh-delete-msg) ! 1829: (define-key mh-folder-mode-map "p" 'mh-previous-line) ! 1830: (define-key mh-folder-mode-map "n" 'mh-next-line) ! 1831: ! 1832: ! 1833: ;;; Build the letter-mode keymap: ! 1834: ! 1835: (define-key mh-letter-mode-map "\^Cb" 'mh-to-bcc) ! 1836: (define-key mh-letter-mode-map "\^Cw" 'mh-check-whom) ! 1837: (define-key mh-letter-mode-map "\^Cc" 'mh-to-cc) ! 1838: (define-key mh-letter-mode-map "\^Cf" 'mh-to-fcc) ! 1839: (define-key mh-letter-mode-map "\^Cs" 'mh-to-subject) ! 1840: (define-key mh-letter-mode-map "\^Ct" 'mh-to-to) ! 1841: (define-key mh-letter-mode-map "\^Cq" 'mh-fully-kill-draft) ! 1842: (define-key mh-letter-mode-map "\^Cy" 'mh-insert-cur-msg) ! 1843: (define-key mh-letter-mode-map "\^C\^Y" 'mh-insert-letter) ! 1844: (define-key mh-letter-mode-map "\^C\^C" 'mh-send-letter) ! 1845: ! 1846: ;;; Build the pick-mode keymap: ! 1847: ! 1848: (define-key mh-pick-mode-map "\^C\^C" 'mh-do-pick-search) ! 1849: (define-key mh-pick-mode-map "\^Cb" 'mh-to-bcc) ! 1850: (define-key mh-pick-mode-map "\^Cc" 'mh-to-cc) ! 1851: (define-key mh-pick-mode-map "\^Cf" 'mh-to-fcc) ! 1852: (define-key mh-pick-mode-map "\^Cs" 'mh-to-subject) ! 1853: (define-key mh-pick-mode-map "\^Ct" 'mh-to-to) ! 1854: (define-key mh-pick-mode-map "\^Cw" 'mh-check-whom)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.