Annotation of 43BSD/contrib/mh/miscellany/mh-e/:mh-e.ml, revision 1.1

1.1     ! root        1: ;;;  mh-e.el   (Version: 2.7)
        !             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: 
        !            38: (defvar mh-progs "/usr/local/mh/"     "Directory containing MH commands")
        !            39: (defvar mh-lib   "/usr/local/lib/mh/" "Directory of MH library")
        !            40: 
        !            41: 
        !            42: ;;; Mode hooks:
        !            43: 
        !            44: (defvar mh-folder-mode-hook nil            "Invoked in mh-folder-mode")
        !            45: (defvar mh-letter-mode-hook nil     "Invoked in mh-letter-mode")
        !            46: 
        !            47: 
        !            48: ;;; Personal preferences:
        !            49: 
        !            50: (defvar mh-auto-fill-letters t     "Invoke auto-fill-mode in letters")
        !            51: (defvar mh-clean-message-header nil
        !            52:   "Remove invisible header lines in messages")
        !            53: (defvar mh-lpr-command-format "lpr -p -J '%s'"
        !            54: "Format for Unix command line to print a message. The format should be
        !            55: a unix command line, with the string "%s" where the folder and message
        !            56: number should appear.")
        !            57: (defvar mh-summary-height 4    "Number of lines in summary window")
        !            58: 
        !            59: ;;; Real constants:
        !            60: 
        !            61: (defvar mh-cmd-note 4                 "Offset to insert notation")
        !            62: (defvar mh-invisible-headers
        !            63:   "^Received: \\|^Message-Id: \\|^Remailed-\\|^Via: \\|^Mail-from: \\|\^Return-
        !            64: Path: \\|^In-Reply-To: \\|^Resent-"
        !            65:   "Regexp specifying headers that are not to be shown.")
        !            66: 
        !            67: 
        !            68: ;;; Global variables:
        !            69: 
        !            70: (defvar mh-user-path  ""            "User's mail folder")
        !            71: (defvar mh-last-folder "inbox"      "Last folder read by mh-rmail")
        !            72: (defvar mh-last-destination nil             "Destination of last "move" command")
        !            73: (defvar        mh-current-folder nil        "Currently active folder")
        !            74: (defvar        mh-folder-buffer nil         "Buffer name of currently active folder")
        !            75: (defvar mh-show-buffer nil          "Name of buffer that displays messages")
        !            76: (defvar mh-letter-mode-map nil      "Command map for composing mail")
        !            77: 
        !            78: ;;; Macros:
        !            79: 
        !            80: (defmacro push (v l)
        !            81:   (list 'setq l (list 'cons v l)))
        !            82: 
        !            83: (defmacro caar (l)
        !            84:   (list 'car (list 'car l)))
        !            85: 
        !            86: (defmacro cadr (l)
        !            87:   (list 'car (list 'cdr l)))
        !            88: 
        !            89: (defmacro cdar (l)
        !            90:   (list 'cdr (list 'car l)))
        !            91: 
        !            92: (defmacro cddr (l)
        !            93:   (list 'cdr (list 'cdr l)))
        !            94: 
        !            95: (defmacro when (pred &rest body)
        !            96:   (list 'cond (cons pred body)))
        !            97: 
        !            98: 
        !            99: 
        !           100: ;;; Entry points:
        !           101: 
        !           102: (defun mh-rmail (&optional arg)
        !           103:   "Inc(orporate) new mail if optional ARG omitted, or scan a MH mail box
        !           104: if arg is present.  This front end uses the MH mail system, which uses
        !           105: different conventions from the usual mail system."
        !           106:   (interactive "P")
        !           107:   (let ((make-backup-files nil)
        !           108:        (pop-up-windows t)
        !           109:        mh-current-folder
        !           110:        mh-folder-buffer)
        !           111: 
        !           112:     (mh-find-path)
        !           113:     (save-window-excursion
        !           114:       (cond (arg
        !           115:             (let ((folder (mh-get-folder-name "mh" mh-last-folder t))
        !           116:                   (range (read-string "range [all]? ")))
        !           117:               (mh-scan-folder folder (if (string= range "") "all" range))))
        !           118:            (t
        !           119:             (mh-make-folder "inbox")
        !           120:             (mh-inc-folder)))
        !           121: 
        !           122:       (let ((mh-show-buffer (concat "show-" mh-current-folder)))
        !           123:        (pop-to-buffer mh-show-buffer)
        !           124:        (unwind-protect
        !           125:            (mh-command-loop)
        !           126:          (kill-buffer mh-folder-buffer)
        !           127:          (kill-buffer mh-show-buffer)
        !           128:          (setq mh-last-folder mh-current-folder))))))
        !           129: 
        !           130: 
        !           131: (defun mh-smail ()
        !           132:   "Send mail using the MH mail system."
        !           133:   (interactive)
        !           134:   (let ((make-backup-files nil)
        !           135:        (pop-up-windows t))
        !           136:     (mh-find-path)
        !           137:     (call-interactively 'mh-send)))
        !           138: 
        !           139: 
        !           140: 
        !           141: ;;; User executable mh-e commands:
        !           142: 
        !           143: (defun mh-answer ()
        !           144:   "Answer a letter."
        !           145:   (interactive)
        !           146:   (save-window-excursion
        !           147:     (let ((msg-filename (mh-msg-filename))
        !           148:          (msg (mh-get-msg-num t))
        !           149:          (reply-to
        !           150:           (mh-get-response
        !           151:            "Reply to (f, t, c, ?): "
        !           152:            '(?f ?t ?c)
        !           153:            "Reply to F(rom), T(o + From), C(c + To + From): ")))
        !           154:       (message "Composing a reply...")
        !           155:       (cond ((equal reply-to ?f)
        !           156:             (mh-exec-cmd "repl" "-build" mh-folder-buffer msg "-nocc" "all"))
        !           157:            ((equal reply-to ?t)
        !           158:             (mh-exec-cmd "repl" "-build" mh-folder-buffer msg "-cc" "to"
        !           159:                          "-nocc" "me"))
        !           160:            ((equal reply-to ?c)
        !           161:             (mh-exec-cmd "repl" "-build" mh-folder-buffer msg "-cc" "all"
        !           162:                          "-nocc" "me")))
        !           163: 
        !           164:       (mh-read-file (concat mh-user-path "draft") "draft")
        !           165:       (delete-other-windows)
        !           166:       (when (or (zerop (buffer-size))
        !           167:                (not (y-or-n-p "The file 'draft' exists.  Use for reply? ")))
        !           168:          (erase-buffer)
        !           169:          (insert-file-contents (concat mh-user-path "reply"))
        !           170:          (delete-file (concat mh-user-path "reply")))
        !           171: 
        !           172:       (let ((to-names (mh-get-field "To:"))
        !           173:            (cc-names (mh-get-field "Cc:")))
        !           174:        (goto-char (dot-max))
        !           175:        (pop-to-buffer "*message*")
        !           176:        (erase-buffer)
        !           177:        (if (file-exists-p msg-filename)
        !           178:            (insert-file-contents msg-filename)
        !           179:            (error "File %s does not exist" msg-filename))
        !           180:        (goto-char (dot-min))
        !           181:        (let ((case-fold-search nil))
        !           182:          (re-search-forward "^$\\|^-*$"))
        !           183:        (recenter 0)
        !           184:        (message "Composing a reply...done")
        !           185:        (if (mh-compose-and-send-mail "")
        !           186:            (mh-annotate "R" mh-folder-buffer msg
        !           187:                         "-component" "Replied-To:"
        !           188:                         "-text" (concat to-names
        !           189:                                         (if (string= cc-names "")
        !           190:                                             ""
        !           191:                                             (concat ", " cc-names)))))))))
        !           192: 
        !           193: 
        !           194: (defun mh-close-folder ()
        !           195:   "Process the outstanding delete and move commands in the current folder."
        !           196:   (interactive)
        !           197:   (message "closing folder...")
        !           198:   (mh-process-commands mh-folder-buffer)
        !           199:   (mh-unmark-all-headers t)
        !           200:   (mh-regenerate-headers "all")
        !           201:   (setq mode-line-format (mh-make-mode-line))
        !           202:   (message "closing folder...done"))
        !           203: 
        !           204: 
        !           205: (defun mh-copy-msg (&optional arg)
        !           206:   "Copy specified message(s) to another folder without deleting them."
        !           207:   (interactive "P")
        !           208:   (let ((msgs (if arg
        !           209:                  (mh-seq-to-msgs (mh-read-seq "Copy"))
        !           210:                  (mh-get-msg-num t))))
        !           211:     (mh-exec-cmd-no-wait "refile" msgs "-link" "-src"
        !           212:                         mh-folder-buffer
        !           213:                         (format "+%s" (mh-get-folder-name "Copy to" "" t)))))
        !           214: 
        !           215: 
        !           216: (defun mh-delete-msg (&optional arg)
        !           217:   "Marks the specified message(s) for later deletion."
        !           218:   (interactive "P")
        !           219:   (let ((msgs (if arg (mh-read-seq "Delete") (mh-get-msg-num t))))
        !           220:     (push msgs mh-delete-list)
        !           221:     (if arg
        !           222:        (mh-notate-seq msgs ?D mh-cmd-note)
        !           223:        (mh-notate ?D mh-cmd-note))
        !           224:     (mh-next-line 1)))
        !           225: 
        !           226: 
        !           227: (defun mh-exit ()
        !           228:   "Exit mh-e and process outstanding delete and move commands."
        !           229:   (interactive)
        !           230:   (cond ((not (or mh-delete-list mh-move-list))
        !           231:         (throw 'exit nil))
        !           232:        ((yes-or-no-p "Exit? ")
        !           233:         (mh-process-commands mh-folder-buffer)
        !           234:         (throw 'exit nil))))
        !           235: 
        !           236: 
        !           237: (defun mh-forward (to subject cc)
        !           238:   "Forward a letter."
        !           239:   (interactive "sTo: \nsSubject: \nsCc: ")
        !           240:   (save-window-excursion
        !           241:     (let ((msg-filename (mh-msg-filename))
        !           242:          (msg (mh-get-msg-num t)))
        !           243:       (cond ((or (not (file-exists-p (concat mh-user-path "draft")))
        !           244:                 (y-or-n-p "The file 'draft' exists.  Discard it? "))
        !           245:             (mh-exec-cmd "forw" "-build" mh-folder-buffer msg)
        !           246:             (mh-read-file (concat mh-user-path "draft") "draft")
        !           247:             (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc))
        !           248:            (t
        !           249:             (mh-read-file (concat mh-user-path "draft") "draft")))
        !           250: 
        !           251:       (goto-char (dot-min))
        !           252:       (delete-other-windows)
        !           253:       (if (mh-compose-and-send-mail "")
        !           254:          (mh-annotate "F" mh-folder-buffer msg
        !           255:                       "-component" "Forwared-To:"
        !           256:                       "-text" (concat to
        !           257:                                       (if (string= cc "")
        !           258:                                           ""
        !           259:                                           (concat ", " cc))))))))
        !           260: 
        !           261: 
        !           262: (defun mh-goto (number &optional no-error-if-no-message)
        !           263:   "Position the cursor at a particular message."
        !           264:   (interactive "nMessage number? ")
        !           265:   (pop-to-buffer mh-folder-buffer)
        !           266:   (let ((starting-place (dot)))
        !           267:     (goto-char (dot-min))
        !           268:     (cond ((not (re-search-forward (concat "^\+?[0-9a-z]*[ ]*" number) nil t))
        !           269:           (goto-char starting-place)
        !           270:           (if (not no-error-if-no-message) (error "No message %d " number)))
        !           271:          (t
        !           272:           (beginning-of-line)
        !           273:           (if (not mh-summarize) (mh-show))))))
        !           274: 
        !           275: 
        !           276: (defun mh-inc-folder ()
        !           277:   "inc(orporate) new mail in the current folder."
        !           278:   (interactive)
        !           279:   (mh-get-new-mail))
        !           280: 
        !           281: 
        !           282: (defun mh-indicate-seq (&optional arg)
        !           283:   "Add the specified message(s) to a sequence."
        !           284:   (interactive "P")
        !           285:   (let ((seq (mh-letter-to-seq last-input-char)))
        !           286:     (if (looking-at "^[0-9a-j]")
        !           287:        (if arg
        !           288:            (mh-remove-seq seq)
        !           289:            (mh-remove-msg-from-seq (mh-get-msg-num t) seq))
        !           290:        (mh-add-msg-to-seq (mh-get-msg-num t) seq))))
        !           291: 
        !           292: 
        !           293: (defun mh-kill-folder ()
        !           294:   "Removes the current folder."
        !           295:   (interactive)
        !           296:   (cond ((yes-or-no-p "Remove current folder ")
        !           297:         (pop-to-buffer " *mh-temp*")
        !           298:         (mh-exec-cmd "rmf" (buffer-name))
        !           299:         (message "Folder removed")
        !           300:         (throw 'exit nil))
        !           301:        (t
        !           302:         (message "Folder not removed"))))
        !           303: 
        !           304: 
        !           305: (defun mh-list-folders ()
        !           306:   "List mail folders."
        !           307:   (interactive)
        !           308:   (message "listing folders...")
        !           309:   (pop-to-buffer " *mh-temp*")
        !           310:   (erase-buffer)
        !           311:   (mh-exec-cmd-output "folders")
        !           312:   (goto-char (dot-min))
        !           313:   (message "listing folders...done"))
        !           314: 
        !           315: 
        !           316: (defun mh-print-msg (&optional arg)
        !           317:   "Print specified message(s) on a line printer."
        !           318:   (interactive "P")
        !           319:   (let ((msgs (if arg
        !           320:                  (reverse (mh-seq-to-msgs (mh-read-seq "Print")))
        !           321:                  (list (mh-get-msg-num t)))))
        !           322:     (message "printing message...")
        !           323:     (shell-command
        !           324:      (concat mh-lib "mhl -noclear -nobell "
        !           325:             (mh-msg-filenames msgs mh-folder-filename) " | "
        !           326:             (format mh-lpr-command-format
        !           327:                     (if arg
        !           328:                         "Mail"
        !           329:                         (concat mh-current-folder "/" (mh-get-msg-num t))))))
        !           330:     (message "printing message...done")))
        !           331: 
        !           332: 
        !           333: (defun mh-move-msg (&optional arg)
        !           334:   "Move specified message(s) to another folder."
        !           335:   (interactive "P")
        !           336:   (let ((msgs (if arg (mh-read-seq "Move") (mh-get-msg-num t))))
        !           337:     (setq mh-last-destination (mh-get-folder-name "Destination" "" t))
        !           338:     (mh-refile msgs mh-last-destination)
        !           339:     (mh-next-line 1)))
        !           340: 
        !           341: 
        !           342: (defun mh-next-line (&optional arg)
        !           343:   "Move to next undeleted message in window and display body if summary
        !           344: flag set."
        !           345:   (interactive "p")
        !           346:   (pop-to-buffer mh-folder-buffer)
        !           347:   (forward-line (if arg arg 1))
        !           348:   (if (not (re-search-forward "^....[^D^]" nil 0 arg))
        !           349:       (progn
        !           350:        (forward-line -1)
        !           351:        (message "No more messages"))
        !           352:       (beginning-of-line))
        !           353:   (if (not mh-summarize) (mh-show)))
        !           354: 
        !           355: 
        !           356: (defun mh-renumber-folder ()
        !           357:   "Renumber messages in folder to be 1..N."
        !           358:   (interactive)
        !           359:   (message "packing buffer...")
        !           360:   (pop-to-buffer mh-folder-buffer)
        !           361:   (mh-pack-folder)
        !           362:   (mh-unmark-all-headers nil)
        !           363:   (mh-position-to-current)
        !           364:   (message "packing buffer...done"))
        !           365: 
        !           366: 
        !           367: (defun mh-page-digest ()
        !           368:   "Advance displayed message to next digested message."
        !           369:   (interactive)
        !           370:   (save-excursion
        !           371:     (pop-to-buffer mh-show-buffer)
        !           372:     (move-to-window-line nil)
        !           373:     (let ((case-fold-search nil))
        !           374:       (when (not (search-forward "\nFrom:" nil t))
        !           375:        (other-window -1)
        !           376:        (error "No more messages")))
        !           377:     (recenter 0)
        !           378:     (other-window -1)))
        !           379: 
        !           380: 
        !           381: (defun mh-previous-line (&optional arg)
        !           382:   "Move to previous message in window and display body if summary flag set."
        !           383:   (interactive "p")
        !           384:   (pop-to-buffer mh-folder-buffer)
        !           385:   (forward-line (- (if arg arg 1)))
        !           386:   (if (not (re-search-backward "^....[^D^]" nil 0 arg))
        !           387:       (message "Beginning of messages")
        !           388:       (if (not mh-summarize) (mh-show))))
        !           389: 
        !           390: 
        !           391: (defun mh-previous-page ()
        !           392:   "Page the displayed message backwards."
        !           393:   (interactive)
        !           394:   (save-excursion
        !           395:     (pop-to-buffer mh-show-buffer)
        !           396:     (scroll-down nil)
        !           397:     (other-window -1)))
        !           398: 
        !           399: 
        !           400: (defun mh-quit ()
        !           401:   "Quit mh-e without processing outstanding delete and move commands."
        !           402:   (interactive)
        !           403:   (if (and (or mh-delete-list mh-move-list)
        !           404:           (not (yes-or-no-p "Quit without processing? ")))
        !           405:       (mh-process-commands mh-folder-buffer))
        !           406:   (throw 'exit nil))
        !           407: 
        !           408: 
        !           409: (defun mh-rescan-folder (&optional arg)
        !           410:   "Optionally process commands in current folder and (re)scan it."
        !           411:   (interactive "P")
        !           412:   (pop-to-buffer mh-folder-buffer)
        !           413:   (if (and (or mh-delete-list mh-move-list)
        !           414:           (y-or-n-p "Process commands? "))
        !           415:       (mh-process-commands mh-folder-buffer))
        !           416:   (mh-regenerate-headers (if arg (read-string "Range? ") "all"))
        !           417:   (setq mode-line-format (mh-make-mode-line))
        !           418:   (mh-unmark-all-headers nil)
        !           419:   (mh-position-to-current))
        !           420: 
        !           421: 
        !           422: (defun mh-redistribute (to cc)
        !           423:   "Redistribute a letter."
        !           424:   (interactive "sTo: \nsCc: ")
        !           425:   (save-window-excursion
        !           426:     (let ((msg-filename (mh-msg-filename))
        !           427:          (msg (mh-get-msg-num t)))
        !           428:       (mh-read-file (concat mh-user-path "draft") "draft")
        !           429:       (delete-other-windows)
        !           430:       (when (or (zerop (buffer-size))
        !           431:                (not (y-or-n-p "The file 'draft' exists.  Redistribute? ")))
        !           432:          (erase-buffer)
        !           433:          (insert-file-contents msg-filename)
        !           434:          (goto-char (dot-min))
        !           435:          (insert "Resent-To: " to "\n")
        !           436:          (if (not (string= cc ""))
        !           437:              (insert "Resent-cc: " cc "\n")))
        !           438: 
        !           439:       (if (mh-compose-and-send-mail "-dist")
        !           440:          (mh-annotate "F" mh-folder-buffer msg
        !           441:                       "-component" "Distributed-to:"
        !           442:                       "-text" (concat to
        !           443:                                       (if (string= cc "")
        !           444:                                           ""
        !           445:                                           (concat ", " cc))))))))
        !           446: 
        !           447: 
        !           448: (defun mh-re-move ()
        !           449:   "Move specified message to same folder as last move."
        !           450:   (interactive)
        !           451:   (if (null mh-last-destination)
        !           452:       (error "No previous move")
        !           453:       (mh-refile (mh-get-msg-num t) mh-last-destination)))
        !           454: 
        !           455: 
        !           456: (defun mh-search-folder ()
        !           457:   "Search folder for letters matching a pattern."
        !           458:   (interactive)
        !           459:   (let* ((range "all")
        !           460:         (seq (mh-new-seq))
        !           461:         (pattern nil))
        !           462:     (mh-get-pick-pattern " *pattern*")
        !           463:     (while (setq pattern (mh-next-pick-field " *pattern*"))
        !           464:       (setq msgs
        !           465:            (mh-seq-from-command seq
        !           466:                                 (nconc (cons "pick" pattern)
        !           467:                                        (list (concat "+" mh-current-folder)
        !           468:                                              range
        !           469:                                              "-sequence" seq "-list"))))
        !           470:       (setq range seq))
        !           471:     (mh-apply-to-seq seq 'mh-notate  (mh-seq-to-notation seq) 0)))
        !           472: 
        !           473: 
        !           474: (defun mh-send (to subject cc)
        !           475:   "Compose and send a letter."
        !           476:   (interactive "sTo: \nsSubject: \nsCc: ")
        !           477:   (message "Composing a message...")
        !           478:   (save-window-excursion
        !           479:     (mh-read-file (concat mh-user-path "draft") "draft")
        !           480:     (delete-other-windows)
        !           481:     (when (or (zerop (buffer-size))
        !           482:              (not (y-or-n-p "The file 'draft' exists.  Use it? ")))
        !           483:        (erase-buffer)
        !           484:        (if (file-exists-p (concat mh-user-path "components"))
        !           485:            (insert-file-contents (concat mh-user-path "components"))
        !           486:            (if (file-exists-p (concat mh-lib "components"))
        !           487:                (insert-file-contents (concat mh-lib "components"))
        !           488:                (error "Can't find components")))
        !           489:        (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc)
        !           490:        (goto-char (dot-max))
        !           491:        (message "Composing a message...done"))
        !           492:     (mh-compose-and-send-mail "")))
        !           493: 
        !           494: 
        !           495: (defun mh-show ()
        !           496:   "Show message indicated by cursor in scan buffer."
        !           497:   (interactive)
        !           498:   (setq mh-summarize nil)
        !           499:   (pop-to-buffer mh-folder-buffer)
        !           500:   (let ((msgn (mh-get-msg-num t))
        !           501:        (msg-filename (mh-msg-filename))
        !           502:        (folder mh-current-folder))
        !           503:     (if (not (file-exists-p msg-filename))
        !           504:        (error "Message %d does not exist." msgn))
        !           505:     (push msgn mh-shown-msgs)
        !           506:     (switch-to-buffer mh-show-buffer)
        !           507:     (erase-buffer)
        !           508:     (insert-file-contents msg-filename)
        !           509:     (setq buffer-file-name msg-filename)
        !           510:     (mh-letter-mode)
        !           511:     (cond (mh-clean-message-header
        !           512:           (mh-clean-message-header)
        !           513:           (goto-char (dot-min)))
        !           514:          (t
        !           515:           (let ((case-fold-search nil))
        !           516:             (re-search-forward "^To:\\|^From:\\|^Subject:" nil t)
        !           517:             (beginning-of-line)
        !           518:             (recenter 0))))
        !           519:     (set-buffer-modified-p nil)
        !           520:     (setq mode-line-format
        !           521:          (concat "{%b} %[%p of +" folder "/" msgn "%]  %M"))
        !           522:     ;; These contortions are to force the summary line to be the top window.
        !           523:     (pop-to-buffer mh-folder-buffer)
        !           524:     (delete-other-windows)
        !           525:     (pop-to-buffer mh-show-buffer)
        !           526:     (pop-to-buffer mh-folder-buffer)
        !           527:     (shrink-window (- (window-height) mh-summary-height))
        !           528:     (recenter 1)))
        !           529: 
        !           530: 
        !           531: (defun mh-summary ()
        !           532:   "Show a summary of mh-e commands."
        !           533:   (interactive)
        !           534:   (message
        !           535: "Next Prev Go Del ^ ! Copy Undo . Toggle Ans Forw Redist Send List Quit Exit")
        !           536:   (sit-for 5))
        !           537: 
        !           538: 
        !           539: (defun mh-toggle-summarize ()
        !           540:   "Turn the summary mode of displaying messages on or off."
        !           541:   (interactive)
        !           542:   (setq mh-summarize (not mh-summarize))
        !           543:   (if (not mh-summarize)
        !           544:       (mh-show)
        !           545:       (delete-other-windows)))
        !           546: 
        !           547: 
        !           548: (defun mh-undo (&optional arg)
        !           549:   "Undo the deletion or move of the specified message(s)."
        !           550:   (interactive "P")
        !           551:   (cond ((looking-at "^....D")
        !           552:         (let ((msgs (if arg (mh-read-seq "undelete") (mh-get-msg-num t))))
        !           553:           (setq mh-delete-list (delq msgs mh-delete-list))
        !           554:           (if arg
        !           555:               (mh-notate-seq msgs ?  mh-cmd-note)
        !           556:               (mh-notate ?  mh-cmd-note))))
        !           557: 
        !           558:        ((looking-at "^....^")
        !           559:         (let ((msgs (if arg (mh-read-seq "unmove") (mh-get-msg-num t))))
        !           560:           (mapcar
        !           561:            (function (lambda (move) (setcdr msgs (delq msgs (cdr move)))))
        !           562:            mh-move-list)
        !           563:           (if arg
        !           564:               (mh-notate-seq msgs ?  mh-cmd-note)
        !           565:               (mh-notate ?  mh-cmd-note))))
        !           566: 
        !           567:        (t nil)))
        !           568: 
        !           569: 
        !           570: (defun mh-visit-folder (&optional arg)
        !           571:   "Visit a new folder."
        !           572:   (interactive "p")
        !           573:   (let* (mh-current-folder
        !           574:         mh-folder-buffer
        !           575:         (folder (mh-get-folder-name "visit" "" t))
        !           576:         (mh-show-buffer (concat "show-" folder)))
        !           577:     (save-window-excursion
        !           578:       (switch-to-buffer (concat "+" folder))
        !           579:       (if (> (buffer-size) 0)
        !           580:          (error "folder +%s is open. close it before revisiting." folder))
        !           581:       (mh-scan-folder folder (if arg (read-string "range? ") "all"))
        !           582:       (pop-to-buffer mh-show-buffer)
        !           583:       (unwind-protect
        !           584:          (mh-command-loop)
        !           585:        (kill-buffer mh-show-buffer)
        !           586:        (kill-buffer mh-folder-buffer)))))
        !           587: 
        !           588: 
        !           589: 
        !           590: ;;; Support routines.
        !           591: 
        !           592: (defun mh-command-loop ()
        !           593:   "Read and execute mh commands."
        !           594:   (pop-to-buffer mh-folder-buffer)
        !           595:   (delete-other-windows)
        !           596:   (recursive-edit))
        !           597: 
        !           598: 
        !           599: (defun mh-refile (msgs destination)
        !           600:   "Refile the msgs in the folder called destination."
        !           601:   (let ((others (assoc destination mh-move-list)))
        !           602:     (if others
        !           603:        (setcdr others (cons msgs (cdr others)))
        !           604:        (push (cons destination (list msgs)) mh-move-list))
        !           605:     (if (integerp msgs)
        !           606:        (mh-notate ?^ mh-cmd-note)
        !           607:        (mh-notate-seq msgs ?^ mh-cmd-note))))
        !           608: 
        !           609: 
        !           610: (defun mh-clean-message-header ()
        !           611:   "Flush extraneous lines in a message header.  The variable
        !           612: mh-invisible-headers contains a regular expression specifying these lines."
        !           613:   (save-restriction
        !           614:     (goto-char (dot-min))
        !           615:     (search-forward "\n\n" nil t)
        !           616:     (narrow-to-region (dot-min) (dot))
        !           617:     (goto-char (dot-min))
        !           618:     (while (re-search-forward mh-invisible-headers nil t)
        !           619:       (beginning-of-line)
        !           620:       (kill-line 1)
        !           621:       (while (looking-at "^[ \t]+")
        !           622:        (beginning-of-line)
        !           623:        (kill-line 1)))))
        !           624: 
        !           625: 
        !           626: (defun mh-read-file (file-name buffer-name)
        !           627:   "Read file FILE-NAME into buffer BUFFER-NAME.  No errors if disk file
        !           628: has been modified."
        !           629:   (pop-to-buffer buffer-name)
        !           630:   (kill-buffer buffer-name)
        !           631:   (pop-to-buffer buffer-name)
        !           632:   (if (file-exists-p file-name)
        !           633:       (insert-file-contents file-name t)
        !           634:       (setq buffer-file-name file-name))
        !           635:   (set-buffer-modified-p nil))
        !           636: 
        !           637: 
        !           638: 
        !           639: ;;; The folder data abstraction.
        !           640: 
        !           641: (defun mh-make-folder (name)
        !           642:   "Create and initialize a new mail folder called NAME and make
        !           643: it the current folder."
        !           644:   (setq mh-current-folder name)
        !           645:   (setq mh-folder-buffer (concat "+" name))
        !           646:   (switch-to-buffer mh-folder-buffer)
        !           647:   (kill-all-local-variables)
        !           648:   (setq buffer-read-only nil)
        !           649:   (mh-folder-mode)
        !           650:   (erase-buffer)
        !           651:   (make-variable-buffer-local 'mh-folder-filename)
        !           652:        ;; "e.g./usr/foldbar/Mail/inbox/"
        !           653:   (setq mh-folder-filename (concat mh-user-path name "/"))
        !           654:   (make-variable-buffer-local 'mh-summarize)    ; Show scan list only?
        !           655:   (setq mh-summarize t)
        !           656:   (make-variable-buffer-local 'mh-next-seq-num)  ; Index of free sequence id
        !           657:   (setq mh-next-seq-num 0)
        !           658:   (make-variable-buffer-local 'mh-delete-list)  ; List of msgs nums to delete
        !           659:   (setq mh-delete-list nil)
        !           660:   (make-variable-buffer-local 'mh-move-list)    ; Alist of dest . msgs nums
        !           661:   (setq mh-move-list nil)
        !           662:   (make-variable-buffer-local 'mh-seq-list)     ; Alist of seq . msgs nums
        !           663:   (setq mh-seq-list nil)
        !           664:   (make-variable-buffer-local 'mh-shown-msgs)  ; List of msgs shown
        !           665:   (setq mh-shown-msgs nil)
        !           666:   (setq buffer-read-only t))
        !           667: 
        !           668: 
        !           669: (defun mh-folder-mode ()
        !           670:   "    \\[mh-next-line]: next message                  \\[mh-previous-line]: p
        !           671: revious message
        !           672:     \\[mh-delete-msg]: delete (mark for deletion)      \\[mh-move-msg]: put (m
        !           673: ark for moving)
        !           674:     \\[mh-undo]: undo last delete or mark              \\[mh-re-move]: repeat 
        !           675: last ^ command
        !           676:     \\[mh-copy-msg]: copy message to another folder
        !           677:     \\[mh-show]: type message                  \\[mh-toggle-summarize]: toggle
        !           678:  summarize mode
        !           679:     \\[scroll-other-window]: page message                     \\[mh-previous-p
        !           680: age]: page message backwards
        !           681:     \\[mh-print-msg]: print message                    \\[mh-goto]: goto a mes
        !           682: sage
        !           683:     \\[mh-exit]: exit                          \\[mh-quit]: quit
        !           684:     \\[mh-send]: send a message                        \\[mh-redistribute]: redistribu
        !           685: te a message
        !           686:     \\[mh-answer]: answer a message            \\[mh-forward]: forward a messa
        !           687: ge
        !           688:   \\[mh-visit-folder]: visit folder                  \\[mh-inc-folder]: inc ma
        !           689: il
        !           690:   \\[mh-close-folder]: close folder                  \\[mh-kill-folder]: kill 
        !           691: folder
        !           692:   \\[mh-list-folders]: list folders                  \\[mh-renumber-folder]: p
        !           693: ack folder
        !           694:   \\[mh-rescan-folder]: rescan folder                \\[mh-search-folder]: sea
        !           695: rch folder
        !           696: Edit the scan list, marking messages.
        !           697: When you are done, type 'e'.  The messages marked for deletion will be
        !           698: deleted and messages marked for moving will be moved.
        !           699: In any of the submodes, such as editing a message or composing a message,
        !           700: exit with \\[exit-emacs]."
        !           701:   (auto-save-mode -1)
        !           702:   (use-local-map mh-keymap)
        !           703:   (setq major-mode 'mh-folder-mode)
        !           704:   (setq mode-name "mh-folder")
        !           705:   (if (and (boundp 'mh-folder-mode-hook) mh-folder-mode-hook)
        !           706:       (funcall mh-folder-mode-hook)))
        !           707: 
        !           708: 
        !           709: (defun mh-scan-folder (folder range)
        !           710:   "Scan the folder FOLDER over the range RANGE."
        !           711:   (mh-make-folder folder)
        !           712:   (mh-regenerate-headers range)
        !           713:   (when (looking-at "scan: no messages ")
        !           714:       (let ((buffer-read-only nil))
        !           715:        (erase-buffer))
        !           716:       (if (string= range "all")
        !           717:          (message  "Folder +%s is empty" folder)
        !           718:          (message  "No messages in +%s, range %s" folder range))
        !           719:       (sit-for 5))
        !           720:   (setq mode-line-format (mh-make-mode-line))
        !           721:   (mh-unmark-all-headers nil)
        !           722:   (mh-position-to-current))
        !           723: 
        !           724: 
        !           725: (defun mh-regenerate-headers (range)
        !           726:   "Replace buffer with scan of its contents over range RANGE."
        !           727:   (let ((buffer-read-only nil))
        !           728:     (message (format "scanning %s..." (buffer-name)))
        !           729:     (delete-other-windows)
        !           730:     (erase-buffer)
        !           731:     (mh-exec-cmd-output "scan" (buffer-name) range)
        !           732:     (goto-char (dot-min))
        !           733:     (message (format "scanning %s...done" (buffer-name)))
        !           734:     ))
        !           735: 
        !           736: 
        !           737: (defun mh-get-new-mail ()
        !           738:   "Read new mail into the current buffer."
        !           739:   (let ((buffer-read-only nil))
        !           740:     (message (format "inc %s..." (buffer-name)))
        !           741:     (goto-char (dot-max))
        !           742:     (set-mark (dot))
        !           743:     (mh-exec-cmd-output "inc")
        !           744:     (message (format "inc %s...done" (buffer-name)))
        !           745:     (goto-char (mark))
        !           746:     (cond ((looking-at "inc: no mail")
        !           747:           (kill-line 1)
        !           748:           (message "No new mail")
        !           749:           (sit-for 5))
        !           750:          (t
        !           751:           (kill-line 2))))
        !           752:     (setq mode-line-format (mh-make-mode-line)))
        !           753: 
        !           754: 
        !           755: (defun mh-make-mode-line ()
        !           756:   "Returns a string for mode-line-format."
        !           757:   (save-excursion
        !           758:    (goto-char (dot-min))
        !           759:    (set-mark (dot))
        !           760:    (goto-char (dot-max))
        !           761:    (let ((lines (count-lines (dot) (mark))))
        !           762:      (goto-char (dot-min))
        !           763:      (let ((first (mh-get-msg-num nil))
        !           764:           (case-fold-search nil))
        !           765:        (re-search-forward "[ ]*[0-9]*\\+" nil t)
        !           766:        (let ((current (mh-get-msg-num nil)))
        !           767:         (goto-char (dot-max))
        !           768:         (previous-line 1)
        !           769:         (let ((last (mh-get-msg-num nil)))
        !           770:           (concat "{%b} %[" lines " messages"
        !           771:                   (if (> lines 0)
        !           772:                       (concat " (" first " - " last ")")
        !           773:                       "")
        !           774:                   (if current
        !           775:                       (concat " cur = " current)
        !           776:                       "")
        !           777:                   "%] ")))))))
        !           778: 
        !           779: 
        !           780: (defun mh-unmark-all-headers (remove-all-flags)
        !           781:   "This function removes all + flags from the headers, and if called
        !           782:   with a non-nil argument, removes all D and ^ flags too."
        !           783:   (switch-to-buffer mh-folder-buffer)
        !           784:   (let ((buffer-read-only nil)
        !           785:        (case-fold-search nil))
        !           786:     (goto-char (dot-min))
        !           787:     (while (if remove-all-flags
        !           788:               (re-search-forward "^....\\+" nil t)
        !           789:               (re-search-forward "^\\D\\|^\\^\\|^....\\+" nil t))
        !           790:       (delete-backward-char 1)
        !           791:       (insert " "))))
        !           792: 
        !           793: 
        !           794: (defun mh-position-to-current ()
        !           795:   "Position the cursor at the current message."
        !           796:   (let ((curmsg (mh-get-cur-msg mh-folder-filename)))
        !           797:     (when (or (zerop curmsg) (mh-goto curmsg t))
        !           798:        (message "No message %s" curmsg)
        !           799:        (goto-char (dot-max))
        !           800:        (forward-line -1))
        !           801:     (when (looking-at "[ ]+[0-9]+")
        !           802:        (mh-notate ?+ 0)
        !           803:        (recenter 0))))
        !           804: 
        !           805: 
        !           806: (defun mh-pack-folder ()
        !           807:   "Closes and packs the current folder."
        !           808:   (let ((buffer-read-only nil))
        !           809:     (message "closing folder...")
        !           810:     (mh-process-commands mh-folder-buffer)
        !           811:     (message "packing folder...")
        !           812:     (mh-exec-cmd "folder" mh-folder-buffer "-pack")
        !           813:     (mh-regenerate-headers "all")
        !           814:     (message "packing done"))
        !           815:   (setq mode-line-format (mh-make-mode-line)))
        !           816: 
        !           817: 
        !           818: (defun mh-apply-to-message-list (func list)
        !           819:   "Apply function FUNC to each item in a message-list LIST,
        !           820: passing the name and list of messages as arguments."
        !           821:   (mapcar (function (lambda (l) (apply func (list (car l) (cdr l))))) list))
        !           822: 
        !           823: 
        !           824: (defun mh-process-commands (buffer)
        !           825:   "Process outstanding commands for the buffer BUFFER."
        !           826:   (message "Processing deletes and moves...")
        !           827:   (switch-to-buffer buffer)
        !           828:   (let ((buffer-read-only nil))
        !           829:     ;; Sequences must be first
        !           830:     (mh-process-seq-commands mh-seq-list)
        !           831: 
        !           832:     ;; Then refile messages
        !           833:     (mh-apply-to-message-list
        !           834:      (function (lambda (dest msgs)
        !           835:                 (apply 'mh-exec-cmd
        !           836:                        (nconc (cons "refile" msgs)
        !           837:                               (list "-src" (format "%s" buffer)
        !           838:                                     (format "+%s" dest))))))
        !           839:      mh-move-list)
        !           840: 
        !           841:     ;; Now delete messages
        !           842:     (if mh-delete-list
        !           843:        (apply 'mh-exec-cmd
        !           844:               (nconc (list "rmm" (format "%s" buffer)) mh-delete-list)))
        !           845: 
        !           846:     ;; Finally update unseen sequence
        !           847:     (if mh-shown-msgs
        !           848:        (apply 'mh-exec-cmd-no-wait
        !           849:               (nconc (list "show" (format "%s" buffer))
        !           850:                      mh-shown-msgs
        !           851:                      (list "-noformat"))))
        !           852: 
        !           853:     (setq mh-delete-list nil
        !           854:          mh-move-list nil
        !           855:          mh-seq-list nil
        !           856:          mh-shown-msgs nil))
        !           857:   (message "Processing deletes and moves...done"))
        !           858: 
        !           859: 
        !           860: 
        !           861: ;;; Routines for editing a message.
        !           862: 
        !           863: (defun mh-letter-mode ()
        !           864:   "Mode for composing letters in mh.
        !           865: ^N and ^P work normally in the body of a letter but hug the end of field names
        !           866: in the header.
        !           867: ^X^C exits and sends a letter."
        !           868:   (text-mode)
        !           869:   (if mh-auto-fill-letters
        !           870:       (auto-fill-mode 1))
        !           871:   (setq paragraph-separate "^[- \t\^L]*$")
        !           872:   (setq paragraph-start "^$\\|^\^L\\|^-+$")
        !           873:   (when (not mh-letter-mode-map)
        !           874:       (setq mh-letter-mode-map (copy-sequence text-mode-map))
        !           875:       (define-key mh-letter-mode-map "\^N" 'mh-header-next)
        !           876:       (define-key mh-letter-mode-map "\^P" 'mh-header-previous))
        !           877:   (use-local-map mh-letter-mode-map)
        !           878:   (setq major-mode 'mh-letter-mode)
        !           879:   (setq mode-name "mh-letter")
        !           880:   (if (and (boundp 'mh-letter-mode-hook) mh-letter-mode-hook)
        !           881:       (funcall mh-letter-mode-hook)))
        !           882: 
        !           883: 
        !           884: (defun mh-header-next (&optional arg)
        !           885:   "Modified ^N command that skips to end of header field names."
        !           886:   (interactive "p")
        !           887:   (next-line (if arg arg 1))
        !           888:   (mh-header-line-position))
        !           889: 
        !           890: 
        !           891: (defun mh-header-previous (&optional arg)
        !           892:   "Modified ^P command that skips to end of header field names."
        !           893:   (interactive "p")
        !           894:   (previous-line (if arg arg 1))
        !           895:   (mh-header-line-position))
        !           896: 
        !           897: 
        !           898: (defun mh-dot-in-header ()
        !           899:     "t iff cursor in message header."
        !           900:     (save-excursion
        !           901:       (let ((wasdot (dot))
        !           902:            (case-fold-search nil))
        !           903:        (goto-char (dot-min))
        !           904:        (re-search-forward "^-*$" nil t)
        !           905:        (beginning-of-line)
        !           906:        (backward-char 1)
        !           907:        (>= (dot) wasdot))))
        !           908: 
        !           909: 
        !           910: (defun mh-header-line-position ()
        !           911:   "Position cursor at end of field name when in header."
        !           912:   (if (mh-dot-in-header)
        !           913:       (when (save-excursion (beginning-of-line) (not (looking-at " \\|\t")))
        !           914:          (beginning-of-line)
        !           915:          (search-forward ":" nil t)
        !           916:          (if (eolp)
        !           917:              (insert " ")
        !           918:              (while (looking-at "[ \t]") (forward-char 1))))))
        !           919: 
        !           920: 
        !           921: 
        !           922: ;;; Routines to make a search pattern and search for a message.
        !           923: 
        !           924: (defun mh-get-pick-pattern (buffer)
        !           925:   "Prompt the user for a pattern to search for in messages.  Upon return,
        !           926: current buffer contains the filled-in template."
        !           927:   (save-window-excursion
        !           928:     (pop-to-buffer buffer)
        !           929:     (if (or (zerop (buffer-size))
        !           930:            (not (y-or-n-p "Reuse pattern? ")))
        !           931:        (mh-pick-template)
        !           932:        (message ""))
        !           933:     (local-set-key "\^X\^C" 'mh-make-pick-pattern)
        !           934:     (let ((mode-line-format "{%b}\tPick Pattern\t^X^C to exit and search"))
        !           935:       (catch 'mh-pattern (recursive-edit)))))
        !           936: 
        !           937: 
        !           938: (defun mh-make-pick-pattern ()
        !           939:   (interactive)
        !           940:   (goto-char (dot-min))
        !           941:   (throw 'mh-pattern nil))
        !           942: 
        !           943: 
        !           944: (defun mh-pick-template ()
        !           945:   (erase-buffer)
        !           946:   (insert "From: \n"
        !           947:          "To: \n"
        !           948:          "Cc: \n"
        !           949:          "Date: \n"
        !           950:          "Subject: \n"
        !           951:          "---------\n")
        !           952:   (goto-char (dot-min))
        !           953:   (end-of-line)
        !           954:   (mh-letter-mode))
        !           955: 
        !           956: 
        !           957: (defun mh-next-pick-field (buffer)
        !           958:   "Return a pattern to search for messages containing the next field, or NIL
        !           959: if no fields remain."
        !           960:   (save-excursion
        !           961:     (pop-to-buffer buffer)
        !           962:     (let ((pat ())
        !           963:          (case-fold-search t))
        !           964:       (cond ((re-search-forward "^\\([a-z].*\\):[ \t]*\\([a-z0-9].*\\)$" nil t)
        !           965:             (region-around-match 1)
        !           966:             (let ((component (concat "-" (downcase (region-to-string)))))
        !           967:               (region-around-match 2)
        !           968:               (setq pat (nconc (list component (region-to-string)) pat)))
        !           969:             (forward-line 1)
        !           970:             pat)
        !           971:            ((re-search-forward "^-*$" nil t)
        !           972:             (forward-char 1)
        !           973:             (set-mark (dot))
        !           974:             (goto-char (dot-max))
        !           975:             (let ((body (region-to-string)))
        !           976:               (if (> (length body) 0)
        !           977:                   (list "-search" body)
        !           978:                   nil)))
        !           979:            (t
        !           980:             nil)))))
        !           981: 
        !           982: 
        !           983: 
        !           984: ;;; Routines compose and send a letter.
        !           985: 
        !           986: (defun mh-compose-and-send-mail (send-args)
        !           987:   "Edit a draft message and send or save it.  SEND-ARGS is passed to the
        !           988: send command.  Returns t if mail is being sent."
        !           989:   (save-window-excursion
        !           990:     (pop-to-buffer "draft")
        !           991:     (mh-letter-mode)
        !           992:     (local-set-key "\^X\^C" 'mh-send-letter)
        !           993:     (local-set-key "\^X\^Y" 'mh-insert-letter)
        !           994:     (mh-header-line-position)
        !           995:     (let ((mode-line-format
        !           996:           "{%b}  %[Mail/draft%] (%p - %m)  (^X^C to finish  ^X^Y to yank msg) 
        !           997:  %M"))
        !           998:       (catch 'mail-send (recursive-edit)))))
        !           999: 
        !          1000: 
        !          1001: (defun mh-send-letter ()
        !          1002:   "Prompt the user as to the disposition of the just-composed letter."
        !          1003:   (interactive)
        !          1004:   (save-buffer)
        !          1005:   (let ((mode-line-format "{%b}  %[Mail/draft%]  (%p - %m)  %M")
        !          1006:        (action (mh-get-response
        !          1007:                 "Ready to send. Action (s, q, e, ?): "
        !          1008:                 '(?s ?b ?q ?e ?\^C)
        !          1009:                 "S-end, Q-uit, E-dit ")))
        !          1010:     (cond ((equal action ?s)
        !          1011:           (message "Sending...")
        !          1012:           (mh-exec-cmd-no-wait "send" "-push" "-unique" send-args
        !          1013:                                (buffer-file-name))
        !          1014:           (message "Sending...done")
        !          1015:           (throw 'mail-send t))
        !          1016: 
        !          1017:          ((equal action ?q)
        !          1018:           (message "Not sent... Message remains in buffer draft")
        !          1019:           (throw 'mail-send nil)))))
        !          1020: 
        !          1021: 
        !          1022: (defun mh-insert-letter ()
        !          1023:   "Insert a message in the current letter."
        !          1024:   (interactive)
        !          1025:   (let ((folder (mh-get-folder-name "Message from" mh-current-folder nil))
        !          1026:        (message (string-to-int (read-input "Message number: " ""))))
        !          1027:     (insert-file-contents (concat mh-user-path folder "/" message))))
        !          1028: 
        !          1029: 
        !          1030: 
        !          1031: ;;; Commands to manipulate sequences.
        !          1032: 
        !          1033: (defmacro mh-seq-name (pair)
        !          1034:   (list 'car pair))
        !          1035: 
        !          1036: (defmacro mh-seq-msgs (pair)
        !          1037:   (list 'cdr pair))
        !          1038: 
        !          1039: 
        !          1040: (defun mh-seq-to-msgs (seq)
        !          1041:   "Returns the list of messages in sequence SEQ."
        !          1042:   (mh-seq-msgs (assoc seq mh-seq-list)))
        !          1043: 
        !          1044: 
        !          1045: (defun mh-read-seq (prompt)
        !          1046:   "Prompt the user with PROMPT and read a sequence name."
        !          1047:   (mh-letter-to-seq
        !          1048:    (string-to-char (read-string (format "%s %s" prompt "sequence: ")))))
        !          1049: 
        !          1050: 
        !          1051: (defun mh-seq-from-command (seq command)
        !          1052:   "Make a sequence called SEQ by executing the form COMMAND."
        !          1053:   (let ((msgs ())
        !          1054:        (case-fold-search t))
        !          1055:     (save-window-excursion
        !          1056:       (apply 'mh-exec-cmd-quiet command)
        !          1057:       (pop-to-buffer " *mh-temp*")
        !          1058:       (goto-char (dot-min))
        !          1059:       (while (re-search-forward "\\([0-9]+\\)" nil t)
        !          1060:        (region-around-match 0)         ; BUG in GNU EMACS: should be 1!
        !          1061:        (let ((num (string-to-int (region-to-string))))
        !          1062:          (if (not (zerop num))
        !          1063:              (push num msgs)))))
        !          1064: 
        !          1065:       (push (cons seq msgs) mh-seq-list)
        !          1066:       msgs))
        !          1067: 
        !          1068: 
        !          1069: (defun mh-remove-seq (seq)
        !          1070:   "Delete the sequence SEQ."
        !          1071:   (let ((entry (assoc seq mh-seq-list)))
        !          1072:     (setq mh-seq-list (delq (car entry) mh-seq-list))
        !          1073:     (mh-apply-to-seq (mh-seq-msgs (car entry)) 'mh-notate ?  0)))
        !          1074: 
        !          1075: 
        !          1076: (defun mh-remove-msg-from-seq (msg-num seq)
        !          1077:   "Remove a message MSG-NUM from the sequence SEQ."
        !          1078:   (let ((seq (assoc seq mh-seq-list)))
        !          1079:     (setcdr (car seq) (delq msg-num (mh-seq-msgs (car seq)))))
        !          1080:   (mh-notate ?  mh-cmd-note))
        !          1081: 
        !          1082: 
        !          1083: (defun mh-add-msg-to-seq (msg-num seq)
        !          1084:   "Add a message MSG-NUM to a sequence SEQ."
        !          1085:   (let ((seq-list (assoc seq mh-seq-list)))
        !          1086:     (mh-notate (mh-seq-to-notation seq) 0)
        !          1087:     (if (null seq-list)
        !          1088:        (push (cons seq (list msg-num)) mh-seq-list)
        !          1089:        (setcdr seq-list (cons msg-num (cdr seq-list))))))
        !          1090: 
        !          1091: 
        !          1092: 
        !          1093: (defun mh-new-seq ()
        !          1094:   "Return a new sequence name."
        !          1095:   (save-excursion
        !          1096:     (switch-to-buffer mh-folder-buffer)
        !          1097:     (if (= mh-next-seq-num 10)
        !          1098:        (error "No more sequences"))
        !          1099:     (setq mh-next-seq-num (+ mh-next-seq-num 1))
        !          1100:     (mh-letter-to-seq (+ (1- mh-next-seq-num) ?a))))
        !          1101: 
        !          1102: 
        !          1103: (defun mh-letter-to-seq (letter)
        !          1104:   "Given a LETTER, return a string that is a valid sequence name."
        !          1105:   (cond ((and (>= letter ?0) (< letter ?9))
        !          1106:         (intern (concat "mhe" (char-to-string letter))))
        !          1107:        ((and (>= letter ?a) (< letter ?z))
        !          1108:         (intern (concat "mhe" (char-to-string letter))))
        !          1109:        (t
        !          1110:         (error "A sequence is named 0...9"))))
        !          1111: 
        !          1112: 
        !          1113: (defun mh-seq-to-notation (seq)
        !          1114:   "Return the string used to indicate sequence SEQ in a scan listing."
        !          1115:   (string-to-char (substring (symbol-name seq) 3 4)))
        !          1116: 
        !          1117: 
        !          1118: (defun mh-notate-seq (seq notation offset)
        !          1119:   "Mark all messages in the sequence SEQ with the NOTATION at character
        !          1120: OFFSET."
        !          1121:   (mh-apply-to-seq seq 'mh-notate notation offset))
        !          1122: 
        !          1123: 
        !          1124: (defun mh-apply-to-seq (seq function &rest args)
        !          1125:   "For each message in sequence SEQ, evaluate the FUNCTION with ARGS."
        !          1126:   (mapcar (function (lambda (msg) (mh-goto msg) (apply function args)))
        !          1127:          (mh-seq-to-msgs seq)))
        !          1128: 
        !          1129: 
        !          1130: (defun mh-process-seq-commands (seq-list)
        !          1131:   "Process outstanding sequence commands for the sequences in SEQ-LIST."
        !          1132:   (mh-apply-to-message-list
        !          1133:    (function (lambda (seq msgs)
        !          1134:               (apply 'mh-exec-cmd-quiet
        !          1135:                      (nconc (list "mark" "-zero" "-seq" (format "%s" seq)
        !          1136:                                   "-add")
        !          1137:                             msgs))))
        !          1138:    seq-list))
        !          1139: 
        !          1140: 
        !          1141: 
        !          1142: ;;; Issue commands to mh.
        !          1143: 
        !          1144: (defun mh-exec-cmd (command &rest args)
        !          1145:   "Execute MH command COMMAND with ARGS.  Any output is shown to the user."
        !          1146:   (save-excursion
        !          1147:     (pop-to-buffer " *mh-temp*")
        !          1148:     (erase-buffer)
        !          1149:     (set-mark (dot))
        !          1150:     (apply 'call-process (nconc (list (concat mh-progs command) nil t nil)
        !          1151:                                (mh-list-to-string args)))
        !          1152:     (when (> (buffer-size) 0)
        !          1153:        (message "%s" (region-to-string))
        !          1154:        (sit-for 5))))
        !          1155: 
        !          1156: 
        !          1157: (defun mh-exec-cmd-quiet (command &rest args)
        !          1158:   "Execute MH command COMMAND with ARGS.  Output is collected, but not shown
        !          1159:  to the user."
        !          1160:   (pop-to-buffer " *mh-temp*")
        !          1161:   (erase-buffer)
        !          1162:   (set-mark (dot))
        !          1163:   (apply 'call-process (nconc (list (concat mh-progs command) nil t nil)
        !          1164:                              (mh-list-to-string args))))
        !          1165: 
        !          1166: 
        !          1167: (defun mh-exec-cmd-output (command &rest args)
        !          1168:   "Execute MH command COMMAND with ARGS putting the output into the current
        !          1169: buffer."
        !          1170:   (apply 'call-process (nconc (list (concat mh-progs command) nil t nil)
        !          1171:                              (mh-list-to-string args))))
        !          1172: 
        !          1173: 
        !          1174: (defun mh-exec-cmd-no-wait (command &rest args)
        !          1175:   "Execute MH command COMMAND with ARGS and do not wait until it finishes."
        !          1176:   (apply 'call-process (nconc (list (concat mh-progs command) nil 0 nil)
        !          1177:                              (mh-list-to-string args))))
        !          1178: 
        !          1179: 
        !          1180: (defun mh-list-to-string (l)
        !          1181:   "Flattens the list L and makes every element a string."
        !          1182:   (let ((new-list nil))
        !          1183:     (while l
        !          1184:       (cond ((symbolp (car l)) (push (format "%s" (car l)) new-list))
        !          1185:            ((numberp (car l)) (push (format "%d" (car l)) new-list))
        !          1186:            ((string= (car l) ""))
        !          1187:            ((stringp (car l)) (push (car l) new-list))
        !          1188:            ((null (car l)))
        !          1189:            ((listp (car l)) (setq new-list
        !          1190:                                   (nconc (mh-list-to-string (car l))
        !          1191:                                          new-list)))
        !          1192:            (t (error "Bad argument %s" (car l))))
        !          1193:       (setq l (cdr l)))
        !          1194:     (nreverse new-list)))
        !          1195: 
        !          1196: 
        !          1197: 
        !          1198: ;;; Commands to annotate a message.
        !          1199: 
        !          1200: (defun mh-annotate (note &rest args)
        !          1201:   "Mark the current message with the character NOTE and annotate the message
        !          1202: with ARGS."
        !          1203:   (apply 'mh-exec-cmd-no-wait (cons "anno" args))
        !          1204:   (mh-notate note 5))
        !          1205: 
        !          1206: 
        !          1207: (defun mh-notate (notation offset)
        !          1208:   "Marks the current message with the character NOTATION at position OFFSET."
        !          1209:   (save-excursion
        !          1210:     (pop-to-buffer mh-folder-buffer)
        !          1211:     (let ((buffer-read-only nil))
        !          1212:       (beginning-of-line)
        !          1213:       (goto-char (+ (dot) offset))
        !          1214:       (delete-char 1)
        !          1215:       (insert notation)
        !          1216:       (beginning-of-line))))
        !          1217: 
        !          1218: 
        !          1219: 
        !          1220: ;;; User prompting commands.
        !          1221: 
        !          1222: (defun mh-get-folder-name (prompt default can-create)
        !          1223:   "Prompt for a folder name with PROMPT.  DEFAULT is the default folder.
        !          1224: If the CAN-CREATE flag is t, then the folder can be made if it does not exist."
        !          1225:   (let ((exists nil)
        !          1226:        (prompt (concat prompt " folder"
        !          1227:                        (if (string= "" default)
        !          1228:                            "? "
        !          1229:                            (concat " [" default "]? "))))
        !          1230:        (file-name))
        !          1231:     (let ((name))
        !          1232:       (while (not exists)
        !          1233:        (setq name (read-string prompt))
        !          1234:        (if (string= name "")
        !          1235:            (setq name default))
        !          1236:        (if (string= (substring name 0 1) "+")
        !          1237:            (setq name (substring name 1)))
        !          1238:        (if (not (string= (substring name 0 1) "/"))
        !          1239:            (setq file-name (concat mh-user-path name))
        !          1240:            (setq file-name name))
        !          1241:        (setq exists (file-exists-p file-name))
        !          1242:        (if (not exists)
        !          1243:            (cond ((and can-create
        !          1244:                        (y-or-n-p (concat "Folder +" name
        !          1245:                                          " does not exist. Create it? ")))
        !          1246:                   (message "Creating %s" name)
        !          1247:                   (call-process "mkdir" nil nil nil file-name)
        !          1248:                   (message "Creating %s...done" name)
        !          1249:                   (setq exists t))
        !          1250: 
        !          1251:                  (can-create
        !          1252:                   (error ""))
        !          1253: 
        !          1254:                  (t
        !          1255:                   (setq prompt (concat "Sorry, no such folder as `" name
        !          1256:                                        "'  Folder name? "))))))
        !          1257:       name)))
        !          1258: 
        !          1259: 
        !          1260: (defun mh-get-response (prompt possibilities help)
        !          1261:   "Prints PROMPT, reads a character, and checks it against the list
        !          1262: of POSSIBILITIES. Returns the character if it is legal.  The HELP string
        !          1263: is displayed if the character is not legal."
        !          1264:   (let ((ok nil)
        !          1265:        (first-char))
        !          1266:     (while (not ok)
        !          1267:       (let ((pos possibilities))
        !          1268:        (message prompt)
        !          1269:        (setq first-char (read-char))
        !          1270:        (while (and (not ok) pos)
        !          1271:          (if (equal first-char (car pos))
        !          1272:              (setq ok t))
        !          1273:          (setq pos (cdr pos)))
        !          1274: 
        !          1275:        (cond ((equal first-char ??)
        !          1276:               (message help)
        !          1277:               (sit-for 5))
        !          1278:              ((not ok)
        !          1279:               (message "Illegal response '%c'" first-char)
        !          1280:               (sit-for 5)))))
        !          1281:     first-char))
        !          1282: 
        !          1283: 
        !          1284: 
        !          1285: ;;; Misc. functions.
        !          1286: 
        !          1287: (defun mh-get-msg-num (error-if-no-message)
        !          1288:   "Returns the message number of the current message.  If the argument
        !          1289: ERROR-IF-NO-MESSAGE is t, then complain if the cursor is not pointing to a
        !          1290: message."
        !          1291:   (save-excursion
        !          1292:     (switch-to-buffer mh-folder-buffer)
        !          1293:     (beginning-of-line)
        !          1294:     (cond ((looking-at "^\+?\\([0-9]+\\)")
        !          1295:           (region-around-match 1)
        !          1296:           (string-to-int (region-to-string)))
        !          1297:          ((looking-at "^\+?[0-9a-z]?[ ]+\\([0-9]+\\)")
        !          1298:           (region-around-match 1)
        !          1299:           (string-to-int (region-to-string)))
        !          1300:          (error-if-no-message
        !          1301:           (error "Cursor not pointing to message"))
        !          1302:          (t nil))))
        !          1303: 
        !          1304: 
        !          1305: (defun mh-msg-filename ()
        !          1306:   "Returns a string containing the pathname for a message."
        !          1307:   (save-excursion
        !          1308:     (switch-to-buffer mh-folder-buffer)
        !          1309:     (concat mh-folder-filename (mh-get-msg-num t))))
        !          1310: 
        !          1311: 
        !          1312: (defun mh-msg-filenames (msgs folder)
        !          1313:   "Returns an arglist for ls specifying the messages MSGS in folder FOLDER."
        !          1314:   (if msgs
        !          1315:       (let ((args (concat folder "{")))
        !          1316:        (while (cdr msgs)
        !          1317:          (setq args (concat args (car msgs) ","))
        !          1318:          (setq msgs (cdr msgs)))
        !          1319:        (concat args (car msgs) "}"))
        !          1320:       ""))
        !          1321: 
        !          1322: 
        !          1323: (defun mh-find-path ()
        !          1324:   "Set mh_path from  ~/.mh_profile."
        !          1325:   (save-window-excursion
        !          1326:     (if (not (file-exists-p "~/.mh_profile"))
        !          1327:        (error "Can find .mh_profile file."))
        !          1328:     (switch-to-buffer " *mh_profile*")
        !          1329:     (erase-buffer)
        !          1330:     (insert-file-contents "~/.mh_profile")
        !          1331:     (if (string= (setq mh-user-path (mh-get-field "Path:")) "")
        !          1332:        (setq mh-user-path "Mail/")
        !          1333:        (setq mh-user-path (concat mh-user-path "/")))
        !          1334:     (if (not (string= (substring mh-user-path 0 1) "/"))
        !          1335:        (setq mh-user-path (concat (getenv "HOME") "/" mh-user-path)))))
        !          1336: 
        !          1337: 
        !          1338: (defun mh-get-cur-msg (folder)
        !          1339:   "Returns the cur message from FOLDER."
        !          1340:   (let ((seq-filename (concat folder ".mh_sequences")))
        !          1341:     (save-window-excursion
        !          1342:       (cond ((file-exists-p seq-filename)
        !          1343:             (switch-to-buffer " *mh_sequences*")
        !          1344:             (erase-buffer)
        !          1345:             (insert-file-contents seq-filename)
        !          1346:             (string-to-int (mh-get-field "cur: ")))
        !          1347:            (t 0)))))
        !          1348: 
        !          1349: 
        !          1350: (defun mh-get-field (field)
        !          1351:   "Get the value of field FIELD in the current buffer."
        !          1352:   (let ((case-fold-search t))
        !          1353:     (goto-char (dot-min))
        !          1354:     (cond ((not (search-forward field nil t)) "")
        !          1355:          (t
        !          1356:           (re-search-forward "[\t ]*\\([a-zA-z0-9/].*\\)$" nil t)
        !          1357:           (region-around-match 1)
        !          1358:           (let ((field (region-to-string)))
        !          1359:             (set-mark (dot))
        !          1360:             (forward-line)
        !          1361:             (while (looking-at "[ \t]") (forward-line 1))
        !          1362:             (backward-char 1)
        !          1363:             (concat field (region-to-string)))))))
        !          1364: 
        !          1365: 
        !          1366: (defun mh-insert-fields (&rest name-values)
        !          1367:   "Insert the NAME-VALUE pairs in the current buffer."
        !          1368:   (let ((case-fold-search t))
        !          1369:     (while name-values
        !          1370:       (let ((field-name (car name-values))
        !          1371:            (value (cadr name-values)))
        !          1372:        (goto-char (dot-min))
        !          1373:        (cond ((not (search-forward (concat "\n" field-name) nil t))
        !          1374:               (search-forward "---")
        !          1375:               (beginning-of-line)
        !          1376:               (insert field-name " " value "\n"))
        !          1377:              (t
        !          1378:               (end-of-line)
        !          1379:               (insert " " value)))
        !          1380:        (setq name-values (cddr name-values))))))
        !          1381: 
        !          1382: 
        !          1383: 
        !          1384: ;;; Build the keymap for mh:
        !          1385: 
        !          1386: (defvar mh-keymap (make-sparse-keymap))
        !          1387: 
        !          1388: (define-key mh-keymap "?" 'mh-summary)
        !          1389: (define-key mh-keymap  "c" 'mh-copy-msg)
        !          1390: (define-key mh-keymap  "d" 'mh-delete-msg)
        !          1391: (define-key mh-keymap  "^" 'mh-move-msg)
        !          1392: (define-key mh-keymap  "!" 'mh-re-move)
        !          1393: (define-key mh-keymap  "u" 'mh-undo)
        !          1394: (define-key mh-keymap "l" 'mh-print-msg)
        !          1395: (define-key mh-keymap  "p" 'mh-previous-line)
        !          1396: (define-key mh-keymap  "n" 'mh-next-line)
        !          1397: (define-key mh-keymap  "g" 'mh-goto)
        !          1398: (define-key mh-keymap  " " 'scroll-other-window)
        !          1399: (define-key mh-keymap  "\e " 'mh-page-digest)
        !          1400: (define-key mh-keymap  "\^H" 'mh-previous-page)
        !          1401: (define-key mh-keymap  "t" 'mh-toggle-summarize)
        !          1402: (define-key mh-keymap  "." 'mh-show)
        !          1403: (define-key mh-keymap  "a" 'mh-answer)
        !          1404: (define-key mh-keymap  "f" 'mh-forward)
        !          1405: (define-key mh-keymap  "r" 'mh-redistribute)
        !          1406: (define-key mh-keymap  "s" 'mh-send)
        !          1407: (define-key mh-keymap  "\^X\^C" 'mh-quit)
        !          1408: (define-key mh-keymap  "q" 'mh-quit)
        !          1409: (define-key mh-keymap  "e" 'mh-exit)
        !          1410: (define-key mh-keymap "0" 'mh-indicate-seq)
        !          1411: (define-key mh-keymap "1" 'mh-indicate-seq)
        !          1412: (define-key mh-keymap "2" 'mh-indicate-seq)
        !          1413: (define-key mh-keymap "3" 'mh-indicate-seq)
        !          1414: (define-key mh-keymap "4" 'mh-indicate-seq)
        !          1415: (define-key mh-keymap "5" 'mh-indicate-seq)
        !          1416: (define-key mh-keymap "6" 'mh-indicate-seq)
        !          1417: (define-key mh-keymap "7" 'mh-indicate-seq)
        !          1418: (define-key mh-keymap "8" 'mh-indicate-seq)
        !          1419: (define-key mh-keymap "9" 'mh-indicate-seq)
        !          1420: (define-key mh-keymap "\ef" 'mh-visit-folder)
        !          1421: (define-key mh-keymap "\ei" 'mh-inc-folder)
        !          1422: (define-key mh-keymap "\ec" 'mh-close-folder)
        !          1423: (define-key mh-keymap "\ek" 'mh-kill-folder)
        !          1424: (define-key mh-keymap "\el" 'mh-list-folders)
        !          1425: (define-key mh-keymap "\ep" 'mh-renumber-folder)
        !          1426: (define-key mh-keymap "\er" 'mh-rescan-folder)
        !          1427: (define-key mh-keymap "\es" 'mh-search-folder)

unix.superglobalmegacorp.com

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