[mew-int 00564] Re: Questions about dealing with CVS commit mail messages via Mew

sen_ml at example.com sen_ml at example.com
Sun Nov 11 00:59:12 JST 2001


I've been thinking more about how to have an external command operate
on multiple messages in Mew.  Actually, I've been studying Mutt's
interface for piping multiple messages to an external command as an
advantage of emulating Mutt's interface is that programs written to
conform to its piping interface can then be used without modification
with Mew.

AFAICT, there appear to be two ways it can happen in Mutt:

  1) Multiple messages are joined together with a prespecified
     separator and the result is piped to the external command.

  2) The external command is invoked as many times as there are
     messages to operate on, and each message is piped to a different
     invocation [1].

I wrote some code to emulate the first way.  It is implemented by a
function named `mew-summary-pipe-msgs'.  I'm thinking of also
implementing the second way.  

Below are my current versions of `mew-summary-pipe-message' and
`mew-summary-pipe-msgs'.

;; 3 functions to provide completion for shell command in
;; `mew-summary-pipe-message'

;; XXX: name of this command leaves a bit to be desired -- I'd like
;;      a shorter name that uses a verb as the word immediately following
;;      `mew'
;;
;; This is a helper function for `mew-complete-command-name'
(defun mew-command-name-completions-table (string)
  "Make table of command names that are completion candidates for STRING.
Returns table (alist) of command names by searching `exec-path'."
  (let ((paths exec-path)
	table)
    (while paths
      (if (file-name-completion string (car paths)) ; can complete in cur dir?
	  (setq table
		(append table
			(mapcar
			 (lambda (x)
			   (cons x ""))
			 (file-name-all-completions string (car paths))))))
      (setq paths (cdr paths)))
    table))

;; XXX: need to disable the use of the space key for completion purposes?  
;;      or users learn to deal w/ using C-q before typing a space...
(defun mew-complete-command-name (prompt string default)
  "Complete command name by searching `exec-path'.

PROMPT is the minibuffer prompt.  STRING is used as the basis for completion.
DEFAULT is a default string to insert after the prompt."
  (completing-read prompt 
		   (mew-command-name-completions-table string) 
		   nil 
		   nil 
		   nil 
		   nil 
		   default))

(defun mew-summary-pipe-message (prefix command)
  "Send current message via pipe.
This function supports completion of the shell command."
  (interactive 
   (list current-prefix-arg 
	 (mew-complete-command-name "Shell command on message: " 
				    "" 
				    mew-last-shell-command)))
  (mew-summary-display 'redisplay)
  (when (y-or-n-p "Send this message to pipe? ")
    (save-excursion
      (set-buffer (mew-buffer-message))
      (save-restriction
	(widen)
	(if (string= command "") (setq command mew-last-shell-command))
	(goto-char (point-min)) ; perhaps this line won't be necessary
	(if prefix (search-forward "\n\n"))
	(let ((max-mini-window-height 1))
	  (shell-command-on-region (point) (point-max) command nil))
	(setq mew-last-shell-command command)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 2 additional functions for handing multiple messages to a shell command

(defun mew-expand-command-name (name)
  "Expand command NAME to a full-path filename by searching `exec-path'."
  (let ((paths exec-path)
	full-path
	path)
    (while (and paths (not path))
      (setq path (file-name-completion name (car paths)))
      (if path
	  (setq full-path (concat (car paths) "/" name)))
      (setq paths (cdr paths)))
    full-path))

;; XXX: unimplemented
(defvar mew-summary-pipe-split nil
  "")

;; XXX: unimplemented
(defvar mew-summary-pipe-decode nil
  "")

;; XXX: think about whether this should be passed to the command
;;      accepting input on STDIN...
;; XXX: consider supporting `mew-summary-pipe-split': 
;;      if t, start as many processes as messages
;;      if nil, start only one process and feed all messages
;;      to it as one stream (interlace messages with 
;;      `mew-summary-pipe-split')
(defvar mew-summary-pipe-sep 
  (concat "THIS-IS-A-SEPARATOR-" "DO-NOT-USE-THIS-IN-YOUR-MESSAGE")
  "Reserved string to indicate a message boundary.")

(defun mew-summary-pipe-msgs (prefix command)
  "Pipe messages marked with `@' to COMMAND.  

After each message is piped to COMMAND, a special separator string
`mew-summary-pipe-sep' is also sent to mark boundaries between
messages.  N.B. This isn't completely safe as `mew-summary-pipe-sep'
might be contained in a message body."
  (interactive
   (list current-prefix-arg
	 (mew-complete-command-name "Shell command on message: "
				    ""
				    mew-last-shell-command)))
  (let (command-name args pipe-process)
    (if (not (string-match "^[ ]*\\([^ ]+\\)\\([ ]*\\)" command))
	(message "Unable to parse command: %s" command)
      (setq command-name 
	    (match-string 1 command))
      (setq args 
	    (match-string 2 command))
      (mew-summary-multi-msgs
       (if (y-or-n-p (format "Execute %s for these messages? " command))
	   (let* ((buf (generate-new-buffer mew-buffer-prefix))
		  (tmp-buf (generate-new-buffer mew-buffer-prefix))
		  (files FILES))
	     (message "Executing %s..." command)
	     ;; XXX: how are errors caught from `start-process' invocation?
	     (setq pipe-process 
		   (start-process "pipe-process" 
				  buf
				  (mew-expand-command-name command-name)
				  args
;	                          mew-summary-pipe-sep ; XXX: pass this?
				  ))
	     (save-excursion
	       ;; XXX: displaying the process output after everything was done
	       ;;      didn't work in some cases -- would get fragmented 
               ;;      output.  doing `display-buffer' fixed the problem...
	       (display-buffer (process-buffer pipe-process))
	       (set-buffer tmp-buf)
	       (mew-frwlet
		mew-cs-text-for-read mew-cs-text-for-write
		(while files
		  (mew-erase-buffer)
		  (insert-file (car files))
		  (insert mew-summary-pipe-sep)
		  (process-send-region pipe-process (point-min) (point-max))
		  (accept-process-output pipe-process 10)
		  (setq files (cdr files)))
		(mew-erase-buffer)))
	     (mew-remove-buffer tmp-buf)
	     ;; tell the process we're done
	     (process-send-eof pipe-process) ; looks necessary based on test
	     (message "Executing %s...done" command)))))))

[1] It also seems to be the case that each message has the
    prespecified separator appended to it.  I don't know why.



More information about the Mew-int mailing list