2

I'm using the following elisp snippet in a dedicated buffer to track progress of a wget download.

(with-current-buffer buf
  (setq proc
    (apply 'start-process "my-download" buf
      (list "wget" (if resume "-c" "") "-O" save-path url)))
  ....
  )

This is great in that it gives me wget's progress, but the problem is that the buffer isn't interpreting a control character the way I want. wget sends a C-m after every progress update, about once a second, because it is expecting to be connected to a vt100 and wants the cursor to return to the beginning of the current line, this so the next progress message will over-write the former one. The emacs buffer just inserts a literal C-m and accumulates lines.

When I run wget directly from an emacs ansi-term buffer, the progress messages appear fine, so I tried looking in the code, but couldn't figure out what specifically was being done for C-m interpretation. I did consider using an ansi-term buffer for my program, but it wouldn't be appropriate because I'll be inserting other information and text there that aren't terminal commands. What I would like is a fundamental-mode buffer modified to react to C-m by performing a (forward-line 0).

  • 2
    It's handled by term-emulate-terminal. Keep in mind that C-m is represented as \r, the carriage return. The function is used as process filter, so at the very least you'll want to write your own and apply it to the process. – wasamasa Mar 11 at 6:48
  • @wasamasa : It worked. Thanks. Do you want to post it as an answer, or should I post the snippet that I used and try to answer my own question? – user1404316 Mar 12 at 1:38
  • Please self-answer and accept it. A comment is one thing, a fully-fledged answer taking all quirks involved into account is another. – wasamasa Mar 12 at 7:44
1

As user @wasamasa suggested in the comments, the solution is to apply a "process filter" function to the buffer that is receiving the STDOUT and STDERR output from the process. An example walk-through for the procedure would be this:

1) Create a list to maintain your processes and their related buffers:

(setq your-proc-list nil)

2) Start with a generic template. The snippet below creates a buffer to receive the process output, starts a process that calls a shell-command, and associates the process with a sentinel function and a filter function.

(let* ((proc-buf  (generate-new-buffer "PROC-BUF-NAME")
       proc-id)
  (with-current-buffer proc-buf
    (setq proc-id
      (apply 'start-process "PROC-NAME" proc-buf
        (list "SHELL-COMMAND"  "SHELL-ARG-1" "SHELL-ARG-2"))))
    (push (cons proc-id proc-buf) your-proc-list)
    (set-process-filter proc-id 'PROC-FILTER-FUNCTION)
    (set-process-sentinel proc-id 'PROC-SENTINEL-FUNCTION)))

The sentinel function is used to monitor any change of state or signals from the process, and to deal with them. The process filter function is the subject of this Q&A. Whenever the process produces output, emacs sends it to the process buffer via the list of filter functions associated with that buffer, basically a pipe. In my question, I wanted to re-interpret C-m. Here's how I did it.

3) The process filter function (emacs documentation):

(defun process-filter-function (proc input-string)
  (let ((proc-buf (process-buffer proc)))
   (when (buffer-live-p proc-buf)
     (with-current-buffer proc-buf
       (let ((inhibit-read-only t))
        (save-excursion
          (goto-char (point-max))
          (if (not (string= "\r" (substring input-string 0 1)))
            (insert input-string)
           (kill-line 0)
           (insert (substring input-string 1)))))))))

Looking at the snippet, you can see that process filter functions take two arguments, sent by emacs, the process ID, and the new input. Emacs also provides handy functions to remind you of the buffer associated with the process, and whether that buffer still exists. For my use-case, I know that any C-m characters will be at the beginning of an input string, so I check for that and when it's found, emulate an actual carriage return with a kill-line.

Note that my solution leaves out some options in the template offered by the referenced emacs documentation, because I don't need those features.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.