Next: , Previous: Process Buffers, Up: Output from Processes

37.9.2 Process Filter Functions

A process filter function is a function that receives the standard output from the associated process. If a process has a filter, then all output from that process is passed to the filter. The process buffer is used directly for output from the process only when there is no filter.

The filter function can only be called when Emacs is waiting for something, because process output arrives only at such times. Emacs waits when reading terminal input (see the function waiting-for-user-input-p), in sit-for and sleep-for (see Waiting), and in accept-process-output (see Accepting Output).

A filter function must accept two arguments: the associated process and a string, which is output just received from it. The function is then free to do whatever it chooses with the output.

Quitting is normally inhibited within a filter function—otherwise, the effect of typing C-g at command level or to quit a user command would be unpredictable. If you want to permit quitting inside a filter function, bind inhibit-quit to nil. In most cases, the right way to do this is with the macro with-local-quit. See Quitting.

If an error happens during execution of a filter function, it is caught automatically, so that it doesn't stop the execution of whatever program was running when the filter function was started. However, if debug-on-error is non-nil, errors are not caught. This makes it possible to use the Lisp debugger to debug the filter function. See Debugger.

Many filter functions sometimes (or always) insert the output in the process's buffer, mimicking the actions of Emacs when there is no filter. Such filter functions need to make sure that they save the current buffer, select the correct buffer (if different) before inserting output, and then restore the original buffer. They should also check whether the buffer is still alive, update the process marker, and in some cases update the value of point. Here is how to do these things:

     (defun ordinary-insertion-filter (proc string)
       (when (buffer-live-p (process-buffer proc))
         (with-current-buffer (process-buffer proc)
           (let ((moving (= (point) (process-mark proc))))
             (save-excursion
               ;; Insert the text, advancing the process marker.
               (goto-char (process-mark proc))
               (insert string)
               (set-marker (process-mark proc) (point)))
             (if moving (goto-char (process-mark proc)))))))

To make the filter force the process buffer to be visible whenever new text arrives, you could insert a line like the following just before the with-current-buffer construct:

     (display-buffer (process-buffer proc))

To force point to the end of the new output, no matter where it was previously, eliminate the variable moving and call goto-char unconditionally.

Note that Emacs automatically saves and restores the match data while executing filter functions. See Match Data.

The output to the filter may come in chunks of any size. A program that produces the same output twice in a row may send it as one batch of 200 characters one time, and five batches of 40 characters the next. If the filter looks for certain text strings in the subprocess output, make sure to handle the case where one of these strings is split across two or more batches of output; one way to do this is to insert the received text into a temporary buffer, which can then be searched.

— Function: set-process-filter process filter

This function gives process the filter function filter. If filter is nil, it gives the process no filter.

— Function: process-filter process

This function returns the filter function of process, or nil if it has none.

Here is an example of the use of a filter function:

     (defun keep-output (process output)
        (setq kept (cons output kept)))
          ⇒ keep-output
     (setq kept nil)
          ⇒ nil
     (set-process-filter (get-process "shell") 'keep-output)
          ⇒ keep-output
     (process-send-string "shell" "ls ~/other\n")
          ⇒ nil
     kept
          ⇒ ("lewis@slug:$ "
     "FINAL-W87-SHORT.MSS    backup.otl              kolstad.mss~
     address.txt             backup.psf              kolstad.psf
     backup.bib~             david.mss               resume-Dec-86.mss~
     backup.err              david.psf               resume-Dec.psf
     backup.mss              dland                   syllabus.mss
     "
     "#backups.mss#          backup.mss~             kolstad.mss
     ")