Annotation of 43BSDReno/contrib/mh/miscellany/mh-e/mh-e.el, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.